// 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. #![allow(unsafe_code)] use crate::{ ciphersuite::CipherSuite, errors::*, group::Group, key_exchange::tripledh::{NonceLen, TripleDH}, keypair::{Key, SizedBytesExt}, opaque::*, slow_hash::NoOpHash, tests::mock_rng::CycleRng, *, }; use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity}; use generic_array::typenum::Unsigned; use generic_bytes::SizedBytes; use rand::{rngs::OsRng, RngCore}; use serde_json::Value; use std::slice::from_raw_parts; use zeroize::Zeroize; // Tests // ===== struct RistrettoSha5123dhNoSlowHash; impl CipherSuite for RistrettoSha5123dhNoSlowHash { type Group = RistrettoPoint; type KeyExchange = TripleDH; type Hash = sha2::Sha512; type SlowHash = NoOpHash; } pub struct TestVectorParameters { pub client_s_pk: Vec, pub client_s_sk: Vec, pub client_e_pk: Vec, pub client_e_sk: Vec, pub server_s_pk: Vec, pub server_s_sk: Vec, pub server_e_pk: Vec, pub server_e_sk: Vec, pub id_u: Vec, pub id_s: Vec, pub password: Vec, pub blinding_factor: Vec, pub oprf_key: Vec, pub envelope_nonce: Vec, pub client_nonce: Vec, pub server_nonce: Vec, pub info1: Vec, pub einfo2: Vec, pub registration_request: Vec, pub registration_response: Vec, pub registration_upload: Vec, pub credential_request: Vec, pub credential_response: Vec, pub credential_finalization: Vec, client_registration_state: Vec, server_registration_state: Vec, client_login_state: Vec, server_login_state: Vec, pub password_file: Vec, pub export_key: Vec, pub session_key: Vec, } static STR_PASSWORD: &str = "password"; static TEST_VECTOR: &str = r#" { "client_s_pk": "6e0a6082dd29936c44b47ecb8a5fe72e4b321a0ac314b0080ca4c48afdabd215", "client_s_sk": "3000848b34d9073885d427e766b7093cc13bdce992ef31cd00ba2c77ff074504", "client_e_pk": "5260ad6eb47ac1db44babcce9327327d50e1a0133c0425acca3efcf41b119718", "client_e_sk": "1526e0bed9af28830da956589d65768ed2a20d9689e82c90b89e4e33904e4009", "server_s_pk": "c21a38653eb19437669bfa066a446b6eea7c1f253ac7adf4798d6c68e171b273", "server_s_sk": "2a38b385e9fb7e0a89aa0f005c729b0c4e22eeedea8c105cf136d9c5c334880f", "server_e_pk": "125f4a2dd9353c8c37a08527a323908835d3fbc374260d32e829d5c3fa81f325", "server_e_sk": "7ef6ada36f3983c0f24705a29d3a453e3e17c28d347f226b24c0aa5ab12a700d", "id_u": "696455", "id_s": "696453", "password": "70617373776f7264", "blinding_factor": "a32862d66eb57246321fb6b229e83786745c3afdf8957ebe38b01c17571ba105", "oprf_key": "0851f5ec43e0b702bfcb9d8ec208085b51c0fc5200234901407c176327aa0b02", "envelope_nonce": "78b006042d011bdca5d0058a978d2103a6d546de311a2e8cd025fbc67303a468", "client_nonce": "43497a6f86ba31a6a7f399271fb3b4b4f82c4af086bc431ebaeab7d768ff8a64", "server_nonce": "680cd27da18bc56d4317e7db61de1726a70ebe4e49aee38a3bdb6787cf85466e", "info1": "696e666f31", "einfo2": "65696e666f32", "registration_request": "14ba86e53018ce5507d2bfb2d98ad3f60e302d826bff3410a5ec669c8e1ef17d", "registration_response": "aee5c937a85acfbdbd71faf1c5519bfe9e44b0b7489dcc663df9f1ca5b520b6ac21a38653eb19437669bfa066a446b6eea7c1f253ac7adf4798d6c68e171b273", "registration_upload": "6e0a6082dd29936c44b47ecb8a5fe72e4b321a0ac314b0080ca4c48afdabd2150278b006042d011bdca5d0058a978d2103a6d546de311a2e8cd025fbc67303a4687618b32fe2ec2a5c2b6efecec1e6e535106de80af68733673daf0b644965966fa3f279e532d7ecef363f8d55ff6df4c473cfb1049a73f632972bfcc6744185d13a671dd6678d49fb1629a6fbfbe266937378fd9e772c2dd72692d1a35c020010", "credential_request": "14ba86e53018ce5507d2bfb2d98ad3f60e302d826bff3410a5ec669c8e1ef17d43497a6f86ba31a6a7f399271fb3b4b4f82c4af086bc431ebaeab7d768ff8a640005696e666f315260ad6eb47ac1db44babcce9327327d50e1a0133c0425acca3efcf41b119718", "credential_response": "aee5c937a85acfbdbd71faf1c5519bfe9e44b0b7489dcc663df9f1ca5b520b6ac21a38653eb19437669bfa066a446b6eea7c1f253ac7adf4798d6c68e171b2730278b006042d011bdca5d0058a978d2103a6d546de311a2e8cd025fbc67303a4687618b32fe2ec2a5c2b6efecec1e6e535106de80af68733673daf0b644965966fa3f279e532d7ecef363f8d55ff6df4c473cfb1049a73f632972bfcc6744185d13a671dd6678d49fb1629a6fbfbe266937378fd9e772c2dd72692d1a35c020010680cd27da18bc56d4317e7db61de1726a70ebe4e49aee38a3bdb6787cf85466e125f4a2dd9353c8c37a08527a323908835d3fbc374260d32e829d5c3fa81f32500068a4c321f3c375613862ba83f7e5abb8f1d26dbd8035d39f192eb1324c2214457098054a2cbdf5d4ab2894eaf152af8c4be61d701c5a1ab1ec3e1cee5810898140b81771db0be", "credential_finalization": "330aa8ac01bbc9d9642fe1c286187379efe12da14aceab86b22449d21f242d89adabc0295751b4e007beabc413ae9cbf9979e324749953705fd85b87c9c1b1a2", "client_registration_state": "a32862d66eb57246321fb6b229e83786745c3afdf8957ebe38b01c17571ba10570617373776f7264", "client_login_state": "a32862d66eb57246321fb6b229e83786745c3afdf8957ebe38b01c17571ba105006714ba86e53018ce5507d2bfb2d98ad3f60e302d826bff3410a5ec669c8e1ef17d43497a6f86ba31a6a7f399271fb3b4b4f82c4af086bc431ebaeab7d768ff8a640005696e666f315260ad6eb47ac1db44babcce9327327d50e1a0133c0425acca3efcf41b11971800401526e0bed9af28830da956589d65768ed2a20d9689e82c90b89e4e33904e400943497a6f86ba31a6a7f399271fb3b4b4f82c4af086bc431ebaeab7d768ff8a6470617373776f7264", "server_registration_state": "0851f5ec43e0b702bfcb9d8ec208085b51c0fc5200234901407c176327aa0b02", "server_login_state": "89cd93dce8f59cf7b187736c50cbc3ca2e0bbbad0a0be1ddee180a2d95db60695b384be2dd673434ef94bcdbb1b457f63f41cd79ed2422c021c34ca1433b70576920ebb950ad1c40c8c015ec6832e12427e755ba21d005f0b6d5d66d2368ab5ec6f32151fced5a3aa25472c425912242de3638ef57f28b0dd02a956064e5bb9b8fcd73add52d233a454d6b20125e9506a95aae8772ebbfac4d70efe1fb10078fd40f93d84aa7db53853ca74436c917c427cd5c2e408860937e6f7ab80816ca47", "password_file": "0851f5ec43e0b702bfcb9d8ec208085b51c0fc5200234901407c176327aa0b026e0a6082dd29936c44b47ecb8a5fe72e4b321a0ac314b0080ca4c48afdabd2150278b006042d011bdca5d0058a978d2103a6d546de311a2e8cd025fbc67303a4687618b32fe2ec2a5c2b6efecec1e6e535106de80af68733673daf0b644965966fa3f279e532d7ecef363f8d55ff6df4c473cfb1049a73f632972bfcc6744185d13a671dd6678d49fb1629a6fbfbe266937378fd9e772c2dd72692d1a35c020010", "export_key": "8197f91f0d4de1ab126d8dfd06abd0d5df420ce40a135ef376e4ffe515930f413632390e7dc3dcfd19afff62b9113e10eb6c359fc327df6e9ad4d0f06c242322", "session_key": "8fcd73add52d233a454d6b20125e9506a95aae8772ebbfac4d70efe1fb10078fd40f93d84aa7db53853ca74436c917c427cd5c2e408860937e6f7ab80816ca47" } "#; fn decode(values: &Value, key: &str) -> Option> { values[key] .as_str() .and_then(|s| hex::decode(&s.to_string()).ok()) } fn populate_test_vectors(values: &Value) -> TestVectorParameters { TestVectorParameters { client_s_pk: decode(&values, "client_s_pk").unwrap(), client_s_sk: decode(&values, "client_s_sk").unwrap(), client_e_pk: decode(&values, "client_e_pk").unwrap(), client_e_sk: decode(&values, "client_e_sk").unwrap(), server_s_pk: decode(&values, "server_s_pk").unwrap(), server_s_sk: decode(&values, "server_s_sk").unwrap(), server_e_pk: decode(&values, "server_e_pk").unwrap(), server_e_sk: decode(&values, "server_e_sk").unwrap(), id_u: decode(&values, "id_u").unwrap(), id_s: decode(&values, "id_s").unwrap(), password: decode(&values, "password").unwrap(), blinding_factor: decode(&values, "blinding_factor").unwrap(), oprf_key: decode(&values, "oprf_key").unwrap(), envelope_nonce: decode(&values, "envelope_nonce").unwrap(), client_nonce: decode(&values, "client_nonce").unwrap(), server_nonce: decode(&values, "server_nonce").unwrap(), info1: decode(&values, "info1").unwrap(), einfo2: decode(&values, "einfo2").unwrap(), registration_request: decode(&values, "registration_request").unwrap(), registration_response: decode(&values, "registration_response").unwrap(), registration_upload: decode(&values, "registration_upload").unwrap(), credential_request: decode(&values, "credential_request").unwrap(), credential_response: decode(&values, "credential_response").unwrap(), credential_finalization: decode(&values, "credential_finalization").unwrap(), client_registration_state: decode(&values, "client_registration_state").unwrap(), client_login_state: decode(&values, "client_login_state").unwrap(), server_registration_state: decode(&values, "server_registration_state").unwrap(), server_login_state: decode(&values, "server_login_state").unwrap(), password_file: decode(&values, "password_file").unwrap(), export_key: decode(&values, "export_key").unwrap(), session_key: decode(&values, "session_key").unwrap(), } } fn stringify_test_vectors(p: &TestVectorParameters) -> String { let mut s = String::new(); s.push_str("{\n"); s.push_str(format!("\"client_s_pk\": \"{}\",\n", hex::encode(&p.client_s_pk)).as_str()); s.push_str(format!("\"client_s_sk\": \"{}\",\n", hex::encode(&p.client_s_sk)).as_str()); s.push_str(format!("\"client_e_pk\": \"{}\",\n", hex::encode(&p.client_e_pk)).as_str()); s.push_str(format!("\"client_e_sk\": \"{}\",\n", hex::encode(&p.client_e_sk)).as_str()); s.push_str(format!("\"server_s_pk\": \"{}\",\n", hex::encode(&p.server_s_pk)).as_str()); s.push_str(format!("\"server_s_sk\": \"{}\",\n", hex::encode(&p.server_s_sk)).as_str()); s.push_str(format!("\"server_e_pk\": \"{}\",\n", hex::encode(&p.server_e_pk)).as_str()); s.push_str(format!("\"server_e_sk\": \"{}\",\n", hex::encode(&p.server_e_sk)).as_str()); s.push_str(format!("\"id_u\": \"{}\",\n", hex::encode(&p.id_u)).as_str()); s.push_str(format!("\"id_s\": \"{}\",\n", hex::encode(&p.id_s)).as_str()); s.push_str(format!("\"password\": \"{}\",\n", hex::encode(&p.password)).as_str()); s.push_str( format!( "\"blinding_factor\": \"{}\",\n", hex::encode(&p.blinding_factor) ) .as_str(), ); s.push_str(format!("\"oprf_key\": \"{}\",\n", hex::encode(&p.oprf_key)).as_str()); s.push_str( format!( "\"envelope_nonce\": \"{}\",\n", hex::encode(&p.envelope_nonce) ) .as_str(), ); s.push_str(format!("\"client_nonce\": \"{}\",\n", hex::encode(&p.client_nonce)).as_str()); s.push_str(format!("\"server_nonce\": \"{}\",\n", hex::encode(&p.server_nonce)).as_str()); s.push_str(format!("\"info1\": \"{}\",\n", hex::encode(&p.info1)).as_str()); s.push_str(format!("\"einfo2\": \"{}\",\n", hex::encode(&p.einfo2)).as_str()); s.push_str( format!( "\"registration_request\": \"{}\",\n", hex::encode(&p.registration_request) ) .as_str(), ); s.push_str( format!( "\"registration_response\": \"{}\",\n", hex::encode(&p.registration_response) ) .as_str(), ); s.push_str( format!( "\"registration_upload\": \"{}\",\n", hex::encode(&p.registration_upload) ) .as_str(), ); s.push_str( format!( "\"credential_request\": \"{}\",\n", hex::encode(&p.credential_request) ) .as_str(), ); s.push_str( format!( "\"credential_response\": \"{}\",\n", hex::encode(&p.credential_response) ) .as_str(), ); s.push_str( format!( "\"credential_finalization\": \"{}\",\n", hex::encode(&p.credential_finalization) ) .as_str(), ); s.push_str( format!( "\"client_registration_state\": \"{}\",\n", hex::encode(&p.client_registration_state) ) .as_str(), ); s.push_str( format!( "\"client_login_state\": \"{}\",\n", hex::encode(&p.client_login_state) ) .as_str(), ); s.push_str( format!( "\"server_registration_state\": \"{}\",\n", hex::encode(&p.server_registration_state) ) .as_str(), ); s.push_str( format!( "\"server_login_state\": \"{}\",\n", hex::encode(&p.server_login_state) ) .as_str(), ); s.push_str( format!( "\"password_file\": \"{}\",\n", hex::encode(&p.password_file) ) .as_str(), ); s.push_str(format!("\"export_key\": \"{}\",\n", hex::encode(&p.export_key)).as_str()); s.push_str(format!("\"session_key\": \"{}\"\n", hex::encode(&p.session_key)).as_str()); s.push_str("}\n"); s } fn generate_parameters() -> TestVectorParameters { let mut rng = OsRng; // Inputs let server_s_kp = CS::generate_random_keypair(&mut rng); let server_e_kp = CS::generate_random_keypair(&mut rng); let client_s_kp = CS::generate_random_keypair(&mut rng); let client_e_kp = CS::generate_random_keypair(&mut rng); let id_u = b"idU"; let id_s = b"idS"; let password = b"password"; let mut oprf_key_raw = [0u8; 32]; rng.fill_bytes(&mut oprf_key_raw); let mut envelope_nonce = [0u8; 32]; rng.fill_bytes(&mut envelope_nonce); let mut client_nonce = vec![0u8; NonceLen::to_usize()]; rng.fill_bytes(&mut client_nonce); let mut server_nonce = vec![0u8; NonceLen::to_usize()]; rng.fill_bytes(&mut server_nonce); let blinding_factor = CS::Group::random_nonzero_scalar(&mut rng); let blinding_factor_bytes = CS::Group::scalar_as_bytes(&blinding_factor).clone(); let info1 = b"info1"; let einfo2 = b"einfo2"; let mut blinding_factor_registration_rng = CycleRng::new(blinding_factor_bytes.to_vec()); let client_registration_start_result = ClientRegistration::::start(&mut blinding_factor_registration_rng, password).unwrap(); let blinding_factor_bytes_returned = CS::Group::scalar_as_bytes(&client_registration_start_result.state.token.blind).clone(); assert_eq!( hex::encode(&blinding_factor_bytes), hex::encode(&blinding_factor_bytes_returned) ); let registration_request_bytes = client_registration_start_result .message .serialize() .to_vec(); let client_registration_state = client_registration_start_result.state.serialize().to_vec(); let mut oprf_key_rng = CycleRng::new(oprf_key_raw.to_vec()); let server_registration_start_result = ServerRegistration::::start( &mut oprf_key_rng, client_registration_start_result.message, server_s_kp.public(), ) .unwrap(); let registration_response_bytes = server_registration_start_result .message .serialize() .to_vec(); let oprf_key_bytes = CS::Group::scalar_as_bytes(&server_registration_start_result.state.oprf_key).clone(); let server_registration_state = server_registration_start_result.state.serialize().to_vec(); let mut client_s_sk_and_nonce: Vec = Vec::new(); client_s_sk_and_nonce.extend_from_slice(&client_s_kp.private().to_arr()); client_s_sk_and_nonce.extend_from_slice(&envelope_nonce); let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce); let client_registration_finish_result = client_registration_start_result .state .finish( &mut finish_registration_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(id_u.to_vec(), id_s.to_vec()), ) .unwrap(); let registration_upload_bytes = client_registration_finish_result .message .serialize() .to_vec(); let password_file = server_registration_start_result .state .finish(client_registration_finish_result.message) .unwrap(); let password_file_bytes = password_file.serialize(); let mut client_login_start: Vec = Vec::new(); client_login_start.extend_from_slice(&blinding_factor_bytes); client_login_start.extend_from_slice(&client_e_kp.private().to_arr()); client_login_start.extend_from_slice(&client_nonce); let mut client_login_start_rng = CycleRng::new(client_login_start); let client_login_start_result = ClientLogin::::start( &mut client_login_start_rng, password, ClientLoginStartParameters::WithInfo(info1.to_vec()), ) .unwrap(); let credential_request_bytes = client_login_start_result .message .serialize() .unwrap() .to_vec(); let client_login_state = client_login_start_result .state .serialize() .unwrap() .to_vec(); let mut server_e_sk_and_nonce_rng = CycleRng::new( [ server_e_kp.private().to_arr().to_vec(), server_nonce.to_vec(), ] .concat(), ); let server_login_start_result = ServerLogin::::start( &mut server_e_sk_and_nonce_rng, password_file, server_s_kp.private(), client_login_start_result.message, ServerLoginStartParameters::WithInfoAndIdentifiers( einfo2.to_vec(), id_u.to_vec(), id_s.to_vec(), ), ) .unwrap(); let credential_response_bytes = server_login_start_result .message .serialize() .unwrap() .to_vec(); let server_login_state = server_login_start_result .state .serialize() .unwrap() .to_vec(); let client_login_finish_result = client_login_start_result .state .finish( server_login_start_result.message, ClientLoginFinishParameters::WithIdentifiers(id_u.to_vec(), id_s.to_vec()), ) .unwrap(); let credential_finalization_bytes = client_login_finish_result.message.serialize().unwrap(); TestVectorParameters { client_s_pk: client_s_kp.public().to_arr().to_vec(), client_s_sk: client_s_kp.private().to_arr().to_vec(), client_e_pk: client_e_kp.public().to_arr().to_vec(), client_e_sk: client_e_kp.private().to_arr().to_vec(), server_s_pk: server_s_kp.public().to_arr().to_vec(), server_s_sk: server_s_kp.private().to_arr().to_vec(), server_e_pk: server_e_kp.public().to_arr().to_vec(), server_e_sk: server_e_kp.private().to_arr().to_vec(), id_u: id_u.to_vec(), id_s: id_s.to_vec(), password: password.to_vec(), blinding_factor: blinding_factor_bytes.to_vec(), oprf_key: oprf_key_bytes.to_vec(), envelope_nonce: envelope_nonce.to_vec(), client_nonce: client_nonce.to_vec(), server_nonce: server_nonce.to_vec(), info1: info1.to_vec(), einfo2: einfo2.to_vec(), registration_request: registration_request_bytes, registration_response: registration_response_bytes, registration_upload: registration_upload_bytes, credential_request: credential_request_bytes, credential_response: credential_response_bytes, credential_finalization: credential_finalization_bytes, password_file: password_file_bytes, client_registration_state, server_registration_state, client_login_state, server_login_state, session_key: client_login_finish_result.session_key.clone(), export_key: client_registration_finish_result.export_key.to_vec(), } } #[test] fn generate_test_vectors() { let parameters = generate_parameters::(); println!("{}", stringify_test_vectors(¶meters)); } #[test] fn test_registration_request() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let mut rng = CycleRng::new(parameters.blinding_factor.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()) ); assert_eq!( hex::encode(¶meters.client_registration_state), hex::encode(client_registration_start_result.state.serialize()) ); Ok(()) } #[test] fn test_registration_response() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let mut oprf_key_rng = CycleRng::new(parameters.oprf_key); let server_registration_start_result = ServerRegistration::::start( &mut oprf_key_rng, RegistrationRequest::deserialize(¶meters.registration_request[..])?, &Key::from_bytes(¶meters.server_s_pk[..])?, )?; assert_eq!( hex::encode(parameters.registration_response), hex::encode(server_registration_start_result.message.serialize()) ); assert_eq!( hex::encode(¶meters.server_registration_state), hex::encode(server_registration_start_result.state.serialize()) ); Ok(()) } #[test] fn test_registration_upload() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); 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 result = ClientRegistration::::deserialize( ¶meters.client_registration_state[..], )? .finish( &mut finish_registration_rng, RegistrationResponse::deserialize(¶meters.registration_response[..])?, ClientRegistrationFinishParameters::WithIdentifiers(parameters.id_u, parameters.id_s), )?; assert_eq!( hex::encode(parameters.registration_upload), hex::encode(result.message.serialize()) ); assert_eq!( hex::encode(parameters.export_key), hex::encode(result.export_key.to_vec()) ); Ok(()) } #[test] fn test_password_file() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let server_registration = ServerRegistration::::deserialize( ¶meters.server_registration_state[..], )?; let password_file = server_registration.finish(RegistrationUpload::deserialize( ¶meters.registration_upload[..], )?)?; assert_eq!( hex::encode(parameters.password_file), hex::encode(password_file.serialize()) ); Ok(()) } #[test] fn test_credential_request() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let client_login_start_rng = [ parameters.blinding_factor, parameters.client_e_sk, parameters.client_nonce, ] .concat(); let mut client_login_start_rng = CycleRng::new(client_login_start_rng); let client_login_start_result = ClientLogin::::start( &mut client_login_start_rng, ¶meters.password, ClientLoginStartParameters::WithInfo(parameters.info1), )?; assert_eq!( hex::encode(¶meters.credential_request), hex::encode(client_login_start_result.message.serialize().unwrap()) ); assert_eq!( hex::encode(¶meters.client_login_state), hex::encode(client_login_start_result.state.serialize()?) ); Ok(()) } #[test] fn test_credential_response() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let mut server_e_sk_and_nonce_rng = CycleRng::new([parameters.server_e_sk, parameters.server_nonce].concat()); let server_login_start_result = ServerLogin::::start( &mut server_e_sk_and_nonce_rng, ServerRegistration::deserialize(¶meters.password_file[..])?, &Key::from_bytes(¶meters.server_s_sk[..])?, CredentialRequest::::deserialize( ¶meters.credential_request[..], )?, ServerLoginStartParameters::WithInfoAndIdentifiers( parameters.einfo2.to_vec(), parameters.id_u, parameters.id_s, ), )?; assert_eq!( hex::encode(¶meters.info1), hex::encode(server_login_start_result.plain_info), ); assert_eq!( hex::encode(¶meters.credential_response), hex::encode(server_login_start_result.message.serialize().unwrap()) ); assert_eq!( hex::encode(¶meters.server_login_state), hex::encode(server_login_start_result.state.serialize().unwrap()) ); Ok(()) } #[test] fn test_credential_finalization() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let client_login_finish_result = ClientLogin::::deserialize( ¶meters.client_login_state[..], )? .finish( CredentialResponse::::deserialize( ¶meters.credential_response[..], )?, ClientLoginFinishParameters::WithIdentifiers(parameters.id_u, parameters.id_s), )?; assert_eq!( hex::encode(¶meters.einfo2), hex::encode(&client_login_finish_result.confidential_info) ); assert_eq!( hex::encode(¶meters.server_s_pk), hex::encode(&client_login_finish_result.server_s_pk.to_arr().to_vec()) ); assert_eq!( hex::encode(¶meters.session_key), hex::encode(&client_login_finish_result.session_key) ); assert_eq!( hex::encode(¶meters.credential_finalization), hex::encode(client_login_finish_result.message.serialize().unwrap()) ); assert_eq!( hex::encode(¶meters.export_key), hex::encode(client_login_finish_result.export_key) ); Ok(()) } #[test] fn test_server_login_finish() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let server_login_result = ServerLogin::::deserialize( ¶meters.server_login_state[..], )? .finish(CredentialFinalization::deserialize( ¶meters.credential_finalization[..], )?)?; assert_eq!( hex::encode(parameters.session_key), hex::encode(&server_login_result.session_key) ); Ok(()) } fn test_complete_flow( registration_password: &[u8], login_password: &[u8], ) -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, registration_password, )?; let server_registration_start_result = ServerRegistration::::start( &mut server_rng, client_registration_start_result.message, server_kp.public(), )?; let client_registration_finish_result = client_registration_start_result.state.finish( &mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default(), )?; let p_file = server_registration_start_result .state .finish(client_registration_finish_result.message)?; let client_login_start_result = ClientLogin::::start( &mut client_rng, login_password, ClientLoginStartParameters::default(), )?; let server_login_start_result = ServerLogin::::start( &mut server_rng, p_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::default(), )?; let client_login_result = client_login_start_result.state.finish( server_login_start_result.message, ClientLoginFinishParameters::default(), ); if hex::encode(registration_password) == hex::encode(login_password) { let client_login_finish_result = client_login_result?; let server_login_finish_result = server_login_start_result .state .finish(client_login_finish_result.message)?; assert_eq!( hex::encode(&server_login_finish_result.session_key), hex::encode(&client_login_finish_result.session_key) ); assert_eq!( hex::encode(client_registration_finish_result.export_key), hex::encode(client_login_finish_result.export_key) ); } else { assert!(match client_login_result { Err(ProtocolError::VerificationError(PakeError::InvalidLoginError)) => true, _ => false, }); } Ok(()) } #[test] fn test_complete_flow_success() -> Result<(), ProtocolError> { test_complete_flow(b"good password", b"good password") } #[test] fn test_complete_flow_fail() -> Result<(), ProtocolError> { test_complete_flow(b"good password", b"bad password") } // Zeroize tests #[test] fn test_zeroize_client_registration_start() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let client_registration_start_result = ClientRegistration::::start( &mut client_rng, STR_PASSWORD.as_bytes(), )?; let mut state = client_registration_start_result.state; let ptrs = state.as_byte_ptrs(); state.zeroize(); for (ptr, len) in ptrs { let bytes = unsafe { from_raw_parts(ptr, len) }; assert!(bytes.iter().all(|&x| x == 0)); } Ok(()) } #[test] fn test_zeroize_server_registration_start() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, STR_PASSWORD.as_bytes(), )?; let server_registration_start_result = ServerRegistration::::start( &mut server_rng, client_registration_start_result.message, server_kp.public(), )?; let mut state = server_registration_start_result.state; let ptrs = state.as_byte_ptrs(); state.zeroize(); for (ptr, len) in ptrs { let bytes = unsafe { from_raw_parts(ptr, len) }; assert!(bytes.iter().all(|&x| x == 0)); } Ok(()) } #[test] fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, STR_PASSWORD.as_bytes(), )?; let server_registration_start_result = ServerRegistration::::start( &mut server_rng, client_registration_start_result.message, server_kp.public(), )?; let client_registration_finish_result = client_registration_start_result.state.finish( &mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default(), )?; let mut state = client_registration_finish_result.state; let ptrs = state.as_byte_ptrs(); state.zeroize(); for (ptr, len) in ptrs { let bytes = unsafe { from_raw_parts(ptr, len) }; assert!(bytes.iter().all(|&x| x == 0)); } Ok(()) } #[test] fn test_zeroize_server_registration_finish() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, STR_PASSWORD.as_bytes(), )?; let server_registration_start_result = ServerRegistration::::start( &mut server_rng, client_registration_start_result.message, server_kp.public(), )?; let client_registration_finish_result = client_registration_start_result.state.finish( &mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default(), )?; let p_file = server_registration_start_result .state .finish(client_registration_finish_result.message)?; let mut state = p_file; let ptrs = state.as_byte_ptrs(); state.zeroize(); for (ptr, len) in ptrs { let bytes = unsafe { from_raw_parts(ptr, len) }; assert!(bytes.iter().all(|&x| x == 0)); } Ok(()) } #[test] fn test_zeroize_client_login_start() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let client_login_start_result = ClientLogin::::start( &mut client_rng, STR_PASSWORD.as_bytes(), ClientLoginStartParameters::default(), )?; let mut state = client_login_start_result.state; let ptrs = state.as_byte_ptrs(); state.zeroize(); for (ptr, len) in ptrs { let bytes = unsafe { from_raw_parts(ptr, len) }; assert!(bytes.iter().all(|&x| x == 0)); } Ok(()) } #[test] fn test_zeroize_server_login_start() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, STR_PASSWORD.as_bytes(), )?; let server_registration_start_result = ServerRegistration::::start( &mut server_rng, client_registration_start_result.message, server_kp.public(), )?; let client_registration_finish_result = client_registration_start_result.state.finish( &mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default(), )?; let p_file = server_registration_start_result .state .finish(client_registration_finish_result.message)?; let client_login_start_result = ClientLogin::::start( &mut client_rng, STR_PASSWORD.as_bytes(), ClientLoginStartParameters::default(), )?; let server_login_start_result = ServerLogin::::start( &mut server_rng, p_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::default(), )?; let mut state = server_login_start_result.state; let ptrs = state.as_byte_ptrs(); state.zeroize(); for (ptr, len) in ptrs { let bytes = unsafe { from_raw_parts(ptr, len) }; assert!(bytes.iter().all(|&x| x == 0)); } Ok(()) } #[test] fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, STR_PASSWORD.as_bytes(), )?; let server_registration_start_result = ServerRegistration::::start( &mut server_rng, client_registration_start_result.message, server_kp.public(), )?; let client_registration_finish_result = client_registration_start_result.state.finish( &mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default(), )?; let p_file = server_registration_start_result .state .finish(client_registration_finish_result.message)?; let client_login_start_result = ClientLogin::::start( &mut client_rng, STR_PASSWORD.as_bytes(), ClientLoginStartParameters::default(), )?; let server_login_start_result = ServerLogin::::start( &mut server_rng, p_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::default(), )?; let client_login_finish_result = client_login_start_result.state.finish( server_login_start_result.message, ClientLoginFinishParameters::default(), )?; let mut state = client_login_finish_result.state; let ptrs = state.as_byte_ptrs(); state.zeroize(); for (ptr, len) in ptrs { let bytes = unsafe { from_raw_parts(ptr, len) }; assert!(bytes.iter().all(|&x| x == 0)); } Ok(()) } #[test] fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, STR_PASSWORD.as_bytes(), )?; let server_registration_start_result = ServerRegistration::::start( &mut server_rng, client_registration_start_result.message, server_kp.public(), )?; let client_registration_finish_result = client_registration_start_result.state.finish( &mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default(), )?; let p_file = server_registration_start_result .state .finish(client_registration_finish_result.message)?; let client_login_start_result = ClientLogin::::start( &mut client_rng, STR_PASSWORD.as_bytes(), ClientLoginStartParameters::default(), )?; let server_login_start_result = ServerLogin::::start( &mut server_rng, p_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::default(), )?; let client_login_finish_result = client_login_start_result.state.finish( server_login_start_result.message, ClientLoginFinishParameters::default(), )?; let server_login_finish_result = server_login_start_result .state .finish(client_login_finish_result.message)?; let mut state = server_login_finish_result.state; let ptrs = state.as_byte_ptrs(); state.zeroize(); for (ptr, len) in ptrs { let bytes = unsafe { from_raw_parts(ptr, len) }; assert!(bytes.iter().all(|&x| x == 0)); } Ok(()) } #[test] fn test_scalar_always_nonzero() -> Result<(), ProtocolError> { // Start out with a bunch of zeros to force resampling of scalar let mut client_registration_rng = CycleRng::new([vec![0u8; 128], vec![1u8; 128]].concat()); let client_registration_start_result = ClientRegistration::::start( &mut client_registration_rng, STR_PASSWORD.as_bytes(), )?; assert_ne!( RistrettoPoint::identity(), client_registration_start_result .message .get_alpha_for_testing() ); Ok(()) }