diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 94c32fb..79562c3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,11 +33,8 @@ jobs: command: clippy args: -Z unstable-options --all - - name: Install cargo-nextest - uses: baptiste0928/cargo-install@v1.3.0 - with: - crate: cargo-nextest - version: 0.9.22 + - name: Setup nextest + uses: taiki-e/install-action@nextest - name: Run cargo nextest env: diff --git a/Cargo.lock b/Cargo.lock index a4a602e..02b4433 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,6 +159,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bincode" version = "1.3.3" @@ -378,6 +384,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + [[package]] name = "constant_time_eq" version = "0.2.5" @@ -607,6 +619,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "darling" version = "0.14.4" @@ -677,6 +717,16 @@ dependencies = [ "syn 2.0.18", ] +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derivative" version = "2.2.0" @@ -754,8 +804,18 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ + "signature 1.6.4", +] + +[[package]] +name = "ed25519" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" +dependencies = [ + "pkcs8", "serde", - "signature", + "signature 2.1.0", ] [[package]] @@ -764,15 +824,28 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek", - "ed25519", + "curve25519-dalek 3.2.0", + "ed25519 1.5.3", "rand 0.7.3", "serde", - "serde_bytes", "sha2 0.9.9", "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" +dependencies = [ + "curve25519-dalek 4.1.1", + "ed25519 2.2.2", + "rand_core 0.6.4", + "serde", + "sha2 0.10.7", + "zeroize", +] + [[package]] name = "either" version = "1.8.1" @@ -884,6 +957,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "fiat-crypto" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" + [[package]] name = "file-per-thread-logger" version = "0.1.6" @@ -931,7 +1010,7 @@ version = "0.10.3" dependencies = [ "asn1_der 0.6.3", "bs58 0.5.0", - "ed25519-dalek", + "ed25519-dalek 2.0.0", "eyre", "lazy_static", "libp2p-identity", @@ -1378,7 +1457,7 @@ checksum = "d2874d9c6575f1d7a151022af5c42bb0ffdcdfbafe0a6fd039de870b384835a2" dependencies = [ "asn1_der 0.7.6", "bs58 0.5.0", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "libsecp256k1", "log", "multihash 0.19.0", @@ -2009,12 +2088,28 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +[[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.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "platforms" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" + [[package]] name = "polyplets" version = "0.3.2" @@ -2332,6 +2427,15 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.36.14" @@ -2515,6 +2619,12 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" + [[package]] name = "slice-group-by" version = "0.3.1" @@ -2533,6 +2643,16 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" diff --git a/keypair/Cargo.toml b/keypair/Cargo.toml index 0feec47..63d3441 100644 --- a/keypair/Cargo.toml +++ b/keypair/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/fluencelabs/trust-graph" [dependencies] serde = { version = "1.0.118", features = ["derive"] } bs58 = "0.5.0" -ed25519-dalek = { version = "1.0.1", features = ["serde", "std"] } +ed25519-dalek = { version = "2.0.0", features = ["serde", "std", "rand_core"] } rand = "0.8.5" thiserror = "1.0.23" lazy_static = "1.4" diff --git a/keypair/src/ed25519.rs b/keypair/src/ed25519.rs index 842a0ad..2ddbef6 100644 --- a/keypair/src/ed25519.rs +++ b/keypair/src/ed25519.rs @@ -19,16 +19,16 @@ // DEALINGS IN THE SOFTWARE. //! Ed25519 keys. -use crate::error::{DecodingError, SigningError, VerificationError}; +use crate::error::{DecodingError, DecodingError::InvalidLength, SigningError, VerificationError}; use core::fmt; use ed25519_dalek::{self as ed25519, Signer as _, Verifier as _}; -use rand::RngCore; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; use zeroize::Zeroize; -/// An Ed25519 keypair. -pub struct Keypair(ed25519::Keypair); +/// An Ed25519 keypair +#[derive(Clone)] +pub struct Keypair(ed25519::SigningKey); impl Keypair { /// Generate a new Ed25519 keypair. @@ -40,13 +40,15 @@ impl Keypair { /// of the secret scalar and the compressed public point, /// an informal standard for encoding Ed25519 keypairs. pub fn encode(&self) -> [u8; 64] { - self.0.to_bytes() + self.0.to_keypair_bytes() } /// Decode a keypair from the format produced by `encode`, /// zeroing the input on success. pub fn decode(kp: &mut [u8]) -> Result { - ed25519::Keypair::from_bytes(kp) + let bytes = <[u8; 64]>::try_from(&*kp).map_err(InvalidLength)?; + + ed25519::SigningKey::from_keypair_bytes(&bytes) .map(|k| { kp.zeroize(); Keypair(k) @@ -61,12 +63,12 @@ impl Keypair { /// Get the public key of this keypair. pub fn public(&self) -> PublicKey { - PublicKey(self.0.public) + PublicKey(self.0.verifying_key()) } /// Get the secret key of this keypair. pub fn secret(&self) -> SecretKey { - SecretKey::from_bytes(&mut self.0.secret.to_bytes()) + SecretKey::from_bytes(&mut self.0.to_bytes()) .expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k") } } @@ -74,26 +76,14 @@ impl Keypair { impl fmt::Debug for Keypair { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Keypair") - .field("public", &self.0.public) + .field("public", &self.0.verifying_key()) .finish() } } -impl Clone for Keypair { - fn clone(&self) -> Self { - let mut sk_bytes = self.0.secret.to_bytes(); - let secret = SecretKey::from_bytes(&mut sk_bytes) - .expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k") - .0; - let public = ed25519::PublicKey::from_bytes(&self.0.public.to_bytes()) - .expect("ed25519::PublicKey::from_bytes(to_bytes(k)) != k"); - Keypair(ed25519::Keypair { secret, public }) - } -} - /// Build keypair from existing ed25519 keypair -impl From for Keypair { - fn from(kp: ed25519::Keypair) -> Self { +impl From for Keypair { + fn from(kp: ed25519::SigningKey) -> Self { Keypair(kp) } } @@ -101,25 +91,21 @@ impl From for Keypair { /// Demote an Ed25519 keypair to a secret key. impl From for SecretKey { fn from(kp: Keypair) -> Self { - SecretKey(kp.0.secret) + SecretKey(kp.0.to_bytes()) } } /// Promote an Ed25519 secret key into a keypair. impl From for Keypair { fn from(sk: SecretKey) -> Self { - let secret: ed25519::ExpandedSecretKey = (&sk.0).into(); - let public = ed25519::PublicKey::from(&secret); - Keypair(ed25519::Keypair { - secret: sk.0, - public, - }) + let signing = ed25519::SigningKey::from_bytes(&sk.0); + Keypair(signing) } } /// An Ed25519 public key. #[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)] -pub struct PublicKey(ed25519::PublicKey); +pub struct PublicKey(ed25519::VerifyingKey); impl PublicKey { /// Verify the Ed25519 signature on a message using the public key. @@ -143,26 +129,21 @@ impl PublicKey { /// Decode a public key from a byte array as produced by `encode`. pub fn decode(bytes: &[u8]) -> Result { - ed25519::PublicKey::from_bytes(bytes) + let bytes = <[u8; 32]>::try_from(bytes).map_err(InvalidLength)?; + ed25519::VerifyingKey::from_bytes(&bytes) .map_err(DecodingError::Ed25519) .map(PublicKey) } } /// An Ed25519 secret key. +#[derive(Clone)] pub struct SecretKey(pub ed25519::SecretKey); /// View the bytes of the secret key. impl AsRef<[u8]> for SecretKey { fn as_ref(&self) -> &[u8] { - self.0.as_bytes() - } -} - -impl Clone for SecretKey { - fn clone(&self) -> Self { - let mut sk_bytes = self.0.to_bytes(); - Self::from_bytes(&mut sk_bytes).expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k") + &self.0[..] } } @@ -175,13 +156,8 @@ impl fmt::Debug for SecretKey { impl SecretKey { /// Generate a new Ed25519 secret key. pub fn generate() -> Self { - let mut bytes = [0u8; 32]; - rand::thread_rng().fill_bytes(&mut bytes); - SecretKey( - ed25519::SecretKey::from_bytes(&bytes).expect( - "this returns `Err` only if the length is wrong; the length is correct; qed", - ), - ) + let signing = ed25519::SigningKey::generate(&mut rand::rngs::OsRng); + SecretKey(signing.to_bytes()) } /// Create an Ed25519 secret key from a byte slice, zeroing the input on success. @@ -189,7 +165,7 @@ impl SecretKey { /// returned. pub fn from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result { let sk_bytes = sk_bytes.as_mut(); - let secret = ed25519::SecretKey::from_bytes(&*sk_bytes).map_err(DecodingError::Ed25519)?; + let secret = <[u8; 32]>::try_from(&*sk_bytes).map_err(InvalidLength)?; sk_bytes.zeroize(); Ok(SecretKey(secret)) } @@ -205,7 +181,7 @@ mod tests { use quickcheck::*; fn eq_keypairs(kp1: &Keypair, kp2: &Keypair) -> bool { - kp1.public() == kp2.public() && kp1.0.secret.as_bytes() == kp2.0.secret.as_bytes() + kp1.public() == kp2.public() && kp1.0.to_bytes() == kp2.0.to_bytes() } #[test] @@ -234,7 +210,7 @@ mod tests { fn ed25519_keypair_from_secret() { fn prop() -> bool { let kp1 = Keypair::generate(); - let mut sk = kp1.0.secret.to_bytes(); + let mut sk = kp1.0.to_bytes(); let kp2 = Keypair::from(SecretKey::from_bytes(&mut sk).unwrap()); eq_keypairs(&kp1, &kp2) && sk == [0u8; 32] } diff --git a/keypair/src/error.rs b/keypair/src/error.rs index 10c3dc6..4d8a642 100644 --- a/keypair/src/error.rs +++ b/keypair/src/error.rs @@ -31,6 +31,8 @@ pub enum Error { /// An error during decoding of key material. #[derive(ThisError, Debug)] pub enum DecodingError { + #[error("Failed to decode, invalid length: {0}")] + InvalidLength(#[from] std::array::TryFromSliceError), #[error("Failed to decode with ed25519: {0}")] Ed25519( #[from] diff --git a/keypair/src/key_pair.rs b/keypair/src/key_pair.rs index 7b6e7a6..c65d3a3 100644 --- a/keypair/src/key_pair.rs +++ b/keypair/src/key_pair.rs @@ -195,7 +195,7 @@ impl KeyPair { pub fn secret(&self) -> eyre::Result> { use KeyPair::*; match self { - Ed25519(pair) => Ok(pair.secret().0.to_bytes().to_vec()), + Ed25519(pair) => Ok(pair.secret().0.to_vec()), #[cfg(not(target_arch = "wasm32"))] Rsa(_) => Err(eyre::eyre!("secret key is not available for RSA")), Secp256k1(pair) => Ok(pair.secret().to_bytes().to_vec()), @@ -284,7 +284,7 @@ impl From for libp2p_identity::Keypair { match key { KeyPair::Ed25519(kp) => { // for some reason, libp2p takes SecretKey's 32 bytes here instead of Keypair's 64 bytes - let secret_bytes = kp.secret().0.to_bytes(); + let secret_bytes = kp.secret().0; let kp = libp2p_identity::Keypair::ed25519_from_bytes(secret_bytes)?; Ok(kp) }