Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ hmac = "0.12"
sha-1 = { version = "0.10", default-features = false, optional = true }
sha2 = { version = "0.10", default-features = false }

# PQC
pqcrypto-mldsa = { version = "0.1.0", optional = true }

# ours
cosey = "0.3"
delog = "0.1.0"
Expand Down Expand Up @@ -153,6 +156,11 @@ ui-client = ["trussed-core/ui-client"]

test-attestation-cert-ids = []

# If any PQC algorithm is set, it loads the dependency
mldsa44 = ["dep:pqcrypto-mldsa", "cosey/mldsa44", "trussed-core/mldsa44"]
mldsa65 = ["dep:pqcrypto-mldsa", "cosey/mldsa65", "trussed-core/mldsa65"]
mldsa87 = ["dep:pqcrypto-mldsa", "cosey/mldsa87", "trussed-core/mldsa87"]

[[test]]
name = "aes256cbc"
required-features = ["crypto-client", "default-mechanisms", "virt"]
Expand Down
9 changes: 9 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ license.workspace = true
repository.workspace = true

[dependencies]
cfg-if = "1.0"
heapless-bytes.workspace = true
littlefs2-core.workspace = true
postcard.workspace = true
rand_core.workspace = true
serde.workspace = true
serde-indexed = "0.1"

# PQC
pqcrypto-mldsa = { version = "0.1.0", optional = true }

[features]
serde-extensions = []

Expand Down Expand Up @@ -54,5 +58,10 @@ totp = []
trng = []
x255 = []

# PQC
mldsa44 = ["dep:pqcrypto-mldsa"]
mldsa65 = ["dep:pqcrypto-mldsa"]
mldsa87 = ["dep:pqcrypto-mldsa"]

[package.metadata.docs.rs]
all-features = true
48 changes: 44 additions & 4 deletions core/src/config.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -13,3 +9,47 @@ 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).
cfg_if::cfg_if! {
if #[cfg(feature = "mldsa87")] {
pub const MAX_SIGNATURE_LENGTH: usize = pqcrypto_mldsa::ffi::PQCLEAN_MLDSA87_CLEAN_CRYPTO_BYTES;
pub const MAX_KEY_MATERIAL_LENGTH: usize = pqcrypto_mldsa::ffi::PQCLEAN_MLDSA87_CLEAN_CRYPTO_PUBLICKEYBYTES
+ pqcrypto_mldsa::ffi::PQCLEAN_MLDSA87_CLEAN_CRYPTO_SECRETKEYBYTES
+ 31;
//pub const MAX_MESSAGE_LENGTH: usize = MAX_KEY_MATERIAL_LENGTH;
pub const MAX_FIDO_WRAPPED_KEY_LENGTH: usize = MAX_SERIALIZED_KEY_LENGTH + 57;
} else if #[cfg(feature = "mldsa65")] {
pub const MAX_SIGNATURE_LENGTH: usize = pqcrypto_mldsa::ffi::PQCLEAN_MLDSA65_CLEAN_CRYPTO_BYTES;
pub const MAX_KEY_MATERIAL_LENGTH: usize = pqcrypto_mldsa::ffi::PQCLEAN_MLDSA65_CLEAN_CRYPTO_PUBLICKEYBYTES
+ pqcrypto_mldsa::ffi::PQCLEAN_MLDSA65_CLEAN_CRYPTO_SECRETKEYBYTES
+ 31;
//pub const MAX_MESSAGE_LENGTH: usize = MAX_KEY_MATERIAL_LENGTH;
pub const MAX_FIDO_WRAPPED_KEY_LENGTH: usize = MAX_SERIALIZED_KEY_LENGTH + 57;
} else if #[cfg(feature = "mldsa44")] {
pub const MAX_SIGNATURE_LENGTH: usize = pqcrypto_mldsa::ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_BYTES;
pub const MAX_KEY_MATERIAL_LENGTH: usize = pqcrypto_mldsa::ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_PUBLICKEYBYTES
+ pqcrypto_mldsa::ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_SECRETKEYBYTES
+ 31;
//pub const MAX_MESSAGE_LENGTH: usize = MAX_KEY_MATERIAL_LENGTH;
pub const MAX_FIDO_WRAPPED_KEY_LENGTH: usize = MAX_SERIALIZED_KEY_LENGTH + 57;
} else {
// Default from before addition of PQC
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_MESSAGE_LENGTH: usize = 1024;
pub const MAX_FIDO_WRAPPED_KEY_LENGTH: usize = 128;
}
}

// 30 bytes are added by CBOR serialization of a FullCredential
pub const MAX_MESSAGE_LENGTH: usize = MAX_FIDO_WRAPPED_KEY_LENGTH + 30 + 2031 + 32 + 37; // TODO: update this to use different consts for each area where this is needed
12 changes: 10 additions & 2 deletions core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -49,7 +49,7 @@ pub mod reboot {
pub type Message = Bytes<MAX_MESSAGE_LENGTH>;
pub type MediumData = Bytes<MAX_MEDIUM_DATA_LENGTH>;
pub type ShortData = Bytes<MAX_SHORT_DATA_LENGTH>;
pub type SerializedKey = Bytes<MAX_KEY_MATERIAL_LENGTH>;
pub type SerializedKey = Bytes<MAX_SERIALIZED_KEY_LENGTH>;
pub type Signature = Bytes<MAX_SIGNATURE_LENGTH>;
pub type UserAttribute = Bytes<MAX_USER_ATTRIBUTE_LENGTH>;

Expand Down Expand Up @@ -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,
}
}

Expand Down
9 changes: 3 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
29 changes: 24 additions & 5 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8, { MAX_KEY_MATERIAL_LENGTH }>;
// 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<u8, { MAX_SERIALIZED_KEY_LENGTH }>;
Comment on lines +12 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be changed, max KEY_MATERIAL_LENGTH should reflect the proper length for key material data.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be an issue with the trussed-rsa-backend and fido-authenticator where it's using the wrong sizes for byte arrays. If you clone this repo, patch to use the local copy, and revert this (and the other reference) back to MAX_KEY_MATERIAL_LENGTH, you'll get all sorts of build errors when building nitrokey-3-firmware/runners/usbip.

cargo build --features provisioner

error[E0308]: mismatched types
   --> /mnt/c/Users/KyleKotowick/Documents/GitHub/sandbox-quantum/fido-authenticator/src/ctap1.rs:150:13
    |
145 |         Ok(register::Response::new(
    |            ----------------------- arguments to this function are incorrect
...
150 |             cert,
    |             ^^^^ expected `1024`, found `2258`
    |
    = note: expected struct `heapless_bytes::Bytes<1024>`
               found struct `heapless_bytes::Bytes<2258>`

cargo build --features provisioner,backend-mldsa44,backend-mldsa65,backend-mldsa87

error[E0308]: `?` operator has incompatible types
  --> /mnt/c/Users/KyleKotowick/Documents/GitHub/sandbox-quantum/trussed-rsa-backend/src/crypto_traits.rs:74:29
   |
74 |               serialized_key: key_parts.serialize().map_err(|_err| {
   |  _____________________________^
75 | |                 error!("Failed to serialize key parts: {:?}", _err);
76 | |                 ClientError::DataTooLarge
77 | |             })?,
   | |_______________^ expected `7523`, found `7519`
   |
   = note: `?` operator cannot convert from `heapless_bytes::Bytes<7519>` to `heapless_bytes::Bytes<7523>`
   = note: expected struct `heapless_bytes::Bytes<7523>`
              found struct `heapless_bytes::Bytes<7519>`

error[E0308]: mismatched types
   --> /mnt/c/Users/KyleKotowick/Documents/GitHub/sandbox-quantum/trussed-rsa-backend/src/lib.rs:215:30
    |
215 |     Ok(reply::SerializeKey { serialized_key })
    |                              ^^^^^^^^^^^^^^ expected `7523`, found `7519`
    |
    = note: expected struct `heapless_bytes::Bytes<7523>`
               found struct `heapless_bytes::Bytes<7519>`

Among others. I've tried to dig into this, but given that the mismatch for the last example is between 7519 bytes and 7523 bytes, which correspond to the MAX_KEY_MATERIAL_LENGTH and MAX_SERIALIZED_KEY_LENGTH sizes respectively, there's something fishy going on. If you could try it, you might have more luck identifying where the issue is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that looks like the issue of using type aliases instead of newtypes, which means that there doesn't need to be explicit conversions between types.

I'm looking into it.

pub type SerializedKeyBytes = Vec<u8, { MAX_SERIALIZED_KEY_LENGTH }>;

// We don't implement serde to make sure nobody inadvertently still uses it
Expand Down Expand Up @@ -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! {
Expand Down Expand Up @@ -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,
}
}

Expand All @@ -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),
})
}
Expand Down
4 changes: 2 additions & 2 deletions src/store/keystore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -181,7 +181,7 @@ impl<S: Store> Keystore for ClientKeystore<S> {

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)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.


let key = key::Key::try_deserialize(&bytes)?;

Expand Down