Merge branch 'develop' into doc

This commit is contained in:
kaczmarczyck
2021-03-15 13:36:41 +01:00
committed by GitHub
8 changed files with 815 additions and 525 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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),

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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)
);
}
}