// SPDX-License-Identifier: MIT OR Apache-2.0 // Copyright (c) VexaHub and contributors. // Copyright (c) Meta Platforms, Inc. and affiliates. use core::ops::Add; use std::vec; use std::vec::Vec; use crate::ciphersuite::{CipherSuite, KeGroup, OprfGroup, OprfHash}; use crate::envelope::EnvelopeLen; use crate::errors::*; use crate::hash::OutputSize; use crate::key_exchange::group::Group; use crate::key_exchange::shared::NonceLen; use crate::key_exchange::{Deserialize, Ke1MessageLen, Ke2MessageLen, KeyExchange, Serialize}; use crate::ksf::Identity; use crate::messages::{ CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen, RegistrationResponseLen, RegistrationUploadLen, }; use crate::opaque::*; use crate::tests::decode; use crate::tests::mock_rng::CycleRng; use crate::*; use digest::OutputSizeUser; use generic_array::typenum::Sum; use generic_array::{ArrayLength, GenericArray}; use rand::Rng; use rand::rngs::SysRng; use rand_core::UnwrapErr; use serde_json::Value; #[allow(non_snake_case)] #[derive(Debug)] pub struct OpaqueTestVectorParameters { pub dummy_public_key: Vec, pub dummy_masking_key: Vec, pub context: Vec, #[allow(dead_code)] // client_private_key is not tested in the test vectors pub client_private_key: Option>, pub client_keyshare_seed: Vec, pub server_public_key: Vec, pub server_private_key: Vec, pub server_keyshare_seed: 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 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 populate_test_vectors(values: &Value) -> OpaqueTestVectorParameters { let mut rng = UnwrapErr(SysRng); OpaqueTestVectorParameters { dummy_public_key: { decode(values, "client_public_key").unwrap_or_else(|| { KeGroup::::serialize_sk(&KeGroup::::random_sk(&mut UnwrapErr(SysRng))) .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_seed: parse!(values, "client_keyshare_seed"), server_public_key: parse!(values, "server_public_key"), server_private_key: parse!(values, "server_private_key"), server_keyshare_seed: parse!(values, "server_keyshare_seed"), 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"), 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_password"), 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 // RegistrationUpload: (KePk + Hash) + Envelope as Group>::PkLen: Add>>, Sum< as Group>::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] .as_array() .into_iter() .flatten() .filter_map(|x| { if let Some(val) = x.get($cs) { Some(populate_test_vectors::<$cs_ty>(val)) } else { None } }) .collect::>() }; } #[test] fn tests() -> Result<(), ProtocolError> { let rfc: Value = serde_json::from_str(super::parser::rfc_to_json(super::rfc9807_vectors::VECTORS).as_str()) .expect("Could not parse json"); std::eprintln!("{}", serde_json::to_string_pretty(&rfc).unwrap()); #[cfg(feature = "ristretto255")] { struct Ristretto255Sha512NoKsf; impl CipherSuite for Ristretto255Sha512NoKsf { type OprfCs = Ristretto255; type KeyExchange = TripleDh; type Ksf = Identity; } let ristretto_real_tvs = json_to_test_vectors!( rfc, "Real", "ristretto255-SHA512, ristretto255", Ristretto255Sha512NoKsf ); let ristretto_fake_tvs = json_to_test_vectors!( rfc, "Fake", "ristretto255-SHA512, ristretto255", Ristretto255Sha512NoKsf ); assert!( !(ristretto_real_tvs.is_empty() || ristretto_fake_tvs.is_empty()), "Parsing error" ); // There should be 2 real test vectors and 1 fake test vector assert_eq!(ristretto_real_tvs.len(), 2); assert_eq!(ristretto_fake_tvs.len(), 1); 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(all(feature = "ristretto255", feature = "curve25519"))] { struct Ristretto255Sha512Curve25519NoKsf; impl CipherSuite for Ristretto255Sha512Curve25519NoKsf { type OprfCs = crate::Ristretto255; type KeyExchange = TripleDh; type Ksf = Identity; } let ristretto_real_tvs = json_to_test_vectors!( rfc, "Real", "ristretto255-SHA512, curve25519", Ristretto255Sha512Curve25519NoKsf ); let ristretto_fake_tvs = json_to_test_vectors!( rfc, "Fake", "ristretto255-SHA512, curve25519", Ristretto255Sha512Curve25519NoKsf ); assert!( !(ristretto_real_tvs.is_empty() || ristretto_fake_tvs.is_empty()), "Parsing error" ); // There should be 2 real test vectors and 1 fake test vector assert_eq!(ristretto_real_tvs.len(), 2); assert_eq!(ristretto_fake_tvs.len(), 1); 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 KeyExchange = TripleDh; type Ksf = Identity; } let p256_real_tvs = json_to_test_vectors!( rfc, "Real", "P256-SHA256, P256_XMD:SHA-256_SSWU_RO_", P256Sha256NoKsf ); let p256_fake_tvs = json_to_test_vectors!( rfc, "Fake", "P256-SHA256, P256_XMD:SHA-256_SSWU_RO_", P256Sha256NoKsf ); assert!( !(p256_real_tvs.is_empty() || p256_fake_tvs.is_empty()), "Parsing error" ); // There should be 2 real test vectors and 1 fake test vector assert_eq!(p256_real_tvs.len(), 2); assert_eq!(p256_fake_tvs.len(), 1); 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> { 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 // RegistrationResponse: KgPk + KePk as voprf::Group>::ElemLen: Add< as Group>::PkLen>, RegistrationResponseLen: ArrayLength, { for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ parameters.oprf_seed.as_slice(), ¶meters.server_private_key, ¶meters.dummy_public_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.server_public_key), hex::encode(server_setup.keypair().public().serialize()), ); 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 // RegistrationUpload: (KePk + Hash) + Envelope as Group>::PkLen: Add>>, Sum< as Group>::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 // CredentialRequest: KgPk + Ke1Message ::KE1Message: Serialize, as voprf::Group>::ElemLen: Add>, CredentialRequestLen: ArrayLength, { for parameters in tvs { let client_login_start = [ parameters.blind_login.as_slice(), ¶meters.client_keyshare_seed, ¶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 ::KE1Message: Deserialize, // RegistrationUpload: (KePk + Hash) + Envelope as Group>::PkLen: Add>>, Sum< as Group>::PkLen, OutputSize>>: ArrayLength + Add>, RegistrationUploadLen: ArrayLength, // ServerRegistration = RegistrationUpload // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse as voprf::Group>::ElemLen: Add, Sum< as voprf::Group>::ElemLen, NonceLen>: ArrayLength + Add>, CredentialResponseWithoutKeLen: ArrayLength, // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message ::KE2Message: Serialize, CredentialResponseWithoutKeLen: Add>, CredentialResponseLen: ArrayLength, { for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ parameters.oprf_seed.as_slice(), ¶meters.server_private_key, ¶meters.dummy_public_key, ] .concat(), )?; let record = ServerRegistration::::deserialize(&get_password_file_bytes::(parameters))?; let mut server_keyshare_seed_and_nonce_rng = CycleRng::new( [ parameters.dummy_masking_key.as_slice(), ¶meters.masking_nonce, ¶meters.server_keyshare_seed, ¶meters.server_nonce, ] .concat(), ); let server_login_start_result = ServerLogin::::start( &mut server_keyshare_seed_and_nonce_rng, &server_setup, Some(record), CredentialRequest::::deserialize(¶meters.KE1).unwrap(), ¶meters.credential_identifier, ServerLoginParameters { 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 ::KE2Message: Deserialize + Serialize, ::KE3Message: Serialize, { for parameters in tvs { let client_login_start = [ parameters.blind_login.as_slice(), ¶meters.client_keyshare_seed, ¶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( &mut UnwrapErr(SysRng), ¶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 ::KE1Message: Deserialize, ::KE3Message: Deserialize, // RegistrationUpload: (KePk + Hash) + Envelope as Group>::PkLen: Add>>, Sum< as Group>::PkLen, OutputSize>>: ArrayLength + Add>, RegistrationUploadLen: ArrayLength, // ServerRegistration = RegistrationUpload { for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ parameters.oprf_seed.as_slice(), ¶meters.server_private_key, ¶meters.dummy_public_key, ] .concat(), )?; let record = ServerRegistration::::deserialize(&get_password_file_bytes::(parameters))?; let mut server_keyshare_seed_and_nonce_rng = CycleRng::new( [ parameters.dummy_masking_key.as_slice(), ¶meters.masking_nonce, ¶meters.server_keyshare_seed, ¶meters.server_nonce, ] .concat(), ); let server_login_start_result = ServerLogin::::start( &mut server_keyshare_seed_and_nonce_rng, &server_setup, Some(record), CredentialRequest::::deserialize(¶meters.KE1).unwrap(), ¶meters.credential_identifier, ServerLoginParameters { 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)?, ServerLoginParameters { context: Some(¶meters.context), identifiers: Identifiers { client: parameters.client_identity.as_deref(), server: parameters.server_identity.as_deref(), }, }, )?; assert_eq!( hex::encode(¶meters.session_key), hex::encode(&server_login_result.session_key) ); } Ok(()) } fn test_fake_vectors( tvs: &[OpaqueTestVectorParameters], ) -> Result<(), ProtocolError> where ::KE1Message: Deserialize, // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse as voprf::Group>::ElemLen: Add, Sum< as voprf::Group>::ElemLen, NonceLen>: ArrayLength + Add>, CredentialResponseWithoutKeLen: ArrayLength, // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message ::KE2Message: Serialize, CredentialResponseWithoutKeLen: Add>, CredentialResponseLen: ArrayLength, { for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ parameters.oprf_seed.as_slice(), ¶meters.server_private_key, ¶meters.dummy_public_key, ] .concat(), )?; let mut server_keyshare_seed_and_nonce_rng = CycleRng::new( [ parameters.dummy_masking_key.as_slice(), ¶meters.masking_nonce, ¶meters.server_keyshare_seed, ¶meters.server_nonce, ] .concat(), ); let server_login_start_result = ServerLogin::::start( &mut server_keyshare_seed_and_nonce_rng, &server_setup, None, CredentialRequest::::deserialize(¶meters.KE1).unwrap(), ¶meters.credential_identifier, ServerLoginParameters { 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(()) }