// Copyright (c) Facebook, Inc. and its affiliates. // // This source code is licensed under both the MIT license found in the // LICENSE-MIT file in the root directory of this source tree and the Apache // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. use core::ops::Add; use std::string::ToString; use std::vec::Vec; use std::{println, vec}; use digest::core_api::{BlockSizeUser, CoreProxy}; use generic_array::typenum::{IsLess, Le, NonZero, Sum, U256}; use generic_array::ArrayLength; use json::JsonValue; use voprf::Group; use crate::ciphersuite::CipherSuite; use crate::envelope::EnvelopeLen; use crate::errors::*; use crate::hash::{OutputSize, ProxyHash}; use crate::key_exchange::group::KeGroup; use crate::key_exchange::traits::{Ke1MessageLen, Ke2MessageLen}; use crate::key_exchange::tripledh::{NonceLen, TripleDH}; use crate::messages::{ CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen, RegistrationResponseLen, RegistrationUploadLen, }; use crate::opaque::*; use crate::slow_hash::NoOpHash; use crate::tests::mock_rng::CycleRng; use crate::*; #[allow(non_snake_case)] #[derive(Debug)] pub struct OpaqueTestVectorParameters { pub dummy_private_key: Vec, pub dummy_masking_key: Vec, pub context: Vec, pub client_private_key: Option>, pub client_keyshare: Vec, pub client_private_keyshare: Vec, pub server_public_key: Vec, pub server_private_key: Vec, pub server_keyshare: Vec, pub server_private_keyshare: Vec, pub client_identity: Option>, pub server_identity: Option>, pub credential_identifier: Vec, pub password: Vec, pub blind_registration: Vec, pub oprf_seed: Vec, pub masking_nonce: Vec, pub envelope_nonce: Vec, pub client_nonce: Vec, pub server_nonce: Vec, pub client_info: Vec, pub server_info: Vec, pub registration_request: Vec, pub registration_response: Vec, pub registration_upload: Vec, pub KE1: Vec, pub blind_login: Vec, pub KE2: Vec, pub KE3: Vec, pub export_key: Vec, pub session_key: Vec, pub auth_key: Vec, pub randomized_pwd: Vec, pub handshake_secret: Vec, pub server_mac_key: Vec, pub client_mac_key: Vec, pub oprf_key: Vec, } macro_rules! parse { ( $v:ident, $s:expr ) => { parse_default!($v, $s, vec![]) }; } macro_rules! parse_default { ( $v:ident, $s:expr, $d:expr ) => { match decode(&$v, $s) { Some(x) => x, None => $d, } }; } /// If no entry is found, default to filling a random buffer of a specified size macro_rules! parse_default_random { ( $v:ident, $s:expr, $size:expr ) => { parse_default!($v, $s, { use rand::rngs::OsRng; use rand::RngCore; let mut rng = OsRng; let mut v = vec![0u8; $size]; rng.fill_bytes(&mut v); v }) }; } fn decode(values: &JsonValue, key: &str) -> Option> { values[key] .as_str() .and_then(|s| hex::decode(&s.to_string()).ok()) } fn populate_test_vectors(values: &JsonValue) -> OpaqueTestVectorParameters { OpaqueTestVectorParameters { dummy_private_key: parse_default_random!(values, "client_private_key", 32), dummy_masking_key: parse_default_random!(values, "masking_key", 64), context: parse!(values, "Context"), client_private_key: decode(values, "client_private_key"), client_keyshare: parse!(values, "client_keyshare"), client_private_keyshare: parse!(values, "client_private_keyshare"), server_public_key: parse!(values, "server_public_key"), server_private_key: parse!(values, "server_private_key"), server_keyshare: parse!(values, "server_keyshare"), server_private_keyshare: parse!(values, "server_private_keyshare"), client_identity: decode(values, "client_identity"), server_identity: decode(values, "server_identity"), credential_identifier: parse!(values, "credential_identifier"), password: parse!(values, "password"), blind_registration: parse!(values, "blind_registration"), oprf_seed: parse!(values, "oprf_seed"), masking_nonce: parse!(values, "masking_nonce"), envelope_nonce: parse!(values, "envelope_nonce"), client_nonce: parse!(values, "client_nonce"), server_nonce: parse!(values, "server_nonce"), client_info: parse!(values, "client_info"), server_info: parse!(values, "server_info"), registration_request: parse!(values, "registration_request"), registration_response: parse!(values, "registration_response"), registration_upload: parse!(values, "registration_upload"), KE1: parse!(values, "KE1"), KE2: parse!(values, "KE2"), KE3: parse!(values, "KE3"), blind_login: parse!(values, "blind_login"), export_key: parse!(values, "export_key"), session_key: parse!(values, "session_key"), auth_key: parse!(values, "auth_key"), randomized_pwd: parse!(values, "randomized_pwd"), handshake_secret: parse!(values, "handshake_secret"), server_mac_key: parse!(values, "server_mac_key"), client_mac_key: parse!(values, "client_mac_key"), oprf_key: parse!(values, "oprf_key"), } } fn get_password_file_bytes(parameters: &OpaqueTestVectorParameters) -> Vec where ::Core: ProxyHash, <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, // Envelope: Nonce + Hash NonceLen: Add>, EnvelopeLen: ArrayLength, // RegistrationUpload: (KePk + Hash) + Envelope ::PkLen: Add>, Sum<::PkLen, OutputSize>: ArrayLength + Add>, RegistrationUploadLen: ArrayLength, // ServerRegistration = RegistrationUpload { let password_file = ServerRegistration::::finish( RegistrationUpload::deserialize(¶meters.registration_upload).unwrap(), ); password_file.serialize().to_vec() } macro_rules! json_to_test_vectors { ( $v:ident, $vector_type:expr, $cs:expr, ) => { $v[$vector_type] .members() .filter_map(|x| { if x.has_key($cs) { Some(populate_test_vectors(&x[$cs])) } else { None } }) .collect::>() }; } #[test] fn tests() -> Result<(), ProtocolError> { let rfc = json::parse(super::parser::rfc_to_json(super::opaque_vectors::VECTORS).as_str()) .expect("Could not parse json"); #[cfg(feature = "ristretto255")] { let ristretto_real_tvs = json_to_test_vectors!(rfc, "Real", "ristretto255, SHA512",); let ristretto_fake_tvs = json_to_test_vectors!(rfc, "Fake", "ristretto255, SHA512",); assert!( !(ristretto_real_tvs.is_empty() || ristretto_fake_tvs.is_empty()), "Parsing error" ); struct Ristretto255Sha512NoSlowHash; impl CipherSuite for Ristretto255Sha512NoSlowHash { type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; type KeyExchange = TripleDH; type Hash = sha2::Sha512; type SlowHash = NoOpHash; } test_registration_request::(&ristretto_real_tvs)?; test_registration_response::(&ristretto_real_tvs)?; test_registration_upload::(&ristretto_real_tvs)?; test_ke1::(&ristretto_real_tvs)?; test_ke2::(&ristretto_real_tvs)?; test_ke3::(&ristretto_real_tvs)?; test_server_login_finish::(&ristretto_real_tvs)?; test_fake_vectors::(&ristretto_fake_tvs)?; } #[cfg(feature = "p256")] { let p256_real_tvs = json_to_test_vectors!(rfc, "Real", "P256_XMD:SHA-256_SSWU_RO_, SHA256",); let p256_fake_tvs = json_to_test_vectors!(rfc, "Fake", "P256_XMD:SHA-256_SSWU_RO_, SHA256",); assert!( !(p256_real_tvs.is_empty() || p256_fake_tvs.is_empty()), "Parsing error" ); struct P256Sha256NoSlowHash; impl CipherSuite for P256Sha256NoSlowHash { type OprfGroup = p256_::ProjectivePoint; type KeGroup = p256_::PublicKey; type KeyExchange = TripleDH; type Hash = sha2::Sha256; type SlowHash = NoOpHash; } test_registration_request::(&p256_real_tvs)?; test_registration_response::(&p256_real_tvs)?; test_registration_upload::(&p256_real_tvs)?; test_ke1::(&p256_real_tvs)?; test_ke2::(&p256_real_tvs)?; test_ke3::(&p256_real_tvs)?; test_server_login_finish::(&p256_real_tvs)?; test_fake_vectors::(&p256_fake_tvs)?; } Ok(()) } fn test_registration_request( tvs: &[OpaqueTestVectorParameters], ) -> Result<(), ProtocolError> where ::Core: ProxyHash, <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, { for parameters in tvs { let mut rng = CycleRng::new(parameters.blind_registration.to_vec()); let client_registration_start_result = ClientRegistration::::start(&mut rng, ¶meters.password)?; assert_eq!( hex::encode(¶meters.registration_request), hex::encode(client_registration_start_result.message.serialize()) ); } Ok(()) } fn test_registration_response( tvs: &[OpaqueTestVectorParameters], ) -> Result<(), ProtocolError> where ::Core: ProxyHash, <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, // RegistrationResponse: KgPk + KePk ::ElemLen: Add<::PkLen>, RegistrationResponseLen: ArrayLength, { for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ parameters.oprf_seed.as_slice(), ¶meters.server_private_key, ¶meters.dummy_private_key, ] .concat(), )?; let server_registration_start_result = ServerRegistration::::start( &server_setup, RegistrationRequest::deserialize(¶meters.registration_request).unwrap(), ¶meters.credential_identifier, )?; assert_eq!( hex::encode(¶meters.oprf_key), hex::encode(server_registration_start_result.oprf_key) ); assert_eq!( hex::encode(¶meters.registration_response), hex::encode(server_registration_start_result.message.serialize()) ); } Ok(()) } fn test_registration_upload( tvs: &[OpaqueTestVectorParameters], ) -> Result<(), ProtocolError> where ::Core: ProxyHash, <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, // Envelope: Nonce + Hash NonceLen: Add>, EnvelopeLen: ArrayLength, // RegistrationUpload: (KePk + Hash) + Envelope ::PkLen: Add>, Sum<::PkLen, OutputSize>: ArrayLength + Add>, RegistrationUploadLen: ArrayLength, { for parameters in tvs { let mut rng = CycleRng::new(parameters.blind_registration.to_vec()); let client_registration_start_result = ClientRegistration::::start(&mut rng, ¶meters.password)?; let mut finish_registration_rng = CycleRng::new(parameters.envelope_nonce.to_vec()); let result = client_registration_start_result.state.finish( &mut finish_registration_rng, ¶meters.password, RegistrationResponse::deserialize(¶meters.registration_response).unwrap(), ClientRegistrationFinishParameters::new( Identifiers { client: parameters.client_identity.as_deref(), server: parameters.server_identity.as_deref(), }, None, ), )?; assert_eq!( hex::encode(¶meters.auth_key), hex::encode(result.auth_key) ); assert_eq!( hex::encode(¶meters.randomized_pwd), hex::encode(result.randomized_pwd) ); assert_eq!( hex::encode(¶meters.registration_upload), hex::encode(result.message.serialize()) ); assert_eq!( hex::encode(¶meters.export_key), hex::encode(result.export_key.to_vec()) ); } Ok(()) } fn test_ke1(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> where ::Core: ProxyHash, <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, // CredentialRequest: KgPk + Ke1Message ::ElemLen: Add>, CredentialRequestLen: ArrayLength, { for parameters in tvs { let client_login_start = [ parameters.blind_login.as_slice(), ¶meters.client_private_keyshare, ¶meters.client_nonce, ] .concat(); println!( "parameters.blind_login: {:?}", hex::encode(¶meters.blind_login) ); let mut client_login_start_rng = CycleRng::new(client_login_start); let client_login_start_result = ClientLogin::::start(&mut client_login_start_rng, ¶meters.password)?; assert_eq!( hex::encode(¶meters.KE1), hex::encode(client_login_start_result.message.serialize()) ); } Ok(()) } fn test_ke2(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> where ::Core: ProxyHash, <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, // Envelope: Nonce + Hash NonceLen: Add>, EnvelopeLen: ArrayLength, // RegistrationUpload: (KePk + Hash) + Envelope ::PkLen: Add>, Sum<::PkLen, OutputSize>: ArrayLength + Add>, RegistrationUploadLen: ArrayLength, // ServerRegistration = RegistrationUpload // MaskedResponse: (Nonce + Hash) + KePk NonceLen: Add>, Sum>: ArrayLength + Add<::PkLen>, MaskedResponseLen: ArrayLength, // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse ::ElemLen: Add, Sum<::ElemLen, NonceLen>: ArrayLength + Add>, CredentialResponseWithoutKeLen: ArrayLength, // MaskedResponse: (Nonce + Hash) + KePk NonceLen: Add>, Sum>: ArrayLength + Add<::PkLen>, MaskedResponseLen: ArrayLength, // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message CredentialResponseWithoutKeLen: Add>, CredentialResponseLen: ArrayLength, { for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ parameters.oprf_seed.as_slice(), ¶meters.server_private_key, ¶meters.dummy_private_key, ] .concat(), )?; let record = ServerRegistration::::deserialize(&get_password_file_bytes::(parameters))?; let mut server_private_keyshare_and_nonce_rng = CycleRng::new( [ parameters.masking_nonce.as_slice(), ¶meters.server_private_keyshare, ¶meters.server_nonce, ] .concat(), ); let server_login_start_result = ServerLogin::::start( &mut server_private_keyshare_and_nonce_rng, &server_setup, Some(record), CredentialRequest::::deserialize(¶meters.KE1).unwrap(), ¶meters.credential_identifier, ServerLoginStartParameters { context: Some(¶meters.context), identifiers: Identifiers { client: parameters.client_identity.as_deref(), server: parameters.server_identity.as_deref(), }, }, )?; assert_eq!( hex::encode(¶meters.handshake_secret), hex::encode(server_login_start_result.handshake_secret) ); assert_eq!( hex::encode(¶meters.server_mac_key), hex::encode(server_login_start_result.server_mac_key) ); assert_eq!( hex::encode(¶meters.oprf_key), hex::encode(server_login_start_result.oprf_key) ); assert_eq!( hex::encode(¶meters.KE2), hex::encode(server_login_start_result.message.serialize()) ); } Ok(()) } fn test_ke3(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> where ::Core: ProxyHash, <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, // MaskedResponse: (Nonce + Hash) + KePk NonceLen: Add>, Sum>: ArrayLength + Add<::PkLen>, MaskedResponseLen: ArrayLength, { for parameters in tvs { let client_login_start = [ parameters.blind_login.as_slice(), ¶meters.client_private_keyshare, ¶meters.client_nonce, ] .concat(); let mut client_login_start_rng = CycleRng::new(client_login_start); let client_login_start_result = ClientLogin::::start(&mut client_login_start_rng, ¶meters.password)?; let client_login_finish_result = client_login_start_result.state.finish( ¶meters.password, CredentialResponse::::deserialize(¶meters.KE2)?, ClientLoginFinishParameters::new( Some(¶meters.context.clone()), Identifiers { client: parameters.client_identity.as_deref(), server: parameters.server_identity.as_deref(), }, None, ), )?; assert_eq!( hex::encode(¶meters.session_key), hex::encode(&client_login_finish_result.session_key) ); assert_eq!( hex::encode(¶meters.handshake_secret), hex::encode(&client_login_finish_result.handshake_secret) ); assert_eq!( hex::encode(¶meters.client_mac_key), hex::encode(&client_login_finish_result.client_mac_key) ); assert_eq!( hex::encode(¶meters.KE3), hex::encode(client_login_finish_result.message.serialize()) ); assert_eq!( hex::encode(¶meters.export_key), hex::encode(client_login_finish_result.export_key) ); } Ok(()) } fn test_server_login_finish( tvs: &[OpaqueTestVectorParameters], ) -> Result<(), ProtocolError> where ::Core: ProxyHash, <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, // Envelope: Nonce + Hash NonceLen: Add>, EnvelopeLen: ArrayLength, // RegistrationUpload: (KePk + Hash) + Envelope ::PkLen: Add>, Sum<::PkLen, OutputSize>: ArrayLength + Add>, RegistrationUploadLen: ArrayLength, // ServerRegistration = RegistrationUpload // MaskedResponse: (Nonce + Hash) + KePk NonceLen: Add>, Sum>: ArrayLength + Add<::PkLen>, MaskedResponseLen: ArrayLength, { for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ parameters.oprf_seed.as_slice(), ¶meters.server_private_key, ¶meters.dummy_private_key, ] .concat(), )?; let record = ServerRegistration::::deserialize(&get_password_file_bytes::(parameters))?; let mut server_private_keyshare_and_nonce_rng = CycleRng::new( [ parameters.masking_nonce.as_slice(), ¶meters.server_private_keyshare, ¶meters.server_nonce, ] .concat(), ); let server_login_start_result = ServerLogin::::start( &mut server_private_keyshare_and_nonce_rng, &server_setup, Some(record), CredentialRequest::::deserialize(¶meters.KE1).unwrap(), ¶meters.credential_identifier, ServerLoginStartParameters { context: Some(¶meters.context), identifiers: Identifiers { client: parameters.client_identity.as_deref(), server: parameters.server_identity.as_deref(), }, }, )?; let server_login_result = server_login_start_result .state .finish(CredentialFinalization::deserialize(¶meters.KE3)?)?; assert_eq!( hex::encode(¶meters.session_key), hex::encode(&server_login_result.session_key) ); } Ok(()) } fn test_fake_vectors( tvs: &[OpaqueTestVectorParameters], ) -> Result<(), ProtocolError> where ::Core: ProxyHash, <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, // MaskedResponse: (Nonce + Hash) + KePk NonceLen: Add>, Sum>: ArrayLength + Add<::PkLen>, MaskedResponseLen: ArrayLength, // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse ::ElemLen: Add, Sum<::ElemLen, NonceLen>: ArrayLength + Add>, CredentialResponseWithoutKeLen: ArrayLength, // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message CredentialResponseWithoutKeLen: Add>, CredentialResponseLen: ArrayLength, { for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ parameters.oprf_seed.as_slice(), ¶meters.server_private_key, ¶meters.dummy_private_key, ] .concat(), )?; let mut server_private_keyshare_and_nonce_rng = CycleRng::new( [ parameters.dummy_masking_key.as_slice(), ¶meters.masking_nonce, ¶meters.server_private_keyshare, ¶meters.server_nonce, ] .concat(), ); let server_login_start_result = ServerLogin::::start( &mut server_private_keyshare_and_nonce_rng, &server_setup, None, CredentialRequest::::deserialize(¶meters.KE1).unwrap(), ¶meters.credential_identifier, ServerLoginStartParameters { context: Some(¶meters.context), identifiers: Identifiers { client: parameters.client_identity.as_deref(), server: parameters.server_identity.as_deref(), }, }, )?; assert_eq!( hex::encode(¶meters.KE2), hex::encode(server_login_start_result.message.serialize()) ); } Ok(()) }