mirror of
https://github.com/trussed-dev/fido-authenticator.git
synced 2026-06-20 04:16:16 -07:00
ctap2: Implement PIN protocol version 2
This commit is contained in:
+4
-1
@@ -18,7 +18,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Implement the `largeBlobKey` extension and the `largeBlobs` command ([#38][])
|
||||
- Fix error type for third invalid PIN entry ([#60][])
|
||||
- Fix error type for cancelled user presence ([#61][])
|
||||
- Extract PIN protocol implementation into separate module ([#62][])
|
||||
- PIN protocol changes:
|
||||
- Extract PIN protocol implementation into separate module ([#62][])
|
||||
- Implement PIN protocol 2 ([#63][])
|
||||
|
||||
[#26]: https://github.com/solokeys/fido-authenticator/issues/26
|
||||
[#28]: https://github.com/solokeys/fido-authenticator/issues/28
|
||||
@@ -31,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
[#60]: https://github.com/Nitrokey/fido-authenticator/pull/60
|
||||
[#61]: https://github.com/Nitrokey/fido-authenticator/pull/61
|
||||
[#62]: https://github.com/Nitrokey/fido-authenticator/pull/62
|
||||
[#63]: https://github.com/Nitrokey/fido-authenticator/pull/63
|
||||
|
||||
## [0.1.1] - 2022-08-22
|
||||
- Fix bug that treated U2F payloads as APDU over APDU in NFC transport @conorpp
|
||||
|
||||
+6
-3
@@ -23,6 +23,7 @@ serde_cbor = { version = "0.11.0", default-features = false }
|
||||
serde-indexed = "0.1.0"
|
||||
sha2 = { version = "0.10", default-features = false }
|
||||
trussed = "0.1"
|
||||
trussed-hkdf = { version = "0.1.0" }
|
||||
trussed-staging = { version = "0.1.0", default-features = false, optional = true }
|
||||
|
||||
apdu-dispatch = { version = "0.1", optional = true }
|
||||
@@ -56,11 +57,13 @@ usbd-ctaphid = "0.1.0"
|
||||
features = ["dispatch"]
|
||||
|
||||
[patch.crates-io]
|
||||
ctap-types = { git = "https://github.com/trussed-dev/ctap-types.git", rev = "7d4ad69e64ad308944c012aef5b9cfd7654d9be8" }
|
||||
ctap-types = { git = "https://github.com/trussed-dev/ctap-types.git", rev = "3735eb5a8e2b30b98afd35b64b4f470e690dcd19" }
|
||||
ctaphid-dispatch = { git = "https://github.com/trussed-dev/ctaphid-dispatch.git", rev = "57cb3317878a8593847595319aa03ef17c29ec5b" }
|
||||
apdu-dispatch = { git = "https://github.com/trussed-dev/apdu-dispatch.git", rev = "915fc237103fcecc29d0f0b73391f19abf6576de" }
|
||||
trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "b1781805a2e33615d2d00b8bec80c0b1f5870ca1" }
|
||||
trussed-staging = { git = "https://github.com/trussed-dev/trussed-staging", rev = "3b9594d93f89a5e760fe78fa5a96f125dfdcd470" }
|
||||
littlefs2 = { git = "https://github.com/trussed-dev/littlefs2.git", rev = "ebd27e49ca321089d01d8c9b169c4aeb58ceeeca" }
|
||||
serde-indexed = { git = "https://github.com/sosthene-nitrokey/serde-indexed.git", rev = "5005d23cb4ee8622e62188ea0f9466146f851f0d" }
|
||||
trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "cff2e663841b6a68d3a8ce12647d57b2b6fbc36c" }
|
||||
trussed-hkdf = { git = "https://github.com/Nitrokey/trussed-hkdf-backend.git", tag = "v0.1.0" }
|
||||
trussed-staging = { git = "https://github.com/trussed-dev/trussed-staging", rev = "3b9594d93f89a5e760fe78fa5a96f125dfdcd470" }
|
||||
trussed-usbip = { git = "https://github.com/Nitrokey/pc-usbip-runner.git", tag = "v0.0.1-nitrokey.1" }
|
||||
usbd-ctaphid = { git = "https://github.com/Nitrokey/usbd-ctaphid.git", tag = "v0.1.0-nitrokey.2" }
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
|
||||
let wrapped_key =
|
||||
syscall!(self
|
||||
.trussed
|
||||
.wrap_key_chacha8poly1305(wrapping_key, private_key, &[]))
|
||||
.wrap_key_chacha8poly1305(wrapping_key, private_key, &[], None))
|
||||
.wrapped_key;
|
||||
// debug!("wrapped_key = {:?}", &wrapped_key);
|
||||
|
||||
|
||||
+36
-31
@@ -68,8 +68,10 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let mut pin_protocols = Vec::<u8, 1>::new();
|
||||
pin_protocols.push(1).unwrap();
|
||||
let mut pin_protocols = Vec::<u8, 2>::new();
|
||||
for pin_protocol in self.pin_protocols() {
|
||||
pin_protocols.push(u8::from(*pin_protocol)).unwrap();
|
||||
}
|
||||
|
||||
let options = ctap2::get_info::CtapOptions {
|
||||
ep: None,
|
||||
@@ -332,11 +334,13 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
|
||||
false => {
|
||||
// WrappedKey version
|
||||
let wrapping_key = self.state.persistent.key_wrapping_key(&mut self.trussed)?;
|
||||
let wrapped_key =
|
||||
syscall!(self
|
||||
.trussed
|
||||
.wrap_key_chacha8poly1305(wrapping_key, private_key, &[]))
|
||||
.wrapped_key;
|
||||
let wrapped_key = syscall!(self.trussed.wrap_key_chacha8poly1305(
|
||||
wrapping_key,
|
||||
private_key,
|
||||
&[],
|
||||
None
|
||||
))
|
||||
.wrapped_key;
|
||||
|
||||
// 32B key, 12B nonce, 16B tag + some info on algorithm (P256/Ed25519)
|
||||
// Turns out it's size 92 (enum serialization not optimized yet...)
|
||||
@@ -692,16 +696,15 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
|
||||
}
|
||||
|
||||
// 3. generate shared secret
|
||||
let shared_secret = self
|
||||
.pin_protocol(pin_protocol)
|
||||
.shared_secret(platform_kek)?;
|
||||
let mut pin_protocol = self.pin_protocol(pin_protocol);
|
||||
let shared_secret = pin_protocol.shared_secret(platform_kek)?;
|
||||
|
||||
// TODO: there are moar early returns!!
|
||||
// - implement Drop?
|
||||
// - do garbage collection outside of this?
|
||||
|
||||
// 4. verify pinAuth
|
||||
shared_secret.verify_pin_auth(&mut self.trussed, new_pin_enc, pin_auth)?;
|
||||
pin_protocol.verify_pin_auth(&shared_secret, new_pin_enc, pin_auth)?;
|
||||
|
||||
// 5. decrypt and verify new PIN
|
||||
let new_pin = self.decrypt_pin_check_length(&shared_secret, new_pin_enc)?;
|
||||
@@ -754,9 +757,8 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
|
||||
self.state.pin_blocked()?;
|
||||
|
||||
// 3. generate shared secret
|
||||
let shared_secret = self
|
||||
.pin_protocol(pin_protocol)
|
||||
.shared_secret(platform_kek)?;
|
||||
let mut pin_protocol_impl = self.pin_protocol(pin_protocol);
|
||||
let shared_secret = pin_protocol_impl.shared_secret(platform_kek)?;
|
||||
|
||||
// 4. verify pinAuth
|
||||
let mut data = MediumData::new();
|
||||
@@ -764,7 +766,7 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
|
||||
.map_err(|_| Error::InvalidParameter)?;
|
||||
data.extend_from_slice(pin_hash_enc)
|
||||
.map_err(|_| Error::InvalidParameter)?;
|
||||
shared_secret.verify_pin_auth(&mut self.trussed, &data, pin_auth)?;
|
||||
pin_protocol_impl.verify_pin_auth(&shared_secret, &data, pin_auth)?;
|
||||
|
||||
// 5. decrement retries
|
||||
self.state.decrement_retries(&mut self.trussed)?;
|
||||
@@ -1050,15 +1052,20 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
|
||||
|
||||
// impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenticator<UP, T>
|
||||
impl<UP: UserPresence, T: TrussedRequirements> crate::Authenticator<UP, T> {
|
||||
fn parse_pin_protocol(&self, version: impl Into<u32>) -> Result<PinProtocolVersion> {
|
||||
match version.into() {
|
||||
1 => Ok(PinProtocolVersion::V1),
|
||||
_ => Err(Error::InvalidParameter),
|
||||
fn parse_pin_protocol(&self, version: impl TryInto<u8>) -> Result<PinProtocolVersion> {
|
||||
if let Ok(version) = version.try_into() {
|
||||
for pin_protocol in self.pin_protocols() {
|
||||
if u8::from(*pin_protocol) == version {
|
||||
return Ok(*pin_protocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::InvalidParameter)
|
||||
}
|
||||
|
||||
// This is the single source of truth for the supported PIN protocols.
|
||||
fn pin_protocols(&self) -> &'static [PinProtocolVersion] {
|
||||
&[PinProtocolVersion::V1]
|
||||
&[PinProtocolVersion::V2, PinProtocolVersion::V1]
|
||||
}
|
||||
|
||||
fn pin_protocol(&mut self, pin_protocol: PinProtocolVersion) -> PinProtocol<'_, T> {
|
||||
@@ -1181,7 +1188,7 @@ impl<UP: UserPresence, T: TrussedRequirements> crate::Authenticator<UP, T> {
|
||||
&mut self,
|
||||
pin_protocol: PinProtocolVersion,
|
||||
shared_secret: &SharedSecret,
|
||||
pin_hash_enc: &Bytes<64>,
|
||||
pin_hash_enc: &Bytes<80>,
|
||||
) -> Result<()> {
|
||||
let pin_hash = shared_secret
|
||||
.decrypt(&mut self.trussed, pin_hash_enc)
|
||||
@@ -1372,9 +1379,6 @@ impl<UP: UserPresence, T: TrussedRequirements> crate::Authenticator<UP, T> {
|
||||
if self.state.persistent.pin_is_set() {
|
||||
// let mut uv_performed = false;
|
||||
if let Some(pin_auth) = pin_auth {
|
||||
if pin_auth.len() != 16 {
|
||||
return Err(Error::InvalidParameter);
|
||||
}
|
||||
// seems a bit redundant to check here in light of 2.
|
||||
// I guess the CTAP spec writers aren't implementers :D
|
||||
if let Some(pin_protocol) = pin_protocol {
|
||||
@@ -1382,7 +1386,7 @@ impl<UP: UserPresence, T: TrussedRequirements> crate::Authenticator<UP, T> {
|
||||
// success --> set uv = 1
|
||||
// error --> PinAuthInvalid
|
||||
self.pin_protocol(pin_protocol)
|
||||
.verify_pin_token(pin_auth, data)?;
|
||||
.verify_pin_token(data, pin_auth)?;
|
||||
|
||||
return Ok(true);
|
||||
} else {
|
||||
@@ -1449,16 +1453,17 @@ impl<UP: UserPresence, T: TrussedRequirements> crate::Authenticator<UP, T> {
|
||||
.key;
|
||||
|
||||
// Verify the auth tag, which uses the same process as the pinAuth
|
||||
let shared_secret = self
|
||||
.pin_protocol(pin_protocol)
|
||||
.shared_secret(&hmac_secret.key_agreement)?;
|
||||
shared_secret.verify_pin_auth(
|
||||
&mut self.trussed,
|
||||
let mut pin_protocol = self.pin_protocol(pin_protocol);
|
||||
let shared_secret = pin_protocol.shared_secret(&hmac_secret.key_agreement)?;
|
||||
pin_protocol.verify_pin_auth(
|
||||
&shared_secret,
|
||||
&hmac_secret.salt_enc,
|
||||
&hmac_secret.salt_auth,
|
||||
)?;
|
||||
|
||||
if hmac_secret.salt_enc.len() != 32 && hmac_secret.salt_enc.len() != 64 {
|
||||
if hmac_secret.salt_enc.len() != 32
|
||||
&& (hmac_secret.salt_enc.len() != 64 || hmac_secret.salt_enc.len() == 80)
|
||||
{
|
||||
debug_now!("invalid hmac-secret length");
|
||||
return Err(Error::InvalidLength);
|
||||
}
|
||||
|
||||
+184
-50
@@ -2,16 +2,30 @@ use crate::{cbor_serialize_message, TrussedRequirements};
|
||||
use ctap_types::{cose::EcdhEsHkdf256PublicKey, Error, Result};
|
||||
use trussed::{
|
||||
cbor_deserialize,
|
||||
client::{Aes256Cbc, CryptoClient, HmacSha256, P256},
|
||||
client::{CryptoClient, HmacSha256, P256},
|
||||
syscall, try_syscall,
|
||||
types::{Bytes, KeyId, KeySerialization, Location, Mechanism, StorageAttributes},
|
||||
types::{
|
||||
Bytes, KeyId, KeySerialization, Location, Mechanism, Message, ShortData, StorageAttributes,
|
||||
},
|
||||
};
|
||||
use trussed_hkdf::{KeyOrData, OkmId};
|
||||
|
||||
const PIN_TOKEN_LENGTH: usize = 16;
|
||||
// PIN protocol 1 supports 16 or 32 bytes, PIN protocol 2 requires 32 bytes.
|
||||
const PIN_TOKEN_LENGTH: usize = 32;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum PinProtocolVersion {
|
||||
V1,
|
||||
V2,
|
||||
}
|
||||
|
||||
impl From<PinProtocolVersion> for u8 {
|
||||
fn from(version: PinProtocolVersion) -> Self {
|
||||
match version {
|
||||
PinProtocolVersion::V1 => 1,
|
||||
PinProtocolVersion::V2 => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -19,10 +33,12 @@ pub struct PinProtocolState {
|
||||
key_agreement_key: KeyId,
|
||||
// only used to delete the old shared secret from VFS when generating a new one. ideally, the
|
||||
// SharedSecret struct would clean up after itself.
|
||||
shared_secret: Option<KeyId>,
|
||||
shared_secret: Option<SharedSecret>,
|
||||
|
||||
// for protocol version 1
|
||||
pin_token_v1: KeyId,
|
||||
// for protocol version 2
|
||||
pin_token_v2: KeyId,
|
||||
}
|
||||
|
||||
impl PinProtocolState {
|
||||
@@ -32,6 +48,7 @@ impl PinProtocolState {
|
||||
key_agreement_key: generate_key_agreement_key(trussed),
|
||||
shared_secret: None,
|
||||
pin_token_v1: generate_pin_token(trussed),
|
||||
pin_token_v2: generate_pin_token(trussed),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +56,7 @@ impl PinProtocolState {
|
||||
syscall!(trussed.delete(self.pin_token_v1));
|
||||
syscall!(trussed.delete(self.key_agreement_key));
|
||||
if let Some(shared_secret) = self.shared_secret {
|
||||
syscall!(trussed.delete(shared_secret));
|
||||
shared_secret.delete(trussed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,19 +84,21 @@ impl<'a, T: TrussedRequirements> PinProtocol<'a, T> {
|
||||
fn pin_token(&self) -> KeyId {
|
||||
match self.version {
|
||||
PinProtocolVersion::V1 => self.state.pin_token_v1,
|
||||
PinProtocolVersion::V2 => self.state.pin_token_v2,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_pin_token(&mut self, pin_token: KeyId) {
|
||||
match self.version {
|
||||
PinProtocolVersion::V1 => self.state.pin_token_v1 = pin_token,
|
||||
PinProtocolVersion::V2 => self.state.pin_token_v2 = pin_token,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn regenerate(&mut self) {
|
||||
syscall!(self.trussed.delete(self.state.key_agreement_key));
|
||||
if let Some(shared_secret) = self.state.shared_secret.take() {
|
||||
syscall!(self.trussed.delete(shared_secret));
|
||||
shared_secret.delete(self.trussed);
|
||||
}
|
||||
self.state.key_agreement_key = generate_key_agreement_key(self.trussed);
|
||||
}
|
||||
@@ -109,10 +128,35 @@ impl<'a, T: TrussedRequirements> PinProtocol<'a, T> {
|
||||
cose_key
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn verify(&mut self, key: KeyId, data: &[u8], signature: &[u8]) -> bool {
|
||||
let actual_signature = syscall!(self.trussed.sign_hmacsha256(key, data)).signature;
|
||||
let expected_signature = match self.version {
|
||||
PinProtocolVersion::V1 => &actual_signature[..16],
|
||||
PinProtocolVersion::V2 => &actual_signature,
|
||||
};
|
||||
expected_signature == signature
|
||||
}
|
||||
|
||||
// in spec: verify(pinUvAuthToken, ...)
|
||||
pub fn verify_pin_token(&mut self, data: &[u8], signature: &[u8]) -> Result<()> {
|
||||
// TODO: check if pin token is in use
|
||||
if verify(self.trussed, self.pin_token(), data, signature) {
|
||||
if self.verify(self.pin_token(), data, signature) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::PinAuthInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
// in spec: verify(shared secret, ...)
|
||||
pub fn verify_pin_auth(
|
||||
&mut self,
|
||||
shared_secret: &SharedSecret,
|
||||
data: &[u8],
|
||||
pin_auth: &[u8],
|
||||
) -> Result<()> {
|
||||
let key_id = shared_secret.hmac_key_id();
|
||||
if self.verify(key_id, data, pin_auth) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::PinAuthInvalid)
|
||||
@@ -120,11 +164,8 @@ impl<'a, T: TrussedRequirements> PinProtocol<'a, T> {
|
||||
}
|
||||
|
||||
// in spec: encrypt(..., pinUvAuthToken)
|
||||
pub fn encrypt_pin_token(&mut self, shared_secret: &SharedSecret) -> Result<Bytes<32>> {
|
||||
let token = syscall!(self
|
||||
.trussed
|
||||
.wrap_key_aes256cbc(shared_secret.key_id, self.pin_token()))
|
||||
.wrapped_key;
|
||||
pub fn encrypt_pin_token(&mut self, shared_secret: &SharedSecret) -> Result<Bytes<48>> {
|
||||
let token = shared_secret.wrap(self.trussed, self.pin_token());
|
||||
Bytes::from_slice(&token).map_err(|_| Error::Other)
|
||||
}
|
||||
|
||||
@@ -156,76 +197,169 @@ impl<'a, T: TrussedRequirements> PinProtocol<'a, T> {
|
||||
syscall!(self.trussed.delete(peer_key));
|
||||
let pre_shared_secret = result.ok()?.shared_secret;
|
||||
|
||||
if let Some(shared_secret) = self.state.shared_secret {
|
||||
syscall!(self.trussed.delete(shared_secret));
|
||||
if let Some(shared_secret) = self.state.shared_secret.take() {
|
||||
shared_secret.delete(self.trussed);
|
||||
}
|
||||
|
||||
let shared_secret = self.kdf(pre_shared_secret);
|
||||
self.state.shared_secret = Some(shared_secret);
|
||||
syscall!(self.trussed.delete(pre_shared_secret));
|
||||
|
||||
Some(SharedSecret {
|
||||
key_id: shared_secret,
|
||||
})
|
||||
let shared_secret = shared_secret?;
|
||||
self.state.shared_secret = Some(shared_secret.clone());
|
||||
Some(shared_secret)
|
||||
}
|
||||
|
||||
fn kdf(&mut self, input: KeyId) -> KeyId {
|
||||
syscall!(self.trussed.derive_key(
|
||||
fn kdf(&mut self, input: KeyId) -> Option<SharedSecret> {
|
||||
match self.version {
|
||||
PinProtocolVersion::V1 => self.kdf_v1(input),
|
||||
PinProtocolVersion::V2 => self.kdf_v2(input),
|
||||
}
|
||||
}
|
||||
|
||||
// PIN protocol 1: derive a single key using SHA-256
|
||||
fn kdf_v1(&mut self, input: KeyId) -> Option<SharedSecret> {
|
||||
let key_id = syscall!(self.trussed.derive_key(
|
||||
Mechanism::Sha256,
|
||||
input,
|
||||
None,
|
||||
StorageAttributes::new().set_persistence(Location::Volatile)
|
||||
))
|
||||
.key
|
||||
.key;
|
||||
Some(SharedSecret::V1 { key_id })
|
||||
}
|
||||
|
||||
// PIN protocol 2: derive two keys using HKDF-SHA-256
|
||||
// In the spec, the keys are concatenated and the relevant part is selected during the key
|
||||
// operations. For simplicity, we store two separate keys instead.
|
||||
fn kdf_v2(&mut self, input: KeyId) -> Option<SharedSecret> {
|
||||
fn hkdf<T: TrussedRequirements>(trussed: &mut T, okm: OkmId, info: &[u8]) -> Option<KeyId> {
|
||||
let info = Message::from_slice(info).ok()?;
|
||||
try_syscall!(trussed.hkdf_expand(okm, info, 32, Location::Volatile))
|
||||
.ok()
|
||||
.map(|reply| reply.key)
|
||||
}
|
||||
|
||||
// salt: 0x00 * 32 => None
|
||||
let okm = try_syscall!(self.trussed.hkdf_extract(
|
||||
KeyOrData::Key(input),
|
||||
None,
|
||||
Location::Volatile
|
||||
))
|
||||
.ok()?
|
||||
.okm;
|
||||
let hmac_key_id = hkdf(self.trussed, okm, b"CTAP2 HMAC key");
|
||||
let aes_key_id = hkdf(self.trussed, okm, b"CTAP2 AES key");
|
||||
|
||||
syscall!(self.trussed.delete(okm.0));
|
||||
|
||||
Some(SharedSecret::V2 {
|
||||
hmac_key_id: hmac_key_id?,
|
||||
aes_key_id: aes_key_id?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SharedSecret {
|
||||
key_id: KeyId,
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SharedSecret {
|
||||
V1 {
|
||||
key_id: KeyId,
|
||||
},
|
||||
V2 {
|
||||
hmac_key_id: KeyId,
|
||||
aes_key_id: KeyId,
|
||||
},
|
||||
}
|
||||
|
||||
impl SharedSecret {
|
||||
pub fn verify_pin_auth<T: HmacSha256>(
|
||||
&self,
|
||||
trussed: &mut T,
|
||||
data: &[u8],
|
||||
pin_auth: &Bytes<16>,
|
||||
) -> Result<()> {
|
||||
if verify(trussed, self.key_id, data, pin_auth) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::PinAuthInvalid)
|
||||
fn aes_key_id(&self) -> KeyId {
|
||||
match self {
|
||||
Self::V1 { key_id } => *key_id,
|
||||
Self::V2 { aes_key_id, .. } => *aes_key_id,
|
||||
}
|
||||
}
|
||||
|
||||
fn hmac_key_id(&self) -> KeyId {
|
||||
match self {
|
||||
Self::V1 { key_id } => *key_id,
|
||||
Self::V2 { hmac_key_id, .. } => *hmac_key_id,
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_iv<T: CryptoClient>(&self, trussed: &mut T) -> ShortData {
|
||||
match self {
|
||||
Self::V1 { .. } => ShortData::from_slice(&[0; 16]).unwrap(),
|
||||
Self::V2 { .. } => syscall!(trussed.random_bytes(16))
|
||||
.bytes
|
||||
.try_convert_into()
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn encrypt<T: CryptoClient>(&self, trussed: &mut T, data: &[u8]) -> Bytes<1024> {
|
||||
syscall!(trussed.encrypt(Mechanism::Aes256Cbc, self.key_id, data, b"", None)).ciphertext
|
||||
let key_id = self.aes_key_id();
|
||||
let iv = self.generate_iv(trussed);
|
||||
let mut ciphertext =
|
||||
syscall!(trussed.encrypt(Mechanism::Aes256Cbc, key_id, data, &[], Some(iv.clone())))
|
||||
.ciphertext;
|
||||
if matches!(self, Self::V2 { .. }) {
|
||||
ciphertext.insert_slice_at(&iv, 0).unwrap();
|
||||
}
|
||||
ciphertext
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn decrypt<T: Aes256Cbc>(&self, trussed: &mut T, data: &[u8]) -> Option<Bytes<1024>> {
|
||||
decrypt(trussed, self.key_id, data)
|
||||
fn wrap<T: CryptoClient>(&self, trussed: &mut T, key: KeyId) -> Bytes<1024> {
|
||||
let wrapping_key = self.aes_key_id();
|
||||
let iv = self.generate_iv(trussed);
|
||||
let mut wrapped_key = syscall!(trussed.wrap_key(
|
||||
Mechanism::Aes256Cbc,
|
||||
wrapping_key,
|
||||
key,
|
||||
&[],
|
||||
Some(iv.clone())
|
||||
))
|
||||
.wrapped_key;
|
||||
if matches!(self, Self::V2 { .. }) {
|
||||
wrapped_key.insert_slice_at(&iv, 0).unwrap();
|
||||
}
|
||||
wrapped_key
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn decrypt<T: CryptoClient>(&self, trussed: &mut T, data: &[u8]) -> Option<Bytes<1024>> {
|
||||
let key_id = self.aes_key_id();
|
||||
let (iv, data) = match self {
|
||||
Self::V1 { .. } => (Default::default(), data),
|
||||
Self::V2 { .. } => {
|
||||
if data.len() < 16 {
|
||||
return None;
|
||||
}
|
||||
data.split_at(16)
|
||||
}
|
||||
};
|
||||
try_syscall!(trussed.decrypt(Mechanism::Aes256Cbc, key_id, data, iv, b"", b""))
|
||||
.ok()
|
||||
.and_then(|response| response.plaintext)
|
||||
}
|
||||
|
||||
pub fn delete<T: CryptoClient>(self, trussed: &mut T) {
|
||||
syscall!(trussed.delete(self.key_id));
|
||||
match self {
|
||||
Self::V1 { key_id } => {
|
||||
syscall!(trussed.delete(key_id));
|
||||
}
|
||||
Self::V2 {
|
||||
hmac_key_id,
|
||||
aes_key_id,
|
||||
} => {
|
||||
for key_id in [hmac_key_id, aes_key_id] {
|
||||
syscall!(trussed.delete(key_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn verify<T: HmacSha256>(trussed: &mut T, key: KeyId, data: &[u8], signature: &[u8]) -> bool {
|
||||
let actual_signature = syscall!(trussed.sign_hmacsha256(key, data)).signature;
|
||||
&actual_signature[..16] == signature
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn decrypt<T: Aes256Cbc>(trussed: &mut T, key: KeyId, data: &[u8]) -> Option<Bytes<1024>> {
|
||||
try_syscall!(trussed.decrypt_aes256cbc(key, data))
|
||||
.ok()
|
||||
.and_then(|response| response.plaintext)
|
||||
}
|
||||
|
||||
fn generate_pin_token<T: HmacSha256>(trussed: &mut T) -> KeyId {
|
||||
syscall!(trussed.generate_secret_key(PIN_TOKEN_LENGTH, Location::Volatile)).key
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ generate_macros!();
|
||||
use core::time::Duration;
|
||||
|
||||
use trussed::{client, syscall, types::Message, Client as TrussedClient};
|
||||
use trussed_hkdf::HkdfClient;
|
||||
|
||||
use ctap_types::heapless_bytes::Bytes;
|
||||
|
||||
@@ -55,6 +56,7 @@ pub trait TrussedRequirements:
|
||||
+ client::Sha256
|
||||
+ client::HmacSha256
|
||||
+ client::Ed255 // + client::Totp
|
||||
+ HkdfClient
|
||||
+ ExtensionRequirements
|
||||
{
|
||||
}
|
||||
@@ -67,6 +69,7 @@ impl<T> TrussedRequirements for T where
|
||||
+ client::Sha256
|
||||
+ client::HmacSha256
|
||||
+ client::Ed255 // + client::Totp
|
||||
+ HkdfClient
|
||||
+ ExtensionRequirements
|
||||
{
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user