diff --git a/Cargo.lock b/Cargo.lock index 8db1ca1..b7d61d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,47 +8,6 @@ dependencies = [ "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "aes" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes-soft 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aes-gcm" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "aes 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ghash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aes-soft" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aesni" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "autocfg" version = "1.0.0" @@ -88,14 +47,6 @@ dependencies = [ "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "block-cipher" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "block-padding" version = "0.1.5" @@ -119,27 +70,6 @@ name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "chacha20" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "stream-cipher 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "chacha20poly1305" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "chacha20 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "poly1305 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crypto-mac" version = "0.8.0" @@ -214,14 +144,6 @@ dependencies = [ "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "ghash" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "polyval 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "hex" version = "0.4.2" @@ -278,9 +200,7 @@ name = "opaque-ke" version = "0.1.0" dependencies = [ "aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "aes-gcm 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "chacha20poly1305 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "curve25519-dalek 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -306,23 +226,6 @@ dependencies = [ "crypto-mac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "poly1305" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "universal-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "polyval" -version = "0.4.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)", - "universal-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "ppv-lite86" version = "0.2.8" @@ -487,14 +390,6 @@ dependencies = [ "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "stream-cipher" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "subtle" version = "2.2.2" @@ -562,15 +457,6 @@ name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "universal-hash" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "version_check" version = "0.9.2" @@ -639,23 +525,16 @@ dependencies = [ [metadata] "checksum aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3672ba0d12aaf256aabcdab516ebed87b5cec4b602316d7a3035fac9b7a9567b" -"checksum aes 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f7001367fde4c768a19d1029f0a8be5abd9308e1119846d5bd9ad26297b8faf5" -"checksum aes-gcm 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86f5007801316299f922a6198d1d09a0bae95786815d066d5880d13f7c45ead1" -"checksum aes-soft 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4925647ee64e5056cf231608957ce7c81e12d6d6e316b9ce1404778cc1d35fa7" -"checksum aesni 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d050d39b0b7688b3a3254394c3e30a9d66c41dcf9b05b0e2dbdc623f6505d264" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" "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.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dbcf92448676f82bb7a334c58bbce8b0d43580fb5362a9d608b18879d12a3d31" -"checksum block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fa136449e765dc7faa244561ccae839c394048667929af599b5d931ebe7b7f10" "checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum chacha20 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "086c0f07ac275808b7bf9a39f2fd013aae1498be83632814c8c4e0bd53f2dc58" -"checksum chacha20poly1305 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "18b0c90556d8e3fec7cf18d84a2f53d27b21288f2fe481b830fadcf809e48205" "checksum crypto-mac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" "checksum curve25519-dalek 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" @@ -665,7 +544,6 @@ dependencies = [ "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ac746a5f3bbfdadd6106868134545e684693d54d9d44f6e9588a7d54af0bf980" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -"checksum ghash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6e27f0689a6e15944bdce7e45425efb87eaa8ab0c6e87f11d0987a9133e2531" "checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" "checksum hkdf 0.9.0-alpha.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e551da9a76291df932270bc2b100d0571588eaa9ef77af7bceee80dba9ace3ad" "checksum hmac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b87b580bd66811cc2324a27f3587de707cacf7525b96dca8122f7493e6cce0da" @@ -675,8 +553,6 @@ dependencies = [ "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.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" -"checksum poly1305 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b42192ab143ed7619bf888a7f9c6733a9a2153b218e2cd557cfdb52fbf9bb1" -"checksum polyval 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9a50142b55ab3ed0e9f68dfb3709f1d90d29da24e91033f28b96330643107dc" "checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" "checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" "checksum proptest 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2520fe6373cf6a3a61e2d200e987c183778ade8d9248ac3e6614ab0edfe4a0c1" @@ -696,7 +572,6 @@ dependencies = [ "checksum serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" "checksum serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" "checksum sha2 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "72377440080fd008550fe9b441e854e43318db116f90181eef92e9ae9aedab48" -"checksum stream-cipher 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "09f8ed9974042b8c3672ff3030a69fcc03b74c47c3d1ecb7755e8a3626011e88" "checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" "checksum syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" "checksum synstructure 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" @@ -705,7 +580,6 @@ dependencies = [ "checksum thiserror-impl 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479" "checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum universal-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" "checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" "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" diff --git a/Cargo.toml b/Cargo.toml index 56de081..0881562 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,9 +26,7 @@ x25519-dalek = "0.6.0" zeroize = "1.1" [dev-dependencies] -aes-gcm = "0.6.0" base64 = "0.12.1" -chacha20poly1305 = "0.5.1" hex = "0.4.2" lazy_static = "1.4.0" serde_json = "1.0.55" diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs index 04b65a5..9f96968 100644 --- a/src/ciphersuite.rs +++ b/src/ciphersuite.rs @@ -15,12 +15,10 @@ use generic_array::typenum::{U32, U64}; use rand_core::{CryptoRng, RngCore}; /// Configures the underlying primitives used in OPAQUE -/// * Aead: an authenticated encryption scheme /// * Group: a finite cyclic group along with a point representation /// * KeyFormat: a keypair type composed of public and private components /// * SlowHash: a slow hashing function, typically used for password hashing pub trait CipherSuite { - type Aead: aead::NewAead + aead::Aead; type Group: Group; type KeyFormat: KeyPair + PartialEq; type SlowHash: SlowHash; diff --git a/src/envelope.rs b/src/envelope.rs new file mode 100644 index 0000000..bb6bbb5 --- /dev/null +++ b/src/envelope.rs @@ -0,0 +1,182 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +use crate::errors::InternalPakeError; +use generic_array::{ + typenum::{Unsigned, U32}, + GenericArray, +}; +use hkdf::Hkdf; +use hmac::{Hmac, Mac, NewMac}; +use rand_core::{CryptoRng, RngCore}; +use sha2::{Digest, Sha256}; + +// Constant string used as salt for HKDF computation +const STR_ENVU: &[u8] = b"EnvU"; + +/// The length of the "export key" output by the client registration +/// and login finish steps +pub(crate) type ExportKeySize = ::OutputSize; + +/// This struct is an instantiation of the envelope as described in +/// https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-06#section-4 +/// +/// Note that earlier versions of this specification described an +/// implementation of this envelope using an encryption scheme that +/// satisfied random-key robustness +/// (https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-05#section-4). +/// The specification update has simplified this assumption by taking +/// an XOR-based approach without compromising on security, and to avoid +/// the confusion around the implementation of an RKR-secure encryption. +pub(crate) struct Envelope { + nonce: Vec, + ciphertext: Vec, + hmac: Vec, +} + +type NonceLen = U32; + +impl Envelope { + /// The additional number of bytes added to the plaintext + pub(crate) fn additional_size() -> usize { + Self::nonce_size() + ::OutputSize::to_usize() + } + + fn hmac_key_size() -> usize { + ::OutputSize::to_usize() + } + + fn hmac_size() -> usize { + ::OutputSize::to_usize() + } + + fn nonce_size() -> usize { + NonceLen::to_usize() + } + + fn export_key_size() -> usize { + ExportKeySize::to_usize() + } + + pub(crate) fn new( + nonce: Vec, + ciphertext: Vec, + hmac: &GenericArray::OutputSize>, + ) -> Self { + Self { + nonce, + ciphertext, + hmac: hmac.to_vec(), + } + } + + /// The format of the output is: + /// nonce | ciphertext | hmac + /// nonce_size bytes | variable length | hmac_size bytes + pub(crate) fn from_bytes(bytes: &[u8]) -> Result { + let ciphertext_start = Self::nonce_size(); + let ciphertext_end = bytes.len() - Self::hmac_size(); + + Ok(Self::new( + bytes[..ciphertext_start].to_vec(), + bytes[ciphertext_start..ciphertext_end].to_vec(), + GenericArray::from_slice(&bytes[ciphertext_end..]), + )) + } + + pub(crate) fn to_bytes(&self) -> Vec { + [&self.nonce[..], &self.ciphertext[..], &self.hmac[..]].concat() + } + + /// Uses a key to convert the plaintext into an envelope, authenticated by the aad field. + /// Note that a new nonce is sampled for each call to seal. + pub(crate) fn seal( + key: &[u8], + plaintext: &[u8], + aad: &[u8], + rng: &mut R, + ) -> Result<(Self, GenericArray), InternalPakeError> { + let mut nonce = vec![0u8; Self::nonce_size()]; + rng.fill_bytes(&mut nonce); + + let h = Hkdf::::new(Some(&nonce), &key); + let mut okm = vec![0u8; plaintext.len() + Self::hmac_key_size() + Self::export_key_size()]; + h.expand(STR_ENVU, &mut okm) + .map_err(|_| InternalPakeError::HkdfError)?; + let xor_key = &okm[..plaintext.len()]; + let hmac_key = &okm[plaintext.len()..plaintext.len() + Self::hmac_key_size()]; + let export_key = &okm[plaintext.len() + Self::hmac_key_size()..]; + + let ciphertext: Vec = xor_key + .iter() + .zip(plaintext.to_vec().iter()) + .map(|(&x1, &x2)| x1 ^ x2) + .collect(); + + let mut hmac = + Hmac::::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?; + hmac.update(&ciphertext); + hmac.update(&aad); + + Ok(( + Self::new(nonce, ciphertext.to_vec(), &hmac.finalize().into_bytes()), + *GenericArray::from_slice(&export_key), + )) + } + + /// Attempts to decrypt the envelope using a key, which is successful only if the key and + /// aad used to construct the envelope are the same. + pub(crate) fn open( + &self, + key: &[u8], + aad: &[u8], + ) -> Result<(Vec, GenericArray), InternalPakeError> { + let h = Hkdf::::new(Some(&self.nonce), &key); + let mut okm = + vec![0u8; self.ciphertext.len() + Self::hmac_key_size() + Self::export_key_size()]; + h.expand(STR_ENVU, &mut okm) + .map_err(|_| InternalPakeError::HkdfError)?; + let xor_key = &okm[..self.ciphertext.len()]; + let hmac_key = &okm[self.ciphertext.len()..self.ciphertext.len() + Self::hmac_key_size()]; + let export_key = &okm[self.ciphertext.len() + Self::hmac_key_size()..]; + + let mut hmac = + Hmac::::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?; + hmac.update(&self.ciphertext); + hmac.update(aad); + if hmac.verify(&self.hmac).is_err() { + return Err(InternalPakeError::SealOpenHmacError); + } + + let plaintext: Vec = xor_key + .to_vec() + .iter() + .zip(self.ciphertext.iter()) + .map(|(&x1, &x2)| x1 ^ x2) + .collect(); + Ok((plaintext, *GenericArray::from_slice(&export_key))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use rand_core::OsRng; + + #[test] + fn seal_and_open() { + let mut rng = OsRng; + let mut key = [0u8; 32]; + rng.fill_bytes(&mut key); + + let mut msg = [0u8; 100]; + rng.fill_bytes(&mut msg); + + let (ciphertext, export_key_1) = Envelope::seal(&key, &msg, b"aad", &mut rng).unwrap(); + let (plaintext, export_key_2) = ciphertext.open(&key, b"aad").unwrap(); + assert_eq!(&msg.to_vec(), &plaintext); + assert_eq!(&export_key_1.to_vec(), &export_key_2.to_vec()); + } +} diff --git a/src/errors.rs b/src/errors.rs index b3d46eb..de76ca1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -28,6 +28,15 @@ pub enum InternalPakeError { HmacError, #[error("Computing the slow hashing function failed")] SlowHashError, + /// This error occurs when the envelope seal fails + #[error("Constructing the envelope seal failed.")] + SealError, + /// This error occurs when the envelope seal open fails + #[error("Opening the envelope seal failed.")] + SealOpenError, + /// This error occurs when the envelope seal open hmac check fails + #[error("HMAC check in seal open failed.")] + SealOpenHmacError, } /// Represents an error in password checking @@ -37,19 +46,10 @@ pub enum PakeError { /// #[error("Internal error during PRF verification: {0}")] CryptoError(InternalPakeError), - /// This error occurs when the symmetric encryption fails - #[error("Symmetric encryption failed.")] - EncryptionError, - /// This error occurs when the symmetric decryption fails - #[error("Symmetric decryption failed.")] - DecryptionError, - /// This error occurs when the symmetric decryption's hmac check fails - #[error("HMAC check in symmetric decryption failed.")] - DecryptionHmacError, /// This error occurs when the server object that is being called finish() on is malformed #[error("Incomplete set of keys passed into finish() function")] IncompleteKeysError, - #[error("The provided server public key doesn't match the encrypted one")] + #[error("The provided server public key doesn't match the sealed one")] IncompatibleServerStaticPublicKeyError, #[error("Error in key exchange protocol when attempting to validate MACs")] KeyExchangeMacValidationError, diff --git a/src/lib.rs b/src/lib.rs index dfa2e8a..cea3e78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,14 +5,13 @@ //! An implementation of the OPAQUE asymmetric password authentication key exchange protocol //! -//! Note: This implementation is in sync with [draft-krawczyk-cfrg-opaque-05](https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-05), +//! Note: This implementation is in sync with [draft-krawczyk-cfrg-opaque-06](https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-06), //! but this specification is subject to change, until the final version published by the IETF. //! //! # Overview //! //! OPAQUE is a protocol between a client and a server. They must first agree on a collection of primitives //! to be kept consistent throughout protocol execution. These include: -//! * an authenticated encryption scheme, //! * a finite cyclic group along with a point representation, //! * a keypair type, and //! * a slow hashing function. @@ -22,7 +21,6 @@ //! use opaque_ke::ciphersuite::CipherSuite; //! struct Default; //! impl CipherSuite for Default { -//! type Aead = chacha20poly1305::ChaCha20Poly1305; //! type Group = curve25519_dalek::ristretto::RistrettoPoint; //! type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -43,7 +41,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Aead = chacha20poly1305::ChaCha20Poly1305; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -75,7 +72,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Aead = chacha20poly1305::ChaCha20Poly1305; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -104,7 +100,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Aead = chacha20poly1305::ChaCha20Poly1305; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -125,7 +120,7 @@ //! //! In the third step (client registration finish), the client takes as input the `r2` message from the server, along //! with the server's static public key `server_kp.public()`, and uses `client_state` from the first step to run -//! `finish` and produce a message `r3` along with the key derivation key `kd_key_registration`: +//! `finish` and produce a message `r3` along with the export key `export_key_registration`: //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -136,7 +131,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Aead = chacha20poly1305::ChaCha20Poly1305; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -151,11 +145,11 @@ //! # let mut server_rng = OsRng; //! let (r2, server_state) = ServerRegistration::::start(r1, &mut server_rng)?; //! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; -//! let (r3, kd_key_registration) = +//! let (r3, export_key_registration) = //! client_state.finish(r2, server_kp.public(), &mut client_rng)?; //! # Ok::<(), ProtocolError>(()) //! ``` -//! `r3` is sent to the server, and the client can optionally use `kd_key_registration` for applications that choose to +//! `r3` is sent to the server, and the client can optionally use `export_key_registration` for applications that choose to //! process user information beyond the OPAQUE functionality (e.g., additional secrets or credentials). //! //! In the fourth step of registration, the server takes as input the `r3` message from the client and uses @@ -170,7 +164,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Aead = chacha20poly1305::ChaCha20Poly1305; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -185,7 +178,7 @@ //! # let mut server_rng = OsRng; //! let (r2, server_state) = ServerRegistration::::start(r1, &mut server_rng)?; //! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; -//! # let (r3, kd_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; +//! # let (r3, export_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; //! let password_file = server_state.finish(r3)?; //! # Ok::<(), ProtocolError>(()) //! ``` @@ -212,7 +205,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Aead = chacha20poly1305::ChaCha20Poly1305; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -241,7 +233,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Aead = chacha20poly1305::ChaCha20Poly1305; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -256,7 +247,7 @@ //! # let mut server_rng = OsRng; //! let (r2, server_state) = ServerRegistration::::start(r1, &mut server_rng)?; //! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; -//! # let (r3, kd_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; +//! # let (r3, export_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; //! # let password_file_bytes = server_state.finish(r3)?.to_bytes(); //! # let (l1, client_state) = ClientLogin::::start( //! # b"password", @@ -274,7 +265,7 @@ //! //! In the third step (client login finish), the client takes as input the `l2` message from the server, along with the //! server's static public key `server_kp.public()`, and uses `client_state` from the first step to run `finish` and produce -//! a message `l3`, the shared secret `client_shared_secret`, and the key derivation key `kd_key_login`: +//! a message `l3`, the shared secret `client_shared_secret`, and the export key `export_key_login`: //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -285,7 +276,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Aead = chacha20poly1305::ChaCha20Poly1305; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -300,7 +290,7 @@ //! # let mut server_rng = OsRng; //! let (r2, server_state) = ServerRegistration::::start(r1, &mut server_rng)?; //! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; -//! # let (r3, kd_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; +//! # let (r3, export_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; //! # let password_file_bytes = server_state.finish(r3)?.to_bytes(); //! # let (l1, client_state) = ClientLogin::::start( //! # b"password", @@ -314,20 +304,20 @@ //! # )?; //! # let (l2, server_state) = //! # ServerLogin::start(password_file, &server_kp.private(), l1, &mut server_rng)?; -//! let (l3, client_shared_secret, kd_key_login) = client_state.finish( +//! let (l3, client_shared_secret, export_key_login) = client_state.finish( //! l2, //! &server_kp.public(), //! &mut client_rng, //! )?; -//! assert_eq!(kd_key_registration, kd_key_login); +//! assert_eq!(export_key_registration, export_key_login); //! # Ok::<(), ProtocolError>(()) //! ``` //! Note that if the client supplies a tuple (password, pepper, server public key) that does not match the tuple //! used to create the password file, then at this point the `finish` algorithm outputs the error `InvalidLoginError`. //! //! If `finish` completes successfully, then `l3` is sent to the server, and (similarly to registration) the client -//! can use `kd_key_login` for applications that can take advantage of the fact that this key is identical to -//! `kd_key_registration`. +//! can use `export_key_login` for applications that can take advantage of the fact that this key is identical to +//! `export_key_registration`. //! //! In the fourth step of login, the server takes as input the `l3` message from the client and uses `server_state` from //! the second step to run `finish`: @@ -341,7 +331,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Aead = chacha20poly1305::ChaCha20Poly1305; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -356,7 +345,7 @@ //! # let mut server_rng = OsRng; //! let (r2, server_state) = ServerRegistration::::start(r1, &mut server_rng)?; //! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; -//! # let (r3, kd_key) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; +//! # let (r3, export_key) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; //! # let password_file_bytes = server_state.finish(r3)?.to_bytes(); //! # let (l1, client_state) = ClientLogin::::start( //! # b"password", @@ -370,7 +359,7 @@ //! # )?; //! # let (l2, server_state) = //! # ServerLogin::start(password_file, &server_kp.private(), l1, &mut server_rng)?; -//! # let (l3, client_shared_secret, kd_key) = client_state.finish( +//! # let (l3, client_shared_secret, export_key) = client_state.finish( //! # l2, //! # &server_kp.public(), //! # &mut client_rng, @@ -385,20 +374,16 @@ // Error types pub mod errors; + // High-level API pub mod opaque; pub mod ciphersuite; -// Your choice of RKR encryption -mod rkr_encryption; -// Your choice of KE +mod envelope; +mod group; mod key_exchange; pub mod keypair; -// Low-level API contains OPRF stuff mod oprf; -// Technical module for your choice of cyclic subgroup to -// do the oprf on -mod group; pub mod slow_hash; #[cfg(test)] diff --git a/src/opaque.rs b/src/opaque.rs index b66801e..6600407 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -7,6 +7,7 @@ use crate::{ ciphersuite::CipherSuite, + envelope::{Envelope, ExportKeySize}, errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError}, group::Group, key_exchange::{ @@ -16,27 +17,13 @@ use crate::{ keypair::{Key, KeyPair, SizedBytes}, oprf, oprf::OprfClientBytes, - rkr_encryption::{RKRCipher, RKRCiphertext}, slow_hash::SlowHash, }; -use generic_array::{ - typenum::{Unsigned, U32}, - GenericArray, -}; -use hkdf::Hkdf; +use generic_array::{typenum::Unsigned, GenericArray}; use rand_core::{CryptoRng, RngCore}; - -use sha2::{Digest, Sha256}; use std::{convert::TryFrom, marker::PhantomData}; use zeroize::Zeroize; -// Constant string used as salt for HKDF computation -const STR_ENVU: &[u8] = b"EnvU"; - -/// The length of the "key-derivation key" output by the client registration -/// and login finish steps -pub const DERIVED_KEY_LEN: usize = 32; - // Messages // ========= @@ -99,19 +86,18 @@ where } } -/// The final message from the client, containing encrypted cryptographic +/// The final message from the client, containing sealed cryptographic /// identifiers -pub struct RegisterThirdMessage { - /// The "envelope" generated by the user, containing encrypted +pub struct RegisterThirdMessage { + /// The "envelope" generated by the user, containing sealed /// cryptographic identifiers - envelope: RKRCiphertext, + envelope: Envelope, /// The user's public key client_s_pk: KeyFormat::Repr, } -impl RegisterThirdMessage +impl RegisterThirdMessage where - Aead: aead::Aead + aead::NewAead, KeyFormat: KeyPair, { pub fn to_bytes(&self) -> Vec { @@ -123,23 +109,25 @@ where } } -impl TryFrom<&[u8]> for RegisterThirdMessage +impl TryFrom<&[u8]> for RegisterThirdMessage where - Aead: aead::Aead + aead::NewAead, KeyFormat: KeyPair, { type Error = ProtocolError; fn try_from(third_message_bytes: &[u8]) -> Result { - let rkr_size = RKRCiphertext::::rkr_with_nonce_size(); let key_len = ::Len::to_usize(); - let checked_bytes = - check_slice_size(third_message_bytes, rkr_size + key_len, "third_message")?; - let unchecked_client_s_pk = KeyFormat::Repr::from_bytes(&checked_bytes[rkr_size..])?; + let envelope_size = key_len + Envelope::additional_size(); + let checked_bytes = check_slice_size( + third_message_bytes, + envelope_size + key_len, + "third_message", + )?; + let unchecked_client_s_pk = KeyFormat::Repr::from_bytes(&checked_bytes[envelope_size..])?; let client_s_pk = KeyFormat::check_public_key(unchecked_client_s_pk)?; Ok(Self { - envelope: RKRCiphertext::from_bytes(&checked_bytes[..rkr_size])?, + envelope: Envelope::from_bytes(&checked_bytes[..envelope_size])?, client_s_pk, }) } @@ -178,18 +166,19 @@ impl LoginFirstMessage { /// The answer sent by the server to the user, upon reception of the /// login attempt. -pub struct LoginSecondMessage { +pub struct LoginSecondMessage { + _key_format: PhantomData, /// the server's oprf output beta: Grp, - /// the user's encrypted information, - envelope: RKRCiphertext, + /// the user's sealed information, + envelope: Envelope, ke2_message: KE2Message, } -impl LoginSecondMessage +impl LoginSecondMessage where - Aead: aead::NewAead + aead::Aead, Grp: Group, + KeyFormat: KeyPair, { pub fn to_bytes(&self) -> Vec { [ @@ -201,18 +190,19 @@ where } } -impl TryFrom<&[u8]> for LoginSecondMessage +impl TryFrom<&[u8]> for LoginSecondMessage where - Aead: aead::NewAead + aead::Aead, Grp: Group, + KeyFormat: KeyPair, { type Error = ProtocolError; fn try_from(second_message_bytes: &[u8]) -> Result { - let cipher_len = RKRCiphertext::::rkr_with_nonce_size(); + let key_len = ::Len::to_usize(); + let envelope_size = key_len + Envelope::additional_size(); let elem_len = Grp::ElemLen::to_usize(); let checked_slice = check_slice_size( second_message_bytes, - elem_len + cipher_len + KE2_MESSAGE_LEN, + elem_len + envelope_size + KE2_MESSAGE_LEN, "login_second_message_bytes", )?; @@ -222,11 +212,11 @@ where let arr = GenericArray::from_slice(beta_bytes); let beta = Grp::from_element_slice(arr)?; - let envelope = - RKRCiphertext::::from_bytes(&checked_slice[elem_len..elem_len + cipher_len])?; - let ke2_message = KE2Message::try_from(&checked_slice[elem_len + cipher_len..])?; + let envelope = Envelope::from_bytes(&checked_slice[elem_len..elem_len + envelope_size])?; + let ke2_message = KE2Message::try_from(&checked_slice[elem_len + envelope_size..])?; Ok(Self { + _key_format: PhantomData, beta, envelope, ke2_message, @@ -235,7 +225,7 @@ where } /// The answer sent by the client to the server, upon reception of the -/// encrypted envelope +/// sealed envelope pub struct LoginThirdMessage { ke3_message: KE3Message, } @@ -260,8 +250,6 @@ impl LoginThirdMessage { /// The state elements the client holds to register itself pub struct ClientRegistration { - /// A choice of symmetric encryption for the envelope - _aead: PhantomData, /// a blinding factor pub(crate) blinding_factor: <::Group as Group>::Scalar, /// the client's password @@ -278,7 +266,6 @@ impl TryFrom<&[u8]> for ClientRegistration { let blinding_factor = CS::Group::from_scalar_slice(blinding_factor_bytes)?; let password = bytes[scalar_len..].to_vec(); Ok(Self { - _aead: PhantomData, blinding_factor, password, }) @@ -311,7 +298,6 @@ impl ClientRegistration { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Aead = chacha20poly1305::ChaCha20Poly1305; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -333,7 +319,6 @@ impl ClientRegistration { Ok(( RegisterFirstMessage:: { alpha }, Self { - _aead: PhantomData, blinding_factor, password: password.to_vec(), }, @@ -341,9 +326,9 @@ impl ClientRegistration { } } -type ClientRegistrationFinishResult = ( - RegisterThirdMessage, - GenericArray::OutputSize>, +type ClientRegistrationFinishResult = ( + RegisterThirdMessage, + GenericArray, ); impl ClientRegistration { @@ -363,7 +348,6 @@ impl ClientRegistration { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Aead = chacha20poly1305::ChaCha20Poly1305; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -383,7 +367,7 @@ impl ClientRegistration { r2: RegisterSecondMessage, server_s_pk: &<::KeyFormat as KeyPair>::Repr, rng: &mut R, - ) -> Result, ProtocolError> { + ) -> Result, ProtocolError> { let client_static_keypair = CS::KeyFormat::generate_random(rng)?; let password_derived_key = get_password_derived_key::( @@ -391,17 +375,9 @@ impl ClientRegistration { r2.beta, &self.blinding_factor, )?; - let h = Hkdf::::new(None, &password_derived_key); - let mut okm = [0u8; 3 * DERIVED_KEY_LEN]; - h.expand(STR_ENVU, &mut okm) - .map_err(|_| InternalPakeError::HkdfError)?; - let encryption_key = &okm[..DERIVED_KEY_LEN]; - let hmac_key = &okm[DERIVED_KEY_LEN..2 * DERIVED_KEY_LEN]; - let kd_key = &okm[2 * DERIVED_KEY_LEN..]; - let envelope = RKRCiphertext::::encrypt( - &encryption_key, - &hmac_key, + let (envelope, export_key) = Envelope::seal( + &password_derived_key, &client_static_keypair.private().to_arr(), &server_s_pk.to_arr(), rng, @@ -412,7 +388,7 @@ impl ClientRegistration { envelope, client_s_pk: client_static_keypair.public().clone(), }, - *GenericArray::from_slice(&kd_key), + export_key, )) } } @@ -447,7 +423,7 @@ impl Drop for ClientLogin { /// The state elements the server holds to record a registration pub struct ServerRegistration { - envelope: Option>, + envelope: Option, client_s_pk: Option<<::KeyFormat as KeyPair>::Repr>, pub(crate) oprf_key: <::Group as Group>::Scalar, } @@ -466,7 +442,7 @@ where let key_len = <<::KeyFormat as KeyPair>::Repr as SizedBytes>::Len::to_usize(); let scalar_len = <::Group as Group>::ScalarLen::to_usize(); - let rkr_size = RKRCiphertext::::rkr_with_nonce_size(); + let envelope_size = key_len + Envelope::additional_size(); if server_registration_bytes.len() == scalar_len { return Ok(Self { @@ -480,7 +456,7 @@ where let checked_bytes = check_slice_size( server_registration_bytes, - rkr_size + key_len + scalar_len, + envelope_size + key_len + scalar_len, "server_registration_bytes", )?; let oprf_key_bytes = GenericArray::from_slice(&checked_bytes[..scalar_len]); @@ -490,8 +466,8 @@ where )?; let client_s_pk = CS::KeyFormat::check_public_key(unchecked_client_s_pk)?; Ok(Self { - envelope: Some(RKRCiphertext::from_bytes( - &checked_bytes[checked_bytes.len() - rkr_size..], + envelope: Some(Envelope::from_bytes( + &checked_bytes[checked_bytes.len() - envelope_size..], )?), client_s_pk: Some(client_s_pk), oprf_key, @@ -536,7 +512,6 @@ where /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Aead = chacha20poly1305::ChaCha20Poly1305; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -583,7 +558,6 @@ where /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Aead = chacha20poly1305::ChaCha20Poly1305; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -601,7 +575,7 @@ where /// ``` pub fn finish( self, - message: RegisterThirdMessage, + message: RegisterThirdMessage, ) -> Result { Ok(Self { envelope: Some(message.envelope), @@ -616,8 +590,6 @@ where /// The state elements the client holds to perform a login pub struct ClientLogin { - /// A choice of symmetric encryption for the envelope - _aead: PhantomData, /// A choice of the keypair type _key_format: PhantomData, /// A blinding factor, which is used to mask (and unmask) secret @@ -637,7 +609,6 @@ impl TryFrom<&[u8]> for ClientLogin { let ke1_state = KE1State::try_from(&bytes[scalar_len..scalar_len + KE1_STATE_LEN])?; let password = bytes[scalar_len + KE1_STATE_LEN..].to_vec(); Ok(Self { - _aead: PhantomData, _key_format: PhantomData, blinding_factor, password, @@ -658,11 +629,7 @@ impl ClientLogin { } } -type ClientLoginFinishResult = ( - LoginThirdMessage, - Vec, - GenericArray::OutputSize>, -); +type ClientLoginFinishResult = (LoginThirdMessage, Vec, GenericArray); impl ClientLogin { /// Returns an initial "blinded" password request to send to the server, as well as a ClientLogin @@ -679,7 +646,6 @@ impl ClientLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Aead = chacha20poly1305::ChaCha20Poly1305; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -706,7 +672,6 @@ impl ClientLogin { Ok(( l1, Self { - _aead: PhantomData, _key_format: PhantomData, blinding_factor, password: password.to_vec(), @@ -715,7 +680,7 @@ impl ClientLogin { )) } - /// "Unblinds" the server's answer and returns the decrypted assets from + /// "Unblinds" the server's answer and returns the opened assets from /// the server /// /// # Arguments @@ -732,7 +697,6 @@ impl ClientLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Aead = chacha20poly1305::ChaCha20Poly1305; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -751,7 +715,7 @@ impl ClientLogin { /// ``` pub fn finish( self, - l2: LoginSecondMessage, + l2: LoginSecondMessage, server_s_pk: &<::KeyFormat as KeyPair>::Repr, _client_e_sk_rng: &mut R, ) -> Result { @@ -762,35 +726,27 @@ impl ClientLogin { l2.beta, &self.blinding_factor, )?; - let h = Hkdf::::new(None, &password_derived_key); - let mut okm = [0u8; 3 * DERIVED_KEY_LEN]; - h.expand(STR_ENVU, &mut okm) - .map_err(|_| InternalPakeError::HkdfError)?; - let encryption_key = &okm[..DERIVED_KEY_LEN]; - let hmac_key = &okm[DERIVED_KEY_LEN..2 * DERIVED_KEY_LEN]; - let kd_key = &okm[2 * DERIVED_KEY_LEN..]; - let client_s_sk = Key::from_bytes( - &l2.envelope - .decrypt(&encryption_key, &hmac_key, &server_s_pk.to_arr()) - .map_err(|e| match e { - PakeError::DecryptionHmacError => PakeError::InvalidLoginError, - err => err, - })?, - )?; + let (client_s_sk, export_key) = &l2 + .envelope + .open(&password_derived_key, &server_s_pk.to_arr()) + .map_err(|e| match e { + InternalPakeError::SealOpenHmacError => PakeError::InvalidLoginError, + err => PakeError::from(err), + })?; let (ke3_state, ke3_message) = generate_ke3::( l2_bytes, l2.ke2_message, &self.ke1_state, server_s_pk.clone(), - client_s_sk, + Key::from_bytes(client_s_sk)?, )?; Ok(( LoginThirdMessage { ke3_message }, ke3_state.shared_secret, - *GenericArray::from_slice(&kd_key), + *export_key, )) } } @@ -810,7 +766,7 @@ impl TryFrom<&[u8]> for ServerLogin { } type ServerLoginStartResult = ( - LoginSecondMessage<::Aead, ::Group>, + LoginSecondMessage<::Group, ::KeyFormat>, ServerLogin, ); @@ -836,7 +792,6 @@ impl ServerLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Aead = chacha20poly1305::ChaCha20Poly1305; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -864,8 +819,8 @@ impl ServerLogin { let client_s_pk = password_file .client_s_pk - .ok_or(PakeError::EncryptionError)?; - let envelope = password_file.envelope.ok_or(PakeError::EncryptionError)?; + .ok_or(InternalPakeError::SealError)?; + let envelope = password_file.envelope.ok_or(InternalPakeError::SealError)?; let l2_component: Vec = [beta.to_bytes().as_slice(), &envelope.to_bytes()].concat(); @@ -880,6 +835,7 @@ impl ServerLogin { )?; let l2 = LoginSecondMessage { + _key_format: PhantomData, beta, envelope, ke2_message, @@ -905,7 +861,6 @@ impl ServerLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Aead = chacha20poly1305::ChaCha20Poly1305; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; diff --git a/src/oprf.rs b/src/oprf.rs index 2436fb5..6e5be4b 100644 --- a/src/oprf.rs +++ b/src/oprf.rs @@ -7,13 +7,8 @@ use crate::{errors::InternalPakeError, group::Group}; use generic_array::{typenum::U64, GenericArray}; use hkdf::Hkdf; use rand_core::{CryptoRng, RngCore}; - use sha2::{Digest, Sha256}; -// Low-level API -// ============= -// This file contains an implementation of an oblivious pseudorandom function (OPRF), as well as password hashing and encryption functions. - pub(crate) struct OprfClientBytes { pub(crate) alpha: Grp, pub(crate) blinding_factor: Grp::Scalar, diff --git a/src/rkr_encryption.rs b/src/rkr_encryption.rs deleted file mode 100644 index f904c30..0000000 --- a/src/rkr_encryption.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. - -use crate::errors::{utils::check_slice_size, InternalPakeError, PakeError}; -use aead::{Aead, NewAead}; -use generic_array::{typenum::Unsigned, GenericArray}; -use hmac::{Hmac, Mac, NewMac}; -use rand_core::{CryptoRng, RngCore}; -use sha2::{Digest, Sha256}; - -/// This trait encapsulates an encryption scheme that satisfies random-key robustness (RKR), which is implemented -/// through encrypt-then-HMAC -- see Section 3.1.1 of -/// https://www.ietf.org/id/draft-krawczyk-cfrg-opaque-03.txt -/// We require an Aead implementation with a 32-bit key size, since we -/// will derive the symmetric key from pw using Sha256 -pub trait RKRCipher: Sized { - /// The requirement of KeySize = U32 is so that we can use a 32-bit hash - /// for key derivation form the user's password - type AEAD: NewAead::OutputSize> + Aead; - - // Required members - fn new( - aead_output: Vec, - hmac: &GenericArray::OutputSize>, - nonce: &GenericArray::NonceSize>, - ) -> Self; - - fn aead_output(&self) -> &Vec; - fn hmac(&self) -> &GenericArray::OutputSize>; - fn nonce(&self) -> &GenericArray::NonceSize>; - - fn to_bytes(&self) -> Vec; - - // Provided members for enc / dec - fn key_len() -> usize { - ::KeySize::to_usize() - } - - fn nonce_size() -> usize { - ::NonceSize::to_usize() - } - - fn hmac_size() -> usize { - ::OutputSize::to_usize() - } - - /// This estimates the size of the ciphertext once we encode —very specifically— - /// the payload we have planned for the protocol's env_u - fn ciphertest_size() -> usize { - Self::key_len() + ::TagSize::to_usize() + Self::hmac_size() - } - - fn rkr_with_nonce_size() -> usize { - Self::ciphertest_size() + Self::nonce_size() - } - - /// The format of the output ciphertext here is: - /// encryption_output | tag | hmac | nonce - /// variable length | AEAD_TAG_SIZE bytes | HMAC_SIZE bytes | NONCE_SIZE bytes - fn from_bytes(bytes: &[u8]) -> Result { - let checked_bytes = check_slice_size(&bytes[..], Self::rkr_with_nonce_size(), "bytes")?; - let nonce_start = bytes.len() - Self::nonce_size(); - let hmac_start = nonce_start - Self::hmac_size(); - - Ok(::new( - bytes[..hmac_start].to_vec(), - GenericArray::from_slice(&checked_bytes[hmac_start..nonce_start]), - GenericArray::from_slice(&checked_bytes[nonce_start..]), - )) - } - - /// Encrypt with AEAD. Note that this encryption scheme needs to satisfy "random-key robustness" (RKR). - fn encrypt( - encryption_key: &[u8], - hmac_key: &[u8], - plaintext: &[u8], - aad: &[u8], - rng: &mut R, - ) -> Result { - let mut nonce = vec![0u8; Self::nonce_size()]; - rng.fill_bytes(&mut nonce); - let gen_nonce = GenericArray::from_slice(&nonce[..]); - - let ciphertext = ::new(GenericArray::from_slice(&encryption_key)) - .encrypt( - GenericArray::from_slice(&nonce), - aead::Payload { - msg: &plaintext, - aad: &aad, - }, - ) - .map_err(|_| PakeError::EncryptionError)?; - - let mut mac = - Hmac::::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?; - mac.update(&ciphertext); - - Ok(::new( - ciphertext, - &mac.finalize().into_bytes(), - gen_nonce, - )) - } - - fn decrypt( - &self, - encryption_key: &[u8], - hmac_key: &[u8], - aad: &[u8], - ) -> Result, PakeError> { - let mut mac = - Hmac::::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?; - mac.update(self.aead_output()); - if mac.verify(self.hmac()).is_err() { - return Err(PakeError::DecryptionHmacError); - } - - Aead::decrypt( - &::new(GenericArray::from_slice(&encryption_key)), - self.nonce(), - aead::Payload { - msg: self.aead_output(), - aad: &aad, - }, - ) - .map_err(|_| PakeError::DecryptionError) - } -} - -/// This struct is a straightforward instantiation of the trait separating the -/// three components in Vecs -pub struct RKRCiphertext { - aead_choice: std::marker::PhantomData, - aead_output: Vec, - hmac: Vec, - nonce: Vec, -} - -impl::OutputSize> + Aead> RKRCipher for RKRCiphertext { - type AEAD = T; - - fn new( - aead_output: Vec, - hmac: &GenericArray::OutputSize>, - nonce: &GenericArray::NonceSize>, - ) -> Self { - Self { - aead_choice: std::marker::PhantomData, - aead_output, - hmac: hmac.to_vec(), - nonce: nonce.to_vec(), - } - } - - fn aead_output(&self) -> &Vec { - &self.aead_output - } - - fn to_bytes(&self) -> Vec { - [&self.aead_output[..], &self.hmac[..], &self.nonce[..]].concat() - } - - fn hmac(&self) -> &GenericArray::OutputSize> { - GenericArray::from_slice(&self.hmac[..]) - } - - fn nonce(&self) -> &GenericArray::NonceSize> { - GenericArray::from_slice(&self.nonce[..]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use chacha20poly1305::ChaCha20Poly1305; - use rand_core::OsRng; - - #[test] - fn encrypt_and_decrypt() { - let mut rng = OsRng; - let mut encryption_key = [0u8; 32]; - rng.fill_bytes(&mut encryption_key); - let mut hmac_key = [0u8; 32]; - rng.fill_bytes(&mut hmac_key); - - let mut msg = [0u8; 100]; - rng.fill_bytes(&mut msg); - - let ciphertext = RKRCiphertext::::encrypt( - &encryption_key, - &hmac_key, - &msg, - b"", - &mut rng, - ) - .unwrap(); - let decrypted = ciphertext.decrypt(&encryption_key, &hmac_key, b"").unwrap(); - assert_eq!(&msg.to_vec(), &decrypted); - } -} diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs index f7ac540..fc13a3f 100644 --- a/src/tests/opaque_ke_test.rs +++ b/src/tests/opaque_ke_test.rs @@ -8,11 +8,11 @@ use crate::{ errors::*, group::Group, key_exchange::NONCE_LEN, - keypair::{Key, KeyPair}, + keypair::{Key, KeyPair, X25519KeyPair}, opaque::*, + slow_hash::NoOpHash, tests::mock_rng::CycleRng, }; -use aes_gcm::Aes256Gcm; use curve25519_dalek::edwards::EdwardsPoint; use rand_core::{OsRng, RngCore}; use serde_json::Value; @@ -21,12 +21,11 @@ use std::convert::TryFrom; // Tests // ===== -struct AesgcmX255193dhNoSlowHash; -impl CipherSuite for AesgcmX255193dhNoSlowHash { - type Aead = Aes256Gcm; +struct X255193dhNoSlowHash; +impl CipherSuite for X255193dhNoSlowHash { type Group = EdwardsPoint; - type KeyFormat = crate::keypair::X25519KeyPair; - type SlowHash = crate::slow_hash::NoOpHash; + type KeyFormat = X25519KeyPair; + type SlowHash = NoOpHash; } pub struct TestVectorParameters { @@ -63,35 +62,35 @@ pub struct TestVectorParameters { static TEST_VECTOR: &str = r#" { - "client_s_pk": "f7b150789db3322c8c7b8c4a10ce42baa5ee846de83eaf04c17ffbd0d9e5cd60", - "client_s_sk": "601ed276a42ec5795b3471f1a64e312f192e17ff252ce6053c8ecaf210138273", - "client_e_pk": "57260d4e231035f0f3e1fb836fe5d9ddb498c956cacb5fab1d6b287e1422376c", - "client_e_sk": "e89d0fa4e387a9bd7c26466704ec30e62f58892bf3dfd1fd25133be52f34ea68", - "server_s_pk": "a2b4e12d0621ebfb2631e00f5c872ab749e1a33915f16fb11203658b2189cc5e", - "server_s_sk": "90b6ca2ea8a37306060c7cd0998d4cdae59e972af7760312f7cf77099e78f940", - "server_e_pk": "64ce4a453eb8c27b1d81f6acdc01d36d3ae6cea506432e9509917b195ad90073", - "server_e_sk": "883148cc1ba70acb1eb909d99e09493b5d4b3fe6b12c75e2f5aeea6c5d4b267f", + "client_s_pk": "4374a0bf4f5f4353c20337574bdc69e6cff18a36e3fd8fe89fe47e02bab06e37", + "client_s_sk": "f0804c53de3fce8c9a6bcccb3380a1427c578a2f834de89fc98447c322776955", + "client_e_pk": "b45316eaa2d87ac1ed5c28b54ed43578fc54d4beb673b748159cea7536215819", + "client_e_sk": "60ff0381b8d8e6913d75e7a3caf2ede4e755d33337a6d904239fa6b00338cb56", + "server_s_pk": "c76f269ba1e0fb19597907ddd9e0fc9a4ac11e499e77b5ae480deb825dc2e044", + "server_s_sk": "384e6efd4236c76e7e84ae13327b867d85881f27df7f65e409c66a8f1f2c8354", + "server_e_pk": "92d4883730b19e767fc2f8224dac4a913f404e6d05a9baaf1f34ec9078dfb648", + "server_e_sk": "e88a6ec8d5644b66c20d6a915a94b27a17787196ab9f9bc0ee600d11d739b576", "password": "70617373776f7264", - "blinding_factor_raw": "b85e0df2ad0495771edf09a04b1073045e6472e2f86a41e9bab3143ebfb8eb08a3462503eb3750bf006dc82c93b37e07cdf3768018c22b431cf5146a9caeda1c", - "blinding_factor": "fac0ed1c38bc8945a91dc4d944af22c466cbffc24fc3d97b8a91798d1ec8b60f", + "blinding_factor_raw": "0412c9968a8e6ea19dd42eca36c35ff784d6e83944d833bdb919f8af6166ce00b742fe149fb6d9ba7d130edc983802bf91b510157d221e800e76c3aed1740159", + "blinding_factor": "0c9957936385474a1862bc9da3d60d6655b030fd6fc8de1fc7842163007f5a03", "pepper": "706570706572", - "oprf_key": "d5cedff72509af4702a985bb31af8dbe88d72c4eee13a09e3f52a76766fa6f0b", - "envelope_nonce": "c87e44792a9dfd8858db676e", - "client_nonce": "1f023acc6155a06166ee7e5b7ef0360277ed5da3a46adcd4a0a5bce938a67a23", - "server_nonce": "d448cb1f58c38605fc29069ac688ec9c667c99d0316b38cd1b2609c1bc14aa90", - "r1": "e46efe7d673805b6135a5293ecab13082b322c45f029595efa4b8d1d53ccd897", - "r2": "a2a3df89cf85976c4aa5add752736419f728805722571a9646983587ce4c55fb", - "r3": "374c49768e4399d4cd46e8b3bc2050e2f6737e3a2f8aee6fddc82e117f340f79a7f10c84445657c6bb4940bd02bc08ca0f107618d810ec94639e8ae43af48ab66f1f75e8bbc169eed0035e347310978bc87e44792a9dfd8858db676ef7b150789db3322c8c7b8c4a10ce42baa5ee846de83eaf04c17ffbd0d9e5cd60", - "l1": "e46efe7d673805b6135a5293ecab13082b322c45f029595efa4b8d1d53ccd8971f023acc6155a06166ee7e5b7ef0360277ed5da3a46adcd4a0a5bce938a67a2357260d4e231035f0f3e1fb836fe5d9ddb498c956cacb5fab1d6b287e1422376c", - "l2": "a2a3df89cf85976c4aa5add752736419f728805722571a9646983587ce4c55fb374c49768e4399d4cd46e8b3bc2050e2f6737e3a2f8aee6fddc82e117f340f79a7f10c84445657c6bb4940bd02bc08ca0f107618d810ec94639e8ae43af48ab66f1f75e8bbc169eed0035e347310978bc87e44792a9dfd8858db676e883148cc1ba70acb1eb909d99e09493b5d4b3fe6b12c75e2f5aeea6c5d4b267f64ce4a453eb8c27b1d81f6acdc01d36d3ae6cea506432e9509917b195ad90073d81a1104fbd599ef56228bdbe9bf7be4a38ae907a8717ca0883b9d69b2efc529", - "l3": "a01332643e8aa7113f6f160205a9b3bd0705f3b33d8e4ea8eab9eae6685a6adb", - "client_registration_state": "fac0ed1c38bc8945a91dc4d944af22c466cbffc24fc3d97b8a91798d1ec8b60f70617373776f7264", - "client_login_state": "fac0ed1c38bc8945a91dc4d944af22c466cbffc24fc3d97b8a91798d1ec8b60fe89d0fa4e387a9bd7c26466704ec30e62f58892bf3dfd1fd25133be52f34ea681f023acc6155a06166ee7e5b7ef0360277ed5da3a46adcd4a0a5bce938a67a23dd1a7c2b4e9f9be94bd36f3b6c7f23aa9f1e6b3fda9030412a918d1288b4af1970617373776f7264", - "server_registration_state": "d5cedff72509af4702a985bb31af8dbe88d72c4eee13a09e3f52a76766fa6f0b", - "server_login_state": "809f95143f8f7fc1d0b42f578a83f714f58cfd96d9499aacee730ad296b37b19c18c903396e85da607d02542d4d07456e5357ff2e2eade3aaa42e532d4e9364f66317ab0460307e33d6151e99c7406f2fa1d309f507b46e43f732924d1dc8d0d", - "password_file": "d5cedff72509af4702a985bb31af8dbe88d72c4eee13a09e3f52a76766fa6f0bf7b150789db3322c8c7b8c4a10ce42baa5ee846de83eaf04c17ffbd0d9e5cd60374c49768e4399d4cd46e8b3bc2050e2f6737e3a2f8aee6fddc82e117f340f79a7f10c84445657c6bb4940bd02bc08ca0f107618d810ec94639e8ae43af48ab66f1f75e8bbc169eed0035e347310978bc87e44792a9dfd8858db676e", - "opaque_key": "682f2868a3e1460fed5a16767bd8778c33b4aecac6607270f848aa61c95a1a68", - "shared_secret": "66317ab0460307e33d6151e99c7406f2fa1d309f507b46e43f732924d1dc8d0d" + "oprf_key": "764fc3396026a513ede0332f2d0801b7c02516241b473362844a9aea613e3800", + "envelope_nonce": "acca14c1d5f7f5843812ad61", + "client_nonce": "db2c06ad77d6d6b73170cb26c082c3fea77c64201b021f3d22f477bd5fd4cf9b", + "server_nonce": "861dbe0a824fc9a6ebc90a798dd5827888c30c8f8f79c361fb487db5b9a65586", + "r1": "3b12967295493838ce743c3fa5e3da39d13589aacad2cb67792df9e99dbefff3", + "r2": "4cce303b33ed400fc60cdaa9d314021c9a9a1c29faab56ede9fc580bdb0287c3", + "r3": "acca14c1d5f7f5843812ad61f0804c53de3fce8c9a6bcccb3380a1427c578a2f4df43147c5396441be3dee47f441e6cb56eca2cf7e25cc94ef233b2f1d3b0e64142d3d40fc61226627bca32c331dc00b9e9e795a1cfb0377d3a79f565307e1074374a0bf4f5f4353c20337574bdc69e6cff18a36e3fd8fe89fe47e02bab06e37", + "l1": "3b12967295493838ce743c3fa5e3da39d13589aacad2cb67792df9e99dbefff3db2c06ad77d6d6b73170cb26c082c3fea77c64201b021f3d22f477bd5fd4cf9bb45316eaa2d87ac1ed5c28b54ed43578fc54d4beb673b748159cea7536215819", + "l2": "4cce303b33ed400fc60cdaa9d314021c9a9a1c29faab56ede9fc580bdb0287c3acca14c1d5f7f5843812ad61f0804c53de3fce8c9a6bcccb3380a1427c578a2f4df43147c5396441be3dee47f441e6cb56eca2cf7e25cc94ef233b2f1d3b0e64142d3d40fc61226627bca32c331dc00b9e9e795a1cfb0377d3a79f565307e107e88a6ec8d5644b66c20d6a915a94b27a17787196ab9f9bc0ee600d11d739b57692d4883730b19e767fc2f8224dac4a913f404e6d05a9baaf1f34ec9078dfb648c87b63566267c0941b269f8ad36228ae24a2cd9eb8f90e8f6bc26140c2f93bb4", + "l3": "a20bb3efbdbccb23cd6206ef0483cf52a1e3b2f700c4c9aac0c9bab2f4326265", + "client_registration_state": "0c9957936385474a1862bc9da3d60d6655b030fd6fc8de1fc7842163007f5a0370617373776f7264", + "client_login_state": "0c9957936385474a1862bc9da3d60d6655b030fd6fc8de1fc7842163007f5a0360ff0381b8d8e6913d75e7a3caf2ede4e755d33337a6d904239fa6b00338cb56db2c06ad77d6d6b73170cb26c082c3fea77c64201b021f3d22f477bd5fd4cf9bb898ccd56020538145fc2192532bde9f8da0183a1dc486ae3086aa3b72c6728570617373776f7264", + "server_registration_state": "764fc3396026a513ede0332f2d0801b7c02516241b473362844a9aea613e3800", + "server_login_state": "75780c16fc843510e2222f195859b3f84c224e378f6afc6685827c30c11add9c3435530551d074a98f30d8e0f078efde0693738808a2c6358a4d34b64004a6c544b77d404067257aab5829b59a5f8a306c586c5a73018a9837c8b9a33d9fed6e", + "password_file": "764fc3396026a513ede0332f2d0801b7c02516241b473362844a9aea613e38004374a0bf4f5f4353c20337574bdc69e6cff18a36e3fd8fe89fe47e02bab06e37acca14c1d5f7f5843812ad61f0804c53de3fce8c9a6bcccb3380a1427c578a2f4df43147c5396441be3dee47f441e6cb56eca2cf7e25cc94ef233b2f1d3b0e64142d3d40fc61226627bca32c331dc00b9e9e795a1cfb0377d3a79f565307e107", + "opaque_key": "653ec91b97e928868c8977b4339530a5c1298dd9c69f1a1752a56785eca6e82c", + "shared_secret": "44b77d404067257aab5829b59a5f8a306c586c5a73018a9837c8b9a33d9fed6e" } "#; @@ -219,14 +218,14 @@ fn stringify_test_vectors(p: &TestVectorParameters) -> String { s } -fn generate_parameters() -> TestVectorParameters { +fn generate_parameters() -> TestVectorParameters { let mut rng = OsRng; // Inputs - let server_s_kp = AesgcmX255193dhNoSlowHash::generate_random_keypair(&mut rng).unwrap(); - let server_e_kp = AesgcmX255193dhNoSlowHash::generate_random_keypair(&mut rng).unwrap(); - let client_s_kp = AesgcmX255193dhNoSlowHash::generate_random_keypair(&mut rng).unwrap(); - let client_e_kp = AesgcmX255193dhNoSlowHash::generate_random_keypair(&mut rng).unwrap(); + let server_s_kp = CS::generate_random_keypair(&mut rng).unwrap(); + let server_e_kp = CS::generate_random_keypair(&mut rng).unwrap(); + let client_s_kp = CS::generate_random_keypair(&mut rng).unwrap(); + let client_e_kp = CS::generate_random_keypair(&mut rng).unwrap(); let password = b"password"; let pepper = b"pepper"; let mut blinding_factor_raw = [0u8; 64]; @@ -241,22 +240,21 @@ fn generate_parameters() -> TestVectorParameters { rng.fill_bytes(&mut server_nonce); let mut blinding_factor_registration_rng = CycleRng::new(blinding_factor_raw.to_vec()); - let (r1, client_registration) = ClientRegistration::::start( + let (r1, client_registration) = ClientRegistration::::start( password, Some(pepper), &mut blinding_factor_registration_rng, ) .unwrap(); let r1_bytes = r1.to_bytes().to_vec(); - let blinding_factor_bytes = client_registration.blinding_factor.to_bytes(); + let blinding_factor_bytes = + CS::Group::scalar_as_bytes(&client_registration.blinding_factor).clone(); let client_registration_state = client_registration.to_bytes().to_vec(); let mut oprf_key_rng = CycleRng::new(oprf_key_raw.to_vec()); - let (r2, server_registration) = - ServerRegistration::::start(r1, &mut oprf_key_rng).unwrap(); + let (r2, server_registration) = ServerRegistration::::start(r1, &mut oprf_key_rng).unwrap(); let r2_bytes = r2.to_bytes().to_vec(); - let oprf_key = server_registration.oprf_key; - let oprf_key_bytes = EdwardsPoint::scalar_as_bytes(&oprf_key); + let oprf_key_bytes = CS::Group::scalar_as_bytes(&server_registration.oprf_key).clone(); let server_registration_state = server_registration.to_bytes().to_vec(); let mut client_s_sk_and_nonce: Vec = Vec::new(); @@ -278,12 +276,8 @@ 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( - password, - Some(pepper), - &mut client_login_start_rng, - ) - .unwrap(); + let (l1, client_login) = + ClientLogin::::start(password, Some(pepper), &mut client_login_start_rng).unwrap(); let l1_bytes = l1.to_bytes().to_vec(); let client_login_state = client_login.to_bytes().to_vec(); @@ -339,7 +333,7 @@ fn generate_parameters() -> TestVectorParameters { #[test] fn generate_test_vectors() { - let parameters = generate_parameters(); + let parameters = generate_parameters::(); println!("{}", stringify_test_vectors(¶meters)); } @@ -347,7 +341,7 @@ fn generate_test_vectors() { fn test_r1() -> Result<(), PakeError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let mut blinding_factor_rng = CycleRng::new(parameters.blinding_factor_raw); - let (r1, client_registration) = ClientRegistration::::start( + let (r1, client_registration) = ClientRegistration::::start( ¶meters.password, Some(¶meters.pepper), &mut blinding_factor_rng, @@ -365,7 +359,7 @@ fn test_r1() -> Result<(), PakeError> { 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( + let (r2, server_registration) = ServerRegistration::::start( RegisterFirstMessage::try_from(¶meters.r1[..]).unwrap(), &mut oprf_key_rng, ) @@ -385,7 +379,7 @@ fn test_r3() -> Result<(), PakeError> { let client_s_sk_and_nonce: Vec = [parameters.client_s_sk, parameters.envelope_nonce].concat(); let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce); - let (r3, opaque_key_registration) = ClientRegistration::::try_from( + let (r3, opaque_key_registration) = ClientRegistration::::try_from( ¶meters.client_registration_state[..], ) .unwrap() @@ -409,7 +403,7 @@ fn test_r3() -> Result<(), PakeError> { fn test_password_file() -> Result<(), PakeError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); - let server_registration = ServerRegistration::::try_from( + let server_registration = ServerRegistration::::try_from( ¶meters.server_registration_state[..], ) .unwrap(); @@ -435,7 +429,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, @@ -454,7 +448,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::( + let (l2, server_login) = ServerLogin::start::( ServerRegistration::try_from(¶meters.password_file[..]).unwrap(), &Key::try_from(¶meters.server_s_sk[..]).unwrap(), LoginFirstMessage::::try_from(¶meters.l1[..]).unwrap(), @@ -476,10 +470,10 @@ 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(¶meters.client_login_state[..]) + ClientLogin::::try_from(¶meters.client_login_state[..]) .unwrap() .finish( - LoginSecondMessage::::try_from(¶meters.l2[..]) + LoginSecondMessage::::try_from(¶meters.l2[..]) .unwrap(), &Key::try_from(¶meters.server_s_pk[..])?, &mut client_e_sk_rng, @@ -522,19 +516,19 @@ fn test_complete_flow( ) -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; - let server_kp = AesgcmX255193dhNoSlowHash::generate_random_keypair(&mut server_rng)?; - let (register_m1, client_state) = ClientRegistration::::start( + let server_kp = X255193dhNoSlowHash::generate_random_keypair(&mut server_rng)?; + let (register_m1, client_state) = ClientRegistration::::start( registration_password, None, &mut client_rng, )?; let (register_m2, server_state) = - ServerRegistration::::start(register_m1, &mut server_rng)?; + ServerRegistration::::start(register_m1, &mut server_rng)?; let (register_m3, registration_opaque_key) = client_state.finish(register_m2, server_kp.public(), &mut client_rng)?; let p_file = server_state.finish(register_m3)?; let (login_m1, client_login_state) = - ClientLogin::::start(login_password, None, &mut client_rng)?; + ClientLogin::::start(login_password, None, &mut client_rng)?; let (login_m2, server_login_state) = ServerLogin::start(p_file, &server_kp.private(), login_m1, &mut server_rng)?; diff --git a/src/tests/serialization.rs b/src/tests/serialization.rs index 1e6b43b..7e51d22 100644 --- a/src/tests/serialization.rs +++ b/src/tests/serialization.rs @@ -5,16 +5,15 @@ use crate::{ ciphersuite::CipherSuite, + envelope::Envelope, group::Group, key_exchange::{KE1Message, NONCE_LEN}, keypair::{KeyPair, SizedBytes, X25519KeyPair}, opaque::*, - rkr_encryption::{RKRCipher as _, RKRCiphertext}, }; use curve25519_dalek::ristretto::RistrettoPoint; - -use chacha20poly1305::ChaCha20Poly1305; +use generic_array::typenum::Unsigned; use rand_core::{OsRng, RngCore}; use sha2::{Digest, Sha256}; @@ -22,7 +21,6 @@ use std::convert::TryFrom; struct Default; impl CipherSuite for Default { - type Aead = ChaCha20Poly1305; type Group = RistrettoPoint; type KeyFormat = crate::keypair::X25519KeyPair; type SlowHash = crate::slow_hash::NoOpHash; @@ -67,16 +65,18 @@ fn server_registration_roundtrip() { assert_eq!(reg_bytes, oprf_bytes); // If we do have envelope and client pk, the server registration contains // the whole kit - let rkr_size = RKRCiphertext::::rkr_with_nonce_size(); - let mut mock_rkr_bytes = vec![0u8; rkr_size]; - rng.fill_bytes(&mut mock_rkr_bytes); - println!("{}", mock_rkr_bytes.len()); + let key_len = + <<::KeyFormat as KeyPair>::Repr as SizedBytes>::Len::to_usize(); + let envelope_size = key_len + Envelope::additional_size(); + let mut mock_envelope_bytes = vec![0u8; envelope_size]; + rng.fill_bytes(&mut mock_envelope_bytes); + println!("{}", mock_envelope_bytes.len()); let mock_client_kp = Default::generate_random_keypair(&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); + bytes.extend_from_slice(&mock_envelope_bytes); let reg = ServerRegistration::::try_from(&bytes[..]).unwrap(); let reg_bytes = reg.to_bytes(); assert_eq!(reg_bytes, bytes); @@ -108,26 +108,16 @@ fn register_third_message_roundtrip() { let skp = Default::generate_random_keypair(&mut rng).unwrap(); let pubkey_bytes = skp.public().to_arr(); - let mut encryption_key = [0u8; 32]; - rng.fill_bytes(&mut encryption_key); - let mut hmac_key = [0u8; 32]; - rng.fill_bytes(&mut hmac_key); + let mut key = [0u8; 32]; + rng.fill_bytes(&mut key); let mut msg = [0u8; 32]; rng.fill_bytes(&mut msg); - let ciphertext = RKRCiphertext::::encrypt( - &encryption_key, - &hmac_key, - &msg, - &pubkey_bytes, - &mut rng, - ) - .unwrap(); + let (ciphertext, _) = Envelope::seal(&key, &msg, &pubkey_bytes, &mut rng).unwrap(); let message: Vec = [&ciphertext.to_bytes(), &pubkey_bytes[..]].concat(); - let r3 = - RegisterThirdMessage::::try_from(&message[..]).unwrap(); + let r3 = RegisterThirdMessage::::try_from(&message[..]).unwrap(); let r3_bytes = r3.to_bytes(); assert_eq!(message, r3_bytes); }