From 215cf51a5e079c3c8103bb2b79897f46b0d869f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Tue, 13 Feb 2024 11:47:54 +0100 Subject: [PATCH 1/2] Use `#[serde(rename)]` to reduce the size taken by serialized data --- Cargo.toml | 4 + src/backend/data.rs | 199 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 199 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2c1bc30..3d3430b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,11 @@ littlefs2 = "0.4.0" [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"] } admin-app = { version = "0.1.0", features = ["migration-tests"] } +serde_cbor = { version = "0.11.2", features = ["std"] } +hex-literal = "0.4.1" [patch.crates-io] littlefs2 = { git = "https://github.com/sosthene-nitrokey/littlefs2.git", rev = "2b45a7559ff44260c6dd693e4cb61f54ae5efc53" } @@ -35,3 +38,4 @@ trussed-manage = { git = "https://github.com/trussed-dev/trussed-staging.git", t apdu-dispatch = { git = "https://github.com/trussed-dev/apdu-dispatch.git", rev = "915fc237103fcecc29d0f0b73391f19abf6576de" } ctaphid-dispatch = { git = "https://github.com/trussed-dev/ctaphid-dispatch.git", rev = "57cb3317878a8593847595319aa03ef17c29ec5b" } admin-app = { git = "https://github.com/Nitrokey/admin-app.git", tag = "v0.1.0-nitrokey.12" } +cbor-smol = { git = "https://github.com/Nitrokey/cbor-smol.git", tag = "v0.4.0-nitrokey.2" } diff --git a/src/backend/data.rs b/src/backend/data.rs index 5e79feb..6d67f61 100644 --- a/src/backend/data.rs +++ b/src/backend/data.rs @@ -106,24 +106,30 @@ pub(crate) type Key = ByteArray; /// to_presistent_storage(salt, wrapped_key); /// } /// ```` -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] struct WrappedKeyData { + #[serde(rename = "w", alias = "wrapped_key")] wrapped_key: Key, + #[serde(rename = "t", alias = "tag")] tag: ChaChaTag, } -#[derive(Debug, Deserialize, Serialize)] +// No need for using SerializeIndexed, cbor_smol already does it for enums +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] enum KeyOrHash { Key(WrappedKeyData), Hash(Hash), } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] pub(crate) struct PinData { #[serde(skip)] id: PinId, + #[serde(rename = "r", alias = "retries")] retries: Option, + #[serde(rename = "s", alias = "salt")] salt: Salt, + #[serde(rename = "d", alias = "data")] data: KeyOrHash, } @@ -438,9 +444,11 @@ impl Deref for PinDataMut<'_> { } } -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)] struct Retries { + #[serde(rename = "m", alias = "max")] max: u8, + #[serde(rename = "l", alias = "left")] left: u8, } @@ -580,4 +588,187 @@ mod tests { let serialized = trussed::cbor_serialize_bytes::<_, 1024>(&salt).unwrap(); assert!(serialized.len() <= SALT_LEN + 1, "{}", serialized.len()); } + + #[test] + fn index_serialization() { + use serde_test::{assert_de_tokens, assert_tokens, Token}; + + let data = PinData { + id: PinId::from(0), + retries: None, + salt: [0xFE; SALT_LEN].into(), + data: KeyOrHash::Hash([0xED; HASH_LEN].into()), + }; + + assert_tokens( + &data, + &[ + Token::Struct { + name: "PinData", + len: 3, + }, + Token::Str("r"), + Token::None, + Token::Str("s"), + Token::Bytes(&[0xFE; SALT_LEN]), + Token::Str("d"), + Token::NewtypeVariant { + name: "KeyOrHash", + variant: "Hash", + }, + Token::Bytes(&[0xED; HASH_LEN]), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &data, + &[ + Token::Map { len: Some(3) }, + Token::Str("retries"), + Token::None, + Token::Str("salt"), + Token::Bytes(&[0xFE; SALT_LEN]), + Token::Str("data"), + Token::Enum { name: "KeyOrHash" }, + Token::U64(1), + Token::Bytes(&[0xED; HASH_LEN]), + Token::MapEnd, + ], + ); + + let data = PinData { + id: PinId::from(0), + retries: Some(Retries { max: 3, left: 2 }), + salt: [0xFE; SALT_LEN].into(), + data: KeyOrHash::Hash([0xDE; HASH_LEN].into()), + }; + + assert_tokens( + &data, + &[ + Token::Struct { + name: "PinData", + len: 3, + }, + Token::Str("r"), + Token::Some, + Token::Struct { + name: "Retries", + len: 2, + }, + Token::Str("m"), + Token::U8(3), + Token::Str("l"), + Token::U8(2), + Token::StructEnd, + Token::Str("s"), + Token::Bytes(&[0xFE; SALT_LEN]), + Token::Str("d"), + Token::NewtypeVariant { + name: "KeyOrHash", + variant: "Hash", + }, + Token::Bytes(&[0xDE; HASH_LEN]), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &data, + &[ + Token::Map { len: Some(3) }, + Token::Str("retries"), + Token::Some, + Token::Map { len: Some(2) }, + Token::Str("left"), + Token::U8(2), + Token::Str("max"), + Token::U8(3), + Token::MapEnd, + Token::Str("salt"), + Token::Bytes(&[0xFE; SALT_LEN]), + Token::Str("data"), + Token::Enum { name: "KeyOrHash" }, + Token::U64(1), + Token::Bytes(&[0xDE; HASH_LEN]), + Token::MapEnd, + ], + ); + + let data = PinData { + id: PinId::from(0), + retries: Some(Retries { max: 3, left: 2 }), + salt: [0xFE; SALT_LEN].into(), + data: KeyOrHash::Key(WrappedKeyData { + wrapped_key: [0xED; KEY_LEN].into(), + tag: [0xDC; CHACHA_TAG_LEN].into(), + }), + }; + + assert_tokens( + &data, + &[ + Token::Struct { + name: "PinData", + len: 3, + }, + Token::Str("r"), + Token::Some, + Token::Struct { + name: "Retries", + len: 2, + }, + Token::Str("m"), + Token::U8(3), + Token::Str("l"), + Token::U8(2), + Token::StructEnd, + Token::Str("s"), + Token::Bytes(&[0xFE; SALT_LEN]), + Token::Str("d"), + Token::NewtypeVariant { + name: "KeyOrHash", + variant: "Key", + }, + Token::Struct { + name: "WrappedKeyData", + len: 2, + }, + Token::Str("w"), + Token::Bytes(&[0xED; KEY_LEN]), + Token::Str("t"), + Token::Bytes(&[0xDC; CHACHA_TAG_LEN]), + Token::StructEnd, + Token::StructEnd, + ], + ); + + assert_de_tokens( + &data, + &[ + Token::Map { len: Some(3) }, + Token::Str("retries"), + Token::Some, + Token::Map { len: Some(2) }, + Token::Str("left"), + Token::U8(2), + Token::Str("max"), + Token::U8(3), + Token::MapEnd, + Token::Str("salt"), + Token::Bytes(&[0xFE; SALT_LEN]), + Token::Str("data"), + Token::Enum { name: "KeyOrHash" }, + Token::U64(0), + Token::Map { len: Some(2) }, + Token::Str("wrapped_key"), + Token::Bytes(&[0xED; KEY_LEN]), + Token::Str("tag"), + Token::Bytes(&[0xDC; CHACHA_TAG_LEN]), + Token::MapEnd, + Token::MapEnd, + ], + ); + } } From 9fdd9fd5033915dfdc4e56fe55a75e80e452f6cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 3 Apr 2024 09:23:42 +0200 Subject: [PATCH 2/2] Add changelog entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0e0041..fb69e93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased][] +- Use serde(rename) to save space on on the size of stored credentials ([#38][]) + +[#38]: https://github.com/trussed-dev/trussed-auth/pull/38 + [Unreleased]: https://github.com/trussed-dev/trussed-auth/compare/v0.3.0...HEAD ## [0.3.0][] - 2024-03-22