{"id":14383672,"url":"https://github.com/resilar/sqleet","last_synced_at":"2025-08-23T16:31:34.332Z","repository":{"id":47540209,"uuid":"104593271","full_name":"resilar/sqleet","owner":"resilar","description":"SQLite3 encryption that sucks less","archived":false,"fork":false,"pushed_at":"2023-05-03T13:00:06.000Z","size":5800,"stargazers_count":378,"open_issues_count":16,"forks_count":56,"subscribers_count":28,"default_branch":"master","last_synced_at":"2024-12-21T11:34:03.527Z","etag":null,"topics":["cryptography","encryption","sqlite","sqlite3"],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/resilar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-09-23T19:24:15.000Z","updated_at":"2024-11-07T12:59:46.000Z","dependencies_parsed_at":"2024-01-14T19:14:35.735Z","dependency_job_id":"6abac79a-cedd-461d-8d7a-e1f95257df6d","html_url":"https://github.com/resilar/sqleet","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/resilar/sqleet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resilar%2Fsqleet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resilar%2Fsqleet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resilar%2Fsqleet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resilar%2Fsqleet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/resilar","download_url":"https://codeload.github.com/resilar/sqleet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resilar%2Fsqleet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271755685,"owners_count":24815459,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-23T02:00:09.327Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cryptography","encryption","sqlite","sqlite3"],"created_at":"2024-08-28T18:00:57.011Z","updated_at":"2025-08-23T16:31:33.606Z","avatar_url":"https://github.com/resilar.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":[],"readme":"**Warning:** This project is currently unmaintained due to [the deprecation of\ncodec API](https://www.sqlite.org/src/timeline?c=5a877221ce90e752) in SQLite3\nupstream. Therefore, sqleet is stuck at SQLite version 3.31.1 until a *major*\nrewrite happens. Meanwhile, I recommend using\n[wxSQLite3](https://github.com/utelle/wxsqlite3) which offers sqleet-compatible\nencryption for the latest version(s) of SQLite3.\n\n---\n\n**sqleet** is an encryption extension for [SQLite3](https://www.sqlite.org/).\nThe encryption is transparent (*on-the-fly*) and based on modern cryptographic\nalgorithms designed for high performance in software and robust side-channel\nresistance. The compilation of sqleet is easy because there are no external\ndependencies, which simplifies cross-compiling and cross-platform development.\n\nIn the spirit of SQLite3, the sqleet source code is in the public domain.\n\n- [Compiling](#compiling)\n- [Example](#example)\n- [Cryptography](#cryptography)\n- [sqleet API](#sqleet-api)\n  - [C programming interface](#c-programming-interface)\n  - [URI configuration interface](#uri-configuration-interface)\n- [Android/iOS support](#androidios-support)\n- [Versioning scheme](#versioning-scheme)\n- [License](#license)\n\n\nCompiling\n---------\n\nSQLite3 shell with sqleet encryption support can be compiled as follows:\n\n```sh\n% # UNIX\n% gcc sqleet.c shell.c -o sqleet -lpthread -ldl\n\n% # Windows\n% gcc sqleet.c shell.c -o sqleet\n```\n\n[Example](#example) demonstrates the use of the sqleet encryption extension with\nthe compiled shell. For application programmers, [sqleet API](#sqleet-api)\noffers [C programming interface](#c-programming-interface) and language-agnostic\n[URI-based configuration interface](#uri-configuration-interface) for run-time\nmanagement of the encryption settings.\n\nTo use sqleet as a library, the recommended way is to download a preconfigured\n[release package](https://github.com/resilar/sqleet/releases/latest) instead of\ncloning the git repository. Release package contains `sqleet.c` and `sqleet.h`\namalgamations that are drop-in replacements for the official `sqlite3.c` and\n`sqlite3.h` amalgamations. Non-amalgamated `sqleet.c` and `sqleet.h` files from\nmaster branch can be used as drop-in replacements similarly, assuming all\nnecessary sqleet source files are available during compilation. However, sqleet\ndevelopment mainly happens in the master branch, so release are considered to be\nmore stable and a better choice for the average user.\n\nBuilding a custom release version of sqleet is a straightforward task.\n\n * Clone or fork sqleet and patch it as you wish\n * Create source and header amalgamations for release\n    * `./script/amalgamate.sh \u003csqleet.c \u003esqlame.c`\n    * `./script/amalgamate.sh \u003csqleet.h \u003esqlame.h`\n * Package the amalgamations with other release files\n\n[script/release.sh](script/release.sh) shows the exact release procedure of\nsqleet.\n\n\nExample\n-------\n\nEncrypting an existing database `hello.db` with a *key* (i.e., password)\n`\"swordfish\"`.\n\n```\n[sqleet]% hexdump -C hello.db\n00000000  53 51 4c 69 74 65 20 66  6f 72 6d 61 74 20 33 00  |SQLite format 3.|\n00000010  10 00 01 01 00 40 20 20  00 00 00 01 00 00 00 02  |.....@  ........|\n*\n00000fd0  00 00 00 2b 01 06 17 17  17 01 37 74 61 62 6c 65  |...+......7table|\n00000fe0  68 65 6c 6c 6f 68 65 6c  6c 6f 02 43 52 45 41 54  |hellohello.CREAT|\n00000ff0  45 20 54 41 42 4c 45 20  68 65 6c 6c 6f 28 78 29  |E TABLE hello(x)|\n*\n00001fe0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 0f  |................|\n00001ff0  01 02 27 48 65 6c 6c 6f  2c 20 77 6f 72 6c 64 21  |..'Hello, world!|\n[sqleet]% ./sqleet hello.db\nSQLite version 3.28.0 2019-04-16 19:49:53\nEnter \".help\" for usage hints.\nsqlite\u003e PRAGMA rekey='swordfish';\nsqlite\u003e .quit\n[sqleet]% hexdump -C hello.db\n00000000  4e 61 0c 1a 25 3f 77 1e  20 50 f4 56 61 c6 b3 37  |Na..%?w. P.Va..7|\n00000010  eb aa d5 59 37 0d e6 41  1d d1 69 c8 8e 9a f5 eb  |...Y7..A..i.....|\n*\n00001fe0  07 79 a0 3b f1 cc 9f 7b  b2 72 11 21 28 15 71 ce  |.y.;...{.r.!(.q.|\n00001ff0  e5 ad 4a cd 75 af 8e 8a  e2 79 f3 d9 2e 21 e8 4b  |..J.u....y...!.K|\n```\n\nNotice that the data of the encrypted database is indistinguishable from random.\nAfter encryption, the unencrypted data is accessible only with the correct key.\n\n```\n[sqleet]% ./sqleet hello.db\nSQLite version 3.28.0 2019-04-16 19:49:53\nEnter \".help\" for usage hints.\nsqlite\u003e .dump\nPRAGMA foreign_keys=OFF;\nBEGIN TRANSACTION;\n/**** ERROR: (26) file is not a database *****/\nROLLBACK; -- due to errors\nsqlite\u003e PRAGMA key='swordfish';\nsqlite\u003e .dump\nPRAGMA foreign_keys=OFF;\nBEGIN TRANSACTION;\nCREATE TABLE hello(x);\nINSERT INTO hello VALUES('Hello, world!');\nCOMMIT;\n```\n\nInstead of `PRAGMA` commands, the key can also be provided in [SQLite3 URI\nfilename](https://www.sqlite.org/uri.html) using `key` parameter.\n\n```\n[sqleet]% ./sqleet 'file:hello.db?key=swordfish' 'SELECT * FROM hello'\nHello, world!\n```\n\n\nCryptography\n------------\n\n- PBKDF2-HMAC-SHA256 key derivation with a 16-byte salt and 12345 iterations.\n- ChaCha20 stream cipher with one-time keys.\n- Poly1305 authentication tags.\n\nA low-level description of the database encryption scheme is available in\n[sqleet.c:265](sqleet.c#L265).\n\n\nsqleet API\n----------\n\nThe public sqleet API consists of [C programming\ninterface](#c-programming-interface) and [URI configuration\ninterface](#uri-configuration-interface).\n\n\n### C programming interface\n\nsqleet defines `SQLITE_HAS_CODEC` compile-time option to expose SQLite3\nencryption API, i.e., C functions `sqlite3_key()` and `sqlite3_rekey()` for\nmanaging database encryption keys. These functions can be called directly from\nC code, while other programming languages need to call the C functions via\n[FFI](https://en.wikipedia.org/wiki/Foreign_function_interface) mechanism.\nAnother way to invoke the functions is with `PRAGMA key` and `PRAGMA rekey`\ncommands (see [Example](#example)).\n\n```c\nSQLITE_API int sqlite3_key(      /* Invoked by PRAGMA key='x' */\n  sqlite3 *db,                   /* Database to key */\n  const void *pKey, int nKey     /* Key (password) */\n);\n```\n\n`sqlite3_key()` is typically called immediately after `sqlite3_open()` to\nspecify an encryption key (password) for the opened database. The function\nvalidates the key by decrypting the first page of the database from disk.\nReturn value is `SQLITE_OK` if the given key was correct; otherwise, a non-zero\nSQLite3 error code is returned and subsequent attempts to read or write the\ndatabase will fail.\n\n```c\nSQLITE_API int sqlite3_rekey(    /* Invoked by PRAGMA rekey='x' */\n  sqlite3 *db,                   /* Database to rekey */\n  const void *pKey, int nKey     /* New key (password) */\n);\n```\n\n`sqlite3_rekey()` changes the database encryption key. This includes encrypting\nthe database the first time, fully decrypting the database (if `nKey == 0`), as\nwell as re-encrypting it with a new key. Internally, the function runs `VACUUM`\ncommand to encrypt or decrypt all pages of the database, whereas re-encryption\nwith a new key is performed directly by processing each page sequentially.\nReturn value is `SQLITE_OK` on success and an SQLite3 error code on failure.\n\nIn addition, there are `sqlite3_key_v2()` and `sqlite3_rekey_v2()` functions\nthat accept name of the target database as the second parameter.\n\n---\n\n**Note**: \\\nIn sqleet, the contents of an encrypted database file are indistinguishable from\nrandom data (of the same length). This is a conscious design decision, but as a\ndrawback, database settings cannot be read from the database file. Therefore, it\nis the user's responsibility to properly initialize database settings before\naccessing the database. The most common issue is that opening a database fails\nregardless of valid key because the page size of the database differs from the\ndefault 4096 and `page_size` has not been set to the correct value with `PRAGMA`\nor [URI API](#uri-configuration-interface).\n\nThe official [SQLite Encryption Extension (SEE)](https://www.sqlite.org/see)\nleaves bytes 16..23 of the database header unencrypted so that page size and\nother settings can be directly read from encrypted databases, which obviously\nmakes SEE-encrypted databases distinguishable from random data. In sqleet, this\nbehavior can be optionally enabled with `-DSKIP_HEADER_BYTES=24` compile-time\nflag (bytes 0..15 contain the KDF salt so only the bytes 16..23 are actually\nskipped and left unencrypted). At run-time, the compile-time default can be\noverridden with URI parameter `skip=n` where `n` is the skip amount. \n\n---\n\n\n### URI configuration interface\n\n**Disclaimer**: URI interface is experimental and subject to changes in future\nversions. Use at your own risk!\n\nRun-time configuration of sqleet encryption is implemented based on [SQLite3\nURI filenames](https://www.sqlite.org/uri.html) which contain configuration\nparameters for databases. List of URI parameters supported by sqleet:\n\n| Parameter   | Description                                                    |\n| :---------- | :------------------------------------------------------------- |\n| `key`       | Encryption key for `sqlite3_key()` after opening the database  |\n| `salt`      | 16-byte salt for the key derivation function (KDF)             |\n| `header`    | 16-byte header overwriting the database magic header (or salt) |\n| `kdf`       | Key derivation function (only `none` supported for now)        |\n| `skip`      | Run-time setting overriding compile-time SKIP_HEADER_BYTES     |\n| `page_size` | Equivalent to `page_size` PRAGMA                               |\n\nParameters `key`, `salt` and `header` have additional `hex`-prefixed versions\nthat accept hex input strings such as `'73716c656574'`.\n\nParameters `salt` and `header` expect 16-byte input strings. Shorter strings are\nzero-padded to 16-bytes, while longer inputs get automatically rejected.\n\nParameter `header` represents the first 16 bytes of the database file, that is,\nSQLite3 magic header string for unencrypted databases. For encrypted databases,\n`header` defaults to the value of `salt` unless explicitly set to other value.\nRemember that `salt` is a parameter for the key derivation function (KDF) which\nis stored in the beginning of the database file *by default*, in which case both\n`salt` and `header` contain the same value (KDF salt). Sometimes, however, the\nuser may want to keep the salt secret, or control the first 16 bytes of the\ndatabase file for some purpose. In such cases, the user stores the salt and then\noverwrites the beginning of the database file with any 16-byte `header` value.\n(If this explanation was too abstract or nonsensical to fully grasp, see the iOS\nworkaround in the end of [Android/iOS support](#androidios-support) for a\npractical real-world use-case of `header` feature).\n\nURI parameter `kdf=none` disables the default PBKDF2-HMAC-SHA256 key derivation.\nIf KDF is disabled, `key` and `hexkey` accept a 32-byte *raw key* that becomes\nthe *master* encryption key which otherwise would be derived by the KDF from the\nkey and salt. Disabling KDF is a powerful feature for *advanced users* who need\nfull control of the key derivation process.\n\nParameters `skip` and `page_size` override the compile-time `SKIP_HEADER_BYTES`\nvalue and the database `PRAGMA page_size` configuration.\n\nChanging URI settings of an existing database can be accomplished with `VACUUM\nINTO` (introduced in SQLite 3.27.0) by giving new URI parameter values in the\n`INTO` filename. For example, `VACUUM INTO 'file:skipped.db?skip=24'` vacuums\nthe current main database to file `skipped.db` with `skip` set to 24. Other URI\nsettings, including the encryption key, are inherited from the main database\nunless `key` parameter is specified, in which case any undefined parameters are\ninitialized to default values. Database settings update with `VACUUM INTO` is\ncomplex operation with many special cases and important details (omitted here).\nSo be prepared for some undocumented behavior, but please open an issue if\nencountering obviously broken on wrong behavior.\n\nErroneus parameters, such as unsupported parameter value or otherwise bad input,\nreturns a non-zero SQLite3 error code when opening (or vacuuming) a database.\nThe current version returns `SQLITE_MISUSE` error, in most cases, if URI parsing\nfails or the resulting configuration is invalid.\n\n\nAndroid/iOS support\n-------------------\n\nsqleet does not have an out-of-the-box support for Android. However, [SQLite\nAndroid Bindings](https://www.sqlite.org/android/doc/trunk/www/index.wiki)\nproject provides an easy way to bundle a custom SQLite3 version (such as\nsqleet) into an Android application with the standard Android interface\n[`android.database.sqlite`](https://developer.android.com/reference/android/database/sqlite/package-summary).\nIn particular, see [Using The SQLite Encryption\nExtension](https://www.sqlite.org/android/doc/trunk/www/see.wiki) page for\nbuild \u0026 usage instructions.\n\nLikewise, sqleet does not offer an iOS version either, but compiling a custom\nSQLite3 with sqleet encryption support for iOS is a straightforward task (e.g.,\ncompile [switflyfalling/SQLiteLib](https://github.com/swiftlyfalling/SQLiteLib)\nwith sqleet release amalgamation instead of the SQLite3 amalgamation).\nMoreover, iOS apps with an *encrypted* WAL-journaled SQLite3 database in a\nshared data container are terminated when sent to the background (see\n[sqlcipher/sqlcipher#255](https://github.com/sqlcipher/sqlcipher/issues/255),\n[TN2408](https://developer.apple.com/library/archive/technotes/tn2408/_index.html)\nand\n[TN2151](https://developer.apple.com/library/archive/technotes/tn2151/_index.html)\nfor more information). A common workaround is to leave the first 32 bytes of\nthe database file unencrypted so that iOS recognizes the file as a regular\nWAL-journaled SQLite3 database and does not terminate the app. Thus, an\niOS-compatible sqleet database can be created with the following URI settings:\n\n```\n[sqleet]% ./sqleet 'file:ios.db?key=swordfish\u0026salt=SodiumChloride42\u0026header=SQLite%20format%203\u0026skip=32'\nSQLite version 3.28.0 2019-04-16 19:49:53\nEnter \".help\" for usage hints.\nsqlite\u003e CREATE TABLE f(x,y);\nsqlite\u003e .quit\n[sqleet]% xxd secrets.db | head -n5\n00000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300  SQLite format 3.\n00000010: 1000 0101 2040 2020 0000 0001 0000 0002  .... @  ........\n00000020: 4640 824c 703e 3f72 dffc 3a19 23a6 c964  F@.Lp\u003e?r..:.#..d\n00000030: a1b3 abf0 8f3c 996f 0eb8 c665 afe1 0d72  .....\u003c.o...e...r\n00000040: b864 57f7 2492 8c31 6398 61d0 5d49 5a28  .dW.$..1c.a.]IZ(\n```\n\n\nVersioning scheme\n-----------------\n\n[sqleet releases](https://github.com/resilar/sqleet/releases/) follow a\nperverse form of semantic versioning which requires some explanation. Major\nversion number increments indicate compatibility breaks as usual, but the minor\nand patch version numbers match the targeted SQLite3 version. For instance,\nsqleet v0.25.1 corresponds to SQLite v3.25.1. Although the target SQLite3\nversion is the primarily supported, sqleet is typically forward and backward\ncompatible across different SQLite3 versions without any changes to the code.\n\nAs a corollary, sqleet releases are published whenever a new SQLite3 version is\nreleased. A new sqleet release thus does not necessarily include bug fixes or\nnew features (except updated SQLite3 version) if there has been no commits to\nsqleet master branch since the previous SQLite3 release. [Releases\npage](https://github.com/resilar/sqleet/releases) contains a changelog for each\nsqleet release version.\n\n\nLicense\n-------\n\nLike SQLite3, sqleet has been released in the public domain (specifically,\nunder the [UNLICENSE](https://unlicense.org/) license). In other words, feel\nfree to do whatever the fuck you want to with the code. In the unlikely case\nthat your country's legal system is broken with respect to public domain\nsoftware, contact `def@huumeet.info` for a custom-licensed version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresilar%2Fsqleet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fresilar%2Fsqleet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresilar%2Fsqleet/lists"}