From 04183749b38626c4514a8760075e652bf4d81b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Tue, 22 Oct 2024 16:04:37 +0200 Subject: [PATCH] Add support for secp256k1 --- Cargo.toml | 2 +- src/core_api.rs | 152 +++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 19 +++++- src/namespacing.rs | 2 + 4 files changed, 165 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 31c8c0a..b8e6fab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ serde_test = "1.0.176" littlefs2 = { git = "https://github.com/trussed-dev/littlefs2.git", rev = "960e57d9fc0d209308c8e15dc26252bbe1ff6ba8" } apdu-dispatch = { git = "https://github.com/trussed-dev/apdu-dispatch.git", rev = "915fc237103fcecc29d0f0b73391f19abf6576de" } ctaphid-dispatch = { git = "https://github.com/trussed-dev/ctaphid-dispatch.git", rev = "57cb3317878a8593847595319aa03ef17c29ec5b" } -trussed = { git = "https://github.com/nitrokey/trussed.git", rev = "540ad725ef44f0d6d3d2da7dd6ec0bacffaeb5bf" } +trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "cc9d367ffa76668e9419ad8a0c04a92903936efb" } trussed-auth = { git = "https://github.com/trussed-dev/trussed-auth.git", tag = "v0.3.0"} trussed-manage = { git = "https://github.com/trussed-dev/trussed-staging.git", tag = "manage-v0.1.0" } trussed-rsa-alloc = { git = "https://github.com/trussed-dev/trussed-rsa-backend.git", tag = "v0.2.1" } diff --git a/src/core_api.rs b/src/core_api.rs index 588b647..989ebdf 100644 --- a/src/core_api.rs +++ b/src/core_api.rs @@ -211,6 +211,7 @@ const SERIALIZED_P521_LEN: usize = 132; const SERIALIZED_BRAINPOOL_P256R1_LEN: usize = 64; const SERIALIZED_BRAINPOOL_P384R1_LEN: usize = 96; const SERIALIZED_BRAINPOOL_P512R1_LEN: usize = 128; +const SERIALIZED_SECP256K1_LEN: usize = 64; const MAX_SERIALIZED_LEN: usize = 132; @@ -453,7 +454,12 @@ impl> Se050Backend { obj: ObjectId, ) -> Result<(), Error> { let buf = &mut [0; 128]; - let material = se050_keystore.load_key(Secrecy::Secret, Some(kind), &key)?; + debug_now!("Reimporting"); + let material = se050_keystore + .load_key(Secrecy::Secret, Some(kind), &key) + .inspect_err(|_err| { + error_now!("Failed reimport: {_err:?}"); + })?; let parsed: VolatileKeyMaterialRef = trussed::cbor_deserialize(&material.material) .map_err(|_err| { error!("Failed to parsed volatile key data: {_err:?}"); @@ -483,6 +489,7 @@ impl> Se050Backend { Kind::BrainpoolP256R1 => (EcCurve::Brainpool256, Some([0; 32].as_slice())), Kind::BrainpoolP384R1 => (EcCurve::Brainpool384, Some([0; 48].as_slice())), Kind::BrainpoolP512R1 => (EcCurve::Brainpool512, Some([0; 64].as_slice())), + Kind::Secp256k1 => (EcCurve::Secp256k1, Some([0; 32].as_slice())), _ => unreachable!(), }; let key_type = if parsed.is_only_private { @@ -566,6 +573,7 @@ impl> Se050Backend { ty: KeyType, ) -> Result { let buf = &mut [0; 1024]; + debug_now!("Deriving key for ty: {ty:?}"); match ty { KeyType::Ed255 => { let material = self @@ -711,6 +719,28 @@ impl> Se050Backend { Ok(reply::DeriveKey { key: result }) } + KeyType::Secp256k1 => { + let material = self + .se + .run_command(&ReadObject::builder().object_id(key).build(), buf) + .map_err(|_err| { + error!("Failed to read key for derive: {_err:?}"); + Error::FunctionFailed + })?; + + debug_now!("Read public key"); + + let result = core_keystore.store_key( + req.attributes.persistence, + Secrecy::Public, + Kind::Secp256k1, + material.data, + )?; + + debug_now!("Stored public key"); + + Ok(reply::DeriveKey { key: result }) + } KeyType::Rsa2048 | KeyType::Rsa3072 | KeyType::Rsa4096 => { self.derive_rsa_key(req, key, ty, core_keystore) } @@ -802,6 +832,7 @@ impl> Se050Backend { KeyType::BrainpoolP256R1 => Kind::BrainpoolP256R1, KeyType::BrainpoolP384R1 => Kind::BrainpoolP384R1, KeyType::BrainpoolP512R1 => Kind::BrainpoolP512R1, + KeyType::Secp256k1 => Kind::Secp256k1, KeyType::Rsa2048 | KeyType::Rsa3072 | KeyType::Rsa4096 => { unreachable!("Volatile rsa keys are derived in a separate function") } @@ -1027,6 +1058,7 @@ impl> Se050Backend { Mechanism::BrainpoolP256R1 => (Kind::BrainpoolP256R1, KeyType::BrainpoolP256R1), Mechanism::BrainpoolP384R1 => (Kind::BrainpoolP384R1, KeyType::BrainpoolP384R1), Mechanism::BrainpoolP512R1 => (Kind::BrainpoolP512R1, KeyType::BrainpoolP512R1), + Mechanism::Secp256k1 => (Kind::Secp256k1, KeyType::Secp256k1), Mechanism::Rsa2048Raw | Mechanism::Rsa2048Pkcs1v15 => { return self.generate_volatile_rsa_key( se050_keystore, @@ -1120,8 +1152,16 @@ impl> Se050Backend { error!("Failed to generate volatile key: {_err:?}"); Error::FunctionFailed })?, + Mechanism::Secp256k1 => self + .se + .run_command(&generate_secp256k1(object_id.0, true), buf) + .map_err(|_err| { + error!("Failed to generate volatile key: {_err:?}"); + Error::FunctionFailed + })?, _ => unreachable!(), } + debug_now!("Generated key for mechanism: {:?}", req.mechanism); let exported = self .se .run_command(&ExportObject::builder().object_id(object_id.0).build(), buf) @@ -1221,6 +1261,14 @@ impl> Se050Backend { Error::FunctionFailed })?, Mechanism::BrainpoolP512R1Prehashed => return Err(Error::MechanismParamInvalid), + Mechanism::Secp256k1 => self + .se + .run_command(&generate_secp256k1(object_id.0, false), buf) + .map_err(|_err| { + error!("Failed to generate key: {_err:?}"); + Error::FunctionFailed + })?, + Mechanism::Secp256k1Prehashed => return Err(Error::MechanismParamInvalid), Mechanism::Rsa2048Raw | Mechanism::Rsa2048Pkcs1v15 => self .se .run_command(&generate_rsa(object_id.0, 2048), buf) @@ -1258,6 +1306,7 @@ impl> Se050Backend { Mechanism::BrainpoolP256R1 => KeyType::BrainpoolP256R1, Mechanism::BrainpoolP384R1 => KeyType::BrainpoolP384R1, Mechanism::BrainpoolP512R1 => KeyType::BrainpoolP512R1, + Mechanism::Secp256k1 => KeyType::Secp256k1, Mechanism::Rsa2048Raw | Mechanism::Rsa2048Pkcs1v15 => KeyType::Rsa2048, Mechanism::Rsa3072Raw | Mechanism::Rsa3072Pkcs1v15 => KeyType::Rsa3072, Mechanism::Rsa4096Raw | Mechanism::Rsa4096Pkcs1v15 => KeyType::Rsa4096, @@ -1287,6 +1336,7 @@ impl> Se050Backend { (Mechanism::BrainpoolP256R1, KeyType::BrainpoolP256R1) => Kind::BrainpoolP256R1, (Mechanism::BrainpoolP384R1, KeyType::BrainpoolP384R1) => Kind::BrainpoolP384R1, (Mechanism::BrainpoolP512R1, KeyType::BrainpoolP512R1) => Kind::BrainpoolP512R1, + (Mechanism::Secp256k1, KeyType::Secp256k1) => Kind::Secp256k1, (Mechanism::X255, KeyType::X255) => Kind::X255, _ => return Err(Error::WrongKeyKind), }; @@ -1574,7 +1624,8 @@ impl> Se050Backend { | Mechanism::P521 | Mechanism::BrainpoolP256R1 | Mechanism::BrainpoolP384R1 - | Mechanism::BrainpoolP512R1 => { + | Mechanism::BrainpoolP512R1 + | Mechanism::Secp256k1 => { debug!("TODO: Implement EcDsa without prehashing"); Err(Error::FunctionNotSupported) } @@ -1583,7 +1634,8 @@ impl> Se050Backend { | Mechanism::P521Prehashed | Mechanism::BrainpoolP256R1Prehashed | Mechanism::BrainpoolP384R1Prehashed - | Mechanism::BrainpoolP512R1Prehashed => self.sign_ecdsa(req, se050_keystore, ns), + | Mechanism::BrainpoolP512R1Prehashed + | Mechanism::Secp256k1Prehashed => self.sign_ecdsa(req, se050_keystore, ns), Mechanism::Ed255 => self.sign_eddsa(req, se050_keystore, ns), Mechanism::Rsa2048Pkcs1v15 | Mechanism::Rsa3072Pkcs1v15 @@ -1746,6 +1798,9 @@ impl> Se050Backend { (Mechanism::BrainpoolP512R1Prehashed, KeyType::BrainpoolP512R1) => { (Kind::BrainpoolP512R1, EcDsaSignatureAlgo::Sha512, 64) } + (Mechanism::Secp256k1Prehashed, KeyType::Secp256k1) => { + (Kind::Secp256k1, EcDsaSignatureAlgo::Sha256, 64) + } _ => return Err(Error::WrongKeyKind), }; @@ -1774,7 +1829,7 @@ impl> Se050Backend { buf, ) .map_err(|_err| { - error!("Failed to perform agree: {_err:?}"); + error!("Failed to perform ecdsa signature: {_err:?}"); Error::FunctionFailed })?; @@ -1841,7 +1896,7 @@ impl> Se050Backend { buf, ) .map_err(|_err| { - error!("Failed to perform agree: {_err:?}"); + error!("Failed to perform Eddsa signature: {_err:?}"); Error::FunctionFailed })?; @@ -1875,7 +1930,8 @@ impl> Se050Backend { | Mechanism::P521 | Mechanism::BrainpoolP256R1 | Mechanism::BrainpoolP384R1 - | Mechanism::BrainpoolP512R1 => { + | Mechanism::BrainpoolP512R1 + | Mechanism::Secp256k1 => { debug!("Implement EcDSA verification without prehashing"); Err(Error::FunctionNotSupported) } @@ -1927,6 +1983,14 @@ impl> Se050Backend { core_keystore, ns, ), + Mechanism::Secp256k1Prehashed => self.verify_ecdsa_prehashed( + req, + Kind::Secp256k1, + EcCurve::Secp256k1, + EcDsaSignatureAlgo::Sha256, + core_keystore, + ns, + ), Mechanism::Ed255 => { self.verify_eddsa(req, Kind::Ed255, EcCurve::IdEccEd25519, core_keystore, ns) } @@ -2168,6 +2232,7 @@ impl> Se050Backend { Mechanism::BrainpoolP256R1 => self.deserialize_brainpool_p256r1_key(req, core_keystore), Mechanism::BrainpoolP384R1 => self.deserialize_brainpool_p384r1_key(req, core_keystore), Mechanism::BrainpoolP512R1 => self.deserialize_brainpool_p512r1_key(req, core_keystore), + Mechanism::Secp256k1 => self.deserialize_secp256k1_key(req, core_keystore), Mechanism::X255 => self.deserialize_x255_key(req, core_keystore), Mechanism::Ed255 => self.deserialize_ed255_key(req, core_keystore), Mechanism::Rsa2048Pkcs1v15 => { @@ -2277,6 +2342,19 @@ impl> Se050Backend { ) } + fn deserialize_secp256k1_key( + &mut self, + req: &request::DeserializeKey, + core_keystore: &mut impl Keystore, + ) -> Result { + self.deserialize_ec_key( + req, + core_keystore, + Kind::Secp256k1, + SERIALIZED_SECP256K1_LEN, + ) + } + fn deserialize_x255_key( &mut self, req: &request::DeserializeKey, @@ -2361,6 +2439,7 @@ impl> Se050Backend { Mechanism::BrainpoolP256R1 => self.serialize_brainpool_p256r1_key(req, core_keystore), Mechanism::BrainpoolP384R1 => self.serialize_brainpool_p384r1_key(req, core_keystore), Mechanism::BrainpoolP512R1 => self.serialize_brainpool_p512r1_key(req, core_keystore), + Mechanism::Secp256k1 => self.serialize_secp256k1_key(req, core_keystore), Mechanism::X255 => self.serialize_x255_key(req, core_keystore), Mechanism::Ed255 => self.serialize_ed255_key(req, core_keystore), Mechanism::Rsa2048Pkcs1v15 | Mechanism::Rsa2048Raw => { @@ -2457,6 +2536,18 @@ impl> Se050Backend { SERIALIZED_BRAINPOOL_P512R1_LEN, ) } + fn serialize_secp256k1_key( + &mut self, + req: &request::SerializeKey, + core_keystore: &mut impl Keystore, + ) -> Result { + self.serialize_ec_key( + req, + core_keystore, + Kind::Secp256k1, + SERIALIZED_SECP256K1_LEN, + ) + } fn serialize_x255_key( &mut self, req: &request::SerializeKey, @@ -2684,6 +2775,7 @@ impl> Se050Backend { Kind::BrainpoolP256R1 => KeyType::BrainpoolP256R1, Kind::BrainpoolP384R1 => KeyType::BrainpoolP384R1, Kind::BrainpoolP512R1 => KeyType::BrainpoolP512R1, + Kind::Secp256k1 => KeyType::Secp256k1, _ => return Err(Error::FunctionFailed), }; let key_id = match ty { @@ -3148,6 +3240,7 @@ impl> Se050Backend { Mechanism::BrainpoolP256R1 => (Kind::BrainpoolP256R1, KeyType::BrainpoolP256R1), Mechanism::BrainpoolP384R1 => (Kind::BrainpoolP384R1, KeyType::BrainpoolP384R1), Mechanism::BrainpoolP512R1 => (Kind::BrainpoolP512R1, KeyType::BrainpoolP512R1), + Mechanism::Secp256k1 => (Kind::Secp256k1, KeyType::Secp256k1), Mechanism::Rsa2048Raw | Mechanism::Rsa2048Pkcs1v15 => { return self.unsafe_inject_volatile_rsa( req, @@ -3369,6 +3462,26 @@ impl> Se050Backend { Error::FunctionFailed })?; } + Mechanism::Secp256k1 => { + // TODO: Find a way to get the public key, so that `derive` works + key_type = P1KeyType::Private; + self.se + .run_command( + &WriteEcKey::builder() + .key_type(key_type) + .private_key(&req.raw_key) + .policy(POLICY) + .transient(true) + .curve(EcCurve::Secp256k1) + .object_id(*id) + .build(), + buf, + ) + .map_err(|_err| { + error!("Failed to inject key: {_err:?}"); + Error::FunctionFailed + })?; + } _ => unreachable!(), }; let exported = self @@ -3622,6 +3735,24 @@ impl> Se050Backend { Error::FunctionFailed })?; } + Mechanism::Secp256k1Prehashed | Mechanism::Secp256k1 => { + // TODO: Find a way to get the public key, so that `derive` works + self.se + .run_command( + &WriteEcKey::builder() + .key_type(P1KeyType::Private) + .private_key(&req.raw_key) + .policy(POLICY) + .curve(EcCurve::Secp256k1) + .object_id(*id) + .build(), + buf, + ) + .map_err(|_err| { + error!("Failed to inject key: {_err:?}"); + Error::FunctionFailed + })?; + } Mechanism::Rsa2048Raw | Mechanism::Rsa2048Pkcs1v15 => { self.unsafe_inject_persistent_rsa(req, id, 2048)? } @@ -3646,6 +3777,7 @@ impl> Se050Backend { Mechanism::BrainpoolP256R1 => KeyType::BrainpoolP256R1, Mechanism::BrainpoolP384R1 => KeyType::BrainpoolP384R1, Mechanism::BrainpoolP512R1 => KeyType::BrainpoolP512R1, + Mechanism::Secp256k1 => KeyType::Secp256k1, Mechanism::Rsa2048Raw | Mechanism::Rsa2048Pkcs1v15 => KeyType::Rsa2048, Mechanism::Rsa3072Raw | Mechanism::Rsa3072Pkcs1v15 => KeyType::Rsa3072, Mechanism::Rsa4096Raw | Mechanism::Rsa4096Pkcs1v15 => KeyType::Rsa4096, @@ -3674,6 +3806,8 @@ impl> Se050Backend { | Mechanism::P256Prehashed | Mechanism::P384Prehashed | Mechanism::P521Prehashed + | Mechanism::Secp256k1 + | Mechanism::Secp256k1Prehashed | Mechanism::BrainpoolP256R1 | Mechanism::BrainpoolP384R1 | Mechanism::BrainpoolP512R1 @@ -3760,6 +3894,10 @@ fn generate_brainpool_p512r1(object_id: ObjectId, transient: bool) -> WriteEcKey generate_ec_key(object_id, transient, EcCurve::Brainpool512) } +fn generate_secp256k1(object_id: ObjectId, transient: bool) -> WriteEcKey<'static> { + generate_ec_key(object_id, transient, EcCurve::Secp256k1) +} + fn generate_rsa(object_id: ObjectId, size: u16) -> WriteRsaKey<'static> { WriteRsaKey::builder() .key_type(P1KeyType::KeyPair) @@ -3786,6 +3924,8 @@ fn supported(mechanism: Mechanism) -> bool { Mechanism::BrainpoolP384R1Prehashed, Mechanism::BrainpoolP512R1, Mechanism::BrainpoolP512R1Prehashed, + Mechanism::Secp256k1, + Mechanism::Secp256k1Prehashed, Mechanism::Rsa2048Raw, Mechanism::Rsa3072Raw, Mechanism::Rsa4096Raw, diff --git a/src/lib.rs b/src/lib.rs index e8c9a16..a4f3118 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,8 @@ use se05x::{ commands::ReadEcCurveList, constants::{ CurveInitializer, BRAINPOOL_P256R1_INITIALIZER, BRAINPOOL_P384R1_INITIALIZER, - BRAINPOOL_P512R1_INITIALIZER, PRIME256V1_INITIALIZER, SECP384R1_INITIALIZER, - SECP521R1_INITIALIZER, + BRAINPOOL_P512R1_INITIALIZER, PRIME256V1_INITIALIZER, SECP256K1_INITIALIZER, + SECP384R1_INITIALIZER, SECP521R1_INITIALIZER, }, Atr, ObjectId, Se05X, }, @@ -42,7 +42,7 @@ const BACKEND_DIR: &Path = path!("se050-bak"); pub const GLOBAL_ATTEST_ID: ObjectId = ObjectId(hex!("F0000012")); /// The version to know wether it should be re-configured -pub const SE050_CONFIGURE_VERSION: u32 = 2; +pub const SE050_CONFIGURE_VERSION: u32 = 3; pub enum Se05xLocation { Persistent, @@ -147,6 +147,7 @@ const REQUIRED_CURVES: &[CurveInitializer] = &[ BRAINPOOL_P256R1_INITIALIZER, BRAINPOOL_P384R1_INITIALIZER, BRAINPOOL_P512R1_INITIALIZER, + SECP256K1_INITIALIZER, ]; #[derive(Default, Debug)] @@ -186,6 +187,18 @@ mod tests { fn backend_version() { // History of previous SE050_CONFIGURE_VERSION and the curves they used let curves_versions: &[(u32, &[_])] = &[ + ( + 3, + &[ + PRIME256V1_INITIALIZER, + SECP384R1_INITIALIZER, + SECP521R1_INITIALIZER, + BRAINPOOL_P256R1_INITIALIZER, + BRAINPOOL_P384R1_INITIALIZER, + BRAINPOOL_P512R1_INITIALIZER, + SECP256K1_INITIALIZER, + ], + ), ( 2, &[ diff --git a/src/namespacing.rs b/src/namespacing.rs index 6aedffc..c4cee86 100644 --- a/src/namespacing.rs +++ b/src/namespacing.rs @@ -353,6 +353,7 @@ enum_number! { BrainpoolP256R1 = 0x9, BrainpoolP384R1 = 0xA, BrainpoolP512R1 = 0xB, + Secp256k1 = 0xC, } } @@ -367,6 +368,7 @@ impl KeyType { Self::BrainpoolP256R1 => Kind::BrainpoolP256R1, Self::BrainpoolP384R1 => Kind::BrainpoolP384R1, Self::BrainpoolP512R1 => Kind::BrainpoolP512R1, + Self::Secp256k1 => Kind::Secp256k1, Self::Rsa2048 => Kind::Rsa2048, Self::Rsa3072 => Kind::Rsa3072, Self::Rsa4096 => Kind::Rsa4096,