Update to heapless 0.9

This commit is contained in:
Sosthène Guédon
2026-03-23 14:07:04 +01:00
committed by Robin Krahl
parent 8faf830ea7
commit c36376c45f
18 changed files with 121 additions and 70 deletions
+11 -5
View File
@@ -54,16 +54,18 @@ trussed-hpke = { version = "0.2.0", optional = true }
trussed-manage = { version = "0.2.1", optional = true }
trussed-wrap-key-to-file = { version = "0.2.0", optional = true }
trussed-fs-info = { version = "0.2.0", optional = true }
heapless = { version = "0.9.1", optional = true }
heapless-bytes = { version = "0.5.0", optional = true }
[dev-dependencies]
hex-literal = "0.4.0"
hmac = "0.12.0"
trussed = { version = "0.1.0", default-features = false, features = ["aes256-cbc", "hmac-sha256", "virt", "x255"] }
trussed = { version = "0.1.0", default-features = false, features = ["aes256-cbc", "crypto-client", "filesystem-client", "hmac-sha256", "virt", "x255"] }
[features]
default = []
default = ["chunked"]
chunked = ["trussed-chunked", "chacha20poly1305/stream"]
chunked = ["trussed-chunked", "chacha20poly1305/stream", "dep:heapless", "dep:heapless-bytes"]
hkdf = ["trussed-hkdf", "dep:hkdf", "dep:sha2"]
hpke = ["trussed-hpke", "dep:hkdf", "dep:sha2", "dep:hex-literal", "dep:aead", "dep:chacha20poly1305"]
manage = ["trussed-manage"]
@@ -83,8 +85,12 @@ log-warn = []
log-error = []
[patch.crates-io]
trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "43ed1efcb19dc9c8bee45d4a1d3ad7dee2bba5ae" }
trussed-core = { git = "https://github.com/trussed-dev/trussed.git", rev = "43ed1efcb19dc9c8bee45d4a1d3ad7dee2bba5ae"}
trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "1e7b09a983dc8ae64a7ad8401ce541a9a77e5939" }
trussed-core = { git = "https://github.com/trussed-dev/trussed.git", rev = "1e7b09a983dc8ae64a7ad8401ce541a9a77e5939"}
littlefs2 = { git = "https://github.com/trussed-dev/littlefs2.git", rev = "e9d3a1ca98f80e92cd20ee9b94707067810b9036" }
littlefs2-core = { git = "https://github.com/trussed-dev/littlefs2.git", rev = "e9d3a1ca98f80e92cd20ee9b94707067810b9036" }
littlefs2-sys = { git = "https://github.com/trussed-dev/littlefs2-sys", rev = "v0.3.1-nitrokey.1" }
trussed-chunked = { path = "extensions/chunked" }
trussed-hkdf = { path = "extensions/hkdf" }
trussed-hpke = { path = "extensions/hpke" }
+3 -3
View File
@@ -10,9 +10,9 @@ pub mod utils;
use serde::{Deserialize, Serialize};
use serde_byte_array::ByteArray;
use trussed_core::{
client::FilesystemClient,
serde_extensions::{Extension, ExtensionClient, ExtensionResult},
types::{KeyId, Location, Message, PathBuf, UserAttribute},
FilesystemClient,
};
pub const CHACHA8_STREAM_NONCE_LEN: usize = 8;
@@ -57,8 +57,8 @@ pub mod request {
use super::*;
use serde::{Deserialize, Serialize};
use serde_byte_array::ByteArray;
use trussed_core::error::Error;
use trussed_core::types::{KeyId, Location, Message, PathBuf, UserAttribute};
use trussed_core::Error;
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct ReadChunk {}
@@ -263,8 +263,8 @@ pub mod request {
pub mod reply {
use super::*;
use serde::{Deserialize, Serialize};
use trussed_core::error::Error;
use trussed_core::types::Message;
use trussed_core::Error;
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct ReadChunk {
+3 -3
View File
@@ -3,9 +3,9 @@
use serde_byte_array::ByteArray;
use trussed_core::{
error::Error,
syscall, try_syscall,
types::{KeyId, Location, Message, PathBuf, UserAttribute},
Error,
};
use crate::{ChunkedClient, CHACHA8_STREAM_NONCE_LEN};
@@ -27,7 +27,7 @@ pub fn write_all(
user_attribute: Option<UserAttribute>,
encryption: Option<EncryptionData>,
) -> Result<(), Error> {
if let (Ok(msg), None) = (Message::from_slice(data), encryption) {
if let (Ok(msg), None) = (Message::try_from(data), encryption) {
// Fast path for small files
try_syscall!(client.write_file(location, path, msg, user_attribute))?;
Ok(())
@@ -63,7 +63,7 @@ fn write_chunked_inner(
let msg = Message::new();
let chunk_size = msg.capacity();
let chunks = data.chunks(chunk_size).map(|chunk| {
Message::from_slice(chunk).expect("Iteration over chunks yields maximum of chunk_size")
Message::try_from(chunk).expect("Iteration over chunks yields maximum of chunk_size")
});
if let Some(encryption_data) = encryption {
try_syscall!(client.start_encrypted_chunked_write(
+1 -1
View File
@@ -7,9 +7,9 @@
use serde::{Deserialize, Serialize};
use trussed_core::{
error::Error,
serde_extensions::{Extension, ExtensionClient, ExtensionResult},
types::Location,
Error,
};
pub struct FsInfoExtension;
+1 -1
View File
@@ -8,9 +8,9 @@
use serde::{Deserialize, Serialize};
use trussed_core::{
config::MAX_MEDIUM_DATA_LENGTH,
error::Error,
serde_extensions::{Extension, ExtensionClient, ExtensionResult},
types::{Bytes, KeyId, Location, Message},
Error,
};
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+1 -1
View File
@@ -11,9 +11,9 @@
use serde::{Deserialize, Serialize};
use serde_byte_array::ByteArray;
use trussed_core::error::Error;
use trussed_core::serde_extensions::{Extension, ExtensionClient, ExtensionResult};
use trussed_core::types::{KeyId, Location, Message, PathBuf, ShortData};
use trussed_core::Error;
#[derive(Deserialize, Serialize)]
pub enum HpkeRequest {
+1 -1
View File
@@ -8,8 +8,8 @@
use littlefs2_core::{path, Path, PathBuf};
use serde::{Deserialize, Serialize};
use trussed_core::{
error::Error,
serde_extensions::{Extension, ExtensionClient, ExtensionResult},
Error,
};
pub struct ManageExtension;
+5 -5
View File
@@ -7,9 +7,9 @@
use serde::{Deserialize, Serialize};
use trussed_core::{
client::ClientError,
serde_extensions::{Extension, ExtensionClient, ExtensionResult},
types::{Bytes, KeyId, Location, Mechanism, PathBuf},
ClientError,
};
#[derive(Debug, Default)]
@@ -25,8 +25,8 @@ pub enum WrapKeyToFileRequest {
pub mod request {
use super::*;
use serde::{Deserialize, Serialize};
use trussed_core::error::Error;
use trussed_core::types::{KeyId, Location, Mechanism, Message, PathBuf};
use trussed_core::Error;
#[derive(Debug, Deserialize, Serialize)]
pub struct WrapKeyToFile {
@@ -90,7 +90,7 @@ pub enum WrapKeyToFileReply {
pub mod reply {
use serde::{Deserialize, Serialize};
use trussed_core::{error::Error, types::KeyId};
use trussed_core::{types::KeyId, Error};
use super::*;
@@ -156,7 +156,7 @@ pub trait WrapKeyToFileClient: ExtensionClient<WrapKeyToFileExtension> {
associated_data: &[u8],
) -> WrapKeyToFileResult<'_, reply::WrapKeyToFile, Self> {
let associated_data =
Bytes::from_slice(associated_data).map_err(|_| ClientError::DataTooLarge)?;
Bytes::try_from(associated_data).map_err(|_| ClientError::DataTooLarge)?;
self.extension(request::WrapKeyToFile {
mechanism,
wrapping_key,
@@ -180,7 +180,7 @@ pub trait WrapKeyToFileClient: ExtensionClient<WrapKeyToFileExtension> {
associated_data: &[u8],
) -> WrapKeyToFileResult<'_, reply::UnwrapKeyFromFile, Self> {
let associated_data =
Bytes::from_slice(associated_data).map_err(|_| ClientError::DataTooLarge)?;
Bytes::try_from(associated_data).map_err(|_| ClientError::DataTooLarge)?;
self.extension(request::UnwrapKeyFromFile {
mechanism,
key,
+54 -9
View File
@@ -5,6 +5,7 @@ mod store;
use store::OpenSeekFrom;
use chacha20poly1305::{
aead,
aead::stream::{DecryptorLE31, EncryptorLE31, Nonce as StreamNonce, StreamLE31},
ChaCha8Poly1305, KeyInit,
};
@@ -27,6 +28,38 @@ use crate::StagingContext;
const POLY1305_TAG_LEN: usize = 16;
const CHACHA8_KEY_LEN: usize = 32;
struct HeaplessBuffer<'a, LenT: heapless::LenType>(&'a mut heapless_bytes::BytesView<LenT>);
impl<'a, LenT: heapless::LenType, S: heapless_bytes::BytesStorage + ?Sized>
From<&'a mut heapless_bytes::BytesInner<LenT, S>> for HeaplessBuffer<'a, LenT>
{
fn from(value: &'a mut heapless_bytes::BytesInner<LenT, S>) -> Self {
Self(value.as_mut_view())
}
}
impl<'a, LenT: heapless::LenType> AsMut<[u8]> for HeaplessBuffer<'a, LenT> {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl<'a, LenT: heapless::LenType> AsRef<[u8]> for HeaplessBuffer<'a, LenT> {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<'a, LenT: heapless::LenType> aead::Buffer for HeaplessBuffer<'a, LenT> {
fn extend_from_slice(&mut self, other: &[u8]) -> aead::Result<()> {
self.0.extend_from_slice(other).map_err(|_| aead::Error)
}
fn truncate(&mut self, len: usize) {
self.0.truncate(len);
}
}
#[derive(Debug)]
pub struct ChunkedReadState {
pub path: PathBuf,
@@ -209,7 +242,7 @@ impl ExtensionImpl<ChunkedExtension> for super::StagingBackend {
let nonce: Bytes<CHACHA8_STREAM_NONCE_LEN> =
filestore.read(&request.path, request.location)?;
let nonce: &StreamNonce<ChaCha8Poly1305, StreamLE31<ChaCha8Poly1305>> =
(&**nonce).into();
(&*nonce).into();
let aead = ChaCha8Poly1305::new((&*key.material).into());
let decryptor = DecryptorLE31::<ChaCha8Poly1305>::from_aead(aead, nonce);
backend_ctx.chunked_io_state =
@@ -263,10 +296,13 @@ fn write_chunk(
}
Some(ChunkedIoState::EncryptedWrite(ref mut write_state)) => {
let mut data =
Bytes::<{ MAX_MESSAGE_LENGTH + POLY1305_TAG_LEN }>::from_slice(data).unwrap();
Bytes::<{ MAX_MESSAGE_LENGTH + POLY1305_TAG_LEN }>::try_from(&**data).unwrap();
write_state
.encryptor
.encrypt_next_in_place(write_state.path.as_ref().as_bytes(), &mut *data)
.encrypt_next_in_place(
write_state.path.as_ref().as_bytes(),
&mut HeaplessBuffer::from(&mut data),
)
.map_err(|_err| {
error!("Failed to encrypt {:?}", _err);
Error::AeadError
@@ -303,10 +339,13 @@ fn write_last_chunk(
}
Some(ChunkedIoState::EncryptedWrite(write_state)) => {
let mut data =
Bytes::<{ MAX_MESSAGE_LENGTH + POLY1305_TAG_LEN }>::from_slice(data).unwrap();
Bytes::<{ MAX_MESSAGE_LENGTH + POLY1305_TAG_LEN }>::try_from(&**data).unwrap();
write_state
.encryptor
.encrypt_last_in_place(&[write_state.location as u8], &mut *data)
.encrypt_last_in_place(
&[write_state.location as u8],
&mut HeaplessBuffer::from(&mut data),
)
.map_err(|_err| {
error!("Failed to encrypt {:?}", _err);
Error::AeadError
@@ -354,12 +393,15 @@ fn read_encrypted_chunk(
read_state
.decryptor
.decrypt_last_in_place(&[read_state.location as u8], &mut *data)
.decrypt_last_in_place(
&[read_state.location as u8],
&mut HeaplessBuffer::from(&mut data),
)
.map_err(|_err| {
error!("Failed to decrypt {:?}", _err);
Error::AeadError
})?;
let data = Bytes::from_slice(&data).expect("decryptor removes the tag");
let data = Bytes::try_from(&*data).expect("decryptor removes the tag");
Ok(reply::ReadChunk {
data,
len: chunked_decrypted_len(len)?,
@@ -368,12 +410,15 @@ fn read_encrypted_chunk(
} else {
read_state
.decryptor
.decrypt_next_in_place(read_state.path.as_ref().as_bytes(), &mut *data)
.decrypt_next_in_place(
read_state.path.as_ref().as_bytes(),
&mut HeaplessBuffer::from(&mut data),
)
.map_err(|_err| {
error!("Failed to decrypt {:?}", _err);
Error::AeadError
})?;
let data = Bytes::from_slice(&data).expect("decryptor removes the tag");
let data = Bytes::try_from(&*data).expect("decryptor removes the tag");
Ok(reply::ReadChunk {
data,
len: chunked_decrypted_len(len)?,
+1 -1
View File
@@ -39,7 +39,7 @@ pub fn fs_read_chunk<const N: usize>(
if length > contents.capacity() {
return Err(Error::FilesystemReadFailure);
}
contents.resize_default(length).unwrap();
contents.resize_zero(length).unwrap();
let file_len = fs
.open_file_and_then(path, &mut |file| {
file.seek(pos.into())?;
+3 -3
View File
@@ -47,7 +47,7 @@ fn get_mat<S: Store>(
warn!("Attempt to HKDF on a private key");
return Err(Error::MechanismInvalid);
}
Bytes::from_slice(&key_mat.material).map_err(|_| {
Bytes::try_from(&*key_mat.material).map_err(|_| {
warn!("Attempt to HKDF a too large key");
Error::InternalError
})?
@@ -65,7 +65,7 @@ fn extract<S: Store>(
.as_ref()
.map(|s| get_mat(s, keystore))
.transpose()?;
let salt_ref = salt.as_deref().map(|d| &**d);
let salt_ref = salt.as_deref().map(|d| &*d);
let (prk, _) = Hkdf::<Sha256>::extract(salt_ref, &ikm);
assert_eq!(prk.len(), 256 / 8);
let key_id = keystore.store_key(
@@ -91,7 +91,7 @@ fn expand<S: Store>(
Error::InternalError
})?;
let mut okm = ShortData::new();
okm.resize_default(req.len).map_err(|_| {
okm.resize_zero(req.len).map_err(|_| {
error!("Attempt to run HKDF with too large output");
Error::WrongMessageLength
})?;
+3 -3
View File
@@ -367,7 +367,7 @@ impl ExtensionImpl<HpkeExtension> for StagingBackend {
// TODO: need to check both secret and public keys
let serialized_key =
keystore.load_key(key::Secrecy::Secret, None, &req.key_to_seal)?;
let mut message = Message::from_slice(&serialized_key.serialize()).unwrap();
let mut message = Message::try_from(&*serialized_key.serialize()).unwrap();
let public_key = load_public_key(&req.public_key, keystore)?;
@@ -392,8 +392,8 @@ impl ExtensionImpl<HpkeExtension> for StagingBackend {
// TODO: need to check both secret and public keys
let serialized_key =
keystore.load_key(key::Secrecy::Secret, None, &req.key_to_seal)?;
let mut message = Bytes::<{ MAX_SERIALIZED_KEY_LENGTH + 32 + 16 }>::from_slice(
&serialized_key.serialize(),
let mut message = Bytes::<{ MAX_SERIALIZED_KEY_LENGTH + 32 + 16 }>::try_from(
&*serialized_key.serialize(),
)
.unwrap();
+2 -2
View File
@@ -37,9 +37,9 @@ fn wrap_key_to_file(
let serialized_key = keystore.load_key(Secrecy::Secret, None, &request.key)?;
let mut data = Bytes::<WRAPPED_TO_FILE_LEN>::from_slice(&serialized_key.serialize()).unwrap();
let mut data = Bytes::<WRAPPED_TO_FILE_LEN>::try_from(&*serialized_key.serialize()).unwrap();
let material_len = data.len();
data.resize_default(material_len + NONCE_LEN).unwrap();
data.resize_zero(material_len + NONCE_LEN).unwrap();
let (material, nonce) = data.split_at_mut(material_len);
keystore.rng().fill_bytes(nonce);
let nonce = (&*nonce).try_into().unwrap();
+4 -4
View File
@@ -59,7 +59,7 @@ fn filesystem() {
.metadata
.is_none(),);
let data = Bytes::from_slice(b"test data").unwrap();
let data = Bytes::try_from(b"test data").unwrap();
syscall!(client.write_file(
Location::Internal,
PathBuf::from(path!("test_file")),
@@ -82,9 +82,9 @@ fn filesystem() {
assert!(empty_data.data.is_empty());
assert_eq!(empty_data.len, data.len());
let large_data = Bytes::from_slice(&[0; 1024]).unwrap();
let large_data2 = Bytes::from_slice(&[1; 1024]).unwrap();
let more_data = Bytes::from_slice(&[2; 42]).unwrap();
let large_data = Bytes::try_from(&[0; 1024]).unwrap();
let large_data2 = Bytes::try_from(&[1; 1024]).unwrap();
let more_data = Bytes::try_from(&[2; 42]).unwrap();
// ======== CHUNKED WRITES ========
syscall!(client.start_chunked_write(
Location::Internal,
+3 -3
View File
@@ -87,9 +87,9 @@ fn encrypted_filesystem() {
.is_none(),
);
let large_data = Bytes::from_slice(&[0; 1024]).unwrap();
let large_data2 = Bytes::from_slice(&[1; 1024]).unwrap();
let more_data = Bytes::from_slice(&[2; 42]).unwrap();
let large_data = Bytes::try_from(&[0; 1024]).unwrap();
let large_data2 = Bytes::try_from(&[1; 1024]).unwrap();
let more_data = Bytes::try_from(&[2; 42]).unwrap();
// ======== CHUNKED WRITES ========
syscall!(client.start_encrypted_chunked_write(
Location::Internal,
+3 -3
View File
@@ -30,14 +30,14 @@ fn hkdf() {
mac.update(MSG);
virt::with_client(StoreConfig::ram(), "hkdf_test", |mut client| {
let prk = syscall!(client.hkdf_extract(
Data(Bytes::from_slice(IKM).unwrap()),
Some(Data(Bytes::from_slice(SALT).unwrap())),
Data(Bytes::try_from(IKM).unwrap()),
Some(Data(Bytes::try_from(SALT).unwrap())),
Location::External,
))
.okm;
let expanded = syscall!(client.hkdf_expand(
prk,
Bytes::from_slice(INFO).unwrap(),
Bytes::try_from(INFO).unwrap(),
16,
Location::Volatile
))
+7 -7
View File
@@ -41,9 +41,9 @@ fn hpke_message() {
let public_key =
syscall!(client.derive_x255_public_key(secret_key, Location::Volatile)).key;
let pl = Bytes::from_slice(b"Plaintext").unwrap();
let aad = Bytes::from_slice(b"AAD").unwrap();
let info = Bytes::from_slice(b"INFO").unwrap();
let pl = Bytes::try_from(b"Plaintext").unwrap();
let aad = Bytes::try_from(b"AAD").unwrap();
let info = Bytes::try_from(b"INFO").unwrap();
let seal = syscall!(client.hpke_seal(
public_key,
pl.clone(),
@@ -69,8 +69,8 @@ fn hpke_wrap_key() {
let key_to_wrap = syscall!(client.generate_secret_key(32, Location::Volatile)).key;
let aad = Bytes::from_slice(b"AAD").unwrap();
let info = Bytes::from_slice(b"INFO").unwrap();
let aad = Bytes::try_from(b"AAD").unwrap();
let info = Bytes::try_from(b"INFO").unwrap();
let seal =
syscall!(client.hpke_seal_key(public_key, key_to_wrap, aad.clone(), info.clone()));
@@ -96,8 +96,8 @@ fn hpke_wrap_key_to_file() {
let key_to_wrap = syscall!(client.generate_secret_key(32, Location::Volatile)).key;
let path = path!("WRAPPED_KEY");
let aad = Bytes::from_slice(b"AAD").unwrap();
let info = Bytes::from_slice(b"INFO").unwrap();
let aad = Bytes::try_from(b"AAD").unwrap();
let info = Bytes::try_from(b"INFO").unwrap();
syscall!(client.hpke_seal_key_to_file(
path.into(),
Location::Volatile,
+15 -15
View File
@@ -27,56 +27,56 @@ fn device_factory_reset() {
syscall!(client1.write_file(
Location::Internal,
path!("to_save_internal").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None,
));
syscall!(client1.write_file(
Location::External,
path!("to_save_external").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None,
));
syscall!(client1.write_file(
Location::Volatile,
path!("to_save_volatile").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None
));
syscall!(client1.write_file(
Location::Internal,
path!("to_delete_internal").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None,
));
syscall!(client1.write_file(
Location::External,
path!("to_delete_external").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None,
));
syscall!(client1.write_file(
Location::Volatile,
path!("to_delete_volatile").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None
));
syscall!(client2.write_file(
Location::Internal,
path!("to_delete_internal").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None,
));
syscall!(client2.write_file(
Location::External,
path!("to_delete_external").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None,
));
syscall!(client2.write_file(
Location::Volatile,
path!("to_delete_volatile").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None
));
@@ -140,37 +140,37 @@ fn client_factory_reset() {
syscall!(client1.write_file(
Location::Internal,
path!("to_save_internal").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None,
));
syscall!(client1.write_file(
Location::External,
path!("to_save_external").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None,
));
syscall!(client1.write_file(
Location::Volatile,
path!("to_save_volatile").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None
));
syscall!(client2.write_file(
Location::Internal,
path!("to_delete_internal").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None,
));
syscall!(client2.write_file(
Location::External,
path!("to_delete_external").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None,
));
syscall!(client2.write_file(
Location::Volatile,
path!("to_delete_volatile").into(),
Bytes::from_slice(b"data").unwrap(),
Bytes::try_from(b"data").unwrap(),
None
));