mirror of
https://github.com/trussed-dev/trussed-auth.git
synced 2026-06-20 04:16:21 -07:00
Implement deriving X25519 keys from pins
This commit is contained in:
+2
-2
@@ -249,8 +249,8 @@ impl ExtensionImpl<AuthExtension> for AuthBackend {
|
||||
let key_id = keystore.store_key(
|
||||
Location::Volatile,
|
||||
Secrecy::Secret,
|
||||
Kind::Symmetric(CHACHA_KEY_LEN),
|
||||
&*k,
|
||||
k.kind(),
|
||||
&k.data(),
|
||||
)?;
|
||||
Ok(reply::GetPinKey {
|
||||
result: Some(key_id),
|
||||
|
||||
+79
-34
@@ -10,6 +10,7 @@ use serde_byte_array::ByteArray;
|
||||
use sha2::{Digest as _, Sha256};
|
||||
use subtle::ConstantTimeEq as _;
|
||||
use trussed::{
|
||||
key,
|
||||
platform::{CryptoRng, RngCore},
|
||||
store::filestore::Filestore,
|
||||
types::{Location, PathBuf},
|
||||
@@ -33,7 +34,7 @@ pub(crate) type ChaChaTag = ByteArray<CHACHA_TAG_LEN>;
|
||||
pub(crate) type ChachaKey = ByteArray<CHACHA_KEY_LEN>;
|
||||
pub(crate) type X25519Key = [u8; X25519_KEY_LEN];
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub(crate) enum Key {
|
||||
Chacha20Poly1305(ChachaKey),
|
||||
X25519(X25519Key),
|
||||
@@ -90,6 +91,27 @@ impl<'de> Deserialize<'de> for Key {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum DecryptedKey {
|
||||
Chacha20Poly1305(ChachaKey),
|
||||
X25519(salty::agreement::SecretKey),
|
||||
}
|
||||
|
||||
impl DecryptedKey {
|
||||
pub fn kind(&self) -> key::Kind {
|
||||
match self {
|
||||
DecryptedKey::Chacha20Poly1305(_) => key::Kind::Symmetric(CHACHA_KEY_LEN),
|
||||
DecryptedKey::X25519(_) => key::Kind::X255,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data(&self) -> [u8; 32] {
|
||||
match self {
|
||||
DecryptedKey::Chacha20Poly1305(k) => (*k).into(),
|
||||
DecryptedKey::X25519(k) => k.to_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent a key wrapped by the pin.
|
||||
/// The key derivation process is as follow (pseudocode):
|
||||
///
|
||||
@@ -348,7 +370,7 @@ pub(crate) struct PinDataMut<'a> {
|
||||
|
||||
enum CheckResult {
|
||||
Validated,
|
||||
Derived { k: ChachaKey, app_key: ChachaKey },
|
||||
Derived { k: DecryptedKey, app_key: ChachaKey },
|
||||
Failed,
|
||||
}
|
||||
|
||||
@@ -382,10 +404,7 @@ impl<'a> PinDataMut<'a> {
|
||||
CheckResult::Failed
|
||||
}
|
||||
}
|
||||
KeyOrHash::Key(WrappedKeyData {
|
||||
wrapped_key: Key::Chacha20Poly1305(wrapped_key),
|
||||
tag,
|
||||
}) => {
|
||||
KeyOrHash::Key(WrappedKeyData { wrapped_key, tag }) => {
|
||||
let app_key = application_key()?;
|
||||
if let Some(k) = self.unwrap_key(pin, &app_key, wrapped_key, &tag) {
|
||||
CheckResult::Derived { k, app_key }
|
||||
@@ -393,13 +412,6 @@ impl<'a> PinDataMut<'a> {
|
||||
CheckResult::Failed
|
||||
}
|
||||
}
|
||||
KeyOrHash::Key(WrappedKeyData {
|
||||
wrapped_key: Key::X25519(_x25519),
|
||||
tag,
|
||||
}) => {
|
||||
let _ = tag;
|
||||
todo!()
|
||||
}
|
||||
};
|
||||
if let Some(retries) = &mut self.data.retries {
|
||||
if res.is_success() {
|
||||
@@ -428,9 +440,9 @@ impl<'a> PinDataMut<'a> {
|
||||
&self,
|
||||
pin: &Pin,
|
||||
application_key: &ChachaKey,
|
||||
mut wrapped_key: ChachaKey,
|
||||
mut wrapped_key: Key,
|
||||
tag: &ChaChaTag,
|
||||
) -> Option<ChachaKey> {
|
||||
) -> Option<DecryptedKey> {
|
||||
use chacha20poly1305::{AeadInPlace, KeyInit};
|
||||
|
||||
let pin_key = derive_key(self.id, pin, &self.salt, application_key);
|
||||
@@ -438,21 +450,35 @@ impl<'a> PinDataMut<'a> {
|
||||
// The pin key is only ever used to once to wrap a key. Nonce reuse is not a concern
|
||||
// Because the salt is also used in the key derivation process, PIN reuse across PINs will still lead to different keys
|
||||
let nonce = Default::default();
|
||||
aead.decrypt_in_place_detached(
|
||||
&nonce,
|
||||
&[u8::from(self.data.id)],
|
||||
&mut *wrapped_key,
|
||||
(&**tag).into(),
|
||||
)
|
||||
.ok()
|
||||
.and(Some(wrapped_key))
|
||||
|
||||
let aad_1;
|
||||
let aad_2;
|
||||
let (aad, mut key_data): (&[u8], _) = match &mut wrapped_key {
|
||||
Key::Chacha20Poly1305(k) => {
|
||||
aad_1 = [u8::from(self.data.id)];
|
||||
(&aad_1, **k)
|
||||
}
|
||||
Key::X25519(k) => {
|
||||
aad_2 = [u8::from(self.data.id), 0];
|
||||
(&aad_2, *k)
|
||||
}
|
||||
};
|
||||
|
||||
aead.decrypt_in_place_detached(&nonce, aad, &mut key_data, (&**tag).into())
|
||||
.ok()?;
|
||||
match wrapped_key {
|
||||
Key::Chacha20Poly1305(_) => Some(DecryptedKey::Chacha20Poly1305((key_data).into())),
|
||||
Key::X25519(_) => Some(DecryptedKey::X25519(
|
||||
salty::agreement::SecretKey::from_seed(&key_data),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pin_key(
|
||||
&mut self,
|
||||
pin: &Pin,
|
||||
application_key: &ChachaKey,
|
||||
) -> Result<Option<ChachaKey>, Error> {
|
||||
) -> Result<Option<DecryptedKey>, Error> {
|
||||
match self.check_or_unwrap(pin, || Ok(*application_key))? {
|
||||
CheckResult::Validated => Err(Error::BadPinType),
|
||||
CheckResult::Derived { k, .. } => Ok(Some(k)),
|
||||
@@ -478,7 +504,7 @@ impl<'a> PinDataMut<'a> {
|
||||
fn new_wrapping_pin<R: CryptoRng + RngCore>(
|
||||
&mut self,
|
||||
new: &Pin,
|
||||
mut old_key: ChachaKey,
|
||||
old_key: DecryptedKey,
|
||||
application_key: &ChachaKey,
|
||||
rng: &mut R,
|
||||
) {
|
||||
@@ -493,20 +519,39 @@ impl<'a> PinDataMut<'a> {
|
||||
// Because the salt is also used in the key derivation process, PIN reuse across PINs will still lead to different keys
|
||||
let nonce = Default::default();
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
let tag: [u8; CHACHA_TAG_LEN] = aead
|
||||
.encrypt_in_place_detached(&nonce, &[u8::from(self.id)], &mut *old_key)
|
||||
.expect("Wrapping the key should always work, length are acceptable")
|
||||
.into();
|
||||
let data = match old_key {
|
||||
DecryptedKey::Chacha20Poly1305(mut k) => {
|
||||
#[allow(clippy::expect_used)]
|
||||
let tag: [u8; CHACHA_TAG_LEN] = aead
|
||||
.encrypt_in_place_detached(&nonce, &[u8::from(self.id)], &mut *k)
|
||||
.expect("Wrapping the key should always work, length are acceptable")
|
||||
.into();
|
||||
|
||||
KeyOrHash::Key(WrappedKeyData {
|
||||
wrapped_key: Key::Chacha20Poly1305(k),
|
||||
tag: tag.into(),
|
||||
})
|
||||
}
|
||||
DecryptedKey::X25519(k) => {
|
||||
let mut data = k.to_bytes();
|
||||
#[allow(clippy::expect_used)]
|
||||
let tag: [u8; CHACHA_TAG_LEN] = aead
|
||||
.encrypt_in_place_detached(&nonce, &[u8::from(self.id), 0], &mut data)
|
||||
.expect("Wrapping the key should always work, length are acceptable")
|
||||
.into();
|
||||
|
||||
KeyOrHash::Key(WrappedKeyData {
|
||||
wrapped_key: Key::X25519(data),
|
||||
tag: tag.into(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
*self.data = PinData {
|
||||
id: self.id,
|
||||
retries: self.retries,
|
||||
salt,
|
||||
data: KeyOrHash::Key(WrappedKeyData {
|
||||
wrapped_key: Key::Chacha20Poly1305(old_key),
|
||||
tag: tag.into(),
|
||||
}),
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+52
-21
@@ -128,7 +128,7 @@ use trussed::{
|
||||
types::{Bytes, Location, Message, PathBuf},
|
||||
virt::{self, Ram},
|
||||
};
|
||||
use trussed_auth::{AuthClient as _, PinId, MAX_HW_KEY_LEN};
|
||||
use trussed_auth::{request::DerivedKeyMechanism, AuthClient as _, PinId, MAX_HW_KEY_LEN};
|
||||
|
||||
use dispatch::{Backend, Dispatch, BACKENDS};
|
||||
|
||||
@@ -207,7 +207,7 @@ fn basic() {
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(!reply.has_pin);
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, false));
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, None));
|
||||
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(reply.has_pin);
|
||||
@@ -251,7 +251,12 @@ fn basic_wrapped() {
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(!reply.has_pin);
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, true));
|
||||
syscall!(client.set_pin(
|
||||
Pin::User,
|
||||
pin1.clone(),
|
||||
None,
|
||||
Some(DerivedKeyMechanism::Chacha8Poly1305)
|
||||
));
|
||||
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(reply.has_pin);
|
||||
@@ -298,7 +303,12 @@ fn hw_key_wrapped() {
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(!reply.has_pin);
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, true));
|
||||
syscall!(client.set_pin(
|
||||
Pin::User,
|
||||
pin1.clone(),
|
||||
None,
|
||||
Some(DerivedKeyMechanism::Chacha8Poly1305)
|
||||
));
|
||||
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(reply.has_pin);
|
||||
@@ -343,12 +353,18 @@ fn missing_hw_key() {
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(!reply.has_pin);
|
||||
|
||||
assert!(try_syscall!(client.set_pin(Pin::User, pin1.clone(), None, true)).is_err());
|
||||
assert!(try_syscall!(client.set_pin(
|
||||
Pin::User,
|
||||
pin1.clone(),
|
||||
None,
|
||||
Some(DerivedKeyMechanism::Chacha8Poly1305)
|
||||
))
|
||||
.is_err());
|
||||
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(!reply.has_pin);
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, false));
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, None));
|
||||
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(reply.has_pin);
|
||||
@@ -392,7 +408,12 @@ fn pin_key() {
|
||||
let pin1 = Bytes::from_slice(b"12345678").unwrap();
|
||||
let pin2 = Bytes::from_slice(b"123456").unwrap();
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), Some(3), true));
|
||||
syscall!(client.set_pin(
|
||||
Pin::User,
|
||||
pin1.clone(),
|
||||
Some(3),
|
||||
Some(DerivedKeyMechanism::Chacha8Poly1305)
|
||||
));
|
||||
assert!(syscall!(client.get_pin_key(Pin::User, pin2.clone()))
|
||||
.result
|
||||
.is_none());
|
||||
@@ -441,7 +462,12 @@ fn reset_pin_key() {
|
||||
let pin2 = Bytes::from_slice(b"123456").unwrap();
|
||||
let pin3 = Bytes::from_slice(b"1234567890").unwrap();
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), Some(3), true));
|
||||
syscall!(client.set_pin(
|
||||
Pin::User,
|
||||
pin1.clone(),
|
||||
Some(3),
|
||||
Some(DerivedKeyMechanism::Chacha8Poly1305)
|
||||
));
|
||||
assert!(syscall!(client.get_pin_key(Pin::User, pin2.clone()))
|
||||
.result
|
||||
.is_none());
|
||||
@@ -489,7 +515,12 @@ fn blocked_pin() {
|
||||
let pin1 = Bytes::from_slice(b"12345678").unwrap();
|
||||
let pin2 = Bytes::from_slice(b"123456").unwrap();
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), Some(3), false));
|
||||
syscall!(client.set_pin(
|
||||
Pin::User,
|
||||
pin1.clone(),
|
||||
Some(3),
|
||||
Some(DerivedKeyMechanism::Chacha8Poly1305)
|
||||
));
|
||||
|
||||
let reply = syscall!(client.check_pin(Pin::User, pin1.clone()));
|
||||
assert!(reply.success);
|
||||
@@ -510,7 +541,7 @@ fn set_blocked_pin() {
|
||||
let pin1 = Bytes::from_slice(b"12345678").unwrap();
|
||||
let pin2 = Bytes::from_slice(b"123456").unwrap();
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), Some(1), false));
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), Some(1), None));
|
||||
let reply = syscall!(client.check_pin(Pin::User, pin1.clone()));
|
||||
assert!(reply.success);
|
||||
let reply = syscall!(client.check_pin(Pin::User, pin2.clone()));
|
||||
@@ -518,7 +549,7 @@ fn set_blocked_pin() {
|
||||
let reply = syscall!(client.check_pin(Pin::User, pin1));
|
||||
assert!(!reply.success);
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin2.clone(), Some(1), false));
|
||||
syscall!(client.set_pin(Pin::User, pin2.clone(), Some(1), None));
|
||||
let reply = syscall!(client.check_pin(Pin::User, pin2));
|
||||
assert!(reply.success);
|
||||
})
|
||||
@@ -530,7 +561,7 @@ fn empty_pin() {
|
||||
let pin1 = Bytes::new();
|
||||
let pin2 = Bytes::from_slice(b"123456").unwrap();
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, false));
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, None));
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(reply.has_pin);
|
||||
let reply = syscall!(client.check_pin(Pin::User, pin1.clone()));
|
||||
@@ -553,7 +584,7 @@ fn max_pin_length() {
|
||||
}
|
||||
};
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, false));
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, None));
|
||||
let reply = syscall!(client.check_pin(Pin::User, pin1));
|
||||
assert!(reply.success);
|
||||
let reply = syscall!(client.check_pin(Pin::User, pin2));
|
||||
@@ -568,9 +599,9 @@ fn pin_retries() {
|
||||
let pin2 = Bytes::from_slice(b"123456").unwrap();
|
||||
let pin3 = Bytes::from_slice(b"654321").unwrap();
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), Some(3), false));
|
||||
syscall!(client.set_pin(Pin::Admin, pin2.clone(), Some(5), false));
|
||||
syscall!(client.set_pin(Pin::Custom, pin3.clone(), None, false));
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), Some(3), None));
|
||||
syscall!(client.set_pin(Pin::Admin, pin2.clone(), Some(5), None));
|
||||
syscall!(client.set_pin(Pin::Custom, pin3.clone(), None, None));
|
||||
|
||||
let reply = syscall!(client.pin_retries(Pin::User));
|
||||
assert_eq!(reply.retries, Some(3));
|
||||
@@ -616,7 +647,7 @@ fn delete_pin() {
|
||||
run(BACKENDS, |client| {
|
||||
let pin = Bytes::from_slice(b"123456").unwrap();
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin.clone(), None, false));
|
||||
syscall!(client.set_pin(Pin::User, pin.clone(), None, None));
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(reply.has_pin);
|
||||
|
||||
@@ -637,8 +668,8 @@ fn delete_all_pins() {
|
||||
let pin1 = Bytes::from_slice(b"123456").unwrap();
|
||||
let pin2 = Bytes::from_slice(b"12345678").unwrap();
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, false));
|
||||
syscall!(client.set_pin(Pin::Admin, pin2.clone(), None, false));
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, None));
|
||||
syscall!(client.set_pin(Pin::Admin, pin2.clone(), None, None));
|
||||
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(reply.has_pin);
|
||||
@@ -726,8 +757,8 @@ fn reset_auth_data() {
|
||||
let pin1 = Bytes::from_slice(b"123456").unwrap();
|
||||
let pin2 = Bytes::from_slice(b"12345678").unwrap();
|
||||
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, false));
|
||||
syscall!(client.set_pin(Pin::Admin, pin2.clone(), None, false));
|
||||
syscall!(client.set_pin(Pin::User, pin1.clone(), None, None));
|
||||
syscall!(client.set_pin(Pin::Admin, pin2.clone(), None, None));
|
||||
|
||||
let reply = syscall!(client.has_pin(Pin::User));
|
||||
assert!(reply.has_pin);
|
||||
|
||||
Reference in New Issue
Block a user