diff --git a/Cargo.toml b/Cargo.toml index 57dc5f996cd..6a4cad0dda0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,6 @@ trussed-core = { version = "0.1.0" } # general bitflags = { version = "2.1" } # const-oid = "0.4.5" -cfg-if = "1.0" flexiber = { version = "0.1.0", features = ["derive", "heapless"] } generic-array = "0.14.4" heapless = { version = "0.7", features = ["serde"] } @@ -153,6 +152,11 @@ ui-client = ["trussed-core/ui-client"] test-attestation-cert-ids = [] +# If any PQC algorithm is set, it loads the dependency +mldsa44 = ["cosey/mldsa44", "trussed-core/mldsa44"] +mldsa65 = ["cosey/mldsa65", "trussed-core/mldsa65"] +mldsa87 = ["cosey/mldsa87", "trussed-core/mldsa87"] + [[test]] name = "aes256cbc" required-features = ["crypto-client", "default-mechanisms", "virt"] diff --git a/core/Cargo.toml b/core/Cargo.toml index 44c53137823..7584ac0dffb 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -54,5 +54,10 @@ totp = [] trng = [] x255 = [] +# PQC +mldsa44 = [] +mldsa65 = [] +mldsa87 = [] + [package.metadata.docs.rs] all-features = true diff --git a/core/src/config.rs b/core/src/config.rs index ca744a93e44..c0594355095 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -1,9 +1,5 @@ -pub const MAX_MESSAGE_LENGTH: usize = 1024; pub const MAX_MEDIUM_DATA_LENGTH: usize = 256; pub const MAX_SHORT_DATA_LENGTH: usize = 128; -pub const MAX_SIGNATURE_LENGTH: usize = 512 * 2; -// FIXME: Value from https://stackoverflow.com/questions/5403808/private-key-length-bytes for Rsa2048 Private key -pub const MAX_KEY_MATERIAL_LENGTH: usize = 1160 * 2 + 72; pub const MAX_USER_ATTRIBUTE_LENGTH: usize = 256; // request size is chosen to not exceed the largest standard syscall, Decrypt, so that the Request @@ -13,3 +9,54 @@ pub const SERDE_EXTENSION_REQUEST_LENGTH: usize = // reply size is chosen to not exceed the largest standard syscall, Encrypt, so that the Reply enum // does not grow from this variant pub const SERDE_EXTENSION_REPLY_LENGTH: usize = MAX_MESSAGE_LENGTH + 2 * MAX_SHORT_DATA_LENGTH; + +// Must be MAX_KEY_MATERIAL_LENGTH + 4 +// Note that this is not the serialized key material (e.g. serialized PKCS#8), but +// the internal Trussed serialization that adds flags and such +pub const MAX_SERIALIZED_KEY_LENGTH: usize = MAX_KEY_MATERIAL_LENGTH + 4; + +// For the PQC algorithms, public and private key are generated at the same time and stored together as +// the private key. Then in the derive call, it just pulls the public key from the private key store +// and re-saves it as a public-only key. Therefore, the max material length is both keys together, plus +// the PKCS8 DER encoding overhead (31 bytes). + +pub const MAX_SIGNATURE_LENGTH: usize = if cfg!(feature = "mldsa87") { + 4627 +} else if cfg!(feature = "mldsa65") { + 3309 +} else if cfg!(feature = "mldsa44") { + 2420 +} else { + // Default from before addition of PQC + 512 * 2 +}; + +pub const MAX_KEY_MATERIAL_LENGTH: usize = if cfg!(feature = "mldsa87") { + 2592 // Public key + + 4896 // Private key + + 31 +} else if cfg!(feature = "mldsa65") { + 1952 // Public key + + 4032 // Private key + + 31 +} else if cfg!(feature = "mldsa44") { + 1312 // Public key + + 2560 // Private key + + 31 +} else { + // FIXME: Value from https://stackoverflow.com/questions/5403808/private-key-length-bytes for Rsa2048 Private key + 1160 * 2 + 72 +}; + +pub const MAX_FIDO_WRAPPED_KEY_LENGTH: usize = + if cfg!(feature = "mldsa87") || cfg!(feature = "mldsa65") || cfg!(feature = "mldsa44") { + MAX_SERIALIZED_KEY_LENGTH + 57 + } else { + // Default from before addition of PQC + 128 + }; + +// 30 bytes are added by CBOR serialization of a FullCredential +// TODO: This was calculated by debugging and finding each location where this variable needed to be larger for one reason or another. +// Update this to use different consts for each area where this is needed, instead of one const used everywhere. +pub const MAX_MESSAGE_LENGTH: usize = MAX_FIDO_WRAPPED_KEY_LENGTH + 30 + 2031 + 32 + 37; diff --git a/core/src/types.rs b/core/src/types.rs index 8ae8caf0b5f..e3108e0a57c 100644 --- a/core/src/types.rs +++ b/core/src/types.rs @@ -9,7 +9,7 @@ pub use littlefs2_core::{DirEntry, Metadata, PathBuf}; #[cfg(feature = "crypto-client")] use crate::api::{reply, request}; use crate::config::{ - MAX_KEY_MATERIAL_LENGTH, MAX_MEDIUM_DATA_LENGTH, MAX_MESSAGE_LENGTH, MAX_SHORT_DATA_LENGTH, + MAX_MEDIUM_DATA_LENGTH, MAX_MESSAGE_LENGTH, MAX_SERIALIZED_KEY_LENGTH, MAX_SHORT_DATA_LENGTH, MAX_SIGNATURE_LENGTH, MAX_USER_ATTRIBUTE_LENGTH, }; @@ -49,7 +49,7 @@ pub mod reboot { pub type Message = Bytes; pub type MediumData = Bytes; pub type ShortData = Bytes; -pub type SerializedKey = Bytes; +pub type SerializedKey = Bytes; pub type Signature = Bytes; pub type UserAttribute = Bytes; @@ -594,6 +594,14 @@ generate_mechanism! { Rsa3072Pkcs1v15, #[cfg(feature = "rsa4096")] Rsa4096Pkcs1v15, + + // Post-Quantum Cryptography + #[cfg(feature = "mldsa44")] + Mldsa44, + #[cfg(feature = "mldsa65")] + Mldsa65, + #[cfg(feature = "mldsa87")] + Mldsa87, } } diff --git a/src/config.rs b/src/config.rs index 32348b2bb18..35d6c30d3dd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,12 +2,9 @@ // Should we use the "config crate that can have a replacement patched in" idea? pub use trussed_core::config::{ - MAX_KEY_MATERIAL_LENGTH, MAX_MEDIUM_DATA_LENGTH, MAX_MESSAGE_LENGTH, MAX_SHORT_DATA_LENGTH, - MAX_SIGNATURE_LENGTH, MAX_USER_ATTRIBUTE_LENGTH, SERDE_EXTENSION_REPLY_LENGTH, - SERDE_EXTENSION_REQUEST_LENGTH, + MAX_KEY_MATERIAL_LENGTH, MAX_MEDIUM_DATA_LENGTH, MAX_MESSAGE_LENGTH, MAX_SERIALIZED_KEY_LENGTH, + MAX_SHORT_DATA_LENGTH, MAX_SIGNATURE_LENGTH, MAX_USER_ATTRIBUTE_LENGTH, + SERDE_EXTENSION_REPLY_LENGTH, SERDE_EXTENSION_REQUEST_LENGTH, }; -// must be MAX_KEY_MATERIAL_LENGTH + 4 -pub const MAX_SERIALIZED_KEY_LENGTH: usize = MAX_KEY_MATERIAL_LENGTH + 4; - pub const USER_ATTRIBUTE_NUMBER: u8 = 37; diff --git a/src/key.rs b/src/key.rs index b071d324a9c..2443ceb1b28 100644 --- a/src/key.rs +++ b/src/key.rs @@ -6,12 +6,12 @@ use serde::{de::Visitor, ser::SerializeMap, Deserialize, Serialize}; use zeroize::Zeroize; pub use crate::Bytes; -use crate::{ - config::{MAX_KEY_MATERIAL_LENGTH, MAX_SERIALIZED_KEY_LENGTH}, - Error, -}; +use crate::Error; +use trussed_core::config::MAX_SERIALIZED_KEY_LENGTH; -pub type Material = Vec; +// Keys are often stored in serialized format (e.g. PKCS#8 used by the RSA backend), +// so material max length must be serialized max length. +pub type Material = Vec; pub type SerializedKeyBytes = Vec; // We don't implement serde to make sure nobody inadvertently still uses it @@ -77,6 +77,13 @@ pub enum Kind { BrainpoolP512R1, X255, Secp256k1, + // Post-quantum cryptography algorithms + #[cfg(feature = "mldsa44")] + Mldsa44, + #[cfg(feature = "mldsa65")] + Mldsa65, + #[cfg(feature = "mldsa87")] + Mldsa87, } bitflags::bitflags! { @@ -223,6 +230,12 @@ impl Kind { Kind::BrainpoolP384R1 => 13, Kind::BrainpoolP512R1 => 14, Kind::Secp256k1 => 15, + #[cfg(feature = "mldsa44")] + Kind::Mldsa44 => 16, + #[cfg(feature = "mldsa65")] + Kind::Mldsa65 => 17, + #[cfg(feature = "mldsa87")] + Kind::Mldsa87 => 18, } } @@ -243,6 +256,12 @@ impl Kind { 13 => Kind::BrainpoolP384R1, 14 => Kind::BrainpoolP512R1, 15 => Kind::Secp256k1, + #[cfg(feature = "mldsa44")] + 16 => Kind::Mldsa44, + #[cfg(feature = "mldsa65")] + 17 => Kind::Mldsa65, + #[cfg(feature = "mldsa87")] + 18 => Kind::Mldsa87, _ => return Err(Error::InvalidSerializedKey), }) } diff --git a/src/store/keystore.rs b/src/store/keystore.rs index 47417c6105e..94f7db14166 100644 --- a/src/store/keystore.rs +++ b/src/store/keystore.rs @@ -2,13 +2,13 @@ use littlefs2_core::{path, PathBuf}; use rand_chacha::ChaCha8Rng; use crate::{ - config::MAX_KEY_MATERIAL_LENGTH, error::{Error, Result}, key, store::{self, Store}, types::{KeyId, Location}, Bytes, }; +use trussed_core::config::MAX_SERIALIZED_KEY_LENGTH; pub type ClientId = PathBuf; @@ -181,7 +181,7 @@ impl Keystore for ClientKeystore { let location = self.location(secrecy, id).ok_or(Error::NoSuchKey)?; - let bytes: Bytes<{ MAX_KEY_MATERIAL_LENGTH }> = store::read(&self.store, location, &path)?; + let bytes: Bytes<{ MAX_SERIALIZED_KEY_LENGTH }> = store::read(&self.store, location, &path)?; let key = key::Key::try_deserialize(&bytes)?;