diff --git a/benches/encls2_client_auth.rs b/benches/encls2_client_auth.rs index 59e7395..fa7dd40 100644 --- a/benches/encls2_client_auth.rs +++ b/benches/encls2_client_auth.rs @@ -5,14 +5,14 @@ use ire::{ dest::DestinationSecretKeys, ls2::{ enc::{ - auth::{ClientInfo, ClientSecretKey, X25519ClientInfo}, + auth::{ClientInfo, ClientSecretKey, PSKClientInfo, X25519ClientInfo}, EncLS2Payload, EncryptedLS2, }, LeaseSet2, }, }, }; -use rand::{thread_rng, RngCore}; +use rand::{thread_rng, Rng, RngCore}; use x25519_dalek::{x25519, X25519_BASEPOINT_BYTES}; fn fake_ls2(created: u32, expires: u16) -> LeaseSet2 { @@ -104,5 +104,88 @@ fn client_x25519(c: &mut Criterion) { ); } -criterion_group!(benches, server_x25519, client_x25519); +fn server_psk(c: &mut Criterion) { + let mut rng = thread_rng(); + let ls2 = fake_ls2(123_456_789, 2345); + + let payload = EncLS2Payload::LS2(ls2); + let credential = b"credential"; + + let blinded_privkey = SigningPrivateKey::new(); + let blinded_pubkey = SigningPublicKey::from_secret(&blinded_privkey).unwrap(); + + c.bench_function_over_inputs( + "EncLS2_Server_PSK", + move |b, &&count| { + let mut client_info = Vec::with_capacity(count); + for _ in 0..count { + let mut psk = [0; 32]; + rng.fill(&mut psk[..]); + client_info.push(PSKClientInfo(psk)); + } + let client_info = ClientInfo::PSK(client_info); + + b.iter(|| { + EncryptedLS2::encrypt_payload( + &payload, + credential, + blinded_pubkey.clone(), + None, + &blinded_privkey, + Some(client_info.clone()), + ) + }) + }, + &[1, 10, 20, 50, 100], + ); +} + +fn client_psk(c: &mut Criterion) { + let mut rng = thread_rng(); + let ls2 = fake_ls2(123_456_789, 2345); + + let payload = EncLS2Payload::LS2(ls2); + let credential = b"credential"; + + let blinded_privkey = SigningPrivateKey::new(); + let blinded_pubkey = SigningPublicKey::from_secret(&blinded_privkey).unwrap(); + + c.bench_function_over_inputs( + "EncLS2_Client_PSK", + move |b, &&count| { + let mut auth_keys = Vec::with_capacity(count); + let mut client_info = Vec::with_capacity(count); + for _ in 0..count { + let mut psk = [0; 32]; + rng.fill(&mut psk[..]); + let mut client_psk = [0; 32]; + client_psk.copy_from_slice(&psk[..]); + auth_keys.push(ClientSecretKey::PSK(client_psk)); + client_info.push(PSKClientInfo(psk)); + } + let client_info = ClientInfo::PSK(client_info); + + let enc_ls2 = EncryptedLS2::encrypt_payload( + &payload, + credential, + blinded_pubkey.clone(), + None, + &blinded_privkey, + Some(client_info.clone()), + ) + .unwrap(); + + b.iter(|| enc_ls2.decrypt(credential, Some(&auth_keys.last().unwrap()))) + }, + &[1, 10, 20, 50, 100], + ); +} + +criterion_group!( + benches, + server_x25519, + client_x25519, + server_psk, + client_psk +); criterion_main!(benches); diff --git a/src/data/ls2/enc/auth.rs b/src/data/ls2/enc/auth.rs index f7ca154..740b706 100644 --- a/src/data/ls2/enc/auth.rs +++ b/src/data/ls2/enc/auth.rs @@ -11,15 +11,20 @@ const X25519_AUTH_INFO: &[u8; 8] = b"ELS2_XCA"; #[derive(Clone)] pub struct X25519ClientInfo(pub [u8; 32]); +#[derive(Clone)] +pub struct PSKClientInfo(pub [u8; 32]); + /// Client information #[derive(Clone)] pub enum ClientInfo { X25519(Vec), + PSK(Vec), } // Client's secret authentication key pub enum ClientSecretKey { X25519([u8; 32]), + PSK([u8; 32]), } /// Per-client authentication data. @@ -33,6 +38,7 @@ pub(super) struct ClientAuthData { #[derive(Debug, PartialEq)] pub(super) enum ClientAuthType { X25519([u8; 32], Vec), + PSK([u8; 32], Vec), } impl ClientAuthType { @@ -43,27 +49,23 @@ impl ClientAuthType { ) -> (Vec, Option) { let mut rng = OsRng::new().unwrap(); - match client_info { - Some(ClientInfo::X25519(clients)) => { + macro_rules! base_auth { + ($clients:ident, $gen_auth_seed:expr, $gen_secret_value:expr, $salt:expr, $auth_type:ident) => {{ let mut auth_cookie = vec![]; auth_cookie.resize(AUTH_COOKIE_LEN, 0); rng.fill_bytes(&mut auth_cookie[..]); - let mut esk = [0u8; 32]; - rng.fill_bytes(&mut esk[..]); - let epk = x25519(esk, X25519_BASEPOINT_BYTES); + let auth_seed = $gen_auth_seed; - let auth_data = clients + let auth_data = $clients .into_iter() .map(|client| { - let shared_secret = x25519(esk, client.0); - let mut okm = [0; S_KEY_LEN + S_IV_LEN + AUTH_ID_LEN]; kdf( - &shared_secret, + &$gen_secret_value(auth_seed, client.0), subcredential, created, - &epk, + &$salt(auth_seed), X25519_AUTH_INFO, &mut okm, ); @@ -86,8 +88,37 @@ impl ClientAuthType { }) .collect(); - (auth_cookie, Some(ClientAuthType::X25519(epk, auth_data))) - } + ( + auth_cookie, + Some(ClientAuthType::$auth_type($salt(auth_seed), auth_data)), + ) + }}; + } + + match client_info { + Some(ClientInfo::X25519(clients)) => base_auth!( + clients, + { + let mut esk = [0u8; 32]; + rng.fill_bytes(&mut esk[..]); + let epk = x25519(esk, X25519_BASEPOINT_BYTES); + (esk, epk) + }, + |(esk, _), client_info| x25519(esk, client_info), + |(_, epk)| epk, + X25519 + ), + Some(ClientInfo::PSK(clients)) => base_auth!( + clients, + { + let mut auth_salt = [0; 32]; + rng.fill_bytes(&mut auth_salt[..]); + auth_salt + }, + |_, client_info| client_info, + |auth_salt| auth_salt, + PSK + ), None => (vec![], None), } } @@ -99,16 +130,14 @@ impl ClientAuthType { subcredential: &[u8], created: u32, ) -> Result, Error> { - match (self, key) { - (ClientAuthType::X25519(epk, auth_data), ClientSecretKey::X25519(sk)) => { - let shared_secret = x25519(*sk, *epk); - + macro_rules! base_auth { + ($secret_value:expr, $salt:expr, $auth_data:ident) => {{ let mut okm = [0; S_KEY_LEN + S_IV_LEN + AUTH_ID_LEN]; kdf( - &shared_secret, + $secret_value, subcredential, created, - &epk[..], + $salt, X25519_AUTH_INFO, &mut okm, ); @@ -117,7 +146,7 @@ impl ClientAuthType { let client_id = &okm[S_KEY_LEN + S_IV_LEN..S_KEY_LEN + S_IV_LEN + AUTH_ID_LEN]; // Scan the list of clients to find ourselves - match auth_data + match $auth_data .iter() .filter_map(|data| { if data.client_id == client_id { @@ -140,7 +169,18 @@ impl ClientAuthType { Some(auth_cookie) => Ok(auth_cookie), None => Err(Error::NotAuthorised), } + }}; + } + + match (self, key) { + (ClientAuthType::X25519(epk, auth_data), ClientSecretKey::X25519(sk)) => { + let shared_secret = x25519(*sk, *epk); + base_auth!(&shared_secret, &epk[..], auth_data) + } + (ClientAuthType::PSK(auth_salt, auth_data), ClientSecretKey::PSK(sk)) => { + base_auth!(&sk[..], &auth_salt[..], auth_data) } + _ => Err(Error::NotAuthorised), } } } diff --git a/src/data/ls2/enc/frame.rs b/src/data/ls2/enc/frame.rs index 706ac77..a8acd3e 100644 --- a/src/data/ls2/enc/frame.rs +++ b/src/data/ls2/enc/frame.rs @@ -137,6 +137,15 @@ named!( point.copy_from_slice(epk); point }, entries)) + ) | + 1 => do_parse!( + auth_salt: take!(32) >> + entries: length_count!(be_u16, client_auth_data) >> + (ClientAuthType::PSK({ + let mut salt = [0; 32]; + salt.copy_from_slice(auth_salt); + salt + }, entries)) ) ) ) @@ -166,6 +175,14 @@ pub(crate) fn gen_enc_ls2_client_auth<'a>( >> gen_many!(&auth_data, gen_client_auth_data) >> gen_slice!(client_auth.inner_ciphertext) ), + Some(ClientAuthType::PSK(auth_salt, auth_data)) => do_gen!( + input, + gen_client_auth_flags(&ClientAuthFlags::with_type(1)) + >> gen_slice!(&auth_salt) + >> gen_be_u16!(auth_data.len()) + >> gen_many!(&auth_data, gen_client_auth_data) + >> gen_slice!(client_auth.inner_ciphertext) + ), } } @@ -317,5 +334,17 @@ mod tests { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04 ][..] ); + + eval!( + EncLS2ClientAuth { + auth_data: Some(ClientAuthType::PSK([0xff; 32], vec![])), + inner_ciphertext: vec![1, 2, 3, 4], + }, + &[ + 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04 + ][..] + ); } }