fido-common: Implement Deserialize for attestation::CredentialData

Relies on ciborium as a dependency because coset requires one of
ciborium's error types. It should be possible to remove this type's
dependence on ciborium.
main
Nick Zana 2 years ago
parent ce4150d184
commit 90f75b88d8

@ -6,8 +6,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ciborium = { version = "0.2.1", default-features = false, optional = true }
coset = { version = "0.3.4", default-features = false }
serde = { version = "1", features = ["derive"], optional = true }
[features]
serde = ["dep:serde"]
serde = ["dep:serde", "dep:ciborium"]

@ -36,7 +36,7 @@ pub mod enterprise;
/// > of registered `WebAuthn` Extensions is maintained in the IANA "WebAuthn
/// > Attestation Statement Format Identifiers" registry
/// > [IANA-WebAuthn-Registries] established by [RFC8809].
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum FormatIdentifier {
/// > The "packed" attestation statement format is a WebAuthn-optimized
@ -74,6 +74,7 @@ pub enum FormatIdentifier {
/// > Attested credential data is a variable-length byte array added to the
/// > authenticator data when generating an attestation object for a given
/// > credential.
#[derive(Debug)]
pub struct CredentialData {
/// > The AAGUID of the authenticator.
pub aaguid: [u8; 16],
@ -82,3 +83,57 @@ pub struct CredentialData {
/// The public key of the credential.
pub public_key: coset::CoseKey,
}
#[cfg(feature = "serde")]
impl TryFrom<&[u8]> for CredentialData {
// TODO: Custom error type?
type Error = coset::CoseError;
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
// aaguid: 16 Bytes
// SAFETY: Validate that data.len >= 16 for aaguid bytes
if data.len() < 16 {
return Err(coset::CoseError::DecodeFailed(ciborium::de::Error::Io(
coset::EndOfFile,
)));
}
let (&aaguid, data) = data.split_array_ref::<16>();
// credentialIdLengh: 2 Bytes
// > Byte length L of credentialId, 16-bit unsigned big-endian integer. Value
// > MUST be ≤ 1023.
// SAFETY: Validate that there are 2 bytes for u16
if data.len() < 2 {
return Err(coset::CoseError::DecodeFailed(ciborium::de::Error::Io(
coset::EndOfFile,
)));
}
let (&credential_id_length, mut data) = data.split_array_ref::<2>();
let credential_id_length = u16::from_be_bytes(credential_id_length);
if credential_id_length > 1023 {
return Err(coset::CoseError::UnexpectedItem(
"a credentialIdLength (L) of greater than 1023",
"a 16-bit unsigned big-endian integer less than or equal to 1023",
));
}
// credentialId: L (credential_id_length) Bytes
let credential_id: &[u8] = data.take(..credential_id_length as usize)
.ok_or(coset::CoseError::DecodeFailed(ciborium::de::Error::Io(
coset::EndOfFile,
)))?;
Ok(Self { aaguid, id: credential_id.to_vec(), public_key: Default::default() })
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for CredentialData {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de> {
let data = Vec::<u8>::deserialize(deserializer)?;
// TODO: Improve error handling
CredentialData::try_from(data.as_slice()).map_err(serde::de::Error::custom)
}
}

@ -1,3 +1,5 @@
#![feature(split_array, slice_take)]
pub mod attestation;
pub mod authenticator;
pub mod credential;

Loading…
Cancel
Save