Merge branch 'develop' into doc
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -18,8 +18,8 @@ use super::data_formats::{
|
||||
extract_unsigned, ok_or_missing, ClientPinSubCommand, ConfigSubCommand, ConfigSubCommandParams,
|
||||
CoseKey, CredentialManagementSubCommand, CredentialManagementSubCommandParameters,
|
||||
GetAssertionExtensions, GetAssertionOptions, MakeCredentialExtensions, MakeCredentialOptions,
|
||||
PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialRpEntity,
|
||||
PublicKeyCredentialUserEntity, SetMinPinLengthParams,
|
||||
PinUvAuthProtocol, PublicKeyCredentialDescriptor, PublicKeyCredentialParameter,
|
||||
PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, SetMinPinLengthParams,
|
||||
};
|
||||
use super::key_material;
|
||||
use super::status_code::Ctap2StatusCode;
|
||||
@@ -302,12 +302,12 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AuthenticatorClientPinParameters {
|
||||
pub pin_uv_auth_protocol: u64,
|
||||
pub pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
pub sub_command: ClientPinSubCommand,
|
||||
pub key_agreement: Option<CoseKey>,
|
||||
pub pin_auth: Option<Vec<u8>>,
|
||||
pub pin_uv_auth_param: Option<Vec<u8>>,
|
||||
pub new_pin_enc: Option<Vec<u8>>,
|
||||
pub pin_hash_enc: Option<Vec<u8>>,
|
||||
pub permissions: Option<u8>,
|
||||
@@ -323,7 +323,7 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
|
||||
0x01 => pin_uv_auth_protocol,
|
||||
0x02 => sub_command,
|
||||
0x03 => key_agreement,
|
||||
0x04 => pin_auth,
|
||||
0x04 => pin_uv_auth_param,
|
||||
0x05 => new_pin_enc,
|
||||
0x06 => pin_hash_enc,
|
||||
0x09 => permissions,
|
||||
@@ -331,10 +331,11 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
|
||||
} = extract_map(cbor_value)?;
|
||||
}
|
||||
|
||||
let pin_uv_auth_protocol = extract_unsigned(ok_or_missing(pin_uv_auth_protocol)?)?;
|
||||
let pin_uv_auth_protocol =
|
||||
PinUvAuthProtocol::try_from(ok_or_missing(pin_uv_auth_protocol)?)?;
|
||||
let sub_command = ClientPinSubCommand::try_from(ok_or_missing(sub_command)?)?;
|
||||
let key_agreement = key_agreement.map(CoseKey::try_from).transpose()?;
|
||||
let pin_auth = pin_auth.map(extract_byte_string).transpose()?;
|
||||
let pin_uv_auth_param = pin_uv_auth_param.map(extract_byte_string).transpose()?;
|
||||
let new_pin_enc = new_pin_enc.map(extract_byte_string).transpose()?;
|
||||
let pin_hash_enc = pin_hash_enc.map(extract_byte_string).transpose()?;
|
||||
// We expect a bit field of 8 bits, and drop everything else.
|
||||
@@ -349,7 +350,7 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
|
||||
pin_uv_auth_protocol,
|
||||
sub_command,
|
||||
key_agreement,
|
||||
pin_auth,
|
||||
pin_uv_auth_param,
|
||||
new_pin_enc,
|
||||
pin_hash_enc,
|
||||
permissions,
|
||||
@@ -706,10 +707,10 @@ mod test {
|
||||
AuthenticatorClientPinParameters::try_from(cbor_value).unwrap();
|
||||
|
||||
let expected_client_pin_parameters = AuthenticatorClientPinParameters {
|
||||
pin_uv_auth_protocol: 1,
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol::V1,
|
||||
sub_command: ClientPinSubCommand::GetPinRetries,
|
||||
key_agreement: Some(cose_key),
|
||||
pin_auth: Some(vec![0xBB]),
|
||||
pin_uv_auth_param: Some(vec![0xBB]),
|
||||
new_pin_enc: Some(vec![0xCC]),
|
||||
pin_hash_enc: Some(vec![0xDD]),
|
||||
permissions: Some(0x03),
|
||||
|
||||
@@ -126,6 +126,7 @@ pub fn process_config(
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::ctap::customization::ENFORCE_ALWAYS_UV;
|
||||
use crate::ctap::data_formats::PinUvAuthProtocol;
|
||||
use crypto::rng256::ThreadRng256;
|
||||
|
||||
#[test]
|
||||
@@ -134,7 +135,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
|
||||
let config_params = AuthenticatorConfigParameters {
|
||||
sub_command: ConfigSubCommand::EnableEnterpriseAttestation,
|
||||
@@ -161,7 +163,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
|
||||
let config_params = AuthenticatorConfigParameters {
|
||||
sub_command: ConfigSubCommand::ToggleAlwaysUv,
|
||||
@@ -197,7 +200,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
persistent_store.set_pin(&[0x88; 16], 4).unwrap();
|
||||
|
||||
let pin_uv_auth_param = Some(vec![
|
||||
@@ -257,7 +261,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
|
||||
// First, increase minimum PIN length from 4 to 6 without PIN auth.
|
||||
let min_pin_length = 6;
|
||||
@@ -301,7 +306,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
|
||||
// First, set RP IDs without PIN auth.
|
||||
let min_pin_length = 6;
|
||||
@@ -377,7 +383,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
|
||||
persistent_store.set_pin(&[0x88; 16], 4).unwrap();
|
||||
// Increase min PIN, force PIN change.
|
||||
@@ -400,7 +407,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
|
||||
persistent_store.set_pin(&[0x88; 16], 4).unwrap();
|
||||
let pin_uv_auth_param = Some(vec![
|
||||
@@ -431,7 +439,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
|
||||
let config_params = AuthenticatorConfigParameters {
|
||||
sub_command: ConfigSubCommand::VendorPrototype,
|
||||
|
||||
@@ -351,7 +351,7 @@ pub fn process_credential_management(
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::super::data_formats::PublicKeyCredentialType;
|
||||
use super::super::data_formats::{PinUvAuthProtocol, PublicKeyCredentialType};
|
||||
use super::super::CtapState;
|
||||
use super::*;
|
||||
use crypto::rng256::{Rng256, ThreadRng256};
|
||||
@@ -382,7 +382,8 @@ mod test {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let credential_source = create_credential_source(&mut rng);
|
||||
|
||||
let user_immediately_present = |_| Ok(());
|
||||
@@ -453,7 +454,8 @@ mod test {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let credential_source1 = create_credential_source(&mut rng);
|
||||
let mut credential_source2 = create_credential_source(&mut rng);
|
||||
credential_source2.rp_id = "another.example.com".to_string();
|
||||
@@ -550,7 +552,8 @@ mod test {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let credential_source = create_credential_source(&mut rng);
|
||||
|
||||
let user_immediately_present = |_| Ok(());
|
||||
@@ -632,7 +635,8 @@ mod test {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let credential_source1 = create_credential_source(&mut rng);
|
||||
let mut credential_source2 = create_credential_source(&mut rng);
|
||||
credential_source2.user_handle = vec![0x02];
|
||||
@@ -737,7 +741,8 @@ mod test {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut credential_source = create_credential_source(&mut rng);
|
||||
credential_source.credential_id = vec![0x1D; 32];
|
||||
|
||||
@@ -808,7 +813,8 @@ mod test {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut credential_source = create_credential_source(&mut rng);
|
||||
credential_source.credential_id = vec![0x1D; 32];
|
||||
|
||||
@@ -880,7 +886,8 @@ mod test {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
|
||||
let user_immediately_present = |_| Ok(());
|
||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||
|
||||
@@ -356,6 +356,7 @@ pub struct GetAssertionHmacSecretInput {
|
||||
pub key_agreement: CoseKey,
|
||||
pub salt_enc: Vec<u8>,
|
||||
pub salt_auth: Vec<u8>,
|
||||
pub pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
}
|
||||
|
||||
impl TryFrom<cbor::Value> for GetAssertionHmacSecretInput {
|
||||
@@ -367,16 +368,20 @@ impl TryFrom<cbor::Value> for GetAssertionHmacSecretInput {
|
||||
1 => key_agreement,
|
||||
2 => salt_enc,
|
||||
3 => salt_auth,
|
||||
4 => pin_uv_auth_protocol,
|
||||
} = extract_map(cbor_value)?;
|
||||
}
|
||||
|
||||
let key_agreement = CoseKey::try_from(ok_or_missing(key_agreement)?)?;
|
||||
let salt_enc = extract_byte_string(ok_or_missing(salt_enc)?)?;
|
||||
let salt_auth = extract_byte_string(ok_or_missing(salt_auth)?)?;
|
||||
let pin_uv_auth_protocol =
|
||||
pin_uv_auth_protocol.map_or(Ok(PinUvAuthProtocol::V1), PinUvAuthProtocol::try_from)?;
|
||||
Ok(Self {
|
||||
key_agreement,
|
||||
salt_enc,
|
||||
salt_auth,
|
||||
pin_uv_auth_protocol,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -638,7 +643,7 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialSource {
|
||||
let cred_protect_policy = cred_protect_policy
|
||||
.map(CredentialProtectionPolicy::try_from)
|
||||
.transpose()?;
|
||||
let creation_order = creation_order.map(extract_unsigned).unwrap_or(Ok(0))?;
|
||||
let creation_order = creation_order.map_or(Ok(0), extract_unsigned)?;
|
||||
let user_name = user_name.map(extract_text_string).transpose()?;
|
||||
let user_icon = user_icon.map(extract_text_string).transpose()?;
|
||||
let cred_blob = cred_blob.map(extract_byte_string).transpose()?;
|
||||
@@ -809,6 +814,24 @@ impl TryFrom<CoseKey> for ecdh::PubKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum PinUvAuthProtocol {
|
||||
V1,
|
||||
V2,
|
||||
}
|
||||
|
||||
impl TryFrom<cbor::Value> for PinUvAuthProtocol {
|
||||
type Error = Ctap2StatusCode;
|
||||
|
||||
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
|
||||
match extract_unsigned(cbor_value)? {
|
||||
1 => Ok(PinUvAuthProtocol::V1),
|
||||
2 => Ok(PinUvAuthProtocol::V2),
|
||||
_ => Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(test, derive(IntoEnumIterator))]
|
||||
pub enum ClientPinSubCommand {
|
||||
@@ -1569,7 +1592,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_get_assertion_extensions() {
|
||||
fn test_from_get_assertion_extensions_default_protocol() {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pk = sk.genpk();
|
||||
@@ -1588,6 +1611,7 @@ mod test {
|
||||
key_agreement: cose_key,
|
||||
salt_enc: vec![0x02; 32],
|
||||
salt_auth: vec![0x03; 16],
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol::V1,
|
||||
};
|
||||
let expected_extensions = GetAssertionExtensions {
|
||||
hmac_secret: Some(expected_input),
|
||||
@@ -1597,6 +1621,38 @@ mod test {
|
||||
assert_eq!(extensions, Ok(expected_extensions));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_get_assertion_extensions_with_protocol() {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pk = sk.genpk();
|
||||
let cose_key = CoseKey::from(pk);
|
||||
let cbor_extensions = cbor_map! {
|
||||
"hmac-secret" => cbor_map! {
|
||||
1 => cbor::Value::from(cose_key.clone()),
|
||||
2 => vec![0x02; 32],
|
||||
3 => vec![0x03; 16],
|
||||
4 => 2,
|
||||
},
|
||||
"credBlob" => true,
|
||||
"largeBlobKey" => true,
|
||||
};
|
||||
let extensions = GetAssertionExtensions::try_from(cbor_extensions);
|
||||
let expected_input = GetAssertionHmacSecretInput {
|
||||
key_agreement: cose_key,
|
||||
salt_enc: vec![0x02; 32],
|
||||
salt_auth: vec![0x03; 16],
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol::V2,
|
||||
};
|
||||
let expected_extensions = GetAssertionExtensions {
|
||||
hmac_secret: Some(expected_input),
|
||||
cred_blob: true,
|
||||
large_blob_key: Some(true),
|
||||
};
|
||||
assert_eq!(extensions, Ok(expected_extensions));
|
||||
// TODO more tests, check default
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_make_credential_options() {
|
||||
let cbor_make_options = cbor_map! {
|
||||
@@ -1759,6 +1815,25 @@ mod test {
|
||||
assert_eq!(cose_key.algorithm, ES256_ALGORITHM);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_pin_uv_auth_protocol() {
|
||||
let cbor_protocol: cbor::Value = cbor_int!(0x01);
|
||||
assert_eq!(
|
||||
PinUvAuthProtocol::try_from(cbor_protocol),
|
||||
Ok(PinUvAuthProtocol::V1)
|
||||
);
|
||||
let cbor_protocol: cbor::Value = cbor_int!(0x02);
|
||||
assert_eq!(
|
||||
PinUvAuthProtocol::try_from(cbor_protocol),
|
||||
Ok(PinUvAuthProtocol::V2)
|
||||
);
|
||||
let cbor_protocol: cbor::Value = cbor_int!(0x03);
|
||||
assert_eq!(
|
||||
PinUvAuthProtocol::try_from(cbor_protocol),
|
||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_into_client_pin_sub_command() {
|
||||
let cbor_sub_command: cbor::Value = cbor_int!(0x01);
|
||||
|
||||
@@ -135,6 +135,7 @@ impl LargeBlobs {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::super::data_formats::PinUvAuthProtocol;
|
||||
use super::*;
|
||||
use crypto::rng256::ThreadRng256;
|
||||
|
||||
@@ -144,7 +145,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
let large_blob = vec![
|
||||
@@ -175,7 +177,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
const BLOB_LEN: usize = 200;
|
||||
@@ -237,7 +240,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
const BLOB_LEN: usize = 200;
|
||||
@@ -283,7 +287,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
const BLOB_LEN: usize = 200;
|
||||
@@ -329,7 +334,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
const BLOB_LEN: usize = 20;
|
||||
@@ -358,7 +364,8 @@ mod test {
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
const BLOB_LEN: usize = 20;
|
||||
|
||||
@@ -1223,7 +1223,8 @@ mod test {
|
||||
use super::command::AuthenticatorAttestationMaterial;
|
||||
use super::data_formats::{
|
||||
CoseKey, GetAssertionHmacSecretInput, GetAssertionOptions, MakeCredentialExtensions,
|
||||
MakeCredentialOptions, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity,
|
||||
MakeCredentialOptions, PinUvAuthProtocol, PublicKeyCredentialRpEntity,
|
||||
PublicKeyCredentialUserEntity,
|
||||
};
|
||||
use super::*;
|
||||
use cbor::{cbor_array, cbor_array_vec, cbor_map};
|
||||
@@ -1983,6 +1984,7 @@ mod test {
|
||||
key_agreement: CoseKey::from(pk),
|
||||
salt_enc: vec![0x02; 32],
|
||||
salt_auth: vec![0x03; 16],
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol::V1,
|
||||
};
|
||||
let get_extensions = GetAssertionExtensions {
|
||||
hmac_secret: Some(hmac_secret_input),
|
||||
@@ -2040,6 +2042,7 @@ mod test {
|
||||
key_agreement: CoseKey::from(pk),
|
||||
salt_enc: vec![0x02; 32],
|
||||
salt_auth: vec![0x03; 16],
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol::V1,
|
||||
};
|
||||
let get_extensions = GetAssertionExtensions {
|
||||
hmac_secret: Some(hmac_secret_input),
|
||||
@@ -2317,7 +2320,8 @@ mod test {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||
let pin_uv_auth_token = [0x88; 32];
|
||||
let client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token);
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
|
||||
let user_immediately_present = |_| Ok(());
|
||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::ctap::client_pin::PIN_TOKEN_LENGTH;
|
||||
use crate::ctap::data_formats::CoseKey;
|
||||
use crate::ctap::data_formats::{CoseKey, PinUvAuthProtocol};
|
||||
use crate::ctap::status_code::Ctap2StatusCode;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec;
|
||||
@@ -21,6 +21,8 @@ use alloc::vec::Vec;
|
||||
use core::convert::TryInto;
|
||||
use crypto::cbc::{cbc_decrypt, cbc_encrypt};
|
||||
use crypto::hkdf::hkdf_empty_salt_256;
|
||||
#[cfg(test)]
|
||||
use crypto::hmac::hmac_256;
|
||||
use crypto::hmac::{verify_hmac_256, verify_hmac_256_first_128bits};
|
||||
use crypto::rng256::Rng256;
|
||||
use crypto::sha256::Sha256;
|
||||
@@ -64,14 +66,13 @@ impl PinProtocol {
|
||||
pub fn decapsulate(
|
||||
&self,
|
||||
peer_cose_key: CoseKey,
|
||||
pin_uv_auth_protocol: u64,
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
) -> Result<Box<dyn SharedSecret>, Ctap2StatusCode> {
|
||||
let pk: crypto::ecdh::PubKey = CoseKey::try_into(peer_cose_key)?;
|
||||
let handshake = self.key_agreement_key.exchange_x(&pk);
|
||||
match pin_uv_auth_protocol {
|
||||
1 => Ok(Box::new(SharedSecretV1::new(handshake))),
|
||||
2 => Ok(Box::new(SharedSecretV2::new(handshake))),
|
||||
_ => Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),
|
||||
PinUvAuthProtocol::V1 => Ok(Box::new(SharedSecretV1::new(handshake))),
|
||||
PinUvAuthProtocol::V2 => Ok(Box::new(SharedSecretV2::new(handshake))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,12 +99,11 @@ pub fn verify_pin_uv_auth_token(
|
||||
token: &[u8; PIN_TOKEN_LENGTH],
|
||||
message: &[u8],
|
||||
signature: &[u8],
|
||||
pin_uv_auth_protocol: u64,
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
match pin_uv_auth_protocol {
|
||||
1 => verify_v1(token, message, signature),
|
||||
2 => verify_v2(token, message, signature),
|
||||
_ => Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),
|
||||
PinUvAuthProtocol::V1 => verify_v1(token, message, signature),
|
||||
PinUvAuthProtocol::V2 => verify_v2(token, message, signature),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +116,10 @@ pub trait SharedSecret {
|
||||
|
||||
/// Verifies that the signature is a valid MAC for the given message.
|
||||
fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Ctap2StatusCode>;
|
||||
|
||||
/// Creates a signature that matches verify.
|
||||
#[cfg(test)]
|
||||
fn authenticate(&self, message: &[u8]) -> Vec<u8>;
|
||||
}
|
||||
|
||||
fn aes256_cbc_encrypt(
|
||||
@@ -210,16 +214,6 @@ impl SharedSecretV1 {
|
||||
aes_enc_key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new shared secret for testing.
|
||||
#[cfg(test)]
|
||||
pub fn new_test(hash: [u8; 32]) -> SharedSecretV1 {
|
||||
let aes_enc_key = crypto::aes256::EncryptionKey::new(&hash);
|
||||
SharedSecretV1 {
|
||||
common_secret: hash,
|
||||
aes_enc_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedSecret for SharedSecretV1 {
|
||||
@@ -234,6 +228,11 @@ impl SharedSecret for SharedSecretV1 {
|
||||
fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Ctap2StatusCode> {
|
||||
verify_v1(&self.common_secret, message, signature)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn authenticate(&self, message: &[u8]) -> Vec<u8> {
|
||||
hmac_256::<Sha256>(&self.common_secret, message)[..16].to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SharedSecretV2 {
|
||||
@@ -264,6 +263,11 @@ impl SharedSecret for SharedSecretV2 {
|
||||
fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Ctap2StatusCode> {
|
||||
verify_v2(&self.hmac_key, message, signature)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn authenticate(&self, message: &[u8]) -> Vec<u8> {
|
||||
hmac_256::<Sha256>(&self.hmac_key, message).to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -300,6 +304,14 @@ mod test {
|
||||
assert_eq!(shared_secret.decrypt(&ciphertext), Ok(plaintext));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_secret_v1_authenticate_verify() {
|
||||
let shared_secret = SharedSecretV1::new([0x55; 32]);
|
||||
let message = [0xAA; 32];
|
||||
let signature = shared_secret.authenticate(&message);
|
||||
assert_eq!(shared_secret.verify(&message, &signature), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_secret_v1_verify() {
|
||||
let shared_secret = SharedSecretV1::new([0x55; 32]);
|
||||
@@ -328,6 +340,14 @@ mod test {
|
||||
assert_eq!(shared_secret.decrypt(&ciphertext), Ok(plaintext));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_secret_v2_authenticate_verify() {
|
||||
let shared_secret = SharedSecretV2::new([0x55; 32]);
|
||||
let message = [0xAA; 32];
|
||||
let signature = shared_secret.authenticate(&message);
|
||||
assert_eq!(shared_secret.verify(&message, &signature), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_secret_v2_verify() {
|
||||
let shared_secret = SharedSecretV2::new([0x55; 32]);
|
||||
@@ -348,23 +368,12 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decapsulate_invalid() {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let pin_protocol = PinProtocol::new(&mut rng);
|
||||
let shared_secret = pin_protocol.decapsulate(pin_protocol.get_public_key(), 3);
|
||||
assert_eq!(
|
||||
shared_secret.err(),
|
||||
Some(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decapsulate_symmetric() {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let pin_protocol1 = PinProtocol::new(&mut rng);
|
||||
let pin_protocol2 = PinProtocol::new(&mut rng);
|
||||
for protocol in 1..=2 {
|
||||
for &protocol in &[PinUvAuthProtocol::V1, PinUvAuthProtocol::V2] {
|
||||
let shared_secret1 = pin_protocol1
|
||||
.decapsulate(pin_protocol2.get_public_key(), protocol)
|
||||
.unwrap();
|
||||
@@ -386,19 +395,24 @@ mod test {
|
||||
0x49, 0x68,
|
||||
];
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &message, &signature, 1),
|
||||
verify_pin_uv_auth_token(&token, &message, &signature, PinUvAuthProtocol::V1),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&[0x12; PIN_TOKEN_LENGTH], &message, &signature, 1),
|
||||
verify_pin_uv_auth_token(
|
||||
&[0x12; PIN_TOKEN_LENGTH],
|
||||
&message,
|
||||
&signature,
|
||||
PinUvAuthProtocol::V1
|
||||
),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &[0xBB], &signature, 1),
|
||||
verify_pin_uv_auth_token(&token, &[0xBB], &signature, PinUvAuthProtocol::V1),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &message, &[0x12; 16], 1),
|
||||
verify_pin_uv_auth_token(&token, &message, &[0x12; 16], PinUvAuthProtocol::V1),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
}
|
||||
@@ -413,31 +427,25 @@ mod test {
|
||||
0x36, 0x93, 0xF7, 0x84,
|
||||
];
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &message, &signature, 2),
|
||||
verify_pin_uv_auth_token(&token, &message, &signature, PinUvAuthProtocol::V2),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&[0x12; PIN_TOKEN_LENGTH], &message, &signature, 2),
|
||||
verify_pin_uv_auth_token(
|
||||
&[0x12; PIN_TOKEN_LENGTH],
|
||||
&message,
|
||||
&signature,
|
||||
PinUvAuthProtocol::V2
|
||||
),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &[0xBB], &signature, 2),
|
||||
verify_pin_uv_auth_token(&token, &[0xBB], &signature, PinUvAuthProtocol::V2),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &message, &[0x12; 32], 2),
|
||||
verify_pin_uv_auth_token(&token, &message, &[0x12; 32], PinUvAuthProtocol::V2),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_pin_uv_auth_token_invalid_protocol() {
|
||||
let token = [0x91; PIN_TOKEN_LENGTH];
|
||||
let message = [0xAA];
|
||||
let signature = [];
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &message, &signature, 3),
|
||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user