From b401cfc89cf513718e0d35434d19035dbdd4a668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Fri, 12 Jun 2020 17:45:39 -0400 Subject: [PATCH 1/4] Added proptest strategy to generate a random keypair --- Cargo.lock | 167 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/keypair.rs | 34 ++++++++-- 3 files changed, 199 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2bfda6d..77c5cb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,11 @@ dependencies = [ "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "base64" version = "0.9.3" @@ -64,6 +69,19 @@ name = "base64" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit-vec" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "1.2.1" @@ -174,6 +192,11 @@ name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -243,6 +266,14 @@ name = "libc" version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "opaque-debug" version = "0.2.3" @@ -262,6 +293,8 @@ dependencies = [ "hkdf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proptest 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "scrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", @@ -297,6 +330,11 @@ dependencies = [ "universal-hash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "1.0.8" @@ -305,6 +343,30 @@ dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proptest" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)", + "rusty-fork 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "quote" version = "1.0.2" @@ -325,6 +387,27 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -346,6 +429,51 @@ dependencies = [ "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "regex-syntax" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ryu" version = "1.0.2" @@ -436,6 +564,19 @@ dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thiserror" version = "1.0.11" @@ -473,6 +614,14 @@ dependencies = [ "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -532,8 +681,11 @@ dependencies = [ "checksum aes-gcm 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "834a6bda386024dbb7c8fc51322856c10ffe69559f972261c868485f5759c638" "checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" "checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +"checksum bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +"checksum bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" @@ -548,6 +700,7 @@ dependencies = [ "checksum curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" @@ -558,16 +711,28 @@ dependencies = [ "checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" "checksum poly1305 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5829f50f48e9ddb79f3f7c3097029d0caee30f8286accb241416df603b080b8" "checksum polyval 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ec3341498978de3bfd12d1b22f1af1de22818f5473a11e8a6ef997989e3a212" +"checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" "checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +"checksum proptest 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2520fe6373cf6a3a61e2d200e987c183778ade8d9248ac3e6614ab0edfe4a0c1" +"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" +"checksum remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +"checksum rusty-fork 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" "checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" "checksum scrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "656c79d0e90d0ab28ac86bf3c3d10bfbbac91450d3f190113b4e76d9fec3cfdd" @@ -579,11 +744,13 @@ dependencies = [ "checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" "checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db" "checksum thiserror-impl 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum universal-hash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df0c900f2f9b4116803415878ff48b63da9edb268668e08cf9292d7503114a01" +"checksum wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" diff --git a/Cargo.toml b/Cargo.toml index f6bd789..2204012 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,3 +32,5 @@ chacha20poly1305 = "0.4.1" hex = "0.4.2" lazy_static = "1.4.0" serde_json = "1.0" +proptest = "0.10.0" +rand = "0.7" \ No newline at end of file diff --git a/src/keypair.rs b/src/keypair.rs index 825a2e4..4fb404f 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -11,7 +11,12 @@ use generic_array::{ typenum::{Sum, Unsigned, U32}, ArrayLength, GenericArray, }; +#[cfg(test)] +use proptest::prelude::*; +#[cfg(test)] +use rand::{rngs::StdRng, SeedableRng}; use rand_core::{CryptoRng, RngCore}; +use std::fmt::Debug; use x25519_dalek::{PublicKey, StaticSecret}; use std::convert::TryFrom; @@ -34,7 +39,7 @@ pub trait SizedBytes: Sized + PartialEq { } /// A Keypair trait with public-private verification -pub trait KeyPair: Sized { +pub trait KeyPair: Sized + Debug { /// The single key representation must have a specific byte size itself type Repr: SizedBytes + Clone; @@ -63,6 +68,21 @@ pub trait KeyPair: Sized { /// Computes the diffie hellman function on a public key and private key fn diffie_hellman(pk: Self::Repr, sk: Self::Repr) -> Vec; + + /// Test-only strategy returning a proptest Strategy based on + /// generate_random + #[cfg(test)] + fn uniform_keypair_strategy() -> BoxedStrategy { + // The no_shrink is because keypairs should be fixed -- shrinking would cause a different + // keypair to be generated, which appears to not be very useful. + any::<[u8; 32]>() + .prop_filter_map("valid random keypair", |seed| { + let mut rng = StdRng::from_seed(seed); + Self::generate_random(&mut rng).ok() + }) + .no_shrink() + .boxed() + } } /// This is a blanket implementation of SizedBytes for any instance of KeyPair @@ -95,7 +115,7 @@ where } /// A minimalist key type built around [u8;32] -#[derive(PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] #[repr(transparent)] pub struct Key(Vec); @@ -130,7 +150,7 @@ impl SizedBytes for Key { } /// A representation of an X25519 keypair according to RFC7748 -#[derive(PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct X25519KeyPair { pk: Key, sk: Key, @@ -200,7 +220,7 @@ impl KeyPair for X25519KeyPair { } /// A custom, minimalistic Key pair struct built on Key, aimed at reproducing the behavior of libsignal's keypairs -#[derive(PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct SignalKeyPair { pk: Key, sk: Key, @@ -281,3 +301,9 @@ impl KeyPair for SignalKeyPair { ::x25519_dalek::x25519(sk_data, pk_data).to_vec() } } + +#[cfg(test)] +mod tests { + use super::*; + use proptest::prelude::*; +} From e9add9fd7a53b5198e5b9378d559a542908b8af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Fri, 12 Jun 2020 18:16:02 -0400 Subject: [PATCH 2/4] add tests showing X25519KeyPair == SignalKeyPair --- src/keypair.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/keypair.rs b/src/keypair.rs index 4fb404f..a7fb3e7 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -306,4 +306,66 @@ impl KeyPair for SignalKeyPair { mod tests { use super::*; use proptest::prelude::*; + + proptest! { + #[test] + fn test_x25519_check(kp in X25519KeyPair::uniform_keypair_strategy()) { + let pk = kp.public(); + prop_assert!(X25519KeyPair::check_public_key(pk.clone()).is_ok()); + } + + #[test] + fn test_x25519_pub_from_priv(kp in X25519KeyPair::uniform_keypair_strategy()) { + let pk = kp.public(); + let sk = kp.private(); + prop_assert_eq!(&X25519KeyPair::public_from_private(sk), pk); + } + + #[test] + fn test_signal_check(kp in SignalKeyPair::uniform_keypair_strategy()) { + let pk = kp.public(); + prop_assert!(SignalKeyPair::check_public_key(pk.clone()).is_ok()); + } + + #[test] + fn test_signal_pub_from_priv(kp in SignalKeyPair::uniform_keypair_strategy()) { + let pk = kp.public(); + let sk = kp.private(); + prop_assert_eq!(&SignalKeyPair::public_from_private(sk), pk); + } + + #[test] + fn test_signal_x25519_roundtrips(kp_signal in SignalKeyPair::uniform_keypair_strategy(), + kp_x25519 in X25519KeyPair::uniform_keypair_strategy()) { + let kp_signal_bytes: &[u8] = &kp_signal.to_arr(); + let kp_x25519_bytes: &[u8] = &kp_x25519.to_arr(); + + let reinterpret_signal = X25519KeyPair::from_bytes(kp_signal_bytes).unwrap(); + let reinterpret_x25519 = SignalKeyPair::from_bytes(kp_x25519_bytes).unwrap(); + + prop_assert_eq!(kp_signal_bytes, &reinterpret_signal.to_arr()[..]); + prop_assert_eq!(kp_x25519_bytes, &reinterpret_x25519.to_arr()[..]); + } + + #[test] + fn test_signal_as_x25519(kp_signal in SignalKeyPair::uniform_keypair_strategy()) { + let kp: X25519KeyPair = X25519KeyPair::from_bytes(&kp_signal.to_arr()).unwrap(); + let pk = kp.public(); + prop_assert!(X25519KeyPair::check_public_key(pk.clone()).is_ok()); + let sk = kp.private(); + prop_assert_eq!(&X25519KeyPair::public_from_private(sk), pk); + + } + + #[test] + fn test_x25519_as_signal(kp_x25519 in X25519KeyPair::uniform_keypair_strategy()) { + let kp: SignalKeyPair = SignalKeyPair::from_bytes(&kp_x25519.to_arr()).unwrap(); + let pk = kp.public(); + prop_assert!(SignalKeyPair::check_public_key(pk.clone()).is_ok()); + let sk = kp.private(); + prop_assert_eq!(&SignalKeyPair::public_from_private(sk), pk); + + } + + } } From e9fa474c586593933dd03364cc7ffccb9d37b66c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Fri, 12 Jun 2020 18:22:19 -0400 Subject: [PATCH 3/4] Remove SignalKeyPair, rename all instances of SignalKeyPair to X25519KeyPair, pass all tests. --- src/keypair.rs | 130 ------------------------------------ src/tests/opaque_ke_test.rs | 34 +++++----- src/tests/serialization.rs | 12 ++-- 3 files changed, 23 insertions(+), 153 deletions(-) diff --git a/src/keypair.rs b/src/keypair.rs index a7fb3e7..e49df75 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -219,93 +219,9 @@ impl KeyPair for X25519KeyPair { } } -/// A custom, minimalistic Key pair struct built on Key, aimed at reproducing the behavior of libsignal's keypairs -#[derive(Debug, PartialEq, Eq)] -pub struct SignalKeyPair { - pk: Key, - sk: Key, -} - -impl SignalKeyPair { - fn clamp_scalar(mut scalar: [u8; 32]) -> ::curve25519_dalek::scalar::Scalar { - scalar[0] &= 248; - scalar[31] &= 127; - scalar[31] |= 64; - - ::curve25519_dalek::scalar::Scalar::from_bits(scalar) - } - - fn gen(rng: &mut R) -> (Vec, Vec) { - let mut bits = [0u8; 32]; - rng.fill_bytes(&mut bits); - - // It's proper to sanitize the scalar here, and reproduces x25519::StaticSecret::new - let sk = SignalKeyPair::clamp_scalar(bits); - let pk = ::curve25519_dalek::constants::X25519_BASEPOINT * sk; - - (pk.as_bytes().to_vec(), sk.as_bytes().to_vec()) - } -} - -impl KeyPair for SignalKeyPair { - type Repr = Key; - - fn public(&self) -> &Self::Repr { - &self.pk - } - - fn private(&self) -> &Self::Repr { - &self.sk - } - - fn new(public: Self::Repr, private: Self::Repr) -> Result { - Ok(SignalKeyPair { - pk: public, - sk: private, - }) - } - - fn generate_random(rng: &mut R) -> Result { - let (public, private) = SignalKeyPair::gen(rng); - Ok(SignalKeyPair { - pk: Key(public), - sk: Key(private), - }) - } - - fn public_from_private(secret: &Self::Repr) -> Self::Repr { - let mut secret_data = [0u8; 32]; - secret_data.copy_from_slice(&secret.0[..]); - let base_data = ::x25519_dalek::X25519_BASEPOINT_BYTES; - Key(::x25519_dalek::x25519(secret_data, base_data).to_vec()) - } - - fn check_public_key(key: Self::Repr) -> Result { - let mut key_bytes = [0u8; 32]; - key_bytes.copy_from_slice(&key); - let point = ::curve25519_dalek::montgomery::MontgomeryPoint(key_bytes) - .to_edwards(1) - .ok_or(InternalPakeError::PointError)?; - if !point.is_torsion_free() { - Err(InternalPakeError::SubGroupError) - } else { - Ok(key) - } - } - - fn diffie_hellman(pk: Self::Repr, sk: Self::Repr) -> Vec { - let mut pk_data = [0; 32]; - pk_data.copy_from_slice(&pk.0[..]); - let mut sk_data = [0; 32]; - sk_data.copy_from_slice(&sk.0[..]); - ::x25519_dalek::x25519(sk_data, pk_data).to_vec() - } -} - #[cfg(test)] mod tests { use super::*; - use proptest::prelude::*; proptest! { #[test] @@ -321,51 +237,5 @@ mod tests { prop_assert_eq!(&X25519KeyPair::public_from_private(sk), pk); } - #[test] - fn test_signal_check(kp in SignalKeyPair::uniform_keypair_strategy()) { - let pk = kp.public(); - prop_assert!(SignalKeyPair::check_public_key(pk.clone()).is_ok()); - } - - #[test] - fn test_signal_pub_from_priv(kp in SignalKeyPair::uniform_keypair_strategy()) { - let pk = kp.public(); - let sk = kp.private(); - prop_assert_eq!(&SignalKeyPair::public_from_private(sk), pk); - } - - #[test] - fn test_signal_x25519_roundtrips(kp_signal in SignalKeyPair::uniform_keypair_strategy(), - kp_x25519 in X25519KeyPair::uniform_keypair_strategy()) { - let kp_signal_bytes: &[u8] = &kp_signal.to_arr(); - let kp_x25519_bytes: &[u8] = &kp_x25519.to_arr(); - - let reinterpret_signal = X25519KeyPair::from_bytes(kp_signal_bytes).unwrap(); - let reinterpret_x25519 = SignalKeyPair::from_bytes(kp_x25519_bytes).unwrap(); - - prop_assert_eq!(kp_signal_bytes, &reinterpret_signal.to_arr()[..]); - prop_assert_eq!(kp_x25519_bytes, &reinterpret_x25519.to_arr()[..]); - } - - #[test] - fn test_signal_as_x25519(kp_signal in SignalKeyPair::uniform_keypair_strategy()) { - let kp: X25519KeyPair = X25519KeyPair::from_bytes(&kp_signal.to_arr()).unwrap(); - let pk = kp.public(); - prop_assert!(X25519KeyPair::check_public_key(pk.clone()).is_ok()); - let sk = kp.private(); - prop_assert_eq!(&X25519KeyPair::public_from_private(sk), pk); - - } - - #[test] - fn test_x25519_as_signal(kp_x25519 in X25519KeyPair::uniform_keypair_strategy()) { - let kp: SignalKeyPair = SignalKeyPair::from_bytes(&kp_x25519.to_arr()).unwrap(); - let pk = kp.public(); - prop_assert!(SignalKeyPair::check_public_key(pk.clone()).is_ok()); - let sk = kp.private(); - prop_assert_eq!(&SignalKeyPair::public_from_private(sk), pk); - - } - } } diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs index 2d42000..a9d666a 100644 --- a/src/tests/opaque_ke_test.rs +++ b/src/tests/opaque_ke_test.rs @@ -7,7 +7,7 @@ use crate::{ errors::*, group::Group, key_exchange::NONCE_LEN, - keypair::{Key, KeyPair, SignalKeyPair}, + keypair::{Key, KeyPair, X25519KeyPair}, opaque::*, slow_hash::NoOpHash, tests::mock_rng::CycleRng, @@ -215,10 +215,10 @@ fn generate_parameters() -> TestVectorParameters { let mut rng = OsRng; // Inputs - let server_s_kp = SignalKeyPair::generate_random(&mut rng).unwrap(); - let server_e_kp = SignalKeyPair::generate_random(&mut rng).unwrap(); - let client_s_kp = SignalKeyPair::generate_random(&mut rng).unwrap(); - let client_e_kp = SignalKeyPair::generate_random(&mut rng).unwrap(); + let server_s_kp = X25519KeyPair::generate_random(&mut rng).unwrap(); + let server_e_kp = X25519KeyPair::generate_random(&mut rng).unwrap(); + let client_s_kp = X25519KeyPair::generate_random(&mut rng).unwrap(); + let client_e_kp = X25519KeyPair::generate_random(&mut rng).unwrap(); let password = b"password"; let pepper = b"pepper"; let mut blinding_factor_raw = [0u8; 64]; @@ -245,7 +245,7 @@ fn generate_parameters() -> TestVectorParameters { let mut oprf_key_rng = CycleRng::new(oprf_key_raw.to_vec()); let (r2, server_registration) = - ServerRegistration::::start(r1, &mut oprf_key_rng) + ServerRegistration::::start(r1, &mut oprf_key_rng) .unwrap(); let r2_bytes = r2.to_bytes().to_vec(); let oprf_key = server_registration.oprf_key; @@ -258,7 +258,7 @@ fn generate_parameters() -> TestVectorParameters { let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce); let (r3, opaque_key_registration) = client_registration - .finish::<_, SignalKeyPair, NoOpHash>( + .finish::<_, X25519KeyPair, NoOpHash>( r2, server_s_kp.public(), &mut finish_registration_rng, @@ -275,7 +275,7 @@ fn generate_parameters() -> TestVectorParameters { client_login_start.extend_from_slice(&client_nonce); let mut client_login_start_rng = CycleRng::new(client_login_start); - let (l1, client_login) = ClientLogin::::start( + let (l1, client_login) = ClientLogin::::start( password, Some(pepper), &mut client_login_start_rng, @@ -363,7 +363,7 @@ fn test_r2() -> Result<(), PakeError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let mut oprf_key_rng = CycleRng::new(parameters.oprf_key); let (r2, server_registration) = - ServerRegistration::::start( + ServerRegistration::::start( RegisterFirstMessage::try_from(¶meters.r1[..]).unwrap(), &mut oprf_key_rng, ) @@ -387,7 +387,7 @@ fn test_r3() -> Result<(), PakeError> { ¶meters.client_registration_state[..], ) .unwrap() - .finish::( + .finish::( RegisterSecondMessage::try_from(¶meters.r2[..]).unwrap(), &Key::try_from(parameters.server_s_pk).unwrap(), &mut finish_registration_rng, @@ -408,7 +408,7 @@ fn test_password_file() -> Result<(), PakeError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let server_registration = - ServerRegistration::::try_from( + ServerRegistration::::try_from( ¶meters.server_registration_state[..], ) .unwrap(); @@ -434,7 +434,7 @@ fn test_l1() -> Result<(), PakeError> { ] .concat(); let mut client_login_start_rng = CycleRng::new(client_login_start); - let (l1, client_login) = ClientLogin::::start( + let (l1, client_login) = ClientLogin::::start( ¶meters.password, Some(¶meters.pepper), &mut client_login_start_rng, @@ -453,7 +453,7 @@ fn test_l2() -> Result<(), PakeError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let mut server_e_sk_rng = CycleRng::new(parameters.server_e_sk); - let (l2, server_login) = ServerLogin::start::<_, Aes256Gcm, _, SignalKeyPair>( + let (l2, server_login) = ServerLogin::start::<_, Aes256Gcm, _, X25519KeyPair>( ServerRegistration::try_from(¶meters.password_file[..]).unwrap(), &Key::try_from(parameters.server_s_sk).unwrap(), LoginFirstMessage::::try_from(¶meters.l1[..]).unwrap(), @@ -475,7 +475,7 @@ fn test_l3() -> Result<(), PakeError> { let mut client_e_sk_rng = CycleRng::new(parameters.client_e_sk.to_vec()); let (l3, shared_secret, opaque_key_login) = - ClientLogin::::try_from( + ClientLogin::::try_from( ¶meters.client_login_state[..], ) .unwrap() @@ -522,14 +522,14 @@ fn test_complete_flow( ) -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; - let server_kp = SignalKeyPair::generate_random(&mut server_rng)?; + let server_kp = X25519KeyPair::generate_random(&mut server_rng)?; let (register_m1, client_state) = ClientRegistration::::start( registration_password, None, &mut client_rng, )?; let (register_m2, server_state) = - ServerRegistration::::start( + ServerRegistration::::start( register_m1, &mut server_rng, )?; @@ -537,7 +537,7 @@ fn test_complete_flow( client_state.finish::<_, _, NoOpHash>(register_m2, server_kp.public(), &mut client_rng)?; let p_file = server_state.finish(register_m3)?; let (login_m1, client_login_state) = - ClientLogin::::start( + ClientLogin::::start( login_password, None, &mut client_rng, diff --git a/src/tests/serialization.rs b/src/tests/serialization.rs index 59dcb22..4c37fdb 100644 --- a/src/tests/serialization.rs +++ b/src/tests/serialization.rs @@ -5,7 +5,7 @@ use crate::{ group::Group, - keypair::{KeyPair, SignalKeyPair, SizedBytes}, + keypair::{KeyPair, SizedBytes, X25519KeyPair}, opaque::*, rkr_encryption::{RKRCipher as _, RKRCiphertext}, }; @@ -43,7 +43,7 @@ fn server_registration_roundtrip() { let sc = ::random_scalar(&mut rng); let mut oprf_bytes: Vec = vec![]; oprf_bytes.extend_from_slice(sc.as_bytes()); - let reg = ServerRegistration::::try_from( + let reg = ServerRegistration::::try_from( &oprf_bytes[..], ) .unwrap(); @@ -55,14 +55,14 @@ fn server_registration_roundtrip() { let mut mock_rkr_bytes = vec![0u8; rkr_size]; rng.fill_bytes(&mut mock_rkr_bytes); println!("{}", mock_rkr_bytes.len()); - let mock_client_kp = SignalKeyPair::generate_random(&mut rng).unwrap(); + let mock_client_kp = X25519KeyPair::generate_random(&mut rng).unwrap(); // serialization order: scalar, public key, envelope let mut bytes = Vec::::new(); bytes.extend_from_slice(sc.as_bytes()); bytes.extend_from_slice(&mock_client_kp.public().to_arr()); bytes.extend_from_slice(&mock_rkr_bytes); let reg = - ServerRegistration::::try_from(&bytes[..]) + ServerRegistration::::try_from(&bytes[..]) .unwrap(); let reg_bytes = reg.to_bytes(); assert_eq!(reg_bytes, bytes); @@ -91,7 +91,7 @@ fn register_second_message_roundtrip() { #[test] fn register_third_message_roundtrip() { let mut rng = OsRng; - let skp = SignalKeyPair::generate_random(&mut rng).unwrap(); + let skp = X25519KeyPair::generate_random(&mut rng).unwrap(); let pubkey_bytes = skp.public().to_arr(); let mut encryption_key = [0u8; 32]; @@ -113,7 +113,7 @@ fn register_third_message_roundtrip() { let message: Vec = [&ciphertext.to_bytes(), &pubkey_bytes[..]].concat(); let r3 = - RegisterThirdMessage::::try_from(&message[..]).unwrap(); + RegisterThirdMessage::::try_from(&message[..]).unwrap(); let r3_bytes = r3.to_bytes(); assert_eq!(message, r3_bytes); } From 3ae1c58d46cfa4fef5212c0d23d3b68b1399214b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Fri, 12 Jun 2020 18:46:08 -0400 Subject: [PATCH 4/4] test DH as well --- Cargo.toml | 2 +- src/keypair.rs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2204012..8e6ca88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,4 +33,4 @@ hex = "0.4.2" lazy_static = "1.4.0" serde_json = "1.0" proptest = "0.10.0" -rand = "0.7" \ No newline at end of file +rand = "0.7" diff --git a/src/keypair.rs b/src/keypair.rs index e49df75..b660ee1 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -237,5 +237,15 @@ mod tests { prop_assert_eq!(&X25519KeyPair::public_from_private(sk), pk); } + + #[test] + fn test_x25519_dh(kp1 in X25519KeyPair::uniform_keypair_strategy(), + kp2 in X25519KeyPair::uniform_keypair_strategy()) { + + let dh1 = X25519KeyPair::diffie_hellman(kp1.public().clone(), kp2.private().clone()); + let dh2 = X25519KeyPair::diffie_hellman(kp2.public().clone(), kp1.private().clone()); + + prop_assert_eq!(dh1,dh2); + } } }