tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ObfuscatingVFS.cpp (24846B)


      1 /*
      2 ** 2020-04-20
      3 **
      4 ** The author disclaims copyright to this source code.  In place of
      5 ** a legal notice, here is a blessing:
      6 **
      7 **    May you do good and not evil.
      8 **    May you find forgiveness for yourself and forgive others.
      9 **    May you share freely, never taking more than you give.
     10 **
     11 ******************************************************************************
     12 **
     13 ** This file implements a VFS shim that obfuscates database content
     14 ** written to disk by applying a CipherStrategy.
     15 **
     16 ** COMPILING
     17 **
     18 ** This extension requires SQLite 3.32.0 or later.
     19 **
     20 **
     21 ** LOADING
     22 **
     23 ** Initialize it using a single API call as follows:
     24 **
     25 **     sqlite3_obfsvfs_init();
     26 **
     27 ** Obfsvfs is a VFS Shim. When loaded, "obfsvfs" becomes the new
     28 ** default VFS and it uses the prior default VFS as the next VFS
     29 ** down in the stack.  This is normally what you want.  However, it
     30 ** complex situations where multiple VFS shims are being loaded,
     31 ** it might be important to ensure that obfsvfs is loaded in the
     32 ** correct order so that it sequences itself into the default VFS
     33 ** Shim stack in the right order.
     34 **
     35 ** USING
     36 **
     37 ** Open database connections using the sqlite3_open_v2() with
     38 ** the SQLITE_OPEN_URI flag and using a URI filename that includes
     39 ** the query parameter "key=XXXXXXXXXXX..." where the XXXX... consists
     40 ** of 64 hexadecimal digits (32 bytes of content).
     41 **
     42 ** Create a new encrypted database by opening a file that does not
     43 ** yet exist using the key= query parameter.
     44 **
     45 ** LIMITATIONS:
     46 **
     47 **    *   An obfuscated database must be created as such.  There is
     48 **        no way to convert an existing database file into an
     49 **        obfuscated database file other than to run ".dump" on the
     50 **        older database and reimport the SQL text into a new
     51 **        obfuscated database.
     52 **
     53 **    *   There is no way to change the key value, other than to
     54 **        ".dump" and restore the database
     55 **
     56 **    *   The database page size must be exactly 8192 bytes.  No other
     57 **        database page sizes are currently supported.
     58 **
     59 **    *   Memory-mapped I/O does not work for obfuscated databases.
     60 **        If you think about it, memory-mapped I/O doesn't make any
     61 **        sense for obfuscated databases since you have to make a
     62 **        copy of the content to deobfuscate anyhow - you might as
     63 **        well use normal read()/write().
     64 **
     65 **    *   Only the main database, the rollback journal, and WAL file
     66 **        are obfuscated.  Other temporary files used for things like
     67 **        SAVEPOINTs or as part of a large external sort remain
     68 **        unobfuscated.
     69 **
     70 **    *   Requires SQLite 3.32.0 or later.
     71 */
     72 #include "ObfuscatingVFS.h"
     73 
     74 #include <string.h>
     75 #include <ctype.h>
     76 #include <stdio.h> /* For debugging only */
     77 
     78 #include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
     79 #include "mozilla/ScopeExit.h"
     80 #include "nsPrintfCString.h"
     81 #include "QuotaVFS.h"
     82 #include "sqlite3.h"
     83 
     84 /*
     85 ** Forward declaration of objects used by this utility
     86 */
     87 using ObfsVfs = sqlite3_vfs;
     88 
     89 /*
     90 ** Useful datatype abbreviations
     91 */
     92 #if !defined(SQLITE_CORE)
     93 using u8 = unsigned char;
     94 #endif
     95 
     96 /* Access to a lower-level VFS that (might) implement dynamic loading,
     97 ** access to randomness, etc.
     98 */
     99 #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
    100 #define ORIGFILE(p) ((sqlite3_file*)(((ObfsFile*)(p)) + 1))
    101 
    102 /*
    103 ** Database page size for obfuscated databases
    104 */
    105 #define OBFS_PGSZ 8192
    106 
    107 #define WAL_FRAMEHDRSIZE 24
    108 
    109 using namespace mozilla;
    110 using namespace mozilla::dom::quota;
    111 
    112 /* An open file */
    113 struct ObfsFile {
    114  sqlite3_file base;  /* IO methods */
    115  const char* zFName; /* Original name of the file */
    116  bool inCkpt;        /* Currently doing a checkpoint */
    117  ObfsFile* pPartner; /* Ptr from WAL to main-db, or from main-db to WAL */
    118  void* pTemp;        /* Temporary storage for encoded pages */
    119  IPCStreamCipherStrategy*
    120      encryptCipherStrategy; /* CipherStrategy for encryption */
    121  IPCStreamCipherStrategy*
    122      decryptCipherStrategy; /* CipherStrategy for decryption */
    123 };
    124 
    125 /*
    126 ** Methods for ObfsFile
    127 */
    128 static int obfsClose(sqlite3_file*);
    129 static int obfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
    130 static int obfsWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
    131 static int obfsTruncate(sqlite3_file*, sqlite3_int64 size);
    132 static int obfsSync(sqlite3_file*, int flags);
    133 static int obfsFileSize(sqlite3_file*, sqlite3_int64* pSize);
    134 static int obfsLock(sqlite3_file*, int);
    135 static int obfsUnlock(sqlite3_file*, int);
    136 static int obfsCheckReservedLock(sqlite3_file*, int* pResOut);
    137 static int obfsFileControl(sqlite3_file*, int op, void* pArg);
    138 static int obfsSectorSize(sqlite3_file*);
    139 static int obfsDeviceCharacteristics(sqlite3_file*);
    140 static int obfsShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
    141 static int obfsShmLock(sqlite3_file*, int offset, int n, int flags);
    142 static void obfsShmBarrier(sqlite3_file*);
    143 static int obfsShmUnmap(sqlite3_file*, int deleteFlag);
    144 static int obfsFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void** pp);
    145 static int obfsUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void* p);
    146 
    147 /*
    148 ** Methods for ObfsVfs
    149 */
    150 static int obfsOpen(sqlite3_vfs*, const char*, sqlite3_file*, int, int*);
    151 static int obfsDelete(sqlite3_vfs*, const char* zPath, int syncDir);
    152 static int obfsAccess(sqlite3_vfs*, const char* zPath, int flags, int*);
    153 static int obfsFullPathname(sqlite3_vfs*, const char* zPath, int, char* zOut);
    154 static void* obfsDlOpen(sqlite3_vfs*, const char* zPath);
    155 static void obfsDlError(sqlite3_vfs*, int nByte, char* zErrMsg);
    156 static void (*obfsDlSym(sqlite3_vfs* pVfs, void* p, const char* zSym))(void);
    157 static void obfsDlClose(sqlite3_vfs*, void*);
    158 static int obfsRandomness(sqlite3_vfs*, int nByte, char* zBufOut);
    159 static int obfsSleep(sqlite3_vfs*, int nMicroseconds);
    160 static int obfsCurrentTime(sqlite3_vfs*, double*);
    161 static int obfsGetLastError(sqlite3_vfs*, int, char*);
    162 static int obfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
    163 static int obfsSetSystemCall(sqlite3_vfs*, const char*, sqlite3_syscall_ptr);
    164 static sqlite3_syscall_ptr obfsGetSystemCall(sqlite3_vfs*, const char* z);
    165 static const char* obfsNextSystemCall(sqlite3_vfs*, const char* zName);
    166 
    167 static const sqlite3_io_methods obfs_io_methods = {
    168    3,                         /* iVersion */
    169    obfsClose,                 /* xClose */
    170    obfsRead,                  /* xRead */
    171    obfsWrite,                 /* xWrite */
    172    obfsTruncate,              /* xTruncate */
    173    obfsSync,                  /* xSync */
    174    obfsFileSize,              /* xFileSize */
    175    obfsLock,                  /* xLock */
    176    obfsUnlock,                /* xUnlock */
    177    obfsCheckReservedLock,     /* xCheckReservedLock */
    178    obfsFileControl,           /* xFileControl */
    179    obfsSectorSize,            /* xSectorSize */
    180    obfsDeviceCharacteristics, /* xDeviceCharacteristics */
    181    obfsShmMap,                /* xShmMap */
    182    obfsShmLock,               /* xShmLock */
    183    obfsShmBarrier,            /* xShmBarrier */
    184    obfsShmUnmap,              /* xShmUnmap */
    185    obfsFetch,                 /* xFetch */
    186    obfsUnfetch                /* xUnfetch */
    187 };
    188 
    189 static constexpr int kKeyBytes = 32;
    190 static constexpr int kIvBytes = IPCStreamCipherStrategy::BlockPrefixLength;
    191 static constexpr int kClearTextPrefixBytesOnFirstPage = 32;
    192 static constexpr int kReservedBytes = 32;
    193 static constexpr int kBasicBlockSize = IPCStreamCipherStrategy::BasicBlockSize;
    194 static_assert(kClearTextPrefixBytesOnFirstPage % kBasicBlockSize == 0);
    195 static_assert(kReservedBytes % kBasicBlockSize == 0);
    196 
    197 /* Obfuscate a page using p->encryptCipherStrategy.
    198 **
    199 ** A new random nonce is created and stored in the last 32 bytes
    200 ** of the page.  All other bytes of the page are obfuscasted using the
    201 ** CipherStrategy.  Except, for page-1 (including the SQLite
    202 ** database header) the first 32 bytes are not obfuscated
    203 **
    204 ** Return a pointer to the obfuscated content, which is held in the
    205 ** p->pTemp buffer.  Or return a NULL pointer if something goes wrong.
    206 ** Errors are reported using NS_WARNING().
    207 */
    208 static void* obfsEncode(ObfsFile* p, /* File containing page to be obfuscated */
    209                        u8* a,       /* database page to be obfuscated */
    210                        int nByte /* Bytes of content in a[]. Must be a multiple
    211                                     of kBasicBlockSize. */
    212 ) {
    213  u8 aIv[kIvBytes];
    214  u8* pOut;
    215  int i;
    216 
    217  static_assert((kIvBytes & (kIvBytes - 1)) == 0);
    218  sqlite3_randomness(kIvBytes, aIv);
    219  pOut = (u8*)p->pTemp;
    220  if (pOut == nullptr) {
    221    pOut = static_cast<u8*>(sqlite3_malloc64(nByte));
    222    if (pOut == nullptr) {
    223      NS_WARNING(nsPrintfCString("unable to allocate a buffer in which to"
    224                                 " write obfuscated database content for %s",
    225                                 p->zFName)
    226                     .get());
    227      return nullptr;
    228    }
    229    p->pTemp = pOut;
    230  }
    231  if (memcmp(a, "SQLite format 3", 16) == 0) {
    232    i = kClearTextPrefixBytesOnFirstPage;
    233    if (a[20] != kReservedBytes) {
    234      NS_WARNING(nsPrintfCString("obfuscated database must have reserved-bytes"
    235                                 " set to %d",
    236                                 kReservedBytes)
    237                     .get());
    238      return nullptr;
    239    }
    240    memcpy(pOut, a, kClearTextPrefixBytesOnFirstPage);
    241  } else {
    242    i = 0;
    243  }
    244  const int payloadLength = nByte - kReservedBytes - i;
    245  MOZ_ASSERT(payloadLength > 0);
    246  // XXX I guess this can be done in-place as well, then we don't need the
    247  // temporary page at all, I guess?
    248  p->encryptCipherStrategy->Cipher(
    249      Span{aIv}, Span{a + i, static_cast<unsigned>(payloadLength)},
    250      Span{pOut + i, static_cast<unsigned>(payloadLength)});
    251  memcpy(pOut + nByte - kReservedBytes, aIv, kIvBytes);
    252 
    253  return pOut;
    254 }
    255 
    256 /* De-obfuscate a page using p->decryptCipherStrategy.
    257 **
    258 ** The deobfuscation is done in-place.
    259 **
    260 ** For pages that begin with the SQLite header text, the first
    261 ** 32 bytes are not deobfuscated.
    262 */
    263 static void obfsDecode(ObfsFile* p, /* File containing page to be obfuscated */
    264                       u8* a,       /* database page to be obfuscated */
    265                       int nByte /* Bytes of content in a[]. Must be a multiple
    266                                    of kBasicBlockSize. */
    267 ) {
    268  int i;
    269 
    270  if (memcmp(a, "SQLite format 3", 16) == 0) {
    271    i = kClearTextPrefixBytesOnFirstPage;
    272  } else {
    273    i = 0;
    274  }
    275  const int payloadLength = nByte - kReservedBytes - i;
    276  MOZ_ASSERT(payloadLength > 0);
    277  p->decryptCipherStrategy->Cipher(
    278      Span{a + nByte - kReservedBytes, kIvBytes},
    279      Span{a + i, static_cast<unsigned>(payloadLength)},
    280      Span{a + i, static_cast<unsigned>(payloadLength)});
    281  memset(a + nByte - kReservedBytes, 0, kIvBytes);
    282 }
    283 
    284 /*
    285 ** Close an obfsucated file.
    286 */
    287 static int obfsClose(sqlite3_file* pFile) {
    288  ObfsFile* p = (ObfsFile*)pFile;
    289  if (p->pPartner) {
    290    MOZ_ASSERT(p->pPartner->pPartner == p);
    291    p->pPartner->pPartner = nullptr;
    292    p->pPartner = nullptr;
    293  }
    294  sqlite3_free(p->pTemp);
    295 
    296  delete p->decryptCipherStrategy;
    297  delete p->encryptCipherStrategy;
    298 
    299  pFile = ORIGFILE(pFile);
    300  return pFile->pMethods->xClose(pFile);
    301 }
    302 
    303 /*
    304 ** Read data from an obfuscated file.
    305 **
    306 ** If the file is less than one full page in length, then return
    307 ** a substitute "prototype" page-1.  This prototype page one
    308 ** specifies a database in WAL mode with an 8192-byte page size
    309 ** and a 32-byte reserved-bytes value.  Those settings are necessary
    310 ** for obfuscation to function correctly.
    311 */
    312 static int obfsRead(sqlite3_file* pFile, void* zBuf, int iAmt,
    313                    sqlite_int64 iOfst) {
    314  int rc;
    315  ObfsFile* p = (ObfsFile*)pFile;
    316  pFile = ORIGFILE(pFile);
    317  rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
    318  if (rc == SQLITE_OK) {
    319    if ((iAmt == OBFS_PGSZ || iAmt == OBFS_PGSZ + WAL_FRAMEHDRSIZE) &&
    320        !p->inCkpt) {
    321      obfsDecode(p, ((u8*)zBuf) + iAmt - OBFS_PGSZ, OBFS_PGSZ);
    322    }
    323  } else if (rc == SQLITE_IOERR_SHORT_READ && iOfst == 0 && iAmt >= 100) {
    324    static const unsigned char aEmptyDb[] = {
    325        // Offset 0, Size 16, The header string: "SQLite format 3\000"
    326        0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61,
    327        0x74, 0x20, 0x33, 0x00,
    328        // XXX Add description for other fields
    329        0x20, 0x00, 0x02, 0x02, kReservedBytes, 0x40, 0x20, 0x20, 0x00, 0x00,
    330        0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    331        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    332        0x00, 0x00,
    333        // Offset 52, Size 4, The page number of the largest root b-tree page
    334        // when in auto-vacuum or incremental-vacuum modes, or zero otherwise.
    335        0x00, 0x00, 0x00, 0x01};
    336 
    337    memcpy(zBuf, aEmptyDb, sizeof(aEmptyDb));
    338    memset(((u8*)zBuf) + sizeof(aEmptyDb), 0, iAmt - sizeof(aEmptyDb));
    339    rc = SQLITE_OK;
    340  }
    341  return rc;
    342 }
    343 
    344 /*
    345 ** Write data to an obfuscated file or journal.
    346 */
    347 static int obfsWrite(sqlite3_file* pFile, const void* zBuf, int iAmt,
    348                     sqlite_int64 iOfst) {
    349  ObfsFile* p = (ObfsFile*)pFile;
    350  pFile = ORIGFILE(pFile);
    351  if (iAmt == OBFS_PGSZ && !p->inCkpt) {
    352    zBuf = obfsEncode(p, (u8*)zBuf, iAmt);
    353    if (zBuf == nullptr) {
    354      return SQLITE_IOERR;
    355    }
    356  }
    357  return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
    358 }
    359 
    360 /*
    361 ** Truncate an obfuscated file.
    362 */
    363 static int obfsTruncate(sqlite3_file* pFile, sqlite_int64 size) {
    364  pFile = ORIGFILE(pFile);
    365  return pFile->pMethods->xTruncate(pFile, size);
    366 }
    367 
    368 /*
    369 ** Sync an obfuscated file.
    370 */
    371 static int obfsSync(sqlite3_file* pFile, int flags) {
    372  pFile = ORIGFILE(pFile);
    373  return pFile->pMethods->xSync(pFile, flags);
    374 }
    375 
    376 /*
    377 ** Return the current file-size of an obfuscated file.
    378 */
    379 static int obfsFileSize(sqlite3_file* pFile, sqlite_int64* pSize) {
    380  ObfsFile* p = (ObfsFile*)pFile;
    381  pFile = ORIGFILE(p);
    382  return pFile->pMethods->xFileSize(pFile, pSize);
    383 }
    384 
    385 /*
    386 ** Lock an obfuscated file.
    387 */
    388 static int obfsLock(sqlite3_file* pFile, int eLock) {
    389  pFile = ORIGFILE(pFile);
    390  return pFile->pMethods->xLock(pFile, eLock);
    391 }
    392 
    393 /*
    394 ** Unlock an obfuscated file.
    395 */
    396 static int obfsUnlock(sqlite3_file* pFile, int eLock) {
    397  pFile = ORIGFILE(pFile);
    398  return pFile->pMethods->xUnlock(pFile, eLock);
    399 }
    400 
    401 /*
    402 ** Check if another file-handle holds a RESERVED lock on an obfuscated file.
    403 */
    404 static int obfsCheckReservedLock(sqlite3_file* pFile, int* pResOut) {
    405  pFile = ORIGFILE(pFile);
    406  return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
    407 }
    408 
    409 /*
    410 ** File control method. For custom operations on an obfuscated file.
    411 */
    412 static int obfsFileControl(sqlite3_file* pFile, int op, void* pArg) {
    413  int rc;
    414  ObfsFile* p = (ObfsFile*)pFile;
    415  pFile = ORIGFILE(pFile);
    416  if (op == SQLITE_FCNTL_PRAGMA) {
    417    char** azArg = (char**)pArg;
    418    MOZ_ASSERT(azArg[1] != nullptr);
    419    if (azArg[2] != nullptr && sqlite3_stricmp(azArg[1], "page_size") == 0) {
    420      /* Do not allow page size changes on an obfuscated database */
    421      return SQLITE_OK;
    422    }
    423  } else if (op == SQLITE_FCNTL_CKPT_START || op == SQLITE_FCNTL_CKPT_DONE) {
    424    p->inCkpt = op == SQLITE_FCNTL_CKPT_START;
    425    if (p->pPartner) {
    426      p->pPartner->inCkpt = p->inCkpt;
    427    }
    428  }
    429  rc = pFile->pMethods->xFileControl(pFile, op, pArg);
    430  if (rc == SQLITE_OK && op == SQLITE_FCNTL_VFSNAME) {
    431    *(char**)pArg = sqlite3_mprintf("obfs/%z", *(char**)pArg);
    432  }
    433  return rc;
    434 }
    435 
    436 /*
    437 ** Return the sector-size in bytes for an obfuscated file.
    438 */
    439 static int obfsSectorSize(sqlite3_file* pFile) {
    440  pFile = ORIGFILE(pFile);
    441  return pFile->pMethods->xSectorSize(pFile);
    442 }
    443 
    444 /*
    445 ** Return the device characteristic flags supported by an obfuscated file.
    446 */
    447 static int obfsDeviceCharacteristics(sqlite3_file* pFile) {
    448  int dc;
    449  pFile = ORIGFILE(pFile);
    450  dc = pFile->pMethods->xDeviceCharacteristics(pFile);
    451  return dc & ~SQLITE_IOCAP_SUBPAGE_READ; /* All except the
    452                                            SQLITE_IOCAP_SUBPAGE_READ bit */
    453 }
    454 
    455 /* Create a shared memory file mapping */
    456 static int obfsShmMap(sqlite3_file* pFile, int iPg, int pgsz, int bExtend,
    457                      void volatile** pp) {
    458  pFile = ORIGFILE(pFile);
    459  return pFile->pMethods->xShmMap(pFile, iPg, pgsz, bExtend, pp);
    460 }
    461 
    462 /* Perform locking on a shared-memory segment */
    463 static int obfsShmLock(sqlite3_file* pFile, int offset, int n, int flags) {
    464  pFile = ORIGFILE(pFile);
    465  return pFile->pMethods->xShmLock(pFile, offset, n, flags);
    466 }
    467 
    468 /* Memory barrier operation on shared memory */
    469 static void obfsShmBarrier(sqlite3_file* pFile) {
    470  pFile = ORIGFILE(pFile);
    471  pFile->pMethods->xShmBarrier(pFile);
    472 }
    473 
    474 /* Unmap a shared memory segment */
    475 static int obfsShmUnmap(sqlite3_file* pFile, int deleteFlag) {
    476  pFile = ORIGFILE(pFile);
    477  return pFile->pMethods->xShmUnmap(pFile, deleteFlag);
    478 }
    479 
    480 /* Fetch a page of a memory-mapped file */
    481 static int obfsFetch(sqlite3_file* pFile, sqlite3_int64 iOfst, int iAmt,
    482                     void** pp) {
    483  *pp = nullptr;
    484  return SQLITE_OK;
    485 }
    486 
    487 /* Release a memory-mapped page */
    488 static int obfsUnfetch(sqlite3_file* pFile, sqlite3_int64 iOfst, void* pPage) {
    489  pFile = ORIGFILE(pFile);
    490  return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
    491 }
    492 
    493 /*
    494 ** Translate a single byte of Hex into an integer.
    495 ** This routine only works if h really is a valid hexadecimal
    496 ** character:  0..9a..fA..F
    497 */
    498 static u8 obfsHexToInt(int h) {
    499  MOZ_ASSERT((h >= '0' && h <= '9') || (h >= 'a' && h <= 'f') ||
    500             (h >= 'A' && h <= 'F'));
    501 #if 1 /* ASCII */
    502  h += 9 * (1 & (h >> 6));
    503 #else /* EBCDIC */
    504  h += 9 * (1 & ~(h >> 4));
    505 #endif
    506  return (u8)(h & 0xf);
    507 }
    508 
    509 /*
    510 ** Open a new file.
    511 **
    512 ** If the file is an ordinary database file, or a rollback or WAL journal
    513 ** file, and if the key=XXXX parameter exists, then try to open the file
    514 ** as an obfuscated database.  All other open attempts fall through into
    515 ** the lower-level VFS shim.
    516 **
    517 ** If the key=XXXX parameter exists but is not 64-bytes of hex key, then
    518 ** put an error message in NS_WARNING() and return SQLITE_CANTOPEN.
    519 */
    520 static int obfsOpen(sqlite3_vfs* pVfs, const char* zName, sqlite3_file* pFile,
    521                    int flags, int* pOutFlags) {
    522  ObfsFile* p;
    523  sqlite3_file* pSubFile;
    524  sqlite3_vfs* pSubVfs;
    525  int rc, i;
    526  const char* zKey;
    527  u8 aKey[kKeyBytes];
    528  pSubVfs = ORIGVFS(pVfs);
    529  if (flags &
    530      (SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
    531    zKey = sqlite3_uri_parameter(zName, "key");
    532  } else {
    533    zKey = nullptr;
    534  }
    535  if (zKey == nullptr) {
    536    return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
    537  }
    538  for (i = 0;
    539       i < kKeyBytes && isxdigit(zKey[i * 2]) && isxdigit(zKey[i * 2 + 1]);
    540       i++) {
    541    aKey[i] = (obfsHexToInt(zKey[i * 2]) << 4) | obfsHexToInt(zKey[i * 2 + 1]);
    542  }
    543  if (i != kKeyBytes) {
    544    NS_WARNING(
    545        nsPrintfCString("invalid query parameter on %s: key=%s", zName, zKey)
    546            .get());
    547    return SQLITE_CANTOPEN;
    548  }
    549  p = (ObfsFile*)pFile;
    550  memset(p, 0, sizeof(*p));
    551 
    552  auto encryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
    553  auto decryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
    554 
    555  auto resetMethods = MakeScopeExit([pFile] { pFile->pMethods = nullptr; });
    556 
    557  if (NS_WARN_IF(NS_FAILED(encryptCipherStrategy->Init(
    558          CipherMode::Encrypt, Span{aKey, sizeof(aKey)},
    559          IPCStreamCipherStrategy::MakeBlockPrefix())))) {
    560    return SQLITE_ERROR;
    561  }
    562 
    563  if (NS_WARN_IF(NS_FAILED(decryptCipherStrategy->Init(
    564          CipherMode::Decrypt, Span{aKey, sizeof(aKey)})))) {
    565    return SQLITE_ERROR;
    566  }
    567 
    568  pSubFile = ORIGFILE(pFile);
    569  p->base.pMethods = &obfs_io_methods;
    570  rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
    571  if (rc) {
    572    return rc;
    573  }
    574 
    575  resetMethods.release();
    576 
    577  if (flags & (SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
    578    sqlite3_file* pDb = sqlite3_database_file_object(zName);
    579    p->pPartner = (ObfsFile*)pDb;
    580    MOZ_ASSERT(p->pPartner->pPartner == nullptr);
    581    p->pPartner->pPartner = p;
    582  }
    583  p->zFName = zName;
    584 
    585  p->encryptCipherStrategy = encryptCipherStrategy.release();
    586  p->decryptCipherStrategy = decryptCipherStrategy.release();
    587 
    588  return SQLITE_OK;
    589 }
    590 
    591 /*
    592 ** All other VFS methods are pass-thrus.
    593 */
    594 static int obfsDelete(sqlite3_vfs* pVfs, const char* zPath, int syncDir) {
    595  return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, syncDir);
    596 }
    597 static int obfsAccess(sqlite3_vfs* pVfs, const char* zPath, int flags,
    598                      int* pResOut) {
    599  return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
    600 }
    601 static int obfsFullPathname(sqlite3_vfs* pVfs, const char* zPath, int nOut,
    602                            char* zOut) {
    603  return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs), zPath, nOut, zOut);
    604 }
    605 static void* obfsDlOpen(sqlite3_vfs* pVfs, const char* zPath) {
    606  return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
    607 }
    608 static void obfsDlError(sqlite3_vfs* pVfs, int nByte, char* zErrMsg) {
    609  ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
    610 }
    611 static void (*obfsDlSym(sqlite3_vfs* pVfs, void* p, const char* zSym))(void) {
    612  return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
    613 }
    614 static void obfsDlClose(sqlite3_vfs* pVfs, void* pHandle) {
    615  ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
    616 }
    617 static int obfsRandomness(sqlite3_vfs* pVfs, int nByte, char* zBufOut) {
    618  return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
    619 }
    620 static int obfsSleep(sqlite3_vfs* pVfs, int nMicroseconds) {
    621  return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicroseconds);
    622 }
    623 static int obfsCurrentTime(sqlite3_vfs* pVfs, double* pTimeOut) {
    624  return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
    625 }
    626 static int obfsGetLastError(sqlite3_vfs* pVfs, int a, char* b) {
    627  return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
    628 }
    629 static int obfsCurrentTimeInt64(sqlite3_vfs* pVfs, sqlite3_int64* p) {
    630  return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
    631 }
    632 static int obfsSetSystemCall(sqlite3_vfs* pVfs, const char* zName,
    633                             sqlite3_syscall_ptr pCall) {
    634  return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs), zName, pCall);
    635 }
    636 static sqlite3_syscall_ptr obfsGetSystemCall(sqlite3_vfs* pVfs,
    637                                             const char* zName) {
    638  return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs), zName);
    639 }
    640 static const char* obfsNextSystemCall(sqlite3_vfs* pVfs, const char* zName) {
    641  return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
    642 }
    643 
    644 namespace mozilla::storage::obfsvfs {
    645 
    646 const char* GetVFSName() { return "obfsvfs"; }
    647 
    648 UniquePtr<sqlite3_vfs> ConstructVFS(const char* aBaseVFSName) {
    649  MOZ_ASSERT(aBaseVFSName);
    650 
    651  if (sqlite3_vfs_find(GetVFSName()) != nullptr) {
    652    return nullptr;
    653  }
    654  sqlite3_vfs* const pOrig = sqlite3_vfs_find(aBaseVFSName);
    655  if (pOrig == nullptr) {
    656    return nullptr;
    657  }
    658 
    659 #ifdef DEBUG
    660  // If the VFS version is higher than the last known one, you should update
    661  // this VFS adding appropriate methods for any methods added in the version
    662  // change.
    663  static constexpr int kLastKnownVfsVersion = 3;
    664  MOZ_ASSERT(pOrig->iVersion <= kLastKnownVfsVersion);
    665 #endif
    666 
    667  const sqlite3_vfs obfs_vfs = {
    668      pOrig->iVersion,                                      /* iVersion  */
    669      static_cast<int>(pOrig->szOsFile + sizeof(ObfsFile)), /* szOsFile */
    670      pOrig->mxPathname,                                    /* mxPathname */
    671      nullptr,                                              /* pNext */
    672      GetVFSName(),                                         /* zName */
    673      pOrig,                                                /* pAppData */
    674      obfsOpen,                                             /* xOpen */
    675      obfsDelete,                                           /* xDelete */
    676      obfsAccess,                                           /* xAccess */
    677      obfsFullPathname,                                     /* xFullPathname */
    678      obfsDlOpen,                                           /* xDlOpen */
    679      obfsDlError,                                          /* xDlError */
    680      obfsDlSym,                                            /* xDlSym */
    681      obfsDlClose,                                          /* xDlClose */
    682      obfsRandomness,                                       /* xRandomness */
    683      obfsSleep,                                            /* xSleep */
    684      obfsCurrentTime,                                      /* xCurrentTime */
    685      obfsGetLastError,                                     /* xGetLastError */
    686      obfsCurrentTimeInt64, /* xCurrentTimeInt64 */
    687      obfsSetSystemCall,    /* xSetSystemCall */
    688      obfsGetSystemCall,    /* xGetSystemCall */
    689      obfsNextSystemCall    /* xNextSystemCall */
    690  };
    691 
    692  return MakeUnique<sqlite3_vfs>(obfs_vfs);
    693 }
    694 
    695 already_AddRefed<QuotaObject> GetQuotaObjectForFile(sqlite3_file* pFile) {
    696  return quotavfs::GetQuotaObjectForFile(ORIGFILE(pFile));
    697 }
    698 
    699 }  // namespace mozilla::storage::obfsvfs