From 2295ebc5256efc1e8eb3fd765b316fed48700cab 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] Use `#[serde(rename)]` to reduce the size taken by serialized data --- Cargo.toml | 9 +- src/backend/data.rs | 199 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 202 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 546e046..47db19f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,13 @@ 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"] } +hex-literal = "0.4.1" [patch.crates-io] -littlefs2 = { git = "https://github.com/Nitrokey/littlefs2", tag = "v0.3.2-nitrokey-2" } -trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "df720980888e3e0d5487250bd14a28db02a5f13b" } +littlefs2 = { git = "https://github.com/trussed-dev/littlefs2", rev = "ebd27e49ca321089d01d8c9b169c4aeb58ceeeca" } +trussed = { git = "https://github.com/Nitrokey/trussed.git", tag = "v0.1.0-nitrokey.17" } +serde-indexed = { git = "https://github.com/nitrokey/serde-indexed.git", rev = "37ba220526961fb8ba067d3fa18ee620337bb73b" } +cbor-smol = { git = "https://github.com/sosthene-nitrokey/cbor-smol.git", rev = "327f723d53263d4ac1da368660de4fe4aa8ebef8" } 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, + ], + ); + } }