Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for wrapping asymmetric keys #41

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ chacha20poly1305 = { version = "0.10.1", default-features = false, features = ["
hkdf = "0.12.3"
hmac = "0.12.1"
rand_core = "0.6.4"
salty = { version = "0.3.0", default-features = false }
serde = { version = "1", default-features = false }
serde-byte-array = "0.1.2"
sha2 = { version = "0.10.6", default-features = false }
Expand All @@ -24,7 +25,9 @@ trussed = { version = "0.1.0", features = ["serde-extensions"] }
[dev-dependencies]
quickcheck = { version = "1.0.3", default-features = false }
rand_core = { version = "0.6.4", default-features = false, features = ["getrandom"] }
serde_test = "1.0.176"
trussed = { version = "0.1.0", features = ["serde-extensions", "virt"] }
serde_cbor = { version = "0.11.2", features = ["std"] }

[patch.crates-io]
littlefs2 = { git = "https://github.com/Nitrokey/littlefs2", tag = "v0.3.2-nitrokey-2" }
Expand Down
43 changes: 25 additions & 18 deletions src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use core::fmt;

use hkdf::Hkdf;
use rand_core::{CryptoRng, RngCore};
use salty::agreement::SecretKey;
use sha2::Sha256;
use trussed::{
backend::Backend,
Expand All @@ -25,9 +26,9 @@ use crate::{
extension::{reply, AuthExtension, AuthReply, AuthRequest},
BACKEND_DIR,
};
use data::{Key, PinData, Salt, KEY_LEN, SALT_LEN};
use data::{DeriveKey, PinData, Salt, CHACHA_KEY_LEN, SALT_LEN};

use self::data::delete_app_salt;
use self::data::{delete_app_salt, ChachaKey, DecryptedKey};

/// max accepted length for the hardware initial key material
pub const MAX_HW_KEY_LEN: usize = 64;
Expand Down Expand Up @@ -149,8 +150,8 @@ impl AuthBackend {
}
}

fn expand(kdf: &Hkdf<Sha256>, client_id: &PathBuf) -> Key {
let mut out = Key::default();
fn expand(kdf: &Hkdf<Sha256>, client_id: &PathBuf) -> ChachaKey {
let mut out = ChachaKey::default();
#[allow(clippy::expect_used)]
kdf.expand(client_id.as_ref().as_bytes(), &mut *out)
.expect("Out data is always valid");
Expand All @@ -162,7 +163,7 @@ impl AuthBackend {
client_id: PathBuf,
global_fs: &mut impl Filestore,
rng: &mut R,
) -> Result<Key, Error> {
) -> Result<ChachaKey, Error> {
Ok(match &self.hw_key {
HardwareKey::Extracted(okm) => Self::expand(okm, &client_id),
HardwareKey::Missing => return Err(Error::MissingHwKey),
Expand All @@ -183,7 +184,7 @@ impl AuthBackend {
global_fs: &mut impl Filestore,
ctx: &mut AuthContext,
rng: &mut R,
) -> Result<Key, Error> {
) -> Result<ChachaKey, Error> {
if let Some(app_key) = ctx.application_key {
return Ok(app_key);
}
Expand All @@ -197,7 +198,7 @@ impl AuthBackend {
/// Per-client context for [`AuthBackend`][]
#[derive(Default, Debug)]
pub struct AuthContext {
application_key: Option<Key>,
application_key: Option<ChachaKey>,
}

impl Backend for AuthBackend {
Expand Down Expand Up @@ -249,8 +250,8 @@ impl ExtensionImpl<AuthExtension> for AuthBackend {
let key_id = keystore.store_key(
Location::Volatile,
Secrecy::Secret,
Kind::Symmetric(KEY_LEN),
&*k,
k.kind(),
&k.data(),
)?;
Ok(reply::GetPinKey {
result: Some(key_id),
Expand All @@ -276,28 +277,34 @@ impl ExtensionImpl<AuthExtension> for AuthBackend {
Ok(reply::ChangePin { success }.into())
}
AuthRequest::SetPin(request) => {
let maybe_app_key = if request.derive_key {
Some(self.get_app_key(client_id, global_fs, ctx, rng)?)
} else {
None
let key_derivation = match request.derive_key {
Some(key_type) => Some(DeriveKey {
application_key: self.get_app_key(client_id, global_fs, ctx, rng)?,
key_type,
}),
None => None,
};
PinData::new(
request.id,
&request.pin,
request.retries,
rng,
maybe_app_key.as_ref(),
key_derivation,
)
.save(fs, self.location)?;
Ok(reply::SetPin.into())
}
AuthRequest::SetPinWithKey(request) => {
let app_key = self.get_app_key(client_id, global_fs, ctx, rng)?;
let key_to_wrap =
keystore.load_key(Secrecy::Secret, Some(Kind::Symmetric(32)), &request.key)?;
let key_to_wrap = (&*key_to_wrap.material)
let key_material = keystore.load_key(Secrecy::Secret, None, &request.key)?;
let material_32: [u8; 32] = (&*key_material.material)
.try_into()
.map_err(|_| Error::ReadFailed)?;
let key_to_wrap = match key_material.kind {
Kind::Symmetric(32) => DecryptedKey::Chacha20Poly1305(material_32.into()),
Kind::X255 => DecryptedKey::X25519(SecretKey::from_seed(&material_32)),
_ => return Err(Error::NotFound)?,
};
PinData::reset_with_key(
request.id,
&request.pin,
Expand Down Expand Up @@ -347,7 +354,7 @@ impl ExtensionImpl<AuthExtension> for AuthBackend {
let key_id = keystore.store_key(
Location::Volatile,
Secrecy::Secret,
Kind::Symmetric(KEY_LEN),
Kind::Symmetric(CHACHA_KEY_LEN),
&*key,
)?;
Ok(reply::GetApplicationKey { key: key_id }.into())
Expand Down
Loading