Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down Expand Up @@ -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"]
Expand Down
5 changes: 5 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,10 @@ totp = []
trng = []
x255 = []

# PQC
mldsa44 = []
mldsa65 = []
mldsa87 = []

[package.metadata.docs.rs]
all-features = true
55 changes: 51 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,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
};

Comment on lines +51 to +58
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 be in FIDO itself not in trussed, or at the very least #[doc(hidden)].

// 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;
Comment on lines +59 to +62
Copy link
Contributor

Choose a reason for hiding this comment

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

This is way too large. It needs to be at least feature flag.
The Nitrokey 3 has up to 7 client apps. Due to the interchange, this means statically allocating 7 buffers of size around 2*Message. We are pretty limited in terms of memory, and this, even with the feature flag disabled, will double the size of the interchanges, leading to too much memory usage.

Copy link
Contributor

Choose a reason for hiding this comment

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

You can use cargo t -- --ignored to run the test in tests/interchange_size to keep track of how high you bumped the interchange size.

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