From 2d50b492f23236b1a4fb9a01cf39581f07b922cf Mon Sep 17 00:00:00 2001 From: Ryan Butler Date: Tue, 21 May 2024 23:05:00 -0400 Subject: [PATCH] did-simple: added ed25519 pubkey type (#104) Includes validation logic for the pubkey. Disclaimer, I consulted with a friend who is a security researcher on how to validate the pubkey. But any additional feedback is appreciated. --- Cargo.lock | 130 +++++++++++++++++++++++- crates/did-simple/Cargo.toml | 8 +- crates/did-simple/src/crypto/ed25519.rs | 50 +++++++++ crates/did-simple/src/crypto/mod.rs | 4 + crates/did-simple/src/lib.rs | 1 + 5 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 crates/did-simple/src/crypto/ed25519.rs create mode 100644 crates/did-simple/src/crypto/mod.rs diff --git a/Cargo.lock b/Cargo.lock index a0cf9c0..6334fcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -922,6 +922,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bevy" version = "0.13.0" @@ -2666,6 +2672,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "const_fn" version = "0.4.9" @@ -2717,7 +2729,7 @@ dependencies = [ "hmac", "percent-encoding", "rand 0.8.5", - "sha2", + "sha2 0.9.9", "time 0.2.27", "version_check", ] @@ -2946,6 +2958,34 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rustc_version 0.4.0", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "d3d12" version = "0.19.0" @@ -3031,6 +3071,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der-parser" version = "8.2.0" @@ -3091,6 +3141,8 @@ version = "0.0.1" dependencies = [ "bs58", "bytes", + "curve25519-dalek", + "ed25519-dalek", "eyre", "hex-literal", "itertools 0.13.0", @@ -3229,6 +3281,30 @@ dependencies = [ "serde", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "educe" version = "0.5.11" @@ -3640,6 +3716,12 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "fixedbitset" version = "0.4.2" @@ -5868,12 +5950,28 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +[[package]] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" + [[package]] name = "plotters" version = "0.3.5" @@ -6912,6 +7010,17 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -6936,6 +7045,15 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -7145,6 +7263,16 @@ dependencies = [ "bitflags 2.4.2", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "standback" version = "0.2.17" diff --git a/crates/did-simple/Cargo.toml b/crates/did-simple/Cargo.toml index 1bc19b8..9dbd0fc 100644 --- a/crates/did-simple/Cargo.toml +++ b/crates/did-simple/Cargo.toml @@ -9,7 +9,11 @@ description = "Dead simple DIDs" publish = true [features] -default = [] +default = ["ed25519"] +ed25519 = [ + "dep:curve25519-dalek", + "dep:ed25519-dalek", +] # Only applications should enable this! If you use did-simple as a dependency, # don't enable this feature - let applications set it instead. @@ -20,6 +24,8 @@ allow-unsafe = [] bs58 = "0.5.1" bytes = "1.6.0" thiserror = "1.0.60" +ed25519-dalek = { version = "2.1.1", optional = true } +curve25519-dalek = { version = "4.1.2", optional = true } [dev-dependencies] eyre = "0.6.12" diff --git a/crates/did-simple/src/crypto/ed25519.rs b/crates/did-simple/src/crypto/ed25519.rs new file mode 100644 index 0000000..3679f73 --- /dev/null +++ b/crates/did-simple/src/crypto/ed25519.rs @@ -0,0 +1,50 @@ +use curve25519_dalek::edwards::CompressedEdwardsY; +use ed25519_dalek::VerifyingKey; + +use crate::key_algos::StaticKeyAlgo as _; + +/// An ed25519 public key. +#[allow(dead_code)] +pub struct PubKey(VerifyingKey); + +impl PubKey { + pub const LEN: usize = Self::key_len(); + + /// Instantiates `PubKey` from some bytes. Performs all necessary validation + /// that the key is valid and of sufficient strength. + /// + /// Note that we will reject any keys that are too weak (aka low order). + pub fn try_from(bytes: &[u8; Self::LEN]) -> Result { + let compressed_edwards = CompressedEdwardsY(bytes.to_owned()); + let Some(edwards) = compressed_edwards.decompress() else { + return Err(TryFromBytesError::NotOnCurve); + }; + let key = VerifyingKey::from(edwards); + if key.is_weak() { + return Err(TryFromBytesError::WeakKey); + } + Ok(Self(key)) + } + + // TODO: Turn this into inline const when that feature stabilizes + const fn key_len() -> usize { + let len = crate::key_algos::Ed25519::PUB_KEY_LEN; + assert!(len == ed25519_dalek::PUBLIC_KEY_LENGTH); + len + } +} + +#[derive(thiserror::Error, Debug)] +pub enum TryFromBytesError { + #[error( + "the provided bytes was not the y coordinate of a valid point on the curve" + )] + NotOnCurve, + #[error("public key has a low order and is too weak, which would allow the key to generate signatures that work for almost any message. To prevent this, we reject weak keys.")] + WeakKey, +} + +/// Errors which may occur while processing signatures and keypairs. +#[derive(thiserror::Error, Debug)] +#[error("invalid signature")] +pub struct SignatureError(#[from] ed25519_dalek::SignatureError); diff --git a/crates/did-simple/src/crypto/mod.rs b/crates/did-simple/src/crypto/mod.rs new file mode 100644 index 0000000..c9fa1c4 --- /dev/null +++ b/crates/did-simple/src/crypto/mod.rs @@ -0,0 +1,4 @@ +//! Implementations of cryptographic operations + +#[cfg(feature = "ed25519")] +pub mod ed25519; diff --git a/crates/did-simple/src/lib.rs b/crates/did-simple/src/lib.rs index 83cc597..2cc6996 100644 --- a/crates/did-simple/src/lib.rs +++ b/crates/did-simple/src/lib.rs @@ -24,6 +24,7 @@ use std::str::FromStr; +pub mod crypto; pub(crate) mod key_algos; pub mod methods; pub mod url;