SQL PRAGMA statements
Table of contents
- Usage of PRAGMA statements
- Key handling
- General PRAGMA statements
- PRAGMA statements for cipher configuration
Usage of PRAGMA statements
The general syntax of PRAGMA
statements is:
PRAGMA [schemaName.]pragmaName [ = newValue];
Notes
- If the parameter
schemaName
is omitted or given asmain
, thePRAGMA
statement affects the main database connection. - If the equal sign and the parameter
newValue
are omitted, the current value of the parameter will be returned. - The following notes are valid only in the context of configuring a cipher encryption scheme:
- If the parameter
schemaName
is omitted or given asmain
, thePRAGMA
statement affects the default values of the encryption parameters. - If the parameter
schemaName
is given astemp
(or the schema name of any attached database), thePRAGMA
statement affects the transient values of the encryption parameters. - Using other schema names than
main
ortemp
has currently no effect on the encryption of attached databases.
- If the parameter
Important
It is strongly recommended to avoid executing PRAGMA
statements for the configuration of the encryption extension within a transaction. The effect of these PRAGMA
statements can’t be rolled back. In some cases execution will even fail (for example, PRAGMA rekey
can’t be executed within a transaction, if the number of reserved bytes per database page changes).
Key handling
Creating a new, encrypted database or accessing an already encrypted database is based on the use of an encryption key. Such a key is usually a password or passphrase or is derived from a password or passphrase.
Most ciphers supported by SQLite3 Multiple Ciphers derive the key from a passphrase. The key and any cipher configuration option have to be set, before any SQL statements (e.g. SELECT
, INSERT
,UPDATE
,DELETE
,CREATE TABLE
, etc.) are executed on the database. If the key is omitted or is an empty string no encryption is performed.
If the encryption scheme is configured via PRAGMA
statements, the order of the PRAGMA
statements matters. The configuration process consists of up to 3 steps:
- Optionally select the cipher scheme using PRAGMA cipher
- Optionally set configuration parameters for the selected encryption scheme using PRAGMA statements for cipher configuration
- Apply the encryption key using PRAGMA key
Step 1 is only required, if a non-default encryption scheme should be used. Step 2 is only required, if the selected encryption scheme should be used with non-default configuration parameters. Step 3 is always required.
PRAGMA key / hexkey
For creating a new, encrypted database or accessing an already encrypted database it is necessary to specify at least the encryption key. This can be done with the PRAGMA key
resp PRAGMA hexkey
statement, which has the following syntax:
PRAGMA key = { passphrase | 'passphrase' };
PRAGMA hexkey = { hex-passphrase | 'hex-passphrase' };
Note
The unquoted variant for the passphrase is only valid, if the passphrase does not contain any whitespace characters. The key pragma only works with string keys. The encoding of the passphrase should be UTF-8, unless a wrapper is used that implicitly performs conversion to UTF-8 internally. If you use a binary key, use the hexkey pragma instead.
Notes
- These pragmas return
ok
even if the provided key isn’t correct. This is because the key isn’t actually used until a subsequent attempt to read or write the database is made. To check whether the provided key was actually correct, you must execute a simple query like e.g.SELECT * FROM sqlite_master;
and check whether that succeeds. - When setting a new key on an empty database (that is, a database with zero bytes length), you have to make a subsequent write access so that the database will actually be encrypted. You’d usually want to write to a new database anyway, but if not, you can execute the VACUUM statement instead to force SQLite to write to the empty database.
Example 1: Passphrase with key derivation
Typically the key value is a passphrase, from which the actual encryption key is derived.
PRAGMA key = 'My very secret passphrase';
PRAGMA hexkey='796f75722d7365637265742d6b6579';
Example 2: Raw key data (without key derivation)
Alternatively, it is possible to specify an exact byte sequence for the encryption key using a blob literal or a specially formatted string literal. In this case it is the responsibility of the application to ensure that the provided literal corresponds to a 64 character hex string, which will be converted directly to 32 bytes (256 bits) of key data.
-- Example of a raw key for the SQLCipher scheme
PRAGMA key = "x'54686973206973206D792076657279207365637265742070617373776F72642E'";
-- Example of a raw key for the sqleet scheme
PRAGMA key = 'raw:54686973206973206D792076657279207365637265742070617373776F72642E';
Note
Currently only the cipher schemes sqleet: ChaCha20 and SQLCipher: AES 256 Bit support this method, requiring the literal syntax as given in the example.
Example 3: Raw key data including salt (without key derivation)
In addition to specifying an exact byte sequence for the encryption key it is possible to provide a specific key salt to use. Normally, for certain cipher schemes a key salt value is generated randomly and stored in the first 16 bytes of the database header. In this case an application would provide 96 characters as a blob literal or a specially formatted string literal. The first 64 characters (32 bytes) will be used as the raw encryption key, and the remaining 32 characters (16 bytes) will be used as the key salt.
-- Example of a raw key for the SQLCipher scheme
PRAGMA key = "x'54686973206973206D792076657279207365637265742070617373776F72642E2E73616C7479206B65792073616C742E'";
-- Example of a raw key for the sqleet scheme
PRAGMA key = 'raw:54686973206973206D792076657279207365637265742070617373776F72642E2E73616C7479206B65792073616C742E';
Note
Currently only the cipher schemes sqleet: ChaCha20 and SQLCipher: AES 256 Bit support this method, requiring the literal syntax as given in the example.
PRAGMA rekey / hexrekey
The PRAGMA rekey
resp PRAGMA hexrekey
statement has 3 use cases:
- Encrypt an existing unencrypted database
- Change the encryption key of an existing encrypted database
- Remove encryption from an existing encrypted database
The PRAGMA rekey
resp PRAGMA hexrekey
statement has the following syntax:
PRAGMA rekey = { passphrase | 'passphrase' };
PRAGMA hexrekey = { hex-passphrase | 'hex-passphrase' };
Note
The unquoted variant for the passphrase is only valid, if the passphrase does not contain any whitespace characters. The rekey pragma only works with string keys. The encoding of the passphrase should be UTF-8, unless a wrapper is used that implicitly performs conversion to UTF-8 internally. If you use a binary key, use the hexrekey pragma instead.
Example 1: Change passphrase
To change the passphrase of an encrypted database the PRAGMA rekey
statement is executed with a non-empty passphrase.
PRAGMA rekey = 'My changed secret passphrase';
PRAGMA hexrekey='796f75722d7365637265742d6b6579';
Example 2: Remove encryption
To remove the encryption from a database the PRAGMA rekey
statement is executed with an empty passphrase.
PRAGMA rekey = '';
General PRAGMA statements
General PRAGMA
statements are used to specify the general behaviour of the encryption extension:
- which cipher should be used, or
- whether the HMAC of database pages should be verified or not.
PRAGMA cipher
The PRAGMA cipher
allows to select the cipher to be used for encrypting the database, and has the following syntax:
PRAGMA cipher = { ciphername | 'ciphername' | "ciphername" };
where ciphername
is one of the following strings:
- aes128cbc = wxSQLite3: AES 128 Bit,
- aes256cbc = wxSQLite3: AES 256 Bit,
- chacha20 = sqleet: ChaCha20,
- sqlcipher = SQLCipher: AES 256 Bit,
- rc4 = System.Data.SQLite: RC4
- ascon128 = Ascon: Ascon-128 v1.2
Example: Select cipher wxSQLite3: AES 256 Bit
To remove the encryption from a database the PRAGMA rekey
statement is executed with an empty passphrase.
PRAGMA cipher = 'aes256cbc';
PRAGMA hmac_check
The PRAGMA hmac_check
sets a boolean flag whether the HMAC should be validated on read operations for encryption schemes using HMACs. It has the following syntax:
PRAGMA hmac_check = { 0 | 1 };
where the value 0
stands for false
or disabled
, and the value 1
stands for true
or enabled
.
Note
- Checking the HMAC on read operations is active by default. With the parameter
hmac_check
the HMAC check can be disabled in case of trying to recover a corrupted database. It is not recommended to deactivate the HMAC check for regular database operation. Therefore the default can not be changed.
PRAGMA mc_legacy_wal
The PRAGMA mc_legacy_wal
sets a boolean flag whether the legacy mode for the WAL journal encryption should be used. It has the following syntax:
PRAGMA mc_legacy_wal = { 0 | 1 };
where the value 0
stands for false
or disabled
, and the value 1
stands for true
or enabled
.
Note
- The legacy mode for WAL journal encryption is off by default. The encryption mode used by all versions up to 1.2.5 is called legacy mode, version 1.3.0 introduced a new encryption mode that provides compatibility with legacy encryption implementations and is less vulnerable to changes in SQLite. It should only be enabled to recover WAL journal files left behind by applications using versions up to 1.2.5.
PRAGMA memory_security
The PRAGMA memory_security
enables additional security measures by clearing memory allocations, before they are freed. This prevents leaking possibly sensitive information via unallocated memory.
PRAGMA memory_security = { 0 | NONE | 1 | FILL };
where the value 0
or NONE
stands for false
or disabled
, and the value 1
or FILL
stands for true
or enabled
.
Note
- If this feature was not compiled in, this pragma will be simply a no-op.
- Depending on the compile time option
SQLITE3MC_USE_RANDOM_FILL_MEMORY
the memory is cleared with zeros or random data. - Other SQLite libraries like the SQLCipher library additionally lock memory allocations, so that they are not swapped from main memory to disk by the operating system. The idea is to prevent leaking possibly sensitive information via swap files. SQLite3 Multiple Ciphers does not offer this feature, because it would feign security that is actually not there. Typically an operating system does not track locking for memory chunks smaller than a page, but SQLite’s memory allocations are often significantly smaller than a page. Therefore it is not guaranteed that several memory allocations within a page are really locked. Even locked memory is tpically written to disk if the operating system switches to hibernate state. This can’t be prevented, unless hibernate state is disabled.
PRAGMA statements for cipher configuration
Each cipher scheme has certain parameters which can be configured. Usually, just selecting a cipher scheme for database encryption should be enough, but if compatibility with other applications matters, it may be necessary to adjust some or all of the cipher parameters.
For each PRAGMA
in the following sections it is noted to which cipher schemes it is applicable.
PRAGMA legacy
Applicable to: wxSQLite3: AES 128 Bit, wxSQLite3: AES 256 Bit, sqleet: ChaCha20, SQLCipher: AES 256 Bit, System.Data.SQLite: RC4
The PRAGMA legacy
defines the legacy mode for a cipher scheme. It has the following syntax:
PRAGMA legacy = { 0 | 1 ... };
Note
- The value range of parameter
legacy
depends on the cipher scheme, although it is typically just a 0 / 1 decision. - This
PRAGMA
usually changes the default parameters according to the selectedlegacy
version. If deviating parameter settings are required, they must be set after setting thelegacy
value.
PRAGMA legacy_page_size
Applicable to: wxSQLite3: AES 128 Bit, wxSQLite3: AES 256 Bit, sqleet: ChaCha20, SQLCipher: AES 256 Bit, System.Data.SQLite: RC4
The PRAGMA legacy_page_size
specifies the database page size to be used in legacy mode for a cipher scheme. It has the following syntax:
PRAGMA legacy_page_size = { 0 | 512 | 1024 | ... 65536 };
In non-legacy mode the page size is detected automatically, but in legacy mode it is necessary to specify the page size, if it deviates from the default page size. The default page size is 4096 bytes, but it can be desirable for some applications to use a larger page size for increased performance.
Note
- To adjust the page size, the pragma has to be executed before setting the encryption key.
- The value for
legacy_page_size
must be a power of two between 512 and 65536 inclusive. - The value 0 corresponds to the default SQLite page size.
PRAGMA kdf_iter
Applicable to: wxSQLite3: AES 256 Bit, sqleet: ChaCha20, SQLCipher: AES 256 Bit Ascon: Ascon-128 v1.2
Most key derivation functions perform a certain number of iterations to strengthen the key and make it resistent to brute force and dictionary attacks. The PRAGMA kdf_iter
statement can be used to increase or decrease the number of iterations used. It has the following syntax:
PRAGMA kdf_iter = { number-of-iterations };
Note
- If a non-default value is used on creating a database, the value must be set every time the database is opened.
- Reducing the number of iterations is strongly discouraged.
PRAGMA fast_kdf_iter
Applicable to: SQLCipher: AES 256 Bit
The cipher scheme SQLCipher also performs a certain number of iterations for HMAC key derivation. The PRAGMA fast_kdf_iter
statement can be used to increase or decrease the number of iterations used. It has the following syntax:
PRAGMA fast_kdf_iter = { number-of-iterations };
Note
- It is not recommended to modify this value.
PRAGMA hmac_use
Applicable to: SQLCipher: AES 256 Bit
The cipher scheme SQLCipher allows to enable or disable the use of per-page HMACs. The PRAGMA hmac_use
statement can be used to enable or disable the use of HMACs. It has the following syntax:
PRAGMA hmac_use = { 0 | 1 };
Note
- It is not recommended to modify this value.
PRAGMA hmac_pgno
Applicable to: SQLCipher: AES 256 Bit
The cipher scheme SQLCipher uses the number of the current page to calculate the HMAC of that page. The PRAGMA hmac_pgno
statement allows to modify in which endianess the page number should be used. It has the following syntax:
PRAGMA hmac_pgno = { 0 | 1 | 2 };
where the storage type is defined as
- 0 = native
- 1 = little endian
- 2 = big endian
Note
- It is not recommended to modify this value.
PRAGMA hmac_salt_mask
Applicable to: SQLCipher: AES 256 Bit
The cipher scheme SQLCipher uses a certain mask byte in calculating the HMAC salt. The PRAGMA hmac_salt_mask
statement allows to modify the mask byte. It has the following syntax:
PRAGMA hmac_salt_mask = { 0 .. 255 };
Note
- The mask byte can be specified as a hex value, e.g.
0x3a
). - It is not recommended to modify this value.
PRAGMA kdf_algorithm
Applicable to: SQLCipher: AES 256 Bit
The PRAGMA kdf_algorithm
statement allows to modify hash algorithm used for key derivation. It has the following syntax:
PRAGMA kdf_algorithm = { 0 | 1 | 2 };
where the value corresponds to the hash algoritm for key derivation function:
- 0 = SHA1
- 1 = SHA256
- 2 = SHA512
PRAGMA hmac_algorithm
Applicable to: SQLCipher: AES 256 Bit
The PRAGMA hmac_algorithm
statement allows to modify hash algorithm used for HMAC calculation. It has the following syntax:
PRAGMA hmac_algorithm = { 0 | 1 | 2 };
where the value corresponds to the hash algoritm for HMAC calculation:
- 0 = SHA1
- 1 = SHA256
- 2 = SHA512
PRAGMA plaintext_header_size
Applicable to: SQLCipher: AES 256 Bit
The cipher scheme SQLCipher introduced a PRAGMA
statement to keep the database header partially unencrypted in version 4. In the first place, it allows to overcome an issue with shared encrypted databases under iOS, when a database is operated in WAL mode. Such a database will be stored in a shared container. In this special case iOS actually examines a database file to determine whether it is an SQLite database in WAL mode. If the database is in WAL mode, then iOS extends special privileges, allowing the application to maintain a file lock on the main database while it is in the background. However, if iOS can’t determine the file type from the database header, then iOS will kill the application process when it attempts to background with a file lock.
The PRAGMA plaintext_header_size
allows to configure the cipher scheme to keep the database header partially unencrypted. It has the following syntax:
PRAGMA plaintext_header_size = { offset };
where the offset (where encryption starts) must be multiple of 16, i.e. 32.
Notes
- The offset recommended by SQLCipher is currently 32. This value ensures that the important SQLite header segments are readable by iOS, i.e. the magic string “SQLite Format 3\0” and the database read/write version numbers indicating a database is operating in WAL journal mode (bytes at offsets 18 and 19). This will allow iOS to identify the file and will permit an application to background correctly without being killed.
- The drawback of this approach is that the cipher salt used for the key derivation can’t be stored in the database header any longer. Therefore it is necessary to retrieve the cipher salt on creating a new database, and to specify the salt on opening an existing database. The cipher salt can be retrieved with the function wxsqlite3_codec_data using parameter cipher_salt, and has to be supplied on opening a database via the database URI parameter
cipher_salt
.