// 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::vec; use std::vec::Vec; use digest::core_api::{BlockSizeUser, CoreProxy}; use digest::OutputSizeUser; use generic_array::typenum::{IsLess, IsLessOrEqual, Le, NonZero, Sum, U256}; use generic_array::{ArrayLength, GenericArray}; use json::JsonValue; use rand::rngs::OsRng; use rand::RngCore; use voprf::Group; use crate::ciphersuite::{CipherSuite, OprfGroup, OprfHash}; use crate::envelope::EnvelopeLen; use crate::errors::*; use crate::hash::{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::ksf::Identity; use crate::messages::{ CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen, RegistrationResponseLen, RegistrationUploadLen, }; use crate::opaque::*; 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, } }; } fn decode(values: &JsonValue, key: &str) -> Option> { values[key].as_str().and_then(|s| hex::decode(s).ok()) } fn populate_test_vectors(values: &JsonValue) -> OpaqueTestVectorParameters where as OutputSizeUser>::OutputSize: IsLess + IsLessOrEqual< as BlockSizeUser>::BlockSize>, OprfHash: Hash, as CoreProxy>::Core: ProxyHash, < as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess, Le<< as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero, { let mut rng = OsRng; OpaqueTestVectorParameters { dummy_private_key: { match decode(values, "client_private_key") { Some(value) => value, None => CS::KeGroup::serialize_sk(CS::KeGroup::random_sk(&mut OsRng)).to_vec(), } }, dummy_masking_key: { match decode(values, "masking_key") { Some(value) => value, None => { let mut bytes = GenericArray:: as OutputSizeUser>::OutputSize>::default(); rng.fill_bytes(&mut bytes); bytes.to_vec() } } }, 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 as OutputSizeUser>::OutputSize: IsLess + IsLessOrEqual< as BlockSizeUser>::BlockSize>, OprfHash: Hash, as CoreProxy>::Core: ProxyHash, < as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess, Le<< as CoreProxy>::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, $cs_ty:ty) => { $v[$vector_type] .members() .filter_map(|x| { if x.has_key($cs) { Some(populate_test_vectors::<$cs_ty>(&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")] { struct Ristretto255Sha512NoKsf; impl CipherSuite for Ristretto255Sha512NoKsf { type OprfCs = crate::Ristretto255; type KeGroup = crate::Ristretto255; type KeyExchange = TripleDh; type Ksf = Identity; } let ristretto_real_tvs = json_to_test_vectors!(rfc, "Real", "ristretto255, SHA512", Ristretto255Sha512NoKsf); let ristretto_fake_tvs = json_to_test_vectors!(rfc, "Fake", "ristretto255, SHA512", Ristretto255Sha512NoKsf); assert!( !(ristretto_real_tvs.is_empty() || ristretto_fake_tvs.is_empty()), "Parsing error" ); 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)?; } struct P256Sha256NoKsf; impl CipherSuite for P256Sha256NoKsf { type OprfCs = p256::NistP256; type KeGroup = p256::NistP256; type KeyExchange = TripleDh; type Ksf = Identity; } let p256_real_tvs = json_to_test_vectors!( rfc, "Real", "P256_XMD:SHA-256_SSWU_RO_, SHA256", P256Sha256NoKsf ); let p256_fake_tvs = json_to_test_vectors!( rfc, "Fake", "P256_XMD:SHA-256_SSWU_RO_, SHA256", P256Sha256NoKsf ); assert!( !(p256_real_tvs.is_empty() || p256_fake_tvs.is_empty()), "Parsing error" ); 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 as OutputSizeUser>::OutputSize: IsLess + IsLessOrEqual< as BlockSizeUser>::BlockSize>, OprfHash: Hash, as CoreProxy>::Core: ProxyHash, < as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess, Le<< as CoreProxy>::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 as OutputSizeUser>::OutputSize: IsLess + IsLessOrEqual< as BlockSizeUser>::BlockSize>, OprfHash: Hash, as CoreProxy>::Core: ProxyHash, < as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess, Le<< as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero, // RegistrationResponse: KgPk + KePk as Group>::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 as OutputSizeUser>::OutputSize: IsLess + IsLessOrEqual< as BlockSizeUser>::BlockSize>, OprfHash: Hash, as CoreProxy>::Core: ProxyHash, < as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess, Le<< as CoreProxy>::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) ); } Ok(()) } fn test_ke1(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> where as OutputSizeUser>::OutputSize: IsLess + IsLessOrEqual< as BlockSizeUser>::BlockSize>, OprfHash: Hash, as CoreProxy>::Core: ProxyHash, < as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess, Le<< as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero, // CredentialRequest: KgPk + Ke1Message as Group>::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(); 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 as OutputSizeUser>::OutputSize: IsLess + IsLessOrEqual< as BlockSizeUser>::BlockSize>, OprfHash: Hash, as CoreProxy>::Core: ProxyHash, < as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess, Le<< as CoreProxy>::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 as Group>::ElemLen: Add, Sum< as Group>::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 as OutputSizeUser>::OutputSize: IsLess + IsLessOrEqual< as BlockSizeUser>::BlockSize>, OprfHash: Hash, as CoreProxy>::Core: ProxyHash, < as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess, Le<< as CoreProxy>::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 as OutputSizeUser>::OutputSize: IsLess + IsLessOrEqual< as BlockSizeUser>::BlockSize>, OprfHash: Hash, as CoreProxy>::Core: ProxyHash, < as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess, Le<< as CoreProxy>::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 as OutputSizeUser>::OutputSize: IsLess + IsLessOrEqual< as BlockSizeUser>::BlockSize>, OprfHash: Hash, as CoreProxy>::Core: ProxyHash, < as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess, Le<< as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero, // MaskedResponse: (Nonce + Hash) + KePk NonceLen: Add>>, Sum>>: ArrayLength + Add<::PkLen>, MaskedResponseLen: ArrayLength, // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse as Group>::ElemLen: Add, Sum< as Group>::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(()) }