diff --git a/src/Cargo.lock b/src/Cargo.lock index ab5ef970..223271c5 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -123,7 +123,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -255,6 +255,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" @@ -292,7 +298,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.68", + "syn 2.0.87", "which", ] @@ -314,7 +320,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -337,7 +343,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", "syn_derive", ] @@ -478,7 +484,19 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array", + "generic-array 0.14.7", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", "rand_core", "subtle", "zeroize", @@ -490,7 +508,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] @@ -524,7 +542,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -535,7 +553,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -622,7 +640,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -638,7 +656,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ "der", - "elliptic-curve", + "elliptic-curve 0.12.3", "rfc6979", "signature 1.6.4", ] @@ -650,7 +668,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12844141594ad74185a926d030f3b605f6a903b4e3fec351f3ea338ac5b7637e" dependencies = [ "der", - "elliptic-curve", + "elliptic-curve 0.12.3", "rfc6979", "signature 2.0.0", ] @@ -667,13 +685,13 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", "der", "digest", - "ff", - "generic-array", - "group", + "ff 0.12.1", + "generic-array 0.14.7", + "group 0.12.1", "hkdf", "pem-rfc7468", "pkcs8", @@ -683,6 +701,24 @@ dependencies = [ "zeroize", ] +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.5", + "digest", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "hkdf", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "enum-as-inner" version = "0.6.0" @@ -692,7 +728,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -721,6 +757,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "flagset" version = "0.4.5" @@ -804,7 +850,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -845,6 +891,16 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", +] + +[[package]] +name = "generic-array" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96512db27971c2c3eece70a1e106fbe6c87760234e31e8f7e5634912fe52794a" +dependencies = [ + "typenum", ] [[package]] @@ -886,7 +942,18 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", + "rand_core", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core", "subtle", ] @@ -1137,7 +1204,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -1203,6 +1270,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1470,7 +1546,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ "ecdsa 0.14.8", - "elliptic-curve", + "elliptic-curve 0.12.3", "sha2", ] @@ -1481,7 +1557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49c124b3cbce43bcbac68c58ec181d98ed6cc7e6d0aa7c3ba97b2563410b0e55" dependencies = [ "ecdsa 0.15.1", - "elliptic-curve", + "elliptic-curve 0.12.3", "primeorder", "sha2", ] @@ -1493,7 +1569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ "ecdsa 0.14.8", - "elliptic-curve", + "elliptic-curve 0.12.3", "sha2", ] @@ -1504,7 +1580,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630a4a9b2618348ececfae61a4905f564b817063bf2d66cdfc2ced523fe1d2d4" dependencies = [ "ecdsa 0.15.1", - "elliptic-curve", + "elliptic-curve 0.12.3", "primeorder", "sha2", ] @@ -1598,7 +1674,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -1672,7 +1748,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -1681,7 +1757,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b54f7131b3dba65a2f414cf5bd25b66d4682e4608610668eae785750ba4c5b2" dependencies = [ - "elliptic-curve", + "elliptic-curve 0.12.3", ] [[package]] @@ -1765,6 +1841,7 @@ dependencies = [ "rustls", "serde", "serde_bytes", + "vsss-rs", "webpki-roots", ] @@ -1774,7 +1851,10 @@ version = "0.1.0" dependencies = [ "qos_hex", "rand", + "rand_core", "sha2", + "thiserror", + "vsss-rs", ] [[package]] @@ -1937,7 +2017,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac", "zeroize", ] @@ -2090,9 +2170,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", + "base16ct 0.1.1", "der", - "generic-array", + "generic-array 0.14.7", "pkcs8", "subtle", "zeroize", @@ -2143,7 +2223,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -2176,7 +2256,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -2206,7 +2286,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -2231,6 +2311,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2323,9 +2413,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.68" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -2341,7 +2431,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -2364,22 +2454,42 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl-no-std" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "thiserror-no-std" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea" +dependencies = [ + "thiserror-impl-no-std", ] [[package]] @@ -2453,7 +2563,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -2519,7 +2629,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] [[package]] @@ -2626,6 +2736,22 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vsss-rs" +version = "4.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fabeca519a296f0b39428cfe496b600c0179c9498687986449d61fa40e60806" +dependencies = [ + "crypto-bigint 0.5.5", + "elliptic-curve 0.13.8", + "generic-array 1.1.0", + "rand_core", + "serde", + "sha3", + "subtle", + "thiserror-no-std", +] + [[package]] name = "want" version = "0.3.1" @@ -2662,7 +2788,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -2684,7 +2810,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2929,12 +3055,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10e6fa9476951a9b93d9a31aa5554b5bbac7aafdc5b23e663eb3f9b635c86053" dependencies = [ - "base16ct", + "base16ct 0.1.1", "chrono", "cookie-factory", "der-parser", "des", - "elliptic-curve", + "elliptic-curve 0.12.3", "hmac", "log", "nom", @@ -2974,5 +3100,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.87", ] diff --git a/src/init/Cargo.lock b/src/init/Cargo.lock index d75afd8f..7fdf9451 100644 --- a/src/init/Cargo.lock +++ b/src/init/Cargo.lock @@ -9,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -91,6 +91,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.22.1" @@ -115,7 +121,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -138,7 +144,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", "syn_derive", ] @@ -216,7 +222,19 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array", + "generic-array 0.14.7", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", "rand_core", "subtle", "zeroize", @@ -228,7 +246,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] @@ -262,7 +280,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] @@ -273,7 +291,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] @@ -329,7 +347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12844141594ad74185a926d030f3b605f6a903b4e3fec351f3ea338ac5b7637e" dependencies = [ "der", - "elliptic-curve", + "elliptic-curve 0.12.3", "rfc6979", "signature", ] @@ -340,13 +358,13 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", "der", "digest", - "ff", - "generic-array", - "group", + "ff 0.12.1", + "generic-array 0.14.7", + "group 0.12.1", "hkdf", "rand_core", "sec1", @@ -354,6 +372,24 @@ dependencies = [ "zeroize", ] +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.5", + "digest", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "hkdf", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -370,6 +406,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "flagset" version = "0.4.5" @@ -390,6 +436,16 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", +] + +[[package]] +name = "generic-array" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96512db27971c2c3eece70a1e106fbe6c87760234e31e8f7e5634912fe52794a" +dependencies = [ + "typenum", ] [[package]] @@ -419,7 +475,18 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", + "rand_core", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core", "subtle", ] @@ -534,7 +601,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -552,6 +619,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "libc" version = "0.2.149" @@ -626,7 +702,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49c124b3cbce43bcbac68c58ec181d98ed6cc7e6d0aa7c3ba97b2563410b0e55" dependencies = [ "ecdsa", - "elliptic-curve", + "elliptic-curve 0.12.3", "primeorder", "sha2", ] @@ -638,7 +714,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630a4a9b2618348ececfae61a4905f564b817063bf2d66cdfc2ced523fe1d2d4" dependencies = [ "ecdsa", - "elliptic-curve", + "elliptic-curve 0.12.3", "primeorder", "sha2", ] @@ -698,7 +774,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b54f7131b3dba65a2f414cf5bd25b66d4682e4608610668eae785750ba4c5b2" dependencies = [ - "elliptic-curve", + "elliptic-curve 0.12.3", ] [[package]] @@ -765,6 +841,7 @@ dependencies = [ "qos_p256", "serde", "serde_bytes", + "vsss-rs", ] [[package]] @@ -772,7 +849,10 @@ name = "qos_crypto" version = "0.1.0" dependencies = [ "rand", + "rand_core", "sha2", + "thiserror", + "vsss-rs", ] [[package]] @@ -864,7 +944,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac", "zeroize", ] @@ -896,9 +976,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", + "base16ct 0.1.1", "der", - "generic-array", + "generic-array 0.14.7", "pkcs8", "subtle", "zeroize", @@ -940,7 +1020,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] @@ -962,7 +1042,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] @@ -992,7 +1072,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] @@ -1006,6 +1086,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "signature" version = "2.0.0" @@ -1057,9 +1147,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -1075,7 +1165,47 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl-no-std" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "thiserror-no-std" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea" +dependencies = [ + "thiserror-impl-no-std", ] [[package]] @@ -1160,6 +1290,22 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vsss-rs" +version = "4.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fabeca519a296f0b39428cfe496b600c0179c9498687986449d61fa40e60806" +dependencies = [ + "crypto-bigint 0.5.5", + "elliptic-curve 0.13.8", + "generic-array 1.1.0", + "rand_core", + "serde", + "sha3", + "subtle", + "thiserror-no-std", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1187,7 +1333,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -1209,7 +1355,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1350,5 +1496,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", ] diff --git a/src/integration/fixtures/preprod/deploy-test/old_dev.share.keep b/src/integration/fixtures/preprod/deploy-test/old_dev.share.keep new file mode 100644 index 00000000..469d86cd Binary files /dev/null and b/src/integration/fixtures/preprod/deploy-test/old_dev.share.keep differ diff --git a/src/integration/fixtures/preprod/deploy-test/quorum_key.pub b/src/integration/fixtures/preprod/deploy-test/quorum_key.pub new file mode 100644 index 00000000..1bc57ac0 --- /dev/null +++ b/src/integration/fixtures/preprod/deploy-test/quorum_key.pub @@ -0,0 +1 @@ +041d1f67e987e6f4c742c1f35c07f1184e950e534e12db50ff468688471aee48def99ebf3fbbbd2e72040627fa6c948adef6caaa8ceb862a5eb655eb6b674bbf21042a5e726d5e85f865599c26949fc672d2660c89824a6d9cecc8a7e8679dd89e56e3f506b35bfaddfb2c0a5f5aad256040160269c0f3cff72737348b97cb44dfe9 \ No newline at end of file diff --git a/src/integration/fixtures/preprod/evm-parser/old_dev.share.keep b/src/integration/fixtures/preprod/evm-parser/old_dev.share.keep new file mode 100644 index 00000000..9ae599fc Binary files /dev/null and b/src/integration/fixtures/preprod/evm-parser/old_dev.share.keep differ diff --git a/src/integration/fixtures/preprod/evm-parser/quorum_key.pub b/src/integration/fixtures/preprod/evm-parser/quorum_key.pub new file mode 100644 index 00000000..cd0087ea --- /dev/null +++ b/src/integration/fixtures/preprod/evm-parser/quorum_key.pub @@ -0,0 +1 @@ +0448c07c3724c9a4fa9b002f12cf326136433625b27f35da8d992a4f4b5688627fbf3705da7042d1bb8c74b719d62f89c889bb45876efcc7faced3f28f5e54969804545f5da84824570e3710dc88c0f1303db91e4b7ff0ee17fe41c66c954d8bcddc4f74910117620565061c7e84a05d13b681e75c90d7ddc0b369a7000cef85367c \ No newline at end of file diff --git a/src/integration/fixtures/preprod/notarizer/old_dev.share.keep b/src/integration/fixtures/preprod/notarizer/old_dev.share.keep new file mode 100644 index 00000000..b59507f8 Binary files /dev/null and b/src/integration/fixtures/preprod/notarizer/old_dev.share.keep differ diff --git a/src/integration/fixtures/preprod/notarizer/quorum_key.pub b/src/integration/fixtures/preprod/notarizer/quorum_key.pub new file mode 100644 index 00000000..baf0589b --- /dev/null +++ b/src/integration/fixtures/preprod/notarizer/quorum_key.pub @@ -0,0 +1 @@ +04bff189bf2177cca7618f09acef5129064d43956d334bedf61cd117c996e5c17f66937dc07f0f18c7d47d2af937e1bd56bd10f71e513dc8d1d218cc39fdeaae3304c849c5e8bb24962b8339cf566ce6853faa754cffa3886919defde706d875d27903808b53677813d27dd1bc985c1d38d21181568adb2ae3c8a7789e9e37e6577f \ No newline at end of file diff --git a/src/integration/fixtures/preprod/old_dev.secret.keep b/src/integration/fixtures/preprod/old_dev.secret.keep new file mode 100644 index 00000000..919b3082 --- /dev/null +++ b/src/integration/fixtures/preprod/old_dev.secret.keep @@ -0,0 +1 @@ +79d88e2069d49dd24452b39cf17eb07581eb5350aa55e918cede5e4e7f171c70 \ No newline at end of file diff --git a/src/integration/fixtures/preprod/signer/old_dev.share.keep b/src/integration/fixtures/preprod/signer/old_dev.share.keep new file mode 100644 index 00000000..082594f5 Binary files /dev/null and b/src/integration/fixtures/preprod/signer/old_dev.share.keep differ diff --git a/src/integration/fixtures/preprod/signer/quorum_key.pub b/src/integration/fixtures/preprod/signer/quorum_key.pub new file mode 100644 index 00000000..b2920781 --- /dev/null +++ b/src/integration/fixtures/preprod/signer/quorum_key.pub @@ -0,0 +1 @@ +048e92f6cdcc0b375505980a298d9b79201db1f08b1f135360d2864af1a67186ec0dbeb570d396a456226b0844be93dbc0180abbf7e2e4c9cfde8d5da4e3f8a49004f3422b8afbe425d6ece77b8d2469954715a2ff273ab7ac89f1ed70e0a9325eaa1698b4351fd1b23734e65c0b6a86b62dd49d70b37c94606aac402cbd84353212 \ No newline at end of file diff --git a/src/integration/fixtures/preprod/tls-fetcher/old_dev.share.keep b/src/integration/fixtures/preprod/tls-fetcher/old_dev.share.keep new file mode 100644 index 00000000..230dbecb Binary files /dev/null and b/src/integration/fixtures/preprod/tls-fetcher/old_dev.share.keep differ diff --git a/src/integration/fixtures/preprod/tls-fetcher/quorum_key.pub b/src/integration/fixtures/preprod/tls-fetcher/quorum_key.pub new file mode 100644 index 00000000..d205a17c --- /dev/null +++ b/src/integration/fixtures/preprod/tls-fetcher/quorum_key.pub @@ -0,0 +1 @@ +04603e5bd2e15714e162b5adad657cc0bd26b8d92a1c8d255b148911a834ac1be5c9a76720aac5ec80a49b6b26c48c4bcecf828f9986e1550c829250566ae14fa40445a0e4b7e2672556492c493b2cc0b9894bee5ca4ee37bae0b717930b0e970cd4922417c326c348a669863c4385dd0676cc8f53c8b6f3692e6c31481ac022735d \ No newline at end of file diff --git a/src/integration/fixtures/preprod/ump/old_dev.share.keep b/src/integration/fixtures/preprod/ump/old_dev.share.keep new file mode 100644 index 00000000..df321014 Binary files /dev/null and b/src/integration/fixtures/preprod/ump/old_dev.share.keep differ diff --git a/src/integration/fixtures/preprod/ump/quorum_key.pub b/src/integration/fixtures/preprod/ump/quorum_key.pub new file mode 100644 index 00000000..2dd01426 --- /dev/null +++ b/src/integration/fixtures/preprod/ump/quorum_key.pub @@ -0,0 +1 @@ +04296d80de8593982b0a70a8cc03f4ae664433df5e091c5555d6f49311b4dba1f5c8073565f6b1f2a17ad274b0e7f50f826431f3d5e47ad1cb5e64ea893ce6409f04044ece64c3cea982c7859398ad7d79462d8e35db1173deac68fe173e9585b8acdd43c24902e4e965f6f45f4ba37ce15394e863d4a20c2094ddeea573c66d3554 \ No newline at end of file diff --git a/src/integration/src/lib.rs b/src/integration/src/lib.rs index 3d074171..2a1a5fea 100644 --- a/src/integration/src/lib.rs +++ b/src/integration/src/lib.rs @@ -38,6 +38,8 @@ pub const LOCAL_HOST: &str = "127.0.0.1"; pub const PCR3: &str = "78fce75db17cd4e0a3fb8dad3ad128ca5e77edbb2b2c7f75329dccd99aa5f6ef4fc1f1a452e315b9e98f9e312e6921e6"; /// QOS dist directory. pub const QOS_DIST_DIR: &str = "./mock/dist"; +/// Mock pcr3 pre-image. +pub const PCR3_PRE_IMAGE_PATH: &str = "./mock/namespaces/pcr3-preimage.txt"; const MSG: &str = "msg"; diff --git a/src/integration/tests/boot.rs b/src/integration/tests/boot.rs index e5f0bd05..5f6dd556 100644 --- a/src/integration/tests/boot.rs +++ b/src/integration/tests/boot.rs @@ -7,7 +7,8 @@ use std::{ use borsh::de::BorshDeserialize; use integration::{ - LOCAL_HOST, PIVOT_OK2_PATH, PIVOT_OK2_SUCCESS_FILE, QOS_DIST_DIR, + LOCAL_HOST, PCR3_PRE_IMAGE_PATH, PIVOT_OK2_PATH, PIVOT_OK2_SUCCESS_FILE, + QOS_DIST_DIR, }; use qos_core::protocol::{ services::{ @@ -81,7 +82,7 @@ async fn standard_boot_e2e() { "--qos-release-dir", QOS_DIST_DIR, "--pcr3-preimage-path", - "./mock/namespaces/pcr3-preimage.txt", + PCR3_PRE_IMAGE_PATH, "--manifest-path", &cli_manifest_path, "--pivot-args", @@ -157,7 +158,7 @@ async fn standard_boot_e2e() { "--manifest-approvals-dir", &*boot_dir, "--pcr3-preimage-path", - "./mock/namespaces/pcr3-preimage.txt", + PCR3_PRE_IMAGE_PATH, "--pivot-hash-path", PIVOT_HASH_PATH, "--qos-release-dir", @@ -361,7 +362,7 @@ async fn standard_boot_e2e() { "--manifest-envelope-path", &manifest_envelope_path, "--pcr3-preimage-path", - "./mock/namespaces/pcr3-preimage.txt", + PCR3_PRE_IMAGE_PATH, "--manifest-set-dir", "./mock/keys/manifest-set", "--alias", diff --git a/src/integration/tests/genesis.rs b/src/integration/tests/genesis.rs index 10518b16..42fccfee 100644 --- a/src/integration/tests/genesis.rs +++ b/src/integration/tests/genesis.rs @@ -6,7 +6,7 @@ use std::{ }; use borsh::de::BorshDeserialize; -use integration::{LOCAL_HOST, QOS_DIST_DIR}; +use integration::{LOCAL_HOST, PCR3_PRE_IMAGE_PATH, QOS_DIST_DIR}; use qos_core::protocol::services::genesis::GenesisOutput; use qos_crypto::{sha_512, shamir::shares_reconstruct}; use qos_nsm::nitro::unsafe_attestation_doc_from_der; @@ -153,7 +153,7 @@ async fn genesis_e2e() { "--qos-release-dir", QOS_DIST_DIR, "--pcr3-preimage-path", - "./mock/pcr3-preimage.txt", + PCR3_PRE_IMAGE_PATH, "--dr-key-path", DR_KEY_PUBLIC_PATH, "--unsafe-skip-attestation" @@ -199,7 +199,10 @@ async fn genesis_e2e() { // Try recovering from a random permutation decrypted_shares.shuffle(&mut thread_rng()); let master_secret: [u8; qos_p256::MASTER_SEED_LEN] = - shares_reconstruct(&decrypted_shares[0..threshold]).try_into().unwrap(); + shares_reconstruct(&decrypted_shares[0..threshold]) + .unwrap() + .try_into() + .unwrap(); let reconstructed = P256Pair::from_master_seed(&master_secret).unwrap(); assert!( reconstructed.public_key() diff --git a/src/integration/tests/key.rs b/src/integration/tests/key.rs index 3769c83b..0568948f 100644 --- a/src/integration/tests/key.rs +++ b/src/integration/tests/key.rs @@ -1,6 +1,8 @@ use std::{fs, process::Command}; -use integration::{LOCAL_HOST, PIVOT_LOOP_PATH, QOS_DIST_DIR}; +use integration::{ + LOCAL_HOST, PCR3_PRE_IMAGE_PATH, PIVOT_LOOP_PATH, QOS_DIST_DIR, +}; use qos_crypto::sha_256; use qos_p256::{P256Pair, P256Public}; use qos_test_primitives::{ChildWrapper, PathWrapper}; @@ -158,7 +160,7 @@ fn generate_manifest_envelope() { "--restart-policy", "always", "--pcr3-preimage-path", - "./mock/namespaces/pcr3-preimage.txt", + PCR3_PRE_IMAGE_PATH, "--pivot-hash-path", PIVOT_HASH_PATH, "--qos-release-dir", @@ -196,7 +198,7 @@ fn generate_manifest_envelope() { "--manifest-approvals-dir", BOOT_DIR, "--pcr3-preimage-path", - "./mock/namespaces/pcr3-preimage.txt", + PCR3_PRE_IMAGE_PATH, "--pivot-hash-path", PIVOT_HASH_PATH, "--qos-release-dir", @@ -293,7 +295,7 @@ fn boot_old_enclave(old_host_port: u16) -> (ChildWrapper, ChildWrapper) { "--host-ip", LOCAL_HOST, "--pcr3-preimage-path", - "./mock/namespaces/pcr3-preimage.txt", + PCR3_PRE_IMAGE_PATH, "--unsafe-skip-attestation", ]) .spawn() @@ -343,7 +345,7 @@ fn boot_old_enclave(old_host_port: u16) -> (ChildWrapper, ChildWrapper) { "--manifest-envelope-path", MANIFEST_ENVELOPE_PATH, "--pcr3-preimage-path", - "./mock/namespaces/pcr3-preimage.txt", + PCR3_PRE_IMAGE_PATH, "--manifest-set-dir", "./mock/keys/manifest-set", "--alias", diff --git a/src/integration/tests/preprod_sharding.rs b/src/integration/tests/preprod_sharding.rs new file mode 100644 index 00000000..86c8291b --- /dev/null +++ b/src/integration/tests/preprod_sharding.rs @@ -0,0 +1,169 @@ +//! Utility script to reshard old dev shares into 2 pieces. +//! This lets us test "true" quorum settings instead of relying on a 1-out-of-1 +//! quorum. +use std::{ + fs, + path::Path, + process::Command, + time::{SystemTime, UNIX_EPOCH}, +}; + +use qos_crypto::shamir::shares_generate; +use qos_p256::{ + derive_secret, encrypt::P256EncryptPair, P256Pair, P256_ENCRYPT_DERIVE_PATH, +}; + +// Note: the dev secret can also be found in our keys repo +// (tkhq/keys:deployment/preprod/evm-parser/manifest-set/dev.secret) +// This secret is not security sensitive since it belongs to our dev/preprod +// environment I've also chosen to commit the old encrypted shares and quorum +// public keys because they're useful anchors for this tests. The quorum public +// keys and old dev shares for each enclaves are committed in +// ./fixtures/preprod/$ENCLAVE_NAME/ +const OLD_DEV_SECRET_PATH: &str = "./fixtures/preprod/old_dev.secret.keep"; + +#[test] +fn preprod_reshard_ceremony() { + // Global setup: our test will write to a new folder in `/tmp` + let unix_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + let tmp_dir = + format!("/tmp/preprod-reshard-{}", unix_timestamp.as_millis()); + fs::create_dir_all(&tmp_dir).unwrap(); + + let tmp_path = |file: &str| -> String { format!("{}/{file}", tmp_dir) }; + + let dev_users_dir = tmp_path("dev-users"); + fs::create_dir_all(dev_users_dir.clone()).unwrap(); + + let enclaves_dir = tmp_path("enclaves"); + fs::create_dir_all(enclaves_dir.clone()).unwrap(); + + let user_dir = |user: &str| format!("{}/{}", dev_users_dir, user); + let enclave_dir = |enclave: &str| format!("{}/{}", enclaves_dir, enclave); + let get_key_paths = + |user: &str| (format!("{user}.secret"), format!("{user}.pub")); + + let user1 = "1"; + let (user1_private_path, user1_public_path) = get_key_paths(user1); + + let user2 = "2"; + let (user2_private_path, user2_public_path) = get_key_paths(user2); + + // Generate user directories and keys + for (user, private, public) in [ + (&user1, &user1_private_path, &user1_public_path), + (&user2, &user2_private_path, &user2_public_path), + ] { + fs::create_dir_all(user_dir(user)).unwrap(); + + let master_seed_path = format!("{}/{}", user_dir(user), private); + let public_path = format!("{}/{}", user_dir(user), public); + assert!(Command::new("../target/debug/qos_client") + .args([ + "generate-file-key", + "--master-seed-path", + &master_seed_path, + "--pub-path", + &public_path, + ]) + .spawn() + .unwrap() + .wait() + .unwrap() + .success()); + + // Assert both public and private key paths now exist + assert!(Path::new(&*user_dir(user)).join(public).is_file()); + assert!(Path::new(&*user_dir(user)).join(private).is_file()); + } + + // Load previous dev secret (1/1 setting) + let dev_secret_utf8_bytes = fs::read(OLD_DEV_SECRET_PATH).unwrap(); + let dev_secret_hex_bytes = + qos_hex::decode(std::str::from_utf8(&dev_secret_utf8_bytes).unwrap()) + .unwrap(); + let dev_key = P256Pair::from_master_seed( + &dev_secret_hex_bytes.clone().try_into().unwrap(), + ) + .unwrap(); + + // For each of the enclaves... + for enclave_name in + ["ump", "evm-parser", "notarizer", "signer", "tls-fetcher", "deploy-test"] + { + // Decrypt the old dev share and assert that the resulting quorum key + // has the right public key. Decrypted dev shares are _basically_ master + // seeds. They're just have a "01" prefix because it's the one and only + // "share" in a 1/1 SSS sharing. + let encrypted_old_dev_share = fs::read(format!( + "./fixtures/preprod/{}/old_dev.share.keep", + enclave_name + )) + .unwrap(); + let mut decrypted_dev_share = + dev_key.decrypt(&encrypted_old_dev_share).unwrap(); + let removed_byte = decrypted_dev_share.remove(0); + assert_eq!(removed_byte, 1); + + let pk = P256Pair::from_master_seed( + &decrypted_dev_share.clone().try_into().unwrap(), + ) + .unwrap() + .public_key(); + let expected_quorum_public_key = fs::read(format!( + "./fixtures/preprod/{}/quorum_key.pub", + enclave_name + )) + .unwrap(); + assert_eq!( + qos_hex::encode(&pk.to_bytes()), + std::str::from_utf8(&expected_quorum_public_key).unwrap() + ); + + // Now we have the proper quorum key, we're ready to shard it in two + // pieces, to our two new users. + let new_shares = shares_generate(&decrypted_dev_share, 2, 2).unwrap(); // (threshold, total) + assert_eq!(new_shares.len(), 2); + + for (user, share) in + [(&user1, &new_shares[0]), (&user2, &new_shares[1])] + { + // Load the key pair for this user + let user_secret_path = + format!("{}/{}.secret", user_dir(user), user); + let user_key_pair = + P256Pair::from_hex_file(user_secret_path.clone()).unwrap(); + // Encrypt the new share to it + let encrypted_share = + user_key_pair.public_key().encrypt(share).unwrap(); + + // And write the resulting file + fs::create_dir_all(enclave_dir(enclave_name)).unwrap(); + let encrypted_share_path = + format!("{}/{}.share", enclave_dir(enclave_name), user); + fs::write(encrypted_share_path.clone(), encrypted_share).unwrap(); + + // Just to make sure: can the user decrypt the share we just created + // with their secret? + assert_can_decrypt(user_secret_path, encrypted_share_path); + } + } + + println!("success, reshard complete. Outputs are in {}", tmp_dir); +} + +// Helper function to assert a given user secret (1st arg) can decrypt a share +// (2nd arg) +fn assert_can_decrypt(user_secret_path: String, sharepath: String) { + let share = fs::read(sharepath).unwrap(); + let master_seed_hex_bytes = fs::read(user_secret_path).unwrap(); + let master_seed_utf8 = std::str::from_utf8(&master_seed_hex_bytes).unwrap(); + let master_seed = qos_hex::decode(master_seed_utf8).unwrap(); + let encryption_pair_secret = derive_secret( + &master_seed.try_into().unwrap(), + P256_ENCRYPT_DERIVE_PATH, + ) + .unwrap(); + let pair = P256EncryptPair::from_bytes(&encryption_pair_secret).unwrap(); + assert!(pair.decrypt(&share).is_ok()); +} diff --git a/src/qos_client/src/cli/services.rs b/src/qos_client/src/cli/services.rs index bee01c6f..1deb8b74 100644 --- a/src/qos_client/src/cli/services.rs +++ b/src/qos_client/src/cli/services.rs @@ -1,8 +1,6 @@ use std::{ - fs, - fs::File, - io, - io::{BufRead, BufReader, Write}, + fs::{self, File}, + io::{self, BufRead, BufReader, Write}, mem, path::{Path, PathBuf}, }; @@ -86,7 +84,7 @@ pub enum Error { #[cfg(feature = "smartcard")] PinEntryError(std::io::Error), /// Failed to read share - ReadShare, + ReadShare(String), /// Error while try to read the quorum public key. FailedToReadQuorumPublicKey(qos_p256::P256Error), /// Error trying to the read a file that is supposed to have a manifest. @@ -1229,10 +1227,8 @@ pub(crate) fn proxy_re_encrypt_share>( let manifest_envelope = read_manifest_envelope(&manifest_envelope_path)?; let attestation_doc = read_attestation_doc(&attestation_doc_path, unsafe_skip_attestation)?; - let encrypted_share = std::fs::read(share_path).map_err(|e| { - eprintln!("{e:?}"); - Error::ReadShare - })?; + let encrypted_share = std::fs::read(share_path) + .map_err(|e| Error::ReadShare(e.to_string()))?; let pcr3_preimage = find_pcr3(&pcr3_preimage_path); @@ -1539,7 +1535,8 @@ pub(crate) fn display>( file_path: P, json: bool, ) -> Result<(), Error> { - let bytes = fs::read(file_path).map_err(|_| Error::ReadShare)?; + let bytes = + fs::read(file_path).map_err(|e| Error::ReadShare(e.to_string()))?; match *display_type { DisplayType::Manifest => { let decoded = Manifest::try_from_slice(&bytes)?; @@ -1581,27 +1578,22 @@ pub(crate) fn dangerous_dev_boot>( pub_key: quorum_public_der.clone(), }; - // Shard it with N=1, K=1 - let share = { - let mut shares = qos_crypto::shamir::shares_generate( - quorum_pair.to_master_seed(), - 1, - 1, - ); - assert_eq!( - shares.len(), - 1, - "Error generating shares - did not get exactly one share." - ); - shares.remove(0) - }; + // Shard it with N=2, K=2 + let shares = + qos_crypto::shamir::shares_generate(quorum_pair.to_master_seed(), 2, 2) + .unwrap(); + assert_eq!( + shares.len(), + 2, + "Error generating shares - did not get exactly two shares." + ); // Read in the pivot let pivot = fs::read(&pivot_path).expect("Failed to read pivot binary."); let mock_pcr = vec![0; 48]; - // Create a manifest with manifest set of 1 - everything hardcoded expect - // pivot config + // Create a manifest with manifest set of 1 + // everything below is hardcoded except pivot config let manifest = Manifest { namespace: Namespace { name: DANGEROUS_DEV_BOOT_NAMESPACE.to_string(), @@ -1623,7 +1615,7 @@ pub(crate) fn dangerous_dev_boot>( members: vec![member.clone()], }, share_set: ShareSet { - threshold: 1, + threshold: 2, // The only member is the quorum member members: vec![member.clone()], }, @@ -1677,21 +1669,36 @@ pub(crate) fn dangerous_dev_boot>( }, }; - // Post the share - let req = ProtocolMsg::ProvisionRequest { + // Post the share a first time. It won't work (1/2 shares aren't enough) + let req1 = ProtocolMsg::ProvisionRequest { share: eph_pub - .encrypt(&share) + .encrypt(&shares[0]) .expect("Failed to encrypt share to eph key."), - approval, + approval: approval.clone(), }; - match request::post(uri, &req).unwrap() { - ProtocolMsg::ProvisionResponse { reconstructed } => { - assert!(reconstructed, "Quorum Key was not reconstructed"); - } - r => panic!("Unexpected response: {r:?}"), + let resp1 = request::post(uri, &req1).unwrap(); + assert!( + matches!( + resp1, + ProtocolMsg::ProvisionResponse { reconstructed: false } + ), + "{resp1:?}" + ); + + // Post the second share; expected to reconstruct. + let req2 = ProtocolMsg::ProvisionRequest { + share: eph_pub + .encrypt(&shares[1]) + .expect("Failed to encrypt share to eph key."), + approval, }; + let resp2 = request::post(uri, &req2).unwrap(); + assert!(matches!( + resp2, + ProtocolMsg::ProvisionResponse { reconstructed: true } + )); - println!("Enclave should be finished booting!"); + println!("Enclave is provisioned!"); } pub(crate) fn shamir_split( @@ -1705,7 +1712,8 @@ pub(crate) fn shamir_split( error: e.to_string(), })?; let shares = - qos_crypto::shamir::shares_generate(&secret, total_shares, threshold); + qos_crypto::shamir::shares_generate(&secret, total_shares, threshold) + .unwrap(); for (i, share) in shares.iter().enumerate() { let file_name = format!("{}.share", i + 1); @@ -1730,8 +1738,9 @@ pub(crate) fn shamir_reconstruct( }) .collect::>, Error>>()?; - let secret = - Zeroizing::new(qos_crypto::shamir::shares_reconstruct(&shares)); + let secret = Zeroizing::new( + qos_crypto::shamir::shares_reconstruct(shares).unwrap(), + ); write_with_msg(output_path.as_ref(), &secret, "Reconstructed secret"); diff --git a/src/qos_core/Cargo.toml b/src/qos_core/Cargo.toml index 74700295..e4b74610 100644 --- a/src/qos_core/Cargo.toml +++ b/src/qos_core/Cargo.toml @@ -13,6 +13,7 @@ qos_nsm = { path = "../qos_nsm", default-features = false } nix = { version = "0.26", features = ["socket"], default-features = false } libc = "=0.2.149" borsh = { version = "1.0", features = ["std", "derive"] , default-features = false} +vsss-rs = { version = "4.3", default-features = false, features = ["std"] } # For AWS Nitro aws-nitro-enclaves-nsm-api = { version = "0.3", default-features = false } diff --git a/src/qos_core/src/protocol/error.rs b/src/qos_core/src/protocol/error.rs index 767ef63e..0f00844e 100644 --- a/src/qos_core/src/protocol/error.rs +++ b/src/qos_core/src/protocol/error.rs @@ -141,6 +141,8 @@ pub enum ProtocolError { /// The new manifest was different from the old manifest when we expected /// them to be the same because they have the same nonce DifferentManifest, + /// Error from the qos crypto library. + QosCrypto(String), } impl From for ProtocolError { diff --git a/src/qos_core/src/protocol/services/genesis.rs b/src/qos_core/src/protocol/services/genesis.rs index 383acd53..95b9e654 100644 --- a/src/qos_core/src/protocol/services/genesis.rs +++ b/src/qos_core/src/protocol/services/genesis.rs @@ -51,15 +51,24 @@ pub struct RecoveredPermutation(Vec); /// Genesis output per Setup Member. #[derive( - PartialEq, Eq, Clone, borsh::BorshSerialize, borsh::BorshDeserialize, + PartialEq, + Eq, + Clone, + borsh::BorshSerialize, + borsh::BorshDeserialize, + serde::Serialize, + serde::Deserialize, )] +#[serde(rename_all = "camelCase")] pub struct GenesisMemberOutput { /// The Quorum Member whom's Setup Key was used. pub share_set_member: QuorumMember, /// Quorum Key Share encrypted to the `setup_member`'s Personal Key. + #[serde(with = "qos_hex::serde")] pub encrypted_quorum_key_share: Vec, /// Sha512 hash of the plaintext quorum key share. Used by the share set /// member to verify they correctly decrypted the share. + #[serde(with = "qos_hex::serde")] pub share_hash: [u8; 64], } @@ -127,7 +136,8 @@ pub(in crate::protocol) fn boot_genesis( master_seed, genesis_set.members.len(), genesis_set.threshold as usize, - ); + ) + .map_err(|e| ProtocolError::QosCrypto(format!("{e:?}")))?; let member_outputs: Result, _> = zip(shares, genesis_set.members.iter().cloned()) .map(|(share, share_set_member)| -> Result { @@ -243,6 +253,7 @@ mod test { qos_crypto::shamir::shares_reconstruct( &shares[0..threshold as usize], ) + .unwrap() .try_into() .unwrap(); let reconstructed_quorum_key = diff --git a/src/qos_core/src/protocol/services/provision.rs b/src/qos_core/src/protocol/services/provision.rs index 799af628..46d50721 100644 --- a/src/qos_core/src/protocol/services/provision.rs +++ b/src/qos_core/src/protocol/services/provision.rs @@ -33,7 +33,8 @@ impl SecretBuilder { /// Attempt to reconstruct the secret from the pub(crate) fn build(&self) -> Result { - let secret = qos_crypto::shamir::shares_reconstruct(&self.shares); + let secret = qos_crypto::shamir::shares_reconstruct(&self.shares) + .map_err(|e| ProtocolError::QosCrypto(format!("{e:?}")))?; if secret.is_empty() { return Err(ProtocolError::ReconstructionErrorEmptySecret); @@ -47,7 +48,7 @@ impl SecretBuilder { self.shares.len() } - fn clear(&mut self) { + pub(crate) fn clear(&mut self) { self.shares = vec![]; } } @@ -250,6 +251,7 @@ mod test { let quorum_key = quorum_pair.to_master_seed(); let encrypted_shares: Vec<_> = shares_generate(quorum_key, 4, threshold) + .unwrap() .iter() .map(|shard| eph_pair.public_key().encrypt(shard).unwrap()) .collect(); @@ -307,6 +309,7 @@ mod test { P256Pair::generate().unwrap().to_master_seed().to_vec(); let encrypted_shares: Vec<_> = shares_generate(&random_key, 4, threshold) + .unwrap() .iter() .map(|shard| eph_pair.public_key().encrypt(shard).unwrap()) .collect(); @@ -351,6 +354,7 @@ mod test { let encrypted_shares: Vec<_> = shares_generate(quorum_key, 4, threshold) + .unwrap() .iter() .map(|shard| eph_pair.public_key().encrypt(shard).unwrap()) .collect(); @@ -368,7 +372,7 @@ mod test { } // 6) Add a bogus shard as the Kth shard - let bogus_share = &[69u8; 2349]; + let bogus_share = &[69u8; 33]; let encrypted_bogus_share = eph_pair.public_key().encrypt(bogus_share).unwrap(); let approval = approvals[threshold].clone(); @@ -401,6 +405,7 @@ mod test { let quorum_key = quorum_pair.to_master_seed(); let mut encrypted_shares: Vec<_> = shares_generate(quorum_key, 4, threshold) + .unwrap() .iter() .map(|shard| eph_pair.public_key().encrypt(shard).unwrap()) .collect(); @@ -437,6 +442,7 @@ mod test { let quorum_key = quorum_pair.to_master_seed(); let mut encrypted_shares: Vec<_> = shares_generate(quorum_key, 4, threshold) + .unwrap() .iter() .map(|shard| eph_pair.public_key().encrypt(shard).unwrap()) .collect(); @@ -479,6 +485,7 @@ mod test { let quorum_key = quorum_pair.to_master_seed(); let mut encrypted_shares: Vec<_> = shares_generate(quorum_key, 4, threshold) + .unwrap() .iter() .map(|shard| eph_pair.public_key().encrypt(shard).unwrap()) .collect(); diff --git a/src/qos_crypto/Cargo.toml b/src/qos_crypto/Cargo.toml index b6a77599..c841d2e2 100644 --- a/src/qos_crypto/Cargo.toml +++ b/src/qos_crypto/Cargo.toml @@ -9,6 +9,9 @@ publish = false [dependencies] sha2 = { version = "0.10", default-features = false } rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] } +vsss-rs = { version = "4.3", default-features = false, features = ["std"] } +rand_core = { version = "0.6.4", default-features = false } +thiserror = "1.0.63" [dev-dependencies] qos_hex = { path = "../qos_hex" } diff --git a/src/qos_crypto/src/lib.rs b/src/qos_crypto/src/lib.rs index cf670b34..093aa025 100644 --- a/src/qos_crypto/src/lib.rs +++ b/src/qos_crypto/src/lib.rs @@ -5,10 +5,27 @@ #![warn(missing_docs, clippy::pedantic)] #![allow(clippy::missing_errors_doc)] +use std::fmt; + use sha2::Digest; +use thiserror::Error; +pub mod n_choose_k; pub mod shamir; +/// Errors for this crate +#[derive(Error, Debug, PartialEq, Eq, Clone)] +pub enum QosCryptoError { + /// Errors from vsss-rs lib + Vsss(vsss_rs::Error), +} + +impl fmt::Display for QosCryptoError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "QosCryptoError: {self:?}") + } +} + /// Create a SHA256 hash digest of `buf`. #[must_use] pub fn sha_256(buf: &[u8]) -> [u8; 32] { diff --git a/src/qos_crypto/src/n_choose_k.rs b/src/qos_crypto/src/n_choose_k.rs new file mode 100644 index 00000000..91fe7c42 --- /dev/null +++ b/src/qos_crypto/src/n_choose_k.rs @@ -0,0 +1,140 @@ +//! n choose k helper + +/// Computes n choose k combinations over a vector of elements of type T. +/// +/// # Arguments +/// +/// * `input` - A reference to a vector of elements of type T. +/// * `k` - The number of elements to choose in each combination. +/// +/// # Examples +/// +/// ``` +/// use qos_crypto::n_choose_k::combinations; +/// +/// let input = vec![1, 2, 3, 4]; +/// let k = 2; +/// let combinations = combinations(&input, k); +/// +/// // Verify that the computed combinations match the expected result +/// assert_eq!(combinations, vec![ +/// vec![1, 2], +/// vec![1, 3], +/// vec![1, 4], +/// vec![2, 3], +/// vec![2, 4], +/// vec![3, 4], +/// ]); +/// ``` +#[must_use] +pub fn combinations(input: &[T], k: usize) -> Vec> { + let n = input.len(); + + if k > n || k == 0 { + return Vec::new(); + } + + let mut combos = + Vec::with_capacity(expected_combinations_count(input.len(), k)); + let mut indices: Vec<_> = (0..k).collect(); + + // Generate combinations + while indices[0] <= n - k { + // Create a combination by mapping indices to corresponding elements in + // the input + let combination: Vec<_> = + indices.iter().map(|&i| input[i].clone()).collect(); + combos.push(combination); + + let mut i = k; + while i > 1 && indices[i - 1] == n - k + i - 1 { + i -= 1; + } + + indices[i - 1] += 1; + + for j in i..k { + indices[j] = indices[j - 1] + 1; + } + } + + combos +} + +fn expected_combinations_count(n: usize, k: usize) -> usize { + factorial(n) / (factorial(k) * factorial(n - k)) +} + +fn factorial(n: usize) -> usize { + if n == 0 || n == 1 { + 1 + } else { + (2..=n).product() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn normal_cases() { + // n = 4, k = 2 + let byte_input = vec![b'a', b'b', b'c', b'd']; + let k1 = 2; + let byte_result = combinations(&byte_input, k1); + assert_eq!( + byte_result.len(), + expected_combinations_count(byte_input.len(), k1) + ); + assert!(byte_result.contains(&vec![b'a', b'b'])); + assert!(byte_result.contains(&vec![b'a', b'c'])); + assert!(byte_result.contains(&vec![b'a', b'd'])); + assert!(byte_result.contains(&vec![b'b', b'c'])); + assert!(byte_result.contains(&vec![b'b', b'd'])); + assert!(byte_result.contains(&vec![b'c', b'd'])); + + // n = 3, k = 3 + let char3_input = vec!['x', 'y', 'z']; + let k2 = 3; + let char3_result = combinations(&char3_input, k2); + assert_eq!( + char3_result.len(), + expected_combinations_count(char3_input.len(), k2) + ); + assert_eq!(char3_result, vec![vec!['x', 'y', 'z']]); + + // n = 2, k = 1 + let char2_input = vec!['p', 'q']; + let k3 = 1; + let char2_result = combinations(&char2_input, k3); + assert_eq!( + char2_result.len(), + expected_combinations_count(char2_input.len(), k3) + ); + assert_eq!(char2_result, vec![vec!['p'], vec!['q']]); + } + + #[test] + fn edge_cases() { + // empty input + let empty_input: Vec = Vec::new(); + let empty_result = combinations(&empty_input, 0); + assert_eq!(empty_result.len(), 0); + + // k = 0 + let input = vec![1, 2, 3, 4, 5]; + let k = 0; + let result = combinations(&input, k); + assert_eq!(result.len(), 0); + } + + #[test] + fn factorial_works() { + assert_eq!(factorial(0), 1); + assert_eq!(factorial(1), 1); + assert_eq!(factorial(5), 120); + assert_eq!(factorial(10), 3_628_800); + assert_eq!(factorial(20), 2_432_902_008_176_640_000); + } +} diff --git a/src/qos_crypto/src/shamir.rs b/src/qos_crypto/src/shamir.rs index f668745a..cb793e65 100644 --- a/src/qos_crypto/src/shamir.rs +++ b/src/qos_crypto/src/shamir.rs @@ -1,214 +1,28 @@ -//! Shamir Secret Sharing implementation. -// Grabbed from here: -// https://github.com/veracruz-project/veracruz/blob/main/sdk/data-generators/shamir-secret-sharing/src/main.rs +//! Shamir Secret Sharing module. We use the [`vsss-rs`](https://crates.io/crates/vsss-rs) +use rand_core::OsRng; +use vsss_rs::Gf256; -use std::{convert::TryFrom, iter}; +use crate::QosCryptoError; -use rand::Rng; - -// lookup tables for log and exp of polynomials in GF(256), -#[rustfmt::skip] -const GF256_LOG: [u8; 256] = [ - 0xff, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1a, 0xc6, - 0x4b, 0xc7, 0x1b, 0x68, 0x33, 0xee, 0xdf, 0x03, - 0x64, 0x04, 0xe0, 0x0e, 0x34, 0x8d, 0x81, 0xef, - 0x4c, 0x71, 0x08, 0xc8, 0xf8, 0x69, 0x1c, 0xc1, - 0x7d, 0xc2, 0x1d, 0xb5, 0xf9, 0xb9, 0x27, 0x6a, - 0x4d, 0xe4, 0xa6, 0x72, 0x9a, 0xc9, 0x09, 0x78, - 0x65, 0x2f, 0x8a, 0x05, 0x21, 0x0f, 0xe1, 0x24, - 0x12, 0xf0, 0x82, 0x45, 0x35, 0x93, 0xda, 0x8e, - 0x96, 0x8f, 0xdb, 0xbd, 0x36, 0xd0, 0xce, 0x94, - 0x13, 0x5c, 0xd2, 0xf1, 0x40, 0x46, 0x83, 0x38, - 0x66, 0xdd, 0xfd, 0x30, 0xbf, 0x06, 0x8b, 0x62, - 0xb3, 0x25, 0xe2, 0x98, 0x22, 0x88, 0x91, 0x10, - 0x7e, 0x6e, 0x48, 0xc3, 0xa3, 0xb6, 0x1e, 0x42, - 0x3a, 0x6b, 0x28, 0x54, 0xfa, 0x85, 0x3d, 0xba, - 0x2b, 0x79, 0x0a, 0x15, 0x9b, 0x9f, 0x5e, 0xca, - 0x4e, 0xd4, 0xac, 0xe5, 0xf3, 0x73, 0xa7, 0x57, - 0xaf, 0x58, 0xa8, 0x50, 0xf4, 0xea, 0xd6, 0x74, - 0x4f, 0xae, 0xe9, 0xd5, 0xe7, 0xe6, 0xad, 0xe8, - 0x2c, 0xd7, 0x75, 0x7a, 0xeb, 0x16, 0x0b, 0xf5, - 0x59, 0xcb, 0x5f, 0xb0, 0x9c, 0xa9, 0x51, 0xa0, - 0x7f, 0x0c, 0xf6, 0x6f, 0x17, 0xc4, 0x49, 0xec, - 0xd8, 0x43, 0x1f, 0x2d, 0xa4, 0x76, 0x7b, 0xb7, - 0xcc, 0xbb, 0x3e, 0x5a, 0xfb, 0x60, 0xb1, 0x86, - 0x3b, 0x52, 0xa1, 0x6c, 0xaa, 0x55, 0x29, 0x9d, - 0x97, 0xb2, 0x87, 0x90, 0x61, 0xbe, 0xdc, 0xfc, - 0xbc, 0x95, 0xcf, 0xcd, 0x37, 0x3f, 0x5b, 0xd1, - 0x53, 0x39, 0x84, 0x3c, 0x41, 0xa2, 0x6d, 0x47, - 0x14, 0x2a, 0x9e, 0x5d, 0x56, 0xf2, 0xd3, 0xab, - 0x44, 0x11, 0x92, 0xd9, 0x23, 0x20, 0x2e, 0x89, - 0xb4, 0x7c, 0xb8, 0x26, 0x77, 0x99, 0xe3, 0xa5, - 0x67, 0x4a, 0xed, 0xde, 0xc5, 0x31, 0xfe, 0x18, - 0x0d, 0x63, 0x8c, 0x80, 0xc0, 0xf7, 0x70, 0x07, -]; - -#[rustfmt::skip] -const GF256_EXP: [u8; 2*255] = [ - 0x01, 0x03, 0x05, 0x0f, 0x11, 0x33, 0x55, 0xff, - 0x1a, 0x2e, 0x72, 0x96, 0xa1, 0xf8, 0x13, 0x35, - 0x5f, 0xe1, 0x38, 0x48, 0xd8, 0x73, 0x95, 0xa4, - 0xf7, 0x02, 0x06, 0x0a, 0x1e, 0x22, 0x66, 0xaa, - 0xe5, 0x34, 0x5c, 0xe4, 0x37, 0x59, 0xeb, 0x26, - 0x6a, 0xbe, 0xd9, 0x70, 0x90, 0xab, 0xe6, 0x31, - 0x53, 0xf5, 0x04, 0x0c, 0x14, 0x3c, 0x44, 0xcc, - 0x4f, 0xd1, 0x68, 0xb8, 0xd3, 0x6e, 0xb2, 0xcd, - 0x4c, 0xd4, 0x67, 0xa9, 0xe0, 0x3b, 0x4d, 0xd7, - 0x62, 0xa6, 0xf1, 0x08, 0x18, 0x28, 0x78, 0x88, - 0x83, 0x9e, 0xb9, 0xd0, 0x6b, 0xbd, 0xdc, 0x7f, - 0x81, 0x98, 0xb3, 0xce, 0x49, 0xdb, 0x76, 0x9a, - 0xb5, 0xc4, 0x57, 0xf9, 0x10, 0x30, 0x50, 0xf0, - 0x0b, 0x1d, 0x27, 0x69, 0xbb, 0xd6, 0x61, 0xa3, - 0xfe, 0x19, 0x2b, 0x7d, 0x87, 0x92, 0xad, 0xec, - 0x2f, 0x71, 0x93, 0xae, 0xe9, 0x20, 0x60, 0xa0, - 0xfb, 0x16, 0x3a, 0x4e, 0xd2, 0x6d, 0xb7, 0xc2, - 0x5d, 0xe7, 0x32, 0x56, 0xfa, 0x15, 0x3f, 0x41, - 0xc3, 0x5e, 0xe2, 0x3d, 0x47, 0xc9, 0x40, 0xc0, - 0x5b, 0xed, 0x2c, 0x74, 0x9c, 0xbf, 0xda, 0x75, - 0x9f, 0xba, 0xd5, 0x64, 0xac, 0xef, 0x2a, 0x7e, - 0x82, 0x9d, 0xbc, 0xdf, 0x7a, 0x8e, 0x89, 0x80, - 0x9b, 0xb6, 0xc1, 0x58, 0xe8, 0x23, 0x65, 0xaf, - 0xea, 0x25, 0x6f, 0xb1, 0xc8, 0x43, 0xc5, 0x54, - 0xfc, 0x1f, 0x21, 0x63, 0xa5, 0xf4, 0x07, 0x09, - 0x1b, 0x2d, 0x77, 0x99, 0xb0, 0xcb, 0x46, 0xca, - 0x45, 0xcf, 0x4a, 0xde, 0x79, 0x8b, 0x86, 0x91, - 0xa8, 0xe3, 0x3e, 0x42, 0xc6, 0x51, 0xf3, 0x0e, - 0x12, 0x36, 0x5a, 0xee, 0x29, 0x7b, 0x8d, 0x8c, - 0x8f, 0x8a, 0x85, 0x94, 0xa7, 0xf2, 0x0d, 0x17, - 0x39, 0x4b, 0xdd, 0x7c, 0x84, 0x97, 0xa2, 0xfd, - 0x1c, 0x24, 0x6c, 0xb4, 0xc7, 0x52, 0xf6, - - 0x01, 0x03, 0x05, 0x0f, 0x11, 0x33, 0x55, 0xff, - 0x1a, 0x2e, 0x72, 0x96, 0xa1, 0xf8, 0x13, 0x35, - 0x5f, 0xe1, 0x38, 0x48, 0xd8, 0x73, 0x95, 0xa4, - 0xf7, 0x02, 0x06, 0x0a, 0x1e, 0x22, 0x66, 0xaa, - 0xe5, 0x34, 0x5c, 0xe4, 0x37, 0x59, 0xeb, 0x26, - 0x6a, 0xbe, 0xd9, 0x70, 0x90, 0xab, 0xe6, 0x31, - 0x53, 0xf5, 0x04, 0x0c, 0x14, 0x3c, 0x44, 0xcc, - 0x4f, 0xd1, 0x68, 0xb8, 0xd3, 0x6e, 0xb2, 0xcd, - 0x4c, 0xd4, 0x67, 0xa9, 0xe0, 0x3b, 0x4d, 0xd7, - 0x62, 0xa6, 0xf1, 0x08, 0x18, 0x28, 0x78, 0x88, - 0x83, 0x9e, 0xb9, 0xd0, 0x6b, 0xbd, 0xdc, 0x7f, - 0x81, 0x98, 0xb3, 0xce, 0x49, 0xdb, 0x76, 0x9a, - 0xb5, 0xc4, 0x57, 0xf9, 0x10, 0x30, 0x50, 0xf0, - 0x0b, 0x1d, 0x27, 0x69, 0xbb, 0xd6, 0x61, 0xa3, - 0xfe, 0x19, 0x2b, 0x7d, 0x87, 0x92, 0xad, 0xec, - 0x2f, 0x71, 0x93, 0xae, 0xe9, 0x20, 0x60, 0xa0, - 0xfb, 0x16, 0x3a, 0x4e, 0xd2, 0x6d, 0xb7, 0xc2, - 0x5d, 0xe7, 0x32, 0x56, 0xfa, 0x15, 0x3f, 0x41, - 0xc3, 0x5e, 0xe2, 0x3d, 0x47, 0xc9, 0x40, 0xc0, - 0x5b, 0xed, 0x2c, 0x74, 0x9c, 0xbf, 0xda, 0x75, - 0x9f, 0xba, 0xd5, 0x64, 0xac, 0xef, 0x2a, 0x7e, - 0x82, 0x9d, 0xbc, 0xdf, 0x7a, 0x8e, 0x89, 0x80, - 0x9b, 0xb6, 0xc1, 0x58, 0xe8, 0x23, 0x65, 0xaf, - 0xea, 0x25, 0x6f, 0xb1, 0xc8, 0x43, 0xc5, 0x54, - 0xfc, 0x1f, 0x21, 0x63, 0xa5, 0xf4, 0x07, 0x09, - 0x1b, 0x2d, 0x77, 0x99, 0xb0, 0xcb, 0x46, 0xca, - 0x45, 0xcf, 0x4a, 0xde, 0x79, 0x8b, 0x86, 0x91, - 0xa8, 0xe3, 0x3e, 0x42, 0xc6, 0x51, 0xf3, 0x0e, - 0x12, 0x36, 0x5a, 0xee, 0x29, 0x7b, 0x8d, 0x8c, - 0x8f, 0x8a, 0x85, 0x94, 0xa7, 0xf2, 0x0d, 0x17, - 0x39, 0x4b, 0xdd, 0x7c, 0x84, 0x97, 0xa2, 0xfd, - 0x1c, 0x24, 0x6c, 0xb4, 0xc7, 0x52, 0xf6, -]; - -/// Multiply in GF(256). -fn gf256_mul(a: u8, b: u8) -> u8 { - if a == 0 || b == 0 { - 0 - } else { - GF256_EXP[usize::from(GF256_LOG[usize::from(a)]) - + usize::from(GF256_LOG[usize::from(b)])] - } -} - -/// Divide in GF(256)/ -fn gf256_div(a: u8, b: u8) -> u8 { - // multiply a against inverse b - gf256_mul(a, GF256_EXP[usize::from(255 - GF256_LOG[usize::from(b)])]) -} - -/// Evaluate a polynomial at x over GF(256) using Horner's method. -fn gf256_eval(f: &[u8], x: u8) -> u8 { - f.iter().rev().fold(0, |acc, c| gf256_mul(acc, x) ^ c) -} - -/// Generate a random polynomial of given degree, fixing f(0) = secret. -fn gf256_generate(secret: u8, degree: usize) -> Vec { - let mut rng = rand::thread_rng(); - iter::once(secret) - .chain(iter::repeat_with(|| rng.gen_range(1..=255)).take(degree)) - .collect() -} - -/// Find f(0) using Lagrange interpolation. -fn gf256_interpolate(xs: &[u8], ys: &[u8]) -> u8 { - assert!(xs.len() == ys.len()); - let mut y = 0u8; - for (i, (x0, y0)) in xs.iter().zip(ys).enumerate() { - let mut li = 1u8; - for (j, (x1, _y1)) in xs.iter().zip(ys).enumerate() { - if i != j { - li = gf256_mul(li, gf256_div(*x1, *x0 ^ *x1)); - } - } - - y ^= gf256_mul(li, *y0); - } - - y -} - -/// Generate n shares requiring k shares to reconstruct. +/// Generate `share_count` shares requiring `threshold` shares to reconstruct. /// -/// # Panics -/// Panics if the number of shares exceeds 255 -#[must_use] -pub fn shares_generate(secret: &[u8], n: usize, k: usize) -> Vec> { - let mut shares = vec![vec![]; n]; - - // we need to store x for each point somewhere, so just prepend - // each array with it - for (i, share) in shares.iter_mut().enumerate().take(n) { - share.push(u8::try_from(i + 1).expect("exceeded 255 shares")); - } - - for x in secret { - // generate random polynomial for each byte - let f = gf256_generate(*x, k - 1); - - // assign each share a point at f(i) - for (i, share) in shares.iter_mut().enumerate().take(n) { - share.push(gf256_eval( - &f, - u8::try_from(i + 1).expect("exceeded 255 shares"), - )); - } - } - - shares +/// Known limitations: +/// threshold >= 2 +/// `share_count` <= 255 +pub fn shares_generate( + secret: &[u8], + share_count: usize, + threshold: usize, +) -> Result>, QosCryptoError> { + Gf256::split_array(threshold, share_count, secret, OsRng) + .map_err(QosCryptoError::Vsss) } /// Reconstruct our secret from the given `shares`. -pub fn shares_reconstruct>(shares: &[S]) -> Vec { - let len = shares.iter().map(|s| s.as_ref().len()).min().unwrap_or(0); - // rather than erroring, return empty secrets if input is malformed. - // This matches the behavior of bad shares (random output) and simplifies - // consumers. - if len == 0 { - return vec![]; - } - - let mut secret = vec![]; - - // x is prepended to each share - let xs: Vec = shares.iter().map(|v| v.as_ref()[0]).collect(); - for i in 1..len { - let ys: Vec = shares.iter().map(|v| v.as_ref()[i]).collect(); - secret.push(gf256_interpolate(&xs, &ys)); - } - - secret +pub fn shares_reconstruct]>>( + shares: B, +) -> Result, QosCryptoError> { + Gf256::combine_array(shares).map_err(QosCryptoError::Vsss) } #[cfg(test)] @@ -216,32 +30,90 @@ mod test { use rand::prelude::SliceRandom; use super::*; + #[test] fn make_and_reconstruct_shares() { let secret = b"this is a crazy secret"; let n = 6; let k = 3; - let all_shares = shares_generate(secret, n, k); + let all_shares = shares_generate(secret, n, k).unwrap(); // Reconstruct with all the shares let shares = all_shares.clone(); - let reconstructed = shares_reconstruct(&shares); + let reconstructed = shares_reconstruct(shares).unwrap(); assert_eq!(secret.to_vec(), reconstructed); // Reconstruct with enough shares let shares = &all_shares[..k]; - let reconstructed = shares_reconstruct(shares); + let reconstructed = shares_reconstruct(shares).unwrap(); assert_eq!(secret.to_vec(), reconstructed); // Reconstruct with not enough shares let shares = &all_shares[..(k - 1)]; - let reconstructed = shares_reconstruct(shares); + let reconstructed = shares_reconstruct(shares).unwrap(); + let old_reconstructed = shares_reconstruct(shares).unwrap(); assert!(secret.to_vec() != reconstructed); + assert!(secret.to_vec() != old_reconstructed); // Reconstruct with enough shuffled shares let mut shares = all_shares.clone()[..k].to_vec(); shares.shuffle(&mut rand::thread_rng()); - let reconstructed = shares_reconstruct(&shares); + let reconstructed = shares_reconstruct(&shares).unwrap(); assert_eq!(secret.to_vec(), reconstructed); + + for combo in crate::n_choose_k::combinations(&all_shares, k) { + let reconstructed = shares_reconstruct(&combo).unwrap(); + assert_eq!(secret.to_vec(), reconstructed); + } + } + + #[test] + fn can_reconstruct_from_old_shares() { + // This test if fundamental to ensure updates to the Shamir Secret + // Sharing logic can be made safely. Here we hardcode shares that were + // created with the oldest version of this logic, and ensure that we can + // reconstruct. If this test starts failing please do _not_ ignore it, + // it's telling you the current quorum key shares will become invalid + // when combined! + // -------- + // These shares were generated with the following QOS commit: + // `31ad6ac8458781f592a442b7dc0e0e019e03f2f4` (2022-05-12) + // with the following test code: + // #[test] + // fn make_shares() { + // let secret = b"my cute little secret"; + // let n = 3; + // let k = 2; + // + // let all_shares = shares_generate(secret, n, k); + // for share in all_shares { + // println!("share: {}", hex::encode(share)); + // } + // } + let shares = [ + qos_hex::decode("01661fc0cc265daa4e7bde354c281dcc23a80c590249") + .unwrap(), + qos_hex::decode("027bb5fb26d326e0fc421cf604e495e3d3e4bd24ab0e") + .unwrap(), + qos_hex::decode("0370d31b89800f2f9255abb73ca0ed0f8329d20fcc33") + .unwrap(), + ]; + + // Setting is 2-out-of-3. Let's try 3 ways. + let reconstructed1 = + shares_reconstruct(vec![shares[0].clone(), shares[1].clone()]) + .unwrap(); + let reconstructed2 = + shares_reconstruct(vec![shares[1].clone(), shares[2].clone()]) + .unwrap(); + let reconstructed3 = + shares_reconstruct(vec![shares[0].clone(), shares[2].clone()]) + .unwrap(); + + // Regardless of the combination we should get the same secret + let expected_secret = b"my cute little secret"; + assert_eq!(reconstructed1, expected_secret); + assert_eq!(reconstructed2, expected_secret); + assert_eq!(reconstructed3, expected_secret); } }