mirror of
https://github.com/trussed-dev/trussed-staging.git
synced 2026-06-20 04:16:23 -07:00
Initial support for the key injection and derivation
These are the features required by Nitrokey Webcrypt.
This commit is contained in:
@@ -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"]
|
||||
|
||||
@@ -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 {}
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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!();
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user