From 6b9e316f5e6dec1163f522d4e4610df203257529 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 22 Feb 2023 10:58:09 +0100 Subject: [PATCH] Improve byte array serialization Per default, byte arrays are serialized inefficiently by serde. serde-byte-array provides a wrapper type with a more efficient serialization format. Fixes https://github.com/trussed-dev/trussed-auth/issues/11 --- Cargo.toml | 1 + src/backend/data.rs | 29 +++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ce448ac..d03d0ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ description = "Authentication extension and backend for Trussed" [dependencies] serde = { version = "1", default-features = false } +serde-byte-array = "0.1.0" sha2 = { version = "0.10.6", default-features = false } subtle = { version = "2.4.1", default-features = false } trussed = { git = "https://github.com/trussed-dev/trussed", rev = "1c55b3b2dd6a9e1cfc55758635baf0d0bbf387d1", features = ["serde-extensions"] } diff --git a/src/backend/data.rs b/src/backend/data.rs index f5de032..c574fb8 100644 --- a/src/backend/data.rs +++ b/src/backend/data.rs @@ -4,6 +4,7 @@ use core::ops::Deref; use serde::{Deserialize, Serialize}; +use serde_byte_array::ByteArray; use sha2::{Digest as _, Sha256}; use subtle::ConstantTimeEq as _; use trussed::{ @@ -19,8 +20,8 @@ const SIZE: usize = 256; const SALT_LEN: usize = 16; const HASH_LEN: usize = 32; -type Salt = [u8; SALT_LEN]; -type Hash = [u8; HASH_LEN]; +type Salt = ByteArray; +type Hash = ByteArray; #[derive(Debug, Deserialize, Serialize)] pub(crate) struct PinData { @@ -37,7 +38,7 @@ impl PinData { R: CryptoRng + RngCore, { let mut salt = Salt::default(); - rng.fill_bytes(&mut salt); + rng.fill_bytes(salt.as_mut()); let hash = hash(id, pin, &salt); Self { id, @@ -111,7 +112,10 @@ impl<'a> PinDataMut<'a> { if self.is_blocked() { return false; } - let success = hash(self.id, pin, &self.salt).ct_eq(&self.hash).into(); + let success = hash(self.id, pin, &self.salt) + .as_ref() + .ct_eq(self.hash.as_ref()) + .into(); if let Some(retries) = &mut self.data.retries { if success { if retries.reset() { @@ -169,8 +173,8 @@ fn hash(id: PinId, pin: &Pin, salt: &Salt) -> Hash { digest.update([u8::from(id)]); digest.update([pin_len(pin)]); digest.update(pin); - digest.update(salt); - digest.finalize().into() + digest.update(salt.as_ref()); + Hash::new(digest.finalize().into()) } fn pin_len(pin: &Pin) -> u8 { @@ -191,10 +195,19 @@ mod tests { max: u8::MAX, left: u8::MAX, }), - salt: [u8::MAX; SALT_LEN], - hash: [u8::MAX; HASH_LEN], + salt: [u8::MAX; SALT_LEN].into(), + hash: [u8::MAX; HASH_LEN].into(), }; let serialized = trussed::cbor_serialize_bytes::<_, 1024>(&data).unwrap(); assert!(serialized.len() <= SIZE); } + + #[test] + #[allow(clippy::unwrap_used)] + fn test_salt_size() { + // We allow one byte overhead for byte array serialization + let salt = Salt::from([u8::MAX; SALT_LEN]); + let serialized = trussed::cbor_serialize_bytes::<_, 1024>(&salt).unwrap(); + assert!(serialized.len() <= SALT_LEN + 1, "{}", serialized.len()); + } }