From 229d6780454ee9f4183722dd11dbd30a0f21f3ef Mon Sep 17 00:00:00 2001 From: neonphog Date: Tue, 20 Aug 2024 17:01:08 -0600 Subject: [PATCH 1/3] breaking sqlcipher usage update --- Cargo.lock | 39 +++++++----- Cargo.toml | 4 +- README.md | 10 ++-- crates/hc_seed_bundle/README.md | 20 +++---- crates/hc_seed_bundle/src/lib.rs | 20 +++---- crates/lair_keystore/Cargo.toml | 2 +- crates/lair_keystore/README.md | 10 ++-- crates/lair_keystore/src/docs/help.md | 2 +- .../src/docs/import-seed-help.md | 2 +- crates/lair_keystore/src/docs/init-help.md | 2 +- crates/lair_keystore/src/docs/server-help.md | 2 +- crates/lair_keystore/src/docs/url-help.md | 2 +- crates/lair_keystore/src/store_sqlite.rs | 60 +++++++++++++------ crates/lair_keystore_api/Cargo.toml | 2 +- crates/lair_keystore_api/README.md | 10 ++-- crates/lair_keystore_api/src/lair_store.rs | 1 + crates/lair_keystore_api/src/lib.rs | 10 ++-- 17 files changed, 117 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30e7b74..635c107 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,9 +147,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bstr" @@ -188,9 +188,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.98" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -745,7 +748,7 @@ dependencies = [ [[package]] name = "lair_keystore" -version = "0.4.5" +version = "0.5.0" dependencies = [ "criterion", "lair_keystore_api", @@ -761,7 +764,7 @@ dependencies = [ [[package]] name = "lair_keystore_api" -version = "0.4.5" +version = "0.5.0" dependencies = [ "assert_cmd", "base64 0.22.1", @@ -841,9 +844,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ "cc", "openssl-sys", @@ -1042,9 +1045,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -1355,7 +1358,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -1489,11 +1492,11 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.31.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -1513,7 +1516,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -1613,6 +1616,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" diff --git a/Cargo.toml b/Cargo.toml index 04c7e70..d158a85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ futures = "0.3.28" # determinism of the strict client/server version checks hc_seed_bundle = { version = "=0.2.4", path = "./crates/hc_seed_bundle" } # lair_keystore_api must be pinned to enable strict version checks -lair_keystore_api = { version = "=0.4.5", path = "./crates/lair_keystore_api" } +lair_keystore_api = { version = "=0.5.0", path = "./crates/lair_keystore_api" } lru = "0.12.3" nanoid = "0.4.0" one_err = "0.0.8" @@ -27,7 +27,7 @@ rcgen = { version = "0.10.0", features = [ "zeroize" ] } rmp-serde = "1.3.0" rmpv = { version = "1.3.0", features = [ "with-serde" ] } rpassword = "7.2.0" -rusqlite = { version = "0.31", features = [ "modern_sqlite" ] } +rusqlite = { version = "0.32.1", features = [ "modern_sqlite" ] } serde = { version = "1", features = [ "derive", "rc" ] } serde_bytes = "0.11.9" serde_json = "1" diff --git a/README.md b/README.md index d59f243..6c3138d 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ License: MIT OR Apache-2.0 ### `lair-keystore --help` ```text -lair_keystore 0.4.5 +lair_keystore 0.5.0 secret lair private keystore USAGE: @@ -74,7 +74,7 @@ SUBCOMMANDS: ``` ### `lair-keystore init --help` ```text -lair-keystore-init 0.4.5 +lair-keystore-init 0.5.0 Set up a new lair private keystore. USAGE: @@ -92,7 +92,7 @@ FLAGS: ``` ### `lair-keystore url --help` ```text -lair-keystore-url 0.4.5 +lair-keystore-url 0.5.0 Print the connection_url for a configured lair-keystore server to stdout and exit. @@ -106,7 +106,7 @@ FLAGS: ``` ### `lair-keystore import-seed --help` ```text -lair-keystore-import-seed 0.4.5 +lair-keystore-import-seed 0.5.0 Load a seed bundle into this lair-keystore instance. Note, this operation requires capturing the pid_file, make sure you do not have a lair-server running. @@ -143,7 +143,7 @@ ARGS: ``` ### `lair-keystore server --help` ```text -lair-keystore-server 0.4.5 +lair-keystore-server 0.5.0 Run a lair keystore server instance. Note you must have initialized a config file first with 'lair-keystore init'. diff --git a/crates/hc_seed_bundle/README.md b/crates/hc_seed_bundle/README.md index 2f10751..293efed 100644 --- a/crates/hc_seed_bundle/README.md +++ b/crates/hc_seed_bundle/README.md @@ -18,19 +18,19 @@ SeedBundle parsing and generation library. #### Rationale - Applications like Holochain have different requirements than classic -blockchain systems in terms of key management. Namely there is no need -for read-only or hardened wallets (Holochain handles these concepts -through capabilities and membranes). + blockchain systems in terms of key management. Namely there is no need + for read-only or hardened wallets (Holochain handles these concepts + through capabilities and membranes). - Applications like Holochain still have need of hierarchy and determinism -in key (or in this case seed) derivation. + in key (or in this case seed) derivation. - Since we're using libsodium for hashing, signature, and encryption -algorithms, let's use it for derivation as well. + algorithms, let's use it for derivation as well. - To be psychologically compatible with the -[Bitcoin "HD Wallet" spec](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki), -we will do away with the "context" part of sodium KDF by always setting -it to `b"SeedBndl"` and focusing on the `subkey_id` and can declare a -chain of subsequent derivations of a 32 byte seed in the form -`m/68/1/65/8` where we apply `subkey_id`s 68, 1, 65, then 8 in turn. + [Bitcoin "HD Wallet" spec](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki), + we will do away with the "context" part of sodium KDF by always setting + it to `b"SeedBndl"` and focusing on the `subkey_id` and can declare a + chain of subsequent derivations of a 32 byte seed in the form + `m/68/1/65/8` where we apply `subkey_id`s 68, 1, 65, then 8 in turn. #### hcSeedBundle Encoding Spec diff --git a/crates/hc_seed_bundle/src/lib.rs b/crates/hc_seed_bundle/src/lib.rs index 3b724bc..1b22e2d 100644 --- a/crates/hc_seed_bundle/src/lib.rs +++ b/crates/hc_seed_bundle/src/lib.rs @@ -23,19 +23,19 @@ //! ### Rationale //! //! - Applications like Holochain have different requirements than classic -//! blockchain systems in terms of key management. Namely there is no need -//! for read-only or hardened wallets (Holochain handles these concepts -//! through capabilities and membranes). +//! blockchain systems in terms of key management. Namely there is no need +//! for read-only or hardened wallets (Holochain handles these concepts +//! through capabilities and membranes). //! - Applications like Holochain still have need of hierarchy and determinism -//! in key (or in this case seed) derivation. +//! in key (or in this case seed) derivation. //! - Since we're using libsodium for hashing, signature, and encryption -//! algorithms, let's use it for derivation as well. +//! algorithms, let's use it for derivation as well. //! - To be psychologically compatible with the -//! [Bitcoin "HD Wallet" spec](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki), -//! we will do away with the "context" part of sodium KDF by always setting -//! it to `b"SeedBndl"` and focusing on the `subkey_id` and can declare a -//! chain of subsequent derivations of a 32 byte seed in the form -//! `m/68/1/65/8` where we apply `subkey_id`s 68, 1, 65, then 8 in turn. +//! [Bitcoin "HD Wallet" spec](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki), +//! we will do away with the "context" part of sodium KDF by always setting +//! it to `b"SeedBndl"` and focusing on the `subkey_id` and can declare a +//! chain of subsequent derivations of a 32 byte seed in the form +//! `m/68/1/65/8` where we apply `subkey_id`s 68, 1, 65, then 8 in turn. //! //! ### hcSeedBundle Encoding Spec //! diff --git a/crates/lair_keystore/Cargo.toml b/crates/lair_keystore/Cargo.toml index 814fb20..61950d2 100644 --- a/crates/lair_keystore/Cargo.toml +++ b/crates/lair_keystore/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lair_keystore" -version = "0.4.5" +version = "0.5.0" description = "secret lair private keystore" license = "MIT OR Apache-2.0" repository = "https://github.com/holochain/lair" diff --git a/crates/lair_keystore/README.md b/crates/lair_keystore/README.md index d59f243..6c3138d 100644 --- a/crates/lair_keystore/README.md +++ b/crates/lair_keystore/README.md @@ -43,7 +43,7 @@ License: MIT OR Apache-2.0 ### `lair-keystore --help` ```text -lair_keystore 0.4.5 +lair_keystore 0.5.0 secret lair private keystore USAGE: @@ -74,7 +74,7 @@ SUBCOMMANDS: ``` ### `lair-keystore init --help` ```text -lair-keystore-init 0.4.5 +lair-keystore-init 0.5.0 Set up a new lair private keystore. USAGE: @@ -92,7 +92,7 @@ FLAGS: ``` ### `lair-keystore url --help` ```text -lair-keystore-url 0.4.5 +lair-keystore-url 0.5.0 Print the connection_url for a configured lair-keystore server to stdout and exit. @@ -106,7 +106,7 @@ FLAGS: ``` ### `lair-keystore import-seed --help` ```text -lair-keystore-import-seed 0.4.5 +lair-keystore-import-seed 0.5.0 Load a seed bundle into this lair-keystore instance. Note, this operation requires capturing the pid_file, make sure you do not have a lair-server running. @@ -143,7 +143,7 @@ ARGS: ``` ### `lair-keystore server --help` ```text -lair-keystore-server 0.4.5 +lair-keystore-server 0.5.0 Run a lair keystore server instance. Note you must have initialized a config file first with 'lair-keystore init'. diff --git a/crates/lair_keystore/src/docs/help.md b/crates/lair_keystore/src/docs/help.md index 10a5d91..2e82256 100644 --- a/crates/lair_keystore/src/docs/help.md +++ b/crates/lair_keystore/src/docs/help.md @@ -1,6 +1,6 @@ ### `lair-keystore --help` ```text -lair_keystore 0.4.5 +lair_keystore 0.5.0 secret lair private keystore USAGE: diff --git a/crates/lair_keystore/src/docs/import-seed-help.md b/crates/lair_keystore/src/docs/import-seed-help.md index 03aa513..a0bd8a5 100644 --- a/crates/lair_keystore/src/docs/import-seed-help.md +++ b/crates/lair_keystore/src/docs/import-seed-help.md @@ -1,6 +1,6 @@ ### `lair-keystore import-seed --help` ```text -lair-keystore-import-seed 0.4.5 +lair-keystore-import-seed 0.5.0 Load a seed bundle into this lair-keystore instance. Note, this operation requires capturing the pid_file, make sure you do not have a lair-server running. diff --git a/crates/lair_keystore/src/docs/init-help.md b/crates/lair_keystore/src/docs/init-help.md index 15edda9..587da7c 100644 --- a/crates/lair_keystore/src/docs/init-help.md +++ b/crates/lair_keystore/src/docs/init-help.md @@ -1,6 +1,6 @@ ### `lair-keystore init --help` ```text -lair-keystore-init 0.4.5 +lair-keystore-init 0.5.0 Set up a new lair private keystore. USAGE: diff --git a/crates/lair_keystore/src/docs/server-help.md b/crates/lair_keystore/src/docs/server-help.md index 89c3993..881b62e 100644 --- a/crates/lair_keystore/src/docs/server-help.md +++ b/crates/lair_keystore/src/docs/server-help.md @@ -1,6 +1,6 @@ ### `lair-keystore server --help` ```text -lair-keystore-server 0.4.5 +lair-keystore-server 0.5.0 Run a lair keystore server instance. Note you must have initialized a config file first with 'lair-keystore init'. diff --git a/crates/lair_keystore/src/docs/url-help.md b/crates/lair_keystore/src/docs/url-help.md index 8d4615d..1546ea4 100644 --- a/crates/lair_keystore/src/docs/url-help.md +++ b/crates/lair_keystore/src/docs/url-help.md @@ -1,6 +1,6 @@ ### `lair-keystore url --help` ```text -lair-keystore-url 0.4.5 +lair-keystore-url 0.5.0 Print the connection_url for a configured lair-keystore server to stdout and exit. diff --git a/crates/lair_keystore/src/store_sqlite.rs b/crates/lair_keystore/src/store_sqlite.rs index 1c82a70..84605c4 100644 --- a/crates/lair_keystore/src/store_sqlite.rs +++ b/crates/lair_keystore/src/store_sqlite.rs @@ -164,15 +164,7 @@ impl SqlPool { let mut write_con = match create_configured_db_connection(&path, key_pragma.clone()) { Ok(con) => con, - Err( - err @ rusqlite::Error::SqliteFailure( - rusqlite::ffi::Error { - code: rusqlite::ffi::ErrorCode::NotADatabase, - .. - }, - .., - ), - ) => { + Err(err) => { if "true" == std::env::var("LAIR_MIGRATE_UNENCRYPTED") .unwrap_or_default() @@ -191,7 +183,6 @@ impl SqlPool { return Err(one_err::OneErr::new(err)); } } - Err(e) => return Err(one_err::OneErr::new(e)), }; // only set WAL mode on the first write connection @@ -523,6 +514,28 @@ fn secure_write_key_pragma( Ok(key_pragma.to_read()) } +fn configure_encryption(con: &rusqlite::Connection) -> rusqlite::Result<()> { + con.execute_batch( + r#" +--ensure we use version 4 settings even if we get a newer sqlcipher +--https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_default_compatibility +PRAGMA cipher_default_compatibility = 4; + +--ensure we use version 4 settings even if we get a newer sqlcipher +--https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_compatibility +PRAGMA cipher_compatibility = 4; + +--sqlcipher ios compatibility requires this, but breaks salting +--https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_plaintext_header_size +PRAGMA cipher_plaintext_header_size = 32; + +--we do our own derivation, so a hard-coded salt is okay +--https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_salt +PRAGMA cipher_salt = "x'01010101010101010101010101010101'"; +"#, + ) +} + fn set_pragmas( con: &rusqlite::Connection, key_pragma: BufRead, @@ -534,6 +547,8 @@ fn set_pragmas( [], )?; + configure_encryption(con)?; + con.pragma_update(None, "trusted_schema", "0".to_string())?; con.pragma_update(None, "synchronous", "1".to_string())?; @@ -584,13 +599,24 @@ fn encrypt_unencrypted_database( conn.execute("BEGIN EXCLUSIVE", ()) .map_err(one_err::OneErr::new)?; - let lock = key_pragma.read_lock(); - conn.execute( - "ATTACH DATABASE :db_name AS encrypted KEY :key", - rusqlite::named_params! { - ":db_name": encrypted_path.to_str(), - ":key": &lock[14..81], - }, + { + let lock = key_pragma.read_lock(); + conn.execute( + "ATTACH DATABASE :db_name AS encrypted KEY :key", + rusqlite::named_params! { + ":db_name": encrypted_path.to_str(), + ":key": &lock[14..81], + }, + ) + .map_err(one_err::OneErr::new)?; + } + + conn.execute_batch( + r#" +PRAGMA encrypted.cipher_compatibility = 4; +PRAGMA encrypted.cipher_plaintext_header_size = 32; +PRAGMA encrypted.cipher_salt = "x'01010101010101010101010101010101'"; +"#, ) .map_err(one_err::OneErr::new)?; diff --git a/crates/lair_keystore_api/Cargo.toml b/crates/lair_keystore_api/Cargo.toml index f649aec..c1ea734 100644 --- a/crates/lair_keystore_api/Cargo.toml +++ b/crates/lair_keystore_api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lair_keystore_api" -version = "0.4.5" +version = "0.5.0" description = "secret lair private keystore API library" license = "MIT OR Apache-2.0" repository = "https://github.com/holochain/lair" diff --git a/crates/lair_keystore_api/README.md b/crates/lair_keystore_api/README.md index 1a1fffe..9011293 100644 --- a/crates/lair_keystore_api/README.md +++ b/crates/lair_keystore_api/README.md @@ -12,14 +12,14 @@ Secret lair private keystore API library. This library crate contains most of the logic for dealing with lair. - If you wish to run an in-process / in-memory keystore, or connect to -an external lair keystore as a client, this is the library for you. + an external lair keystore as a client, this is the library for you. - If you want to run the canonical lair-keystore, see the -[lair_keystore](https://crates.io/crates/lair_keystore) crate. + [lair_keystore](https://crates.io/crates/lair_keystore) crate. - If you want to run a canonical lair-keystore in-process, using -the canonical sqlcipher database, see the -[lair_keystore](https://crates.io/crates/lair_keystore) crate. + the canonical sqlcipher database, see the + [lair_keystore](https://crates.io/crates/lair_keystore) crate. - See the [lair_api] module for information about the lair_keystore_api -protocol. + protocol. - See [LairClient] for the client struct api. ##### Establishing a client connection to a canonical ipc keystore binary: diff --git a/crates/lair_keystore_api/src/lair_store.rs b/crates/lair_keystore_api/src/lair_store.rs index 1fffa23..c6fd6c6 100644 --- a/crates/lair_keystore_api/src/lair_store.rs +++ b/crates/lair_keystore_api/src/lair_store.rs @@ -140,6 +140,7 @@ pub enum LairEntryInner { /// - derived /// - used for ed25519 signatures /// - used for x25519 encryption + /// /// The secretstream seed uses the base passphrase-derived secret /// for decryption. Seed { diff --git a/crates/lair_keystore_api/src/lib.rs b/crates/lair_keystore_api/src/lib.rs index 1b2b6e5..c7a671d 100644 --- a/crates/lair_keystore_api/src/lib.rs +++ b/crates/lair_keystore_api/src/lib.rs @@ -17,14 +17,14 @@ //! This library crate contains most of the logic for dealing with lair. //! //! - If you wish to run an in-process / in-memory keystore, or connect to -//! an external lair keystore as a client, this is the library for you. +//! an external lair keystore as a client, this is the library for you. //! - If you want to run the canonical lair-keystore, see the -//! [lair_keystore](https://crates.io/crates/lair_keystore) crate. +//! [lair_keystore](https://crates.io/crates/lair_keystore) crate. //! - If you want to run a canonical lair-keystore in-process, using -//! the canonical sqlcipher database, see the -//! [lair_keystore](https://crates.io/crates/lair_keystore) crate. +//! the canonical sqlcipher database, see the +//! [lair_keystore](https://crates.io/crates/lair_keystore) crate. //! - See the [lair_api] module for information about the lair_keystore_api -//! protocol. +//! protocol. //! - See [LairClient] for the client struct api. //! //! #### Establishing a client connection to a canonical ipc keystore binary: From 16031e8fa67fa0449a578ce9401423d140863991 Mon Sep 17 00:00:00 2001 From: neonphog Date: Tue, 20 Aug 2024 17:05:52 -0600 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 14 ++++++++++---- crates/lair_keystore/CHANGELOG.md | 9 --------- crates/lair_keystore_api/CHANGELOG.md | 1 - 3 files changed, 10 insertions(+), 14 deletions(-) delete mode 100644 crates/lair_keystore/CHANGELOG.md delete mode 100644 crates/lair_keystore_api/CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 35ad88c..9159352 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,13 @@ -# 20210727.145430 +## 0.5.0 -## [lair\_keystore\_client-0.0.2](crates/lair_keystore_client/CHANGELOG.md#0.0.2) +- breaking sqlcipher update for ios compatibility -## [lair\_keystore-0.0.2](crates/lair_keystore/CHANGELOG.md#0.0.2) +## 0.4.1 -## [lair\_keystore\_api-0.0.2](crates/lair_keystore_api/CHANGELOG.md#0.0.2) +- Add a way to migrate unencrypted databases to encrypted by providing an environment variable `LAIR_MIGRATE_UNENCRYPTED="true"`, Lair will detect databases which can't be opened and attempt migration. #121 + +# 0.4.0 + +- pin serde and rmp-serde #119 + +## 0.0.2 diff --git a/crates/lair_keystore/CHANGELOG.md b/crates/lair_keystore/CHANGELOG.md deleted file mode 100644 index 60243f3..0000000 --- a/crates/lair_keystore/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -## 0.4.1 - -- Add a way to migrate unencrypted databases to encrypted by providing an environment variable `LAIR_MIGRATE_UNENCRYPTED="true"`, Lair will detect databases which can't be opened and attempt migration. #121 - -# 0.4.0 - -- pin serde and rmp-serde #119 - -## 0.0.2 diff --git a/crates/lair_keystore_api/CHANGELOG.md b/crates/lair_keystore_api/CHANGELOG.md deleted file mode 100644 index 62c8de4..0000000 --- a/crates/lair_keystore_api/CHANGELOG.md +++ /dev/null @@ -1 +0,0 @@ -## 0.0.2 From 91ca2dbefd72abe9e7083a1aa48c5acd34e5d386 Mon Sep 17 00:00:00 2001 From: neonphog Date: Wed, 21 Aug 2024 16:36:06 -0600 Subject: [PATCH 3/3] actual salt --- README.md | 3 +- crates/lair_keystore/README.md | 3 +- crates/lair_keystore/src/lib.rs | 3 +- crates/lair_keystore/src/server.rs | 1 + crates/lair_keystore/src/store_sqlite.rs | 113 ++++++++++++++--------- crates/lair_keystore_api/src/config.rs | 10 +- 6 files changed, 84 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 6c3138d..083d473 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,7 @@ lair_keystore = "0.1.1" - Library usage with underscores: ```rust -use lair_keystore::create_sql_pool_factory; -let _sqlite_store_factory = create_sql_pool_factory("."); +use lair_keystore::*; ``` ## `lair-keystore` commandline executable usage: diff --git a/crates/lair_keystore/README.md b/crates/lair_keystore/README.md index 6c3138d..083d473 100644 --- a/crates/lair_keystore/README.md +++ b/crates/lair_keystore/README.md @@ -32,8 +32,7 @@ lair_keystore = "0.1.1" - Library usage with underscores: ```rust -use lair_keystore::create_sql_pool_factory; -let _sqlite_store_factory = create_sql_pool_factory("."); +use lair_keystore::*; ``` ## `lair-keystore` commandline executable usage: diff --git a/crates/lair_keystore/src/lib.rs b/crates/lair_keystore/src/lib.rs index 08838fa..4c79a66 100644 --- a/crates/lair_keystore/src/lib.rs +++ b/crates/lair_keystore/src/lib.rs @@ -37,8 +37,7 @@ //! - Library usage with underscores: //! //! ``` -//! use lair_keystore::create_sql_pool_factory; -//! let _sqlite_store_factory = create_sql_pool_factory("."); +//! use lair_keystore::*; //! ``` //! //! # `lair-keystore` commandline executable usage: diff --git a/crates/lair_keystore/src/server.rs b/crates/lair_keystore/src/server.rs index 641bc3f..44e23c9 100644 --- a/crates/lair_keystore/src/server.rs +++ b/crates/lair_keystore/src/server.rs @@ -64,6 +64,7 @@ impl StandaloneServer { // construct our sqlite store factory let store_factory = crate::store_sqlite::create_sql_pool_factory( &self.config.store_file, + &self.config.database_salt, ); // spawn the server diff --git a/crates/lair_keystore/src/store_sqlite.rs b/crates/lair_keystore/src/store_sqlite.rs index 84605c4..09b7d54 100644 --- a/crates/lair_keystore/src/store_sqlite.rs +++ b/crates/lair_keystore/src/store_sqlite.rs @@ -16,23 +16,29 @@ const READ_CON_COUNT: usize = 3; /// by an encrypted (sqlcipher) sqlite database. /// WARNING: If running on windows, this currently degenerates to a /// plaintext (non-encrypted) sqlite database. -pub fn create_sql_pool_factory

(sqlite_file_path: P) -> LairStoreFactory +pub fn create_sql_pool_factory

( + sqlite_file_path: P, + db_salt: &BinDataSized<16>, +) -> LairStoreFactory where P: AsRef, { let sqlite_file_path = sqlite_file_path.as_ref().to_owned(); - struct X(std::path::PathBuf); + struct X(std::path::PathBuf, BinDataSized<16>); impl AsLairStoreFactory for X { fn connect_to_store( &self, unlock_secret: sodoken::BufReadSized<32>, ) -> BoxFuture<'static, LairResult> { let sqlite_file_path = self.0.clone(); - async move { SqlPool::new(sqlite_file_path, unlock_secret).await } - .boxed() + let db_salt = self.1.clone(); + async move { + SqlPool::new(sqlite_file_path, unlock_secret, db_salt).await + } + .boxed() } } - let inner = X(sqlite_file_path); + let inner = X(sqlite_file_path, db_salt.clone()); LairStoreFactory(Arc::new(inner)) } @@ -135,6 +141,7 @@ impl SqlPool { fn new_sync( path: std::path::PathBuf, db_key: sodoken::BufReadSized<32>, + db_salt: BinDataSized<16>, ) -> LairResult { use rusqlite::OpenFlags; @@ -161,29 +168,34 @@ impl SqlPool { // initialize the sqlcipher key pragma let key_pragma = secure_write_key_pragma(dbk_secret)?; - let mut write_con = - match create_configured_db_connection(&path, key_pragma.clone()) { - Ok(con) => con, - Err(err) => { - if "true" - == std::env::var("LAIR_MIGRATE_UNENCRYPTED") - .unwrap_or_default() - .as_str() - { - encrypt_unencrypted_database( - &path, - key_pragma.clone(), - )?; - create_configured_db_connection( - &path, - key_pragma.clone(), - ) - .map_err(one_err::OneErr::new)? - } else { - return Err(one_err::OneErr::new(err)); - } + let mut write_con = match create_configured_db_connection( + &path, + key_pragma.clone(), + db_salt.clone(), + ) { + Ok(con) => con, + Err(err) => { + if "true" + == std::env::var("LAIR_MIGRATE_UNENCRYPTED") + .unwrap_or_default() + .as_str() + { + encrypt_unencrypted_database( + &path, + key_pragma.clone(), + db_salt.clone(), + )?; + create_configured_db_connection( + &path, + key_pragma.clone(), + db_salt.clone(), + ) + .map_err(one_err::OneErr::new)? + } else { + return Err(one_err::OneErr::new(err)); } - }; + } + }; // only set WAL mode on the first write connection // it's a slow operation, and not needed on subsequent connections. @@ -222,7 +234,7 @@ impl SqlPool { .map_err(one_err::OneErr::new)?; // set generic pragmas - set_pragmas(&read_con, key_pragma.clone()) + set_pragmas(&read_con, key_pragma.clone(), db_salt.clone()) .map_err(one_err::OneErr::new)?; *rc_mut = Some(read_con); @@ -245,11 +257,14 @@ impl SqlPool { pub fn new( path: std::path::PathBuf, db_key: sodoken::BufReadSized<32>, + db_salt: BinDataSized<16>, ) -> impl Future> + 'static + Send { async move { - tokio::task::spawn_blocking(move || Self::new_sync(path, db_key)) - .await - .map_err(one_err::OneErr::new)? + tokio::task::spawn_blocking(move || { + Self::new_sync(path, db_key, db_salt) + }) + .await + .map_err(one_err::OneErr::new)? } } @@ -302,6 +317,7 @@ impl SqlPool { fn create_configured_db_connection( path: &std::path::PathBuf, key_pragma: BufRead, + db_salt: BinDataSized<16>, ) -> rusqlite::Result { use rusqlite::OpenFlags; @@ -315,7 +331,7 @@ fn create_configured_db_connection( )?; // set generic pragmas - set_pragmas(&write_con, key_pragma)?; + set_pragmas(&write_con, key_pragma, db_salt)?; Ok(write_con) } @@ -517,10 +533,6 @@ fn secure_write_key_pragma( fn configure_encryption(con: &rusqlite::Connection) -> rusqlite::Result<()> { con.execute_batch( r#" ---ensure we use version 4 settings even if we get a newer sqlcipher ---https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_default_compatibility -PRAGMA cipher_default_compatibility = 4; - --ensure we use version 4 settings even if we get a newer sqlcipher --https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_compatibility PRAGMA cipher_compatibility = 4; @@ -528,17 +540,28 @@ PRAGMA cipher_compatibility = 4; --sqlcipher ios compatibility requires this, but breaks salting --https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_plaintext_header_size PRAGMA cipher_plaintext_header_size = 32; - ---we do our own derivation, so a hard-coded salt is okay ---https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_salt -PRAGMA cipher_salt = "x'01010101010101010101010101010101'"; "#, ) } +fn set_salt( + con: &rusqlite::Connection, + name: &'static str, + db_salt: BinDataSized<16>, +) -> rusqlite::Result<()> { + let mut salt = format!("PRAGMA {name} = \"x'"); + for b in *db_salt.0 { + salt.push_str(&format!("{b:02X}")); + } + salt.push_str("'\";"); + + con.execute_optional(&salt, []) +} + fn set_pragmas( con: &rusqlite::Connection, key_pragma: BufRead, + db_salt: BinDataSized<16>, ) -> rusqlite::Result<()> { con.busy_timeout(std::time::Duration::from_millis(30_000))?; @@ -547,6 +570,8 @@ fn set_pragmas( [], )?; + set_salt(con, "cipher_salt", db_salt)?; + configure_encryption(con)?; con.pragma_update(None, "trusted_schema", "0".to_string())?; @@ -559,6 +584,7 @@ fn set_pragmas( fn encrypt_unencrypted_database( path: &std::path::PathBuf, key_pragma: BufRead, + db_salt: BinDataSized<16>, ) -> LairResult<()> { // e.g. keystore/store_file -> keystore/store_file-encrypted let encrypted_path = path @@ -615,11 +641,13 @@ fn encrypt_unencrypted_database( r#" PRAGMA encrypted.cipher_compatibility = 4; PRAGMA encrypted.cipher_plaintext_header_size = 32; -PRAGMA encrypted.cipher_salt = "x'01010101010101010101010101010101'"; "#, ) .map_err(one_err::OneErr::new)?; + set_salt(&conn, "encrypted.cipher_salt", db_salt) + .map_err(one_err::OneErr::new)?; + conn.query_row("SELECT sqlcipher_export('encrypted')", (), |_| Ok(0)) .map_err(one_err::OneErr::new)?; @@ -653,8 +681,9 @@ mod tests { sqlite.push("db.sqlite3"); let db_key = sodoken::BufReadSized::new_no_lock([0; 32]); + let db_salt = BinDataSized([0; 16].into()); - let pool = SqlPool::new(sqlite, db_key).await.unwrap(); + let pool = SqlPool::new(sqlite, db_key, db_salt).await.unwrap(); let pk = pool .new_seed("test-tag".into(), false) diff --git a/crates/lair_keystore_api/src/config.rs b/crates/lair_keystore_api/src/config.rs index c711a66..235a798 100644 --- a/crates/lair_keystore_api/src/config.rs +++ b/crates/lair_keystore_api/src/config.rs @@ -53,6 +53,9 @@ pub struct LairServerConfigInner { /// in case the pub key does not exist in the lair store. pub signature_fallback: LairServerSignatureFallback, + /// salt for sqlcipher connection + pub database_salt: BinDataSized<16>, + /// salt for decrypting runtime data pub runtime_secrets_salt: BinDataSized<16>, @@ -110,7 +113,7 @@ impl std::fmt::Display for LairServerConfigInner { lines.push("# - test-arg1"); lines.push("# - test-arg2"); lines.push("# ```"); - } else if line.starts_with("runtimeSecretsSalt:") { + } else if line.starts_with("databaseSalt:") { lines.push(""); lines.push("# -- cryptographic secrets --"); lines.push("# If you modify the data below, you risk losing access to your keys."); @@ -152,6 +155,10 @@ impl LairServerConfigInner { let pw_hash = >::new_mem_locked()?; sodoken::hash::blake2b::hash(pw_hash.clone(), passphrase).await?; + // generate a random salt for the sqlcipher database + let db_salt = >::new_no_lock(); + sodoken::random::bytes_buf(db_salt.clone()).await?; + // generate a random salt for the pwhash let salt = >::new_no_lock(); sodoken::random::bytes_buf(salt.clone()).await?; @@ -254,6 +261,7 @@ impl LairServerConfigInner { pid_file, store_file, signature_fallback: LairServerSignatureFallback::None, + database_salt: db_salt.try_unwrap_sized().unwrap().into(), runtime_secrets_salt: salt.try_unwrap_sized().unwrap().into(), runtime_secrets_mem_limit: mem_limit, runtime_secrets_ops_limit: ops_limit,