Initial support for the key injection and derivation

These are the features required by Nitrokey Webcrypt.
This commit is contained in:
Szczepan Zalega
2023-06-29 13:09:41 +02:00
parent c6aa6bdd65
commit 21c40cd09a
4 changed files with 285 additions and 0 deletions
+5
View File
@@ -19,12 +19,17 @@ delog = "0.1.6"
littlefs2 = "0.4.0"
serde-byte-array = "0.1.2"
# For hmacsha256p256
hmac = { version = "0.12", optional = true }
sha2 = { version = "0.10", default-features = false, optional = true }
[dev-dependencies]
trussed = { version = "0.1.0", default-features = false, features = ["serde-extensions", "virt"] }
[features]
default = []
hmacsha256p256 = ["hmac", "sha2"]
wrap-key-to-file = ["chacha20poly1305"]
chunked = []
encrypted-chunked = ["chunked", "chacha20poly1305/stream"]
+239
View File
@@ -0,0 +1,239 @@
// Copyright (C) Nitrokey GmbH
// SPDX-License-Identifier: Apache-2.0 or MIT
use serde::{Deserialize, Serialize};
use trussed::types::SerializedKey;
use trussed::{
client::ClientError,
key::{self, Kind},
serde_extensions::{Extension, ExtensionClient, ExtensionImpl, ExtensionResult},
service::{Keystore, ServiceResources},
types::{Bytes, CoreContext, KeyId, Location, Mechanism},
Error,
};
#[derive(Debug, Default)]
pub struct HmacSha256P256Extension;
#[derive(Debug, Deserialize, Serialize)]
#[allow(missing_docs)]
pub enum HmacSha256P256Request {
DeriveFromHash(request::DeriveFromHash),
InjectAnyKey(request::InjectAnyKey),
}
mod request {
use super::*;
use serde::{Deserialize, Serialize};
use trussed::types::{KeyId, Location, Mechanism, Message, SerializedKey};
use trussed::Error;
#[derive(Debug, Deserialize, Serialize)]
pub struct DeriveFromHash {
pub mechanism: Mechanism,
pub key: KeyId,
pub location: Location,
pub data: Option<Message>,
}
impl TryFrom<HmacSha256P256Request> for DeriveFromHash {
type Error = Error;
fn try_from(request: HmacSha256P256Request) -> Result<Self, Self::Error> {
match request {
HmacSha256P256Request::DeriveFromHash(request) => Ok(request),
_ => Err(Error::InternalError),
}
}
}
impl From<DeriveFromHash> for HmacSha256P256Request {
fn from(request: DeriveFromHash) -> Self {
Self::DeriveFromHash(request)
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct InjectAnyKey {
pub location: Location,
pub kind: Kind,
pub raw_key: SerializedKey,
}
impl TryFrom<HmacSha256P256Request> for InjectAnyKey {
type Error = Error;
fn try_from(request: HmacSha256P256Request) -> Result<Self, Self::Error> {
match request {
HmacSha256P256Request::InjectAnyKey(request) => Ok(request),
_ => Err(Error::InternalError),
}
}
}
impl From<InjectAnyKey> for HmacSha256P256Request {
fn from(request: InjectAnyKey) -> Self {
Self::InjectAnyKey(request)
}
}
}
#[derive(Debug, Deserialize, Serialize)]
#[allow(missing_docs)]
pub enum HmacSha256P256Reply {
DeriveFromHash(reply::DeriveFromHash),
InjectAnyKey(reply::InjectAnyKey),
}
mod reply {
use serde::{Deserialize, Serialize};
use trussed::{types::KeyId, Error};
use super::*;
#[derive(Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct DeriveFromHash {
pub key: Option<KeyId>,
}
impl TryFrom<HmacSha256P256Reply> for DeriveFromHash {
type Error = Error;
fn try_from(reply: HmacSha256P256Reply) -> Result<Self, Self::Error> {
match reply {
HmacSha256P256Reply::DeriveFromHash(reply) => Ok(reply),
_ => Err(Error::InternalError),
}
}
}
impl From<DeriveFromHash> for HmacSha256P256Reply {
fn from(reply: DeriveFromHash) -> Self {
Self::DeriveFromHash(reply)
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct InjectAnyKey {
pub key: Option<KeyId>,
}
impl TryFrom<HmacSha256P256Reply> for InjectAnyKey {
type Error = Error;
fn try_from(reply: HmacSha256P256Reply) -> Result<Self, Self::Error> {
match reply {
HmacSha256P256Reply::InjectAnyKey(reply) => Ok(reply),
_ => Err(Error::InternalError),
}
}
}
impl From<InjectAnyKey> for HmacSha256P256Reply {
fn from(reply: InjectAnyKey) -> Self {
Self::InjectAnyKey(reply)
}
}
}
impl Extension for HmacSha256P256Extension {
type Request = HmacSha256P256Request;
type Reply = HmacSha256P256Reply;
}
pub fn derive_key_from_hash(
keystore: &mut impl Keystore,
request: &request::DeriveFromHash,
) -> Result<reply::DeriveFromHash, Error> {
use hmac::{Hmac, Mac};
type HmacSha256P256 = Hmac<sha2::Sha256>;
let key_id = request.key;
let key = keystore.load_key(key::Secrecy::Secret, None, &key_id)?;
let shared_secret = key.material;
let mut mac =
HmacSha256P256::new_from_slice(shared_secret.as_ref()).map_err(|_| Error::InternalError)?;
if let Some(data) = &request.data {
mac.update(data);
}
let derived_key: [u8; 32] = mac
.finalize()
.into_bytes()
.try_into()
.map_err(|_| Error::InternalError)?;
let key_id = keystore.store_key(
request.location,
key::Secrecy::Secret,
key::Kind::P256, // TODO use mechanism/kind from the request
&derived_key,
)?;
Ok(reply::DeriveFromHash { key: Some(key_id) })
}
pub fn inject_any_key(
keystore: &mut impl Keystore,
request: &request::InjectAnyKey,
) -> Result<reply::InjectAnyKey, Error> {
let key_id = keystore.store_key(
request.location,
key::Secrecy::Secret,
request.kind,
&request.raw_key,
)?;
Ok(reply::InjectAnyKey { key: Some(key_id) })
}
impl ExtensionImpl<HmacSha256P256Extension> for super::StagingBackend {
fn extension_request<P: trussed::Platform>(
&mut self,
core_ctx: &mut CoreContext,
_backend_ctx: &mut Self::Context,
request: &HmacSha256P256Request,
resources: &mut ServiceResources<P>,
) -> Result<HmacSha256P256Reply, Error> {
let keystore = &mut resources.keystore(core_ctx)?;
match request {
HmacSha256P256Request::DeriveFromHash(request) => {
derive_key_from_hash(keystore, request).map(Into::into)
}
HmacSha256P256Request::InjectAnyKey(request) => {
inject_any_key(keystore, request).map(Into::into)
}
}
}
}
type HmacSha256P256Result<'a, R, C> = ExtensionResult<'a, HmacSha256P256Extension, R, C>;
pub trait HmacSha256P256Client: ExtensionClient<HmacSha256P256Extension> {
fn derive_from_hash(
&mut self,
mechanism: Mechanism,
key: KeyId,
location: Location,
data: &[u8],
) -> HmacSha256P256Result<'_, reply::DeriveFromHash, Self> {
let data = Bytes::from_slice(data).map_err(|_| ClientError::DataTooLarge)?;
self.extension(request::DeriveFromHash {
mechanism,
key,
location,
data: Some(data),
})
}
fn inject_any_key(
&mut self,
raw_key: SerializedKey,
location: Location,
kind: Kind,
) -> HmacSha256P256Result<'_, reply::InjectAnyKey, Self> {
self.extension(request::InjectAnyKey {
location,
kind,
raw_key,
})
}
}
impl<C: ExtensionClient<HmacSha256P256Extension>> HmacSha256P256Client for C {}
+3
View File
@@ -18,6 +18,9 @@ pub mod wrap_key_to_file;
#[cfg(feature = "chunked")]
pub mod streaming;
#[cfg(feature = "hmacsha256p256")]
pub mod hmacsha256p256;
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct StagingBackend {}
+38
View File
@@ -0,0 +1,38 @@
// Copyright (C) Nitrokey GmbH
// SPDX-License-Identifier: Apache-2.0 or MIT
#![cfg(all(feature = "virt", feature = "hmacsha256p256"))]
use trussed::client::CryptoClient;
use trussed::key::Kind;
use trussed::syscall;
use trussed::types::{Location::*, Mechanism, SignatureSerialization};
use trussed::types::Location;
use trussed_staging::virt::with_ram_client;
use trussed::client::P256;
use trussed_staging::hmacsha256p256::HmacSha256P256Client;
#[test]
fn hmac_inject_any() {
with_ram_client("staging-tests", |mut client| {
let client = &mut client;
let key = syscall!(client.inject_any_key(
b"12345678123456781234567812345678",
Volatile,
Kind::P256
))
.key
.unwrap();
let pk = syscall!(client.derive_p256_public_key(key, Location::Volatile)).key;
let signature =
syscall!(client.sign(Mechanism::P256, key, &[], SignatureSerialization::Raw)).signature;
assert!(signature.len() > 0);
todo!();
});
}