use ctap_types::ctap1::{authenticate, register, Authenticator, ControlByte, Error, Result};
use heapless_bytes::Bytes;
use serde_bytes::ByteArray;
use trussed_core::{
syscall,
types::{KeySerialization, Location, Mechanism, SignatureSerialization},
};
use crate::{
constants,
credential::{self, Credential, Key, StrippedCredential},
SigningAlgorithm, TrussedRequirements, UserPresence,
};
type Commitment = Bytes<324>;
impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenticator<UP, T> {
fn register(&mut self, reg: ®ister::Request) -> Result<register::Response> {
if self.state.persistent.always_uv() {
return Err(Error::CommandNotAllowedNoEf);
}
self.up
.user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT)
.map_err(|_| Error::ConditionsOfUseNotSatisfied)?;
let private_key = syscall!(self.trussed.generate_p256_private_key(Location::Volatile)).key;
let public_key = syscall!(self
.trussed
.derive_p256_public_key(private_key, Location::Volatile))
.key;
let serialized_cose_public_key = syscall!(self
.trussed
.serialize_p256_key(public_key, KeySerialization::EcdhEsHkdf256))
.serialized_key;
syscall!(self.trussed.delete(public_key));
let cose_key: cosey::EcdhEsHkdf256PublicKey =
cbor_smol::cbor_deserialize(&serialized_cose_public_key).unwrap();
let wrapping_key = self
.state
.persistent
.key_wrapping_key(&mut self.trussed)
.map_err(|_| Error::UnspecifiedCheckingError)?;
let wrapped_key =
syscall!(self
.trussed
.wrap_key_chacha8poly1305(wrapping_key, private_key, &[], None))
.wrapped_key;
syscall!(self.trussed.delete(private_key));
let key = Key::WrappedKey(
Bytes::try_from(&*wrapped_key).map_err(|_| Error::UnspecifiedCheckingError)?,
);
let nonce = ByteArray::new(self.nonce());
let credential = StrippedCredential {
ctap: credential::CtapVersion::U2fV2,
creation_time: self
.state
.persistent
.timestamp(&mut self.trussed)
.map_err(|_| Error::NotEnoughMemory)?,
use_counter: true,
algorithm: SigningAlgorithm::P256 as i32,
key,
nonce,
hmac_secret: None,
cred_protect: None,
large_blob_key: None,
third_party_payment: None,
};
let kek = self
.state
.persistent
.key_encryption_key(&mut self.trussed)
.map_err(|_| Error::NotEnoughMemory)?;
let credential_id = credential
.id(&mut self.trussed, kek, reg.app_id)
.map_err(|_| Error::NotEnoughMemory)?;
let mut commitment = Commitment::new();
commitment.push(0).unwrap(); commitment.extend_from_slice(reg.app_id).unwrap();
commitment.extend_from_slice(reg.challenge).unwrap();
commitment.extend_from_slice(&credential_id.0).unwrap();
commitment.push(0x04).unwrap(); commitment.extend_from_slice(&cose_key.x).unwrap();
commitment.extend_from_slice(&cose_key.y).unwrap();
let attestation = self.state.identity.attestation(&mut self.trussed);
let (signature, cert) = match attestation {
(Some((key, cert)), _aaguid) => {
info!("aaguid: {}", hex_str!(&_aaguid));
(
Bytes::try_from(
&*syscall!(self.trussed.sign(
Mechanism::P256,
key,
&commitment,
SignatureSerialization::Asn1Der
))
.signature,
)
.unwrap(),
cert,
)
}
_ => {
info!("Not provisioned with attestation key!");
return Err(Error::KeyReferenceNotFound);
}
};
Ok(register::Response::new(
0x05,
&cose_key,
credential_id.0,
signature,
cert,
))
}
fn authenticate(&mut self, auth: &authenticate::Request) -> Result<authenticate::Response> {
if self.state.persistent.always_uv() {
return Err(Error::CommandNotAllowedNoEf);
}
let cred = Credential::try_from_bytes(self, auth.app_id, auth.key_handle);
let user_presence_byte = match auth.control_byte {
ControlByte::CheckOnly => {
return if cred.is_ok() {
Err(Error::ConditionsOfUseNotSatisfied)
} else {
Err(Error::IncorrectDataParameter)
};
}
ControlByte::EnforceUserPresenceAndSign => {
if !self.skip_up_check() {
self.up
.user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT)
.map_err(|_| Error::ConditionsOfUseNotSatisfied)?;
}
0x01
}
ControlByte::DontEnforceUserPresenceAndSign => 0x00,
};
let cred = cred.map_err(|_| Error::IncorrectDataParameter)?;
let key = match cred.key() {
Key::WrappedKey(bytes) => {
let wrapping_key = self
.state
.persistent
.key_wrapping_key(&mut self.trussed)
.map_err(|_| Error::IncorrectDataParameter)?;
let key_result = syscall!(self.trussed.unwrap_key_chacha8poly1305(
wrapping_key,
bytes,
&[],
Location::Volatile,
))
.key;
match key_result {
Some(key) => {
info!("loaded u2f key!");
key
}
None => {
info!("issue with unwrapping credential id key");
return Err(Error::IncorrectDataParameter);
}
}
}
_ => return Err(Error::IncorrectDataParameter),
};
if cred.algorithm() != -7 {
info!("Unexpected mechanism for u2f");
return Err(Error::IncorrectDataParameter);
}
let sig_count = self
.state
.persistent
.timestamp(&mut self.trussed)
.map_err(|_| Error::UnspecifiedNonpersistentExecutionError)?;
let mut commitment = Commitment::new();
commitment.extend_from_slice(auth.app_id).unwrap();
commitment.push(user_presence_byte).unwrap();
commitment
.extend_from_slice(&sig_count.to_be_bytes())
.unwrap();
commitment.extend_from_slice(auth.challenge).unwrap();
let signature = Bytes::try_from(
&*syscall!(self.trussed.sign(
Mechanism::P256,
key,
&commitment,
SignatureSerialization::Asn1Der
))
.signature,
)
.unwrap();
Ok(authenticate::Response {
user_presence: user_presence_byte,
count: sig_count,
signature,
})
}
}