New metadata format (#539)
* new metadata format is used * Update bootloader/src/main.rs Co-authored-by: ztoked <zhalvorsen@google.com> * splits the metadata signed and unsigned parts evenly * fixes pylint Co-authored-by: ztoked <zhalvorsen@google.com>
This commit is contained in:
@@ -15,11 +15,10 @@
|
||||
use super::data_formats::{
|
||||
extract_array, extract_bool, extract_byte_string, extract_map, extract_text_string,
|
||||
extract_unsigned, ok_or_missing, ClientPinSubCommand, ConfigSubCommand, ConfigSubCommandParams,
|
||||
CoseKey, CoseSignature, CredentialManagementSubCommand,
|
||||
CredentialManagementSubCommandParameters, GetAssertionExtensions, GetAssertionOptions,
|
||||
MakeCredentialExtensions, MakeCredentialOptions, PinUvAuthProtocol,
|
||||
PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialRpEntity,
|
||||
PublicKeyCredentialUserEntity, SetMinPinLengthParams,
|
||||
CoseKey, CredentialManagementSubCommand, CredentialManagementSubCommandParameters,
|
||||
GetAssertionExtensions, GetAssertionOptions, MakeCredentialExtensions, MakeCredentialOptions,
|
||||
PinUvAuthProtocol, PublicKeyCredentialDescriptor, PublicKeyCredentialParameter,
|
||||
PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, SetMinPinLengthParams,
|
||||
};
|
||||
use super::status_code::Ctap2StatusCode;
|
||||
use super::{cbor_read, key_material};
|
||||
@@ -596,7 +595,6 @@ pub struct AuthenticatorVendorUpgradeParameters {
|
||||
pub address: Option<usize>,
|
||||
pub data: Vec<u8>,
|
||||
pub hash: Vec<u8>,
|
||||
pub signature: Option<CoseSignature>,
|
||||
}
|
||||
|
||||
impl TryFrom<cbor::Value> for AuthenticatorVendorUpgradeParameters {
|
||||
@@ -608,7 +606,6 @@ impl TryFrom<cbor::Value> for AuthenticatorVendorUpgradeParameters {
|
||||
0x01 => address,
|
||||
0x02 => data,
|
||||
0x03 => hash,
|
||||
0x04 => signature,
|
||||
} = extract_map(cbor_value)?;
|
||||
}
|
||||
let address = address
|
||||
@@ -617,12 +614,10 @@ impl TryFrom<cbor::Value> for AuthenticatorVendorUpgradeParameters {
|
||||
.map(|u| u as usize);
|
||||
let data = extract_byte_string(ok_or_missing(data)?)?;
|
||||
let hash = extract_byte_string(ok_or_missing(hash)?)?;
|
||||
let signature = signature.map(CoseSignature::try_from).transpose()?;
|
||||
Ok(AuthenticatorVendorUpgradeParameters {
|
||||
address,
|
||||
data,
|
||||
hash,
|
||||
signature,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -631,7 +626,7 @@ impl TryFrom<cbor::Value> for AuthenticatorVendorUpgradeParameters {
|
||||
mod test {
|
||||
use super::super::data_formats::{
|
||||
AuthenticatorTransport, PublicKeyCredentialRpEntity, PublicKeyCredentialType,
|
||||
PublicKeyCredentialUserEntity, SignatureAlgorithm,
|
||||
PublicKeyCredentialUserEntity,
|
||||
};
|
||||
use super::super::ES256_CRED_PARAM;
|
||||
use super::*;
|
||||
@@ -1096,10 +1091,6 @@ mod test {
|
||||
let cbor_value = cbor_map! {
|
||||
0x02 => [0xFF; 0x100],
|
||||
0x03 => [0x44; 32],
|
||||
0x04 => cbor_map! {
|
||||
"alg" => -7,
|
||||
"signature" => [0x55; 64],
|
||||
},
|
||||
};
|
||||
assert_eq!(
|
||||
AuthenticatorVendorUpgradeParameters::try_from(cbor_value),
|
||||
@@ -1107,14 +1098,10 @@ mod test {
|
||||
address: None,
|
||||
data: vec![0xFF; 0x100],
|
||||
hash: vec![0x44; 32],
|
||||
signature: Some(CoseSignature {
|
||||
algorithm: SignatureAlgorithm::Es256,
|
||||
bytes: [0x55; 64],
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
// Valid without signature
|
||||
// Valid with address
|
||||
let cbor_value = cbor_map! {
|
||||
0x01 => 0x1000,
|
||||
0x02 => [0xFF; 0x100],
|
||||
@@ -1126,7 +1113,6 @@ mod test {
|
||||
address: Some(0x1000),
|
||||
data: vec![0xFF; 0x100],
|
||||
hash: vec![0x44; 32],
|
||||
signature: None,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -912,53 +912,6 @@ impl TryFrom<CoseKey> for ecdsa::PubKey {
|
||||
}
|
||||
}
|
||||
|
||||
/// Data structure for receiving a signature.
|
||||
///
|
||||
/// See https://datatracker.ietf.org/doc/html/rfc8152#appendix-C.1.1 for reference.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CoseSignature {
|
||||
pub algorithm: SignatureAlgorithm,
|
||||
pub bytes: [u8; ecdsa::Signature::BYTES_LENGTH],
|
||||
}
|
||||
|
||||
impl TryFrom<cbor::Value> for CoseSignature {
|
||||
type Error = Ctap2StatusCode;
|
||||
|
||||
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
|
||||
destructure_cbor_map! {
|
||||
let {
|
||||
"alg" => algorithm,
|
||||
"signature" => bytes,
|
||||
} = extract_map(cbor_value)?;
|
||||
}
|
||||
|
||||
let algorithm = SignatureAlgorithm::try_from(ok_or_missing(algorithm)?)?;
|
||||
let bytes = extract_byte_string(ok_or_missing(bytes)?)?;
|
||||
if bytes.len() != ecdsa::Signature::BYTES_LENGTH {
|
||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
Ok(CoseSignature {
|
||||
algorithm,
|
||||
bytes: *array_ref![bytes.as_slice(), 0, ecdsa::Signature::BYTES_LENGTH],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<CoseSignature> for ecdsa::Signature {
|
||||
type Error = Ctap2StatusCode;
|
||||
|
||||
fn try_from(cose_signature: CoseSignature) -> Result<Self, Ctap2StatusCode> {
|
||||
match cose_signature.algorithm {
|
||||
SignatureAlgorithm::Es256 => ecdsa::Signature::from_bytes(&cose_signature.bytes)
|
||||
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),
|
||||
#[cfg(feature = "ed25519")]
|
||||
SignatureAlgorithm::Eddsa => Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM),
|
||||
SignatureAlgorithm::Unknown => Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub enum PinUvAuthProtocol {
|
||||
@@ -1292,7 +1245,6 @@ mod test {
|
||||
cbor_array, cbor_bool, cbor_bytes, cbor_bytes_lit, cbor_false, cbor_int, cbor_null,
|
||||
cbor_text, cbor_unsigned,
|
||||
};
|
||||
use crypto::sha256::Sha256;
|
||||
use rng256::Rng256;
|
||||
|
||||
#[test]
|
||||
@@ -2003,64 +1955,6 @@ mod test {
|
||||
assert_eq!(cose_key.algorithm, ES256_ALGORITHM);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_into_cose_signature() {
|
||||
let mut env = TestEnv::new();
|
||||
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
|
||||
let dummy_signature = sk.sign_rfc6979::<Sha256>(&[]);
|
||||
let mut bytes = [0; ecdsa::Signature::BYTES_LENGTH];
|
||||
dummy_signature.to_bytes(&mut bytes);
|
||||
let cbor_value = cbor_map! {
|
||||
"alg" => ES256_ALGORITHM,
|
||||
"signature" => bytes,
|
||||
};
|
||||
let cose_signature = CoseSignature::try_from(cbor_value).unwrap();
|
||||
let created_signature = crypto::ecdsa::Signature::try_from(cose_signature).unwrap();
|
||||
let mut created_bytes = [0; ecdsa::Signature::BYTES_LENGTH];
|
||||
created_signature.to_bytes(&mut created_bytes);
|
||||
assert_eq!(bytes[..], created_bytes[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cose_signature_wrong_algorithm() {
|
||||
let mut env = TestEnv::new();
|
||||
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
|
||||
let dummy_signature = sk.sign_rfc6979::<Sha256>(&[]);
|
||||
let mut bytes = [0; ecdsa::Signature::BYTES_LENGTH];
|
||||
dummy_signature.to_bytes(&mut bytes);
|
||||
let cbor_value = cbor_map! {
|
||||
"alg" => -1, // unused algorithm
|
||||
"signature" => bytes,
|
||||
};
|
||||
let cose_signature = CoseSignature::try_from(cbor_value).unwrap();
|
||||
let created_signature = crypto::ecdsa::Signature::try_from(cose_signature);
|
||||
// Can not compare directly, since ecdsa::Signature does not implement Debug.
|
||||
assert_eq!(
|
||||
created_signature.err(),
|
||||
Some(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cose_signature_wrong_signature_length() {
|
||||
let cbor_value = cbor_map! {
|
||||
"alg" => ES256_ALGORITHM,
|
||||
"signature" => [0; ecdsa::Signature::BYTES_LENGTH - 1],
|
||||
};
|
||||
assert_eq!(
|
||||
CoseSignature::try_from(cbor_value),
|
||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
);
|
||||
let cbor_value = cbor_map! {
|
||||
"alg" => ES256_ALGORITHM,
|
||||
"signature" => [0; ecdsa::Signature::BYTES_LENGTH + 1],
|
||||
};
|
||||
assert_eq!(
|
||||
CoseSignature::try_from(cbor_value),
|
||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_pin_uv_auth_protocol() {
|
||||
let cbor_protocol: cbor::Value = cbor_int!(0x01);
|
||||
|
||||
177
src/ctap/mod.rs
177
src/ctap/mod.rs
@@ -47,11 +47,10 @@ use self::credential_id::{
|
||||
use self::credential_management::process_credential_management;
|
||||
use self::crypto_wrapper::PrivateKey;
|
||||
use self::data_formats::{
|
||||
AuthenticatorTransport, CoseKey, CoseSignature, CredentialProtectionPolicy,
|
||||
EnterpriseAttestationMode, GetAssertionExtensions, PackedAttestationStatement,
|
||||
PinUvAuthProtocol, PublicKeyCredentialDescriptor, PublicKeyCredentialParameter,
|
||||
PublicKeyCredentialSource, PublicKeyCredentialType, PublicKeyCredentialUserEntity,
|
||||
SignatureAlgorithm,
|
||||
AuthenticatorTransport, CoseKey, CredentialProtectionPolicy, EnterpriseAttestationMode,
|
||||
GetAssertionExtensions, PackedAttestationStatement, PinUvAuthProtocol,
|
||||
PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialSource,
|
||||
PublicKeyCredentialType, PublicKeyCredentialUserEntity, SignatureAlgorithm,
|
||||
};
|
||||
use self::hid::{ChannelID, CtapHid, CtapHidCommand, KeepaliveStatus, ProcessedPacket};
|
||||
use self::large_blobs::LargeBlobs;
|
||||
@@ -77,7 +76,7 @@ use alloc::string::{String, ToString};
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use arrayref::array_ref;
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
||||
use core::convert::TryFrom;
|
||||
use crypto::hmac::hmac_256;
|
||||
use crypto::sha256::Sha256;
|
||||
@@ -212,45 +211,61 @@ fn truncate_to_char_boundary(s: &str, mut max: usize) -> &str {
|
||||
|
||||
/// Parses the metadata of an upgrade, and checks its correctness.
|
||||
///
|
||||
/// Returns the hash over the upgrade, including partition and some metadata.
|
||||
/// The metadata consists of:
|
||||
/// - 32B upgrade hash (SHA256)
|
||||
/// - 4B timestamp (little endian encoding)
|
||||
/// - 4B partition address (little endian encoding)
|
||||
/// The upgrade hash is computed over the firmware image and all metadata,
|
||||
/// except the hash itself.
|
||||
/// The metadata is a page starting with:
|
||||
/// - 32 B upgrade hash (SHA256)
|
||||
/// - 64 B signature,
|
||||
/// that are not signed over. The second part is included in the signature with
|
||||
/// - 8 B version and
|
||||
/// - 4 B partition address in little endian encoding
|
||||
/// written at METADATA_SIGN_OFFSET.
|
||||
///
|
||||
/// Checks hash and signature correctness, and whether the partition offset matches.
|
||||
fn parse_metadata(
|
||||
upgrade_locations: &impl UpgradeStorage,
|
||||
public_key_bytes: &[u8],
|
||||
metadata: &[u8],
|
||||
) -> Result<[u8; 32], Ctap2StatusCode> {
|
||||
const METADATA_LEN: usize = 40;
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
const METADATA_LEN: usize = 0x1000;
|
||||
const METADATA_SIGN_OFFSET: usize = 0x800;
|
||||
if metadata.len() != METADATA_LEN {
|
||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
let metadata_address = LittleEndian::read_u32(&metadata[METADATA_SIGN_OFFSET + 8..][..4]);
|
||||
if metadata_address as usize != upgrade_locations.partition_address() {
|
||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
// The hash implementation handles this in chunks, so no memory issues.
|
||||
let partition_slice = upgrade_locations
|
||||
.read_partition(0, upgrade_locations.partition_length())
|
||||
.map_err(|_| Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?;
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&metadata[METADATA_SIGN_OFFSET..]);
|
||||
hasher.update(partition_slice);
|
||||
hasher.update(&metadata[32..METADATA_LEN]);
|
||||
let computed_hash = hasher.finalize();
|
||||
if &computed_hash != array_ref!(metadata, 0, 32) {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE);
|
||||
}
|
||||
Ok(computed_hash)
|
||||
|
||||
verify_signature(
|
||||
array_ref!(metadata, 32, 64),
|
||||
public_key_bytes,
|
||||
&computed_hash,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verifies the signature over the given hash.
|
||||
///
|
||||
/// The public key is COSE encoded, and the hash is a SHA256.
|
||||
fn verify_signature(
|
||||
signature: Option<CoseSignature>,
|
||||
signature_bytes: &[u8; 64],
|
||||
public_key_bytes: &[u8],
|
||||
signed_hash: &[u8; 32],
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
let signature =
|
||||
ecdsa::Signature::try_from(signature.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?)?;
|
||||
let signature = ecdsa::Signature::from_bytes(signature_bytes)
|
||||
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)?;
|
||||
let cbor_public_key = cbor_read(public_key_bytes)?;
|
||||
let cose_key = CoseKey::try_from(cbor_public_key)?;
|
||||
let public_key = ecdsa::PubKey::try_from(cose_key)?;
|
||||
@@ -1454,7 +1469,6 @@ impl CtapState {
|
||||
address,
|
||||
data,
|
||||
hash,
|
||||
signature,
|
||||
} = params;
|
||||
let upgrade_locations = env
|
||||
.upgrade_storage()
|
||||
@@ -1468,9 +1482,7 @@ impl CtapState {
|
||||
.map_err(|_| Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)?
|
||||
} else {
|
||||
// Compares the hash inside the metadata to the actual hash.
|
||||
let upgrade_hash = parse_metadata(upgrade_locations, &data)?;
|
||||
// Only signed firmware images may be fully written.
|
||||
verify_signature(signature, key_material::UPGRADE_PUBLIC_KEY, &upgrade_hash)?;
|
||||
parse_metadata(upgrade_locations, key_material::UPGRADE_PUBLIC_KEY, &data)?;
|
||||
// Write the metadata page after verifying that its hash is signed.
|
||||
upgrade_locations
|
||||
.write_metadata(&data)
|
||||
@@ -3456,37 +3468,64 @@ mod test {
|
||||
#[test]
|
||||
fn test_parse_metadata() {
|
||||
let mut env = TestEnv::new();
|
||||
// The test buffer starts fully erased with 0xFF bytes.
|
||||
// The compiler issues an incorrect warning.
|
||||
#[allow(unused_mut)]
|
||||
let mut upgrade_locations = env.upgrade_storage().unwrap();
|
||||
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
|
||||
let upgrade_locations = env.upgrade_storage().unwrap();
|
||||
|
||||
const METADATA_LEN: usize = 0x1000;
|
||||
const METADATA_SIGN_OFFSET: usize = 0x800;
|
||||
let mut metadata = vec![0xFF; METADATA_LEN];
|
||||
LittleEndian::write_u32(&mut metadata[METADATA_SIGN_OFFSET + 8..][..4], 0x60000);
|
||||
|
||||
let partition_length = upgrade_locations.partition_length();
|
||||
let mut signed_over_data = metadata[METADATA_SIGN_OFFSET..].to_vec();
|
||||
signed_over_data.extend(
|
||||
upgrade_locations
|
||||
.read_partition(0, partition_length)
|
||||
.unwrap(),
|
||||
);
|
||||
let signed_hash = Sha256::hash(&signed_over_data);
|
||||
|
||||
metadata[..32].copy_from_slice(&signed_hash);
|
||||
let signature = private_key.sign_rfc6979::<Sha256>(&signed_over_data);
|
||||
let mut signature_bytes = [0; ecdsa::Signature::BYTES_LENGTH];
|
||||
signature.to_bytes(&mut signature_bytes);
|
||||
metadata[32..96].copy_from_slice(&signature_bytes);
|
||||
|
||||
let public_key = private_key.genpk();
|
||||
let mut public_key_bytes = vec![];
|
||||
cbor_write(
|
||||
cbor::Value::from(CoseKey::from(public_key)),
|
||||
&mut public_key_bytes,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Partition of 0x40000 bytes and 8 bytes metadata are hashed.
|
||||
let hashed_data = vec![0xFF; 0x40000 + 8];
|
||||
let expected_hash = Sha256::hash(&hashed_data);
|
||||
let mut metadata = vec![0xFF; 40];
|
||||
metadata[..32].copy_from_slice(&expected_hash);
|
||||
assert_eq!(
|
||||
parse_metadata(upgrade_locations, &metadata),
|
||||
Ok(expected_hash)
|
||||
parse_metadata(upgrade_locations, &public_key_bytes, &metadata),
|
||||
Ok(())
|
||||
);
|
||||
|
||||
// Any manipulation of data fails.
|
||||
metadata[32] = 0x88;
|
||||
metadata[METADATA_SIGN_OFFSET] = 0x88;
|
||||
assert_eq!(
|
||||
parse_metadata(upgrade_locations, &metadata),
|
||||
parse_metadata(upgrade_locations, &public_key_bytes, &metadata),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE)
|
||||
);
|
||||
metadata[32] = 0xFF;
|
||||
metadata[METADATA_SIGN_OFFSET] = 0xFF;
|
||||
metadata[0] ^= 0x01;
|
||||
assert_eq!(
|
||||
parse_metadata(upgrade_locations, &metadata),
|
||||
parse_metadata(upgrade_locations, &public_key_bytes, &metadata),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE)
|
||||
);
|
||||
metadata[0] ^= 0x01;
|
||||
metadata[32] ^= 0x01;
|
||||
assert_eq!(
|
||||
parse_metadata(upgrade_locations, &public_key_bytes, &metadata),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE)
|
||||
);
|
||||
metadata[32] ^= 0x01;
|
||||
upgrade_locations.write_partition(0, &[0x88; 1]).unwrap();
|
||||
assert_eq!(
|
||||
parse_metadata(upgrade_locations, &metadata),
|
||||
parse_metadata(upgrade_locations, &public_key_bytes, &metadata),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE)
|
||||
);
|
||||
}
|
||||
@@ -3501,10 +3540,6 @@ mod test {
|
||||
|
||||
let mut signature_bytes = [0; ecdsa::Signature::BYTES_LENGTH];
|
||||
signature.to_bytes(&mut signature_bytes);
|
||||
let cose_signature = CoseSignature {
|
||||
algorithm: SignatureAlgorithm::Es256,
|
||||
bytes: signature_bytes,
|
||||
};
|
||||
|
||||
let public_key = private_key.genpk();
|
||||
let mut public_key_bytes = vec![];
|
||||
@@ -3515,34 +3550,22 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
verify_signature(
|
||||
Some(cose_signature.clone()),
|
||||
&public_key_bytes,
|
||||
&signed_hash
|
||||
),
|
||||
verify_signature(&signature_bytes, &public_key_bytes, &signed_hash),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
verify_signature(Some(cose_signature.clone()), &public_key_bytes, &[0x55; 32]),
|
||||
verify_signature(&signature_bytes, &public_key_bytes, &[0x55; 32]),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE)
|
||||
);
|
||||
public_key_bytes[0] ^= 0x01;
|
||||
assert_eq!(
|
||||
verify_signature(Some(cose_signature), &public_key_bytes, &signed_hash),
|
||||
verify_signature(&signature_bytes, &public_key_bytes, &signed_hash),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_INVALID_CBOR)
|
||||
);
|
||||
public_key_bytes[0] ^= 0x01;
|
||||
assert_eq!(
|
||||
verify_signature(None, &public_key_bytes, &signed_hash),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)
|
||||
);
|
||||
signature_bytes[0] ^= 0x01;
|
||||
let cose_signature = CoseSignature {
|
||||
algorithm: SignatureAlgorithm::Es256,
|
||||
bytes: signature_bytes,
|
||||
};
|
||||
assert_eq!(
|
||||
verify_signature(Some(cose_signature), &public_key_bytes, &signed_hash),
|
||||
verify_signature(&signature_bytes, &public_key_bytes, &signed_hash),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE)
|
||||
);
|
||||
}
|
||||
@@ -3555,38 +3578,37 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
const METADATA_LEN: usize = 40;
|
||||
const METADATA_LEN: usize = 0x1000;
|
||||
const METADATA_SIGN_OFFSET: usize = 0x800;
|
||||
let mut metadata = vec![0xFF; METADATA_LEN];
|
||||
LittleEndian::write_u32(&mut metadata[METADATA_SIGN_OFFSET + 8..][..4], 0x60000);
|
||||
|
||||
let data = vec![0xFF; 0x1000];
|
||||
let hash = Sha256::hash(&data).to_vec();
|
||||
let upgrade_locations = env.upgrade_storage().unwrap();
|
||||
let partition_length = upgrade_locations.partition_length();
|
||||
let mut signed_over_data = upgrade_locations
|
||||
.read_partition(0, partition_length)
|
||||
.unwrap()
|
||||
.to_vec();
|
||||
signed_over_data.extend(&[0xFF; METADATA_LEN - 32]);
|
||||
let mut signed_over_data = metadata[METADATA_SIGN_OFFSET..].to_vec();
|
||||
signed_over_data.extend(
|
||||
upgrade_locations
|
||||
.read_partition(0, partition_length)
|
||||
.unwrap(),
|
||||
);
|
||||
let signed_hash = Sha256::hash(&signed_over_data);
|
||||
let mut metadata = vec![0xFF; METADATA_LEN];
|
||||
metadata[..32].copy_from_slice(&signed_hash);
|
||||
let metadata_hash = Sha256::hash(&metadata).to_vec();
|
||||
|
||||
metadata[..32].copy_from_slice(&signed_hash);
|
||||
let signature = private_key.sign_rfc6979::<Sha256>(&signed_over_data);
|
||||
let mut signature_bytes = [0; ecdsa::Signature::BYTES_LENGTH];
|
||||
signature.to_bytes(&mut signature_bytes);
|
||||
let cose_signature = CoseSignature {
|
||||
algorithm: SignatureAlgorithm::Es256,
|
||||
bytes: signature_bytes,
|
||||
};
|
||||
metadata[32..96].copy_from_slice(&signature_bytes);
|
||||
let metadata_hash = Sha256::hash(&metadata).to_vec();
|
||||
|
||||
// Write to partition and metadata.
|
||||
// Write to partition.
|
||||
let response = ctap_state.process_vendor_upgrade(
|
||||
&mut env,
|
||||
AuthenticatorVendorUpgradeParameters {
|
||||
address: Some(0x20000),
|
||||
data: data.clone(),
|
||||
hash: hash.clone(),
|
||||
signature: None,
|
||||
},
|
||||
);
|
||||
assert_eq!(response, Ok(ResponseData::AuthenticatorVendorUpgrade));
|
||||
@@ -3599,7 +3621,6 @@ mod test {
|
||||
address: None,
|
||||
data: metadata.clone(),
|
||||
hash: metadata_hash.clone(),
|
||||
signature: Some(cose_signature.clone()),
|
||||
},
|
||||
);
|
||||
assert_eq!(response, Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE));
|
||||
@@ -3611,7 +3632,6 @@ mod test {
|
||||
address: None,
|
||||
data: metadata[..METADATA_LEN - 1].to_vec(),
|
||||
hash: metadata_hash,
|
||||
signature: Some(cose_signature),
|
||||
},
|
||||
);
|
||||
assert_eq!(response, Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER));
|
||||
@@ -3623,7 +3643,6 @@ mod test {
|
||||
address: Some(0x40000),
|
||||
data: data.clone(),
|
||||
hash,
|
||||
signature: None,
|
||||
},
|
||||
);
|
||||
assert_eq!(response, Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER));
|
||||
@@ -3635,7 +3654,6 @@ mod test {
|
||||
address: Some(0x20000),
|
||||
data,
|
||||
hash: [0xEE; 32].to_vec(),
|
||||
signature: None,
|
||||
},
|
||||
);
|
||||
assert_eq!(response, Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE));
|
||||
@@ -3655,7 +3673,6 @@ mod test {
|
||||
address: Some(0),
|
||||
data,
|
||||
hash,
|
||||
signature: None,
|
||||
},
|
||||
);
|
||||
assert_eq!(response, Err(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND));
|
||||
|
||||
Reference in New Issue
Block a user