From 7d9a23b14fc53ef972871f175cc671642f5b2441 Mon Sep 17 00:00:00 2001 From: aya015757881 <2581015450@qq.com> Date: Fri, 21 Jun 2024 16:52:35 +0800 Subject: [PATCH 1/3] feat: bip32 key derivation for ed25519 public key --- Cargo.lock | 145 ++++++++++++++---- Cargo.toml | 2 + anychain-kms/Cargo.toml | 3 + .../extended_key/extended_private_key.rs | 19 ++- .../bip32/extended_key/extended_public_key.rs | 26 ++-- anychain-kms/src/bip32/extended_key/mod.rs | 8 +- anychain-kms/src/bip32/mod.rs | 9 +- anychain-kms/src/bip32/private_key.rs | 38 +++-- anychain-kms/src/bip32/public_key.rs | 82 ++++++---- anychain-kms/src/bip39/mod.rs | 4 +- anychain-kms/src/lib.rs | 10 +- 11 files changed, 231 insertions(+), 115 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5058877..abb49a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,7 +37,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom", + "getrandom 0.2.14", "once_cell", "version_check", ] @@ -145,7 +145,7 @@ dependencies = [ "bech32", "hex", "libsecp256k1", - "rand", + "rand 0.8.5", "serde", "sha2 0.10.8", "thiserror", @@ -244,14 +244,17 @@ version = "0.1.6" dependencies = [ "anyhow", "bs58 0.4.0", + "curve25519-dalek 4.1.3", + "ed25519-dalek 1.0.1", "encoding_rs", + "group", "hex", "hex-literal", "hmac 0.12.1", "libsecp256k1", "once_cell", "pbkdf2 0.12.2", - "rand", + "rand 0.8.5", "ripemd", "rustc-hash", "sha2 0.10.8", @@ -269,7 +272,7 @@ dependencies = [ "base58", "base64 0.21.7", "p256", - "rand", + "rand 0.8.5", ] [[package]] @@ -288,7 +291,7 @@ dependencies = [ "anychain-core", "base58", "blake2b_simd 1.0.2", - "ed25519-dalek", + "ed25519-dalek 2.1.1", "libsecp256k1", "parity-scale-codec", "serde_json", @@ -453,7 +456,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", ] [[package]] @@ -544,7 +547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "rand", + "rand 0.8.5", "rand_core 0.6.4", "serde", "unicode-normalization", @@ -1045,16 +1048,17 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "platforms", + "group", + "rand_core 0.6.4", "rustc_version", "subtle", "zeroize", @@ -1188,10 +1192,19 @@ dependencies = [ "digest 0.10.7", "elliptic-curve", "rfc6979", - "signature", + "signature 2.2.0", "spki", ] +[[package]] +name = "ed25519" +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.3" @@ -1199,7 +1212,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", - "signature", + "signature 2.2.0", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519 1.5.3", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", ] [[package]] @@ -1208,8 +1235,8 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "curve25519-dalek 4.1.2", - "ed25519", + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", "serde", "sha2 0.10.8", "subtle", @@ -1375,7 +1402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" dependencies = [ "byteorder", - "rand", + "rand 0.8.5", "rustc-hex", "static_assertions", ] @@ -1387,7 +1414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand", + "rand 0.8.5", "rustc-hex", "static_assertions", ] @@ -1628,6 +1655,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.14" @@ -1636,7 +1674,7 @@ checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -1645,7 +1683,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" dependencies = [ - "rand", + "rand 0.8.5", "rand_core 0.6.4", ] @@ -1972,7 +2010,7 @@ dependencies = [ "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", - "rand", + "rand 0.8.5", "serde", "sha2 0.9.9", "typenum", @@ -2411,12 +2449,6 @@ dependencies = [ "spki", ] -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" - [[package]] name = "polkavm-common" version = "0.8.0" @@ -2610,6 +2642,19 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -2617,10 +2662,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -2636,6 +2691,9 @@ name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] [[package]] name = "rand_core" @@ -2643,7 +2701,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.14", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -2875,7 +2942,7 @@ dependencies = [ "aead", "arrayref", "arrayvec 0.7.4", - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "getrandom_or_panic", "merlin", "rand_core 0.6.4", @@ -3086,6 +3153,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + [[package]] name = "signature" version = "2.2.0" @@ -3137,7 +3210,7 @@ dependencies = [ "parking_lot", "paste", "primitive-types 0.12.2", - "rand", + "rand 0.8.5", "scale-info", "schnorrkel", "secp256k1", @@ -3557,7 +3630,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.7", - "rand", + "rand 0.8.5", "static_assertions", ] @@ -3662,8 +3735,8 @@ dependencies = [ "arrayref", "constcat", "digest 0.10.7", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_core 0.6.4", "sha2 0.10.8", "sha3", @@ -3671,6 +3744,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3853,7 +3932,7 @@ dependencies = [ "memfd", "memoffset", "paste", - "rand", + "rand 0.8.5", "rustix 0.36.17", "wasmtime-asm-macros", "wasmtime-environ", diff --git a/Cargo.toml b/Cargo.toml index 13951f3..04e0240 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,8 @@ blake2b_simd = "1.0.0" bech32 = "0.9.0" hex = "0.4.2" libsecp256k1 = "0.7.1" +ed25519-dalek = "1.0.1" +curve25519-dalek = { version = "4.1.3", features = ["group"] } bls-signatures = "0.14.0" base58 = { version = "0.2" } rand = { version = "0.8.5" } diff --git a/anychain-kms/Cargo.toml b/anychain-kms/Cargo.toml index ba0917f..f7cf324 100644 --- a/anychain-kms/Cargo.toml +++ b/anychain-kms/Cargo.toml @@ -27,7 +27,10 @@ subtle = { workspace = true } ripemd = { workspace = true } hex = { workspace = true } libsecp256k1 = { workspace = true } +ed25519-dalek = { workspace = true } +curve25519-dalek = { workspace = true } encoding_rs = { version = "0.8.33" } +group = "0.13.0" [dev-dependencies] hex-literal = "0.4" diff --git a/anychain-kms/src/bip32/extended_key/extended_private_key.rs b/anychain-kms/src/bip32/extended_key/extended_private_key.rs index 204dbb1..3e108cc 100644 --- a/anychain-kms/src/bip32/extended_key/extended_private_key.rs +++ b/anychain-kms/src/bip32/extended_key/extended_private_key.rs @@ -1,8 +1,9 @@ //! Extended private keys use crate::bip32::{ - ChildNumber, Depth, Error, ExtendedKey, ExtendedKeyAttrs, ExtendedPublicKey, HmacSha512, - KeyFingerprint, Prefix, PrivateKey, PrivateKeyBytes, PublicKey, Result, KEY_SIZE, + ChildNumber, Depth, Error, ExtendedKey, ExtendedKeyAttrs, + ExtendedPublicKey, HmacSha512, KeyFingerprint, Prefix, + PrivateKey, PublicKey, Result, KEY_SIZE, }; use core::{ fmt::{self, Debug}, @@ -25,11 +26,13 @@ const BIP39_DOMAIN_SEPARATOR: [u8; 12] = [ ]; /// Extended private secp256k1 ECDSA signing key. -pub type XPrv = ExtendedPrivateKey; +pub type XprvSecp256k1 = ExtendedPrivateKey; + + /// Extended private keys derived using BIP32. /// /// Generic around a [`PrivateKey`] type. When the `secp256k1` feature of this -/// crate is enabled, the [`XPrv`] type provides a convenient alias for +/// crate is enabled, the [`XprvSecp256k1`] type provides a convenient alias for /// extended ECDSA/secp256k1 private keys. #[derive(Clone)] pub struct ExtendedPrivateKey { @@ -72,7 +75,7 @@ where let result = hmac.finalize().into_bytes(); let (secret_key, chain_code) = result.split_at(KEY_SIZE); - let private_key = PrivateKey::from_bytes(secret_key.try_into()?)?; + let private_key = PrivateKey::from_bytes(secret_key.to_vec())?; let attrs = ExtendedKeyAttrs { depth: 0, parent_fingerprint: KeyFingerprint::default(), @@ -116,7 +119,7 @@ where // // ...so instead, we simply return an error if this were ever to happen, // as the chances of it happening are vanishingly small. - let private_key = self.private_key.derive_child(child_key.try_into()?)?; + let private_key = self.private_key.derive_child(child_key.to_vec())?; let attrs = ExtendedKeyAttrs { parent_fingerprint: self.private_key.public_key().fingerprint(), @@ -145,7 +148,7 @@ where } /// Serialize the raw private key as a byte array. - pub fn to_bytes(&self) -> PrivateKeyBytes { + pub fn to_bytes(&self) -> Vec { self.private_key.to_bytes() } @@ -239,7 +242,7 @@ where fn try_from(extended_key: ExtendedKey) -> Result> { if extended_key.prefix.is_private() && extended_key.key_bytes[0] == 0 { Ok(ExtendedPrivateKey { - private_key: PrivateKey::from_bytes(extended_key.key_bytes[1..].try_into()?)?, + private_key: PrivateKey::from_bytes(extended_key.key_bytes[1..].to_vec())?, attrs: extended_key.attrs.clone(), }) } else { diff --git a/anychain-kms/src/bip32/extended_key/extended_public_key.rs b/anychain-kms/src/bip32/extended_key/extended_public_key.rs index acc0d1c..eb622ad 100644 --- a/anychain-kms/src/bip32/extended_key/extended_public_key.rs +++ b/anychain-kms/src/bip32/extended_key/extended_public_key.rs @@ -1,8 +1,7 @@ //! Extended public keys use crate::bip32::{ - ChildNumber, DerivationPath, Error, ExtendedKey, ExtendedKeyAttrs, ExtendedPrivateKey, - HmacSha512, KeyFingerprint, Prefix, PrivateKey, PublicKey, PublicKeyBytes, Result, KEY_SIZE, + ChildNumber, DerivationPath, Error, ExtendedKey, ExtendedKeyAttrs, ExtendedPrivateKey, HmacSha512, KeyFingerprint, Prefix, PrivateKey, PublicKey, Result, KEY_SIZE }; use core::str::FromStr; use hmac::Mac; @@ -10,13 +9,16 @@ use hmac::Mac; #[cfg(feature = "alloc")] use alloc::string::{String, ToString}; -/// Extended public secp256k1 ECDSA verification key. +/// Extended public secp256k1 ECDSA verifiying key. +pub type XpubSecp256k1 = ExtendedPublicKey; + +/// Extended public ed25519 EDDSA verifiying key. +pub type XpubEd25519 = ExtendedPublicKey; -pub type XPub = ExtendedPublicKey; /// Extended public keys derived using BIP32. /// /// Generic around a [`PublicKey`] type. When the `secp256k1` feature of this -/// crate is enabled, the [`XPub`] type provides a convenient alias for +/// crate is enabled, the [`XpubSecp256k1`] type provides a convenient alias for /// extended ECDSA/secp256k1 public keys. #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct ExtendedPublicKey { @@ -68,8 +70,8 @@ where hmac.update(&child_number.to_bytes()); let result = hmac.finalize().into_bytes(); - let (child_key, chain_code) = result.split_at(KEY_SIZE); - let public_key = self.public_key.derive_child(child_key.try_into()?)?; + let (tweak, chain_code) = result.split_at(KEY_SIZE); + let public_key = self.public_key.derive_child(tweak.to_vec())?; let attrs = ExtendedKeyAttrs { parent_fingerprint: self.public_key.fingerprint(), @@ -82,7 +84,7 @@ where } /// Serialize the raw public key as a byte array (e.g. SEC1-encoded). - pub fn to_bytes(&self) -> PublicKeyBytes { + pub fn to_bytes(&self) -> Vec { self.public_key.to_bytes() } @@ -91,7 +93,11 @@ where ExtendedKey { prefix, attrs: self.attrs.clone(), - key_bytes: self.to_bytes(), + key_bytes: { + let mut key_bytes = [0u8; KEY_SIZE + 1]; + key_bytes.copy_from_slice(&self.to_bytes()); + key_bytes + } } } @@ -134,7 +140,7 @@ where fn try_from(extended_key: ExtendedKey) -> Result> { if extended_key.prefix.is_public() { Ok(ExtendedPublicKey { - public_key: PublicKey::from_bytes(extended_key.key_bytes)?, + public_key: PublicKey::from_bytes(extended_key.key_bytes.to_vec())?, attrs: extended_key.attrs.clone(), }) } else { diff --git a/anychain-kms/src/bip32/extended_key/mod.rs b/anychain-kms/src/bip32/extended_key/mod.rs index ddde1c4..1c14ef5 100644 --- a/anychain-kms/src/bip32/extended_key/mod.rs +++ b/anychain-kms/src/bip32/extended_key/mod.rs @@ -71,7 +71,7 @@ impl FromStr for ExtendedKey { fn from_str(base58: &str) -> Result { let mut bytes = [0u8; Self::BYTE_SIZE + 4]; // with 4-byte checksum let decoded_len = bs58::decode(base58).with_check(None).into(&mut bytes)?; - + if decoded_len != Self::BYTE_SIZE { return Err(Error::Decode); } @@ -114,8 +114,8 @@ impl Drop for ExtendedKey { #[cfg(test)] mod tests { - use crate::bip32::Prefix; - use crate::bip32::{DerivationPath, ExtendedKey, XPrv}; + use crate::bip32::{Prefix, XprvSecp256k1}; + use crate::bip32::{DerivationPath, ExtendedKey,}; use alloc::string::ToString; use hex_literal::hex; @@ -190,7 +190,7 @@ mod tests { let _seed = hex::decode("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be").unwrap(); let seed2 = "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be".as_bytes(); let path: DerivationPath = "m/44'/60/0'/10001".parse().unwrap(); - let xprv = XPrv::new_from_path(seed2, &path).unwrap(); + let xprv = XprvSecp256k1::new_from_path(seed2, &path).unwrap(); println!("xprv: {}", xprv.to_extended_key(Prefix::XPRV)); let xpub = xprv.public_key(); diff --git a/anychain-kms/src/bip32/mod.rs b/anychain-kms/src/bip32/mod.rs index 9eafe16..6a54a86 100644 --- a/anychain-kms/src/bip32/mod.rs +++ b/anychain-kms/src/bip32/mod.rs @@ -12,10 +12,10 @@ pub use extended_key::{ attrs::ExtendedKeyAttrs, extended_private_key::ExtendedPrivateKey, extended_public_key::ExtendedPublicKey, ExtendedKey, }; -pub use extended_key::{extended_private_key::XPrv, extended_public_key::XPub}; +pub use extended_key::{extended_private_key::XprvSecp256k1, extended_public_key::XpubSecp256k1}; pub use prefix::Prefix; -pub use private_key::{PrivateKey, PrivateKeyBytes}; -pub use public_key::{PublicKey, PublicKeyBytes}; +pub use private_key::PrivateKey; +pub use public_key::PublicKey; pub use derivation_path::DerivationPath; @@ -35,7 +35,6 @@ pub type Version = u32; /// HMAC with SHA-512 pub type HmacSha512 = hmac::Hmac; -/// Size of input key material and derived keys. pub const KEY_SIZE: usize = 32; #[cfg(test)] @@ -92,7 +91,7 @@ mod test_mod { let seed = hex::decode(vector.seed).unwrap(); vector.ckd.iter().for_each(|item| { let path: DerivationPath = item.0.parse().unwrap(); - let xprv = XPrv::new_from_path(seed.clone(), &path).unwrap(); + let xprv = XprvSecp256k1::new_from_path(seed.clone(), &path).unwrap(); let xpub = xprv.public_key(); assert_eq!(item.1, xprv.to_string(Prefix::XPRV).as_str()); assert_eq!(item.2, xpub.to_string(Prefix::XPUB).as_str()); diff --git a/anychain-kms/src/bip32/private_key.rs b/anychain-kms/src/bip32/private_key.rs index e9ef711..00dde70 100644 --- a/anychain-kms/src/bip32/private_key.rs +++ b/anychain-kms/src/bip32/private_key.rs @@ -2,11 +2,9 @@ use crate::bip32::{PublicKey, Result, KEY_SIZE}; -use crate::bip32::{Error, XPrv}; +use crate::bip32::{Error, XprvSecp256k1}; use libsecp256k1; -/// Bytes which represent a private key. -pub type PrivateKeyBytes = [u8; KEY_SIZE]; /// Trait for key types which can be derived using BIP32. pub trait PrivateKey: Sized { @@ -14,15 +12,15 @@ pub trait PrivateKey: Sized { type PublicKey: PublicKey; /// Initialize this key from bytes. - fn from_bytes(bytes: &PrivateKeyBytes) -> Result; + fn from_bytes(bytes: Vec) -> Result; /// Serialize this key as bytes. - fn to_bytes(&self) -> PrivateKeyBytes; + fn to_bytes(&self) -> Vec; - /// Derive a child key from a parent key and the a provided tweak value, + /// Derive a child key from a parent key with provided tweak value, /// i.e. where `other` is referred to as "I sub L" in BIP32 and sourced /// from the left half of the HMAC-SHA-512 output. - fn derive_child(&self, other: PrivateKeyBytes) -> Result; + fn derive_child(&self, tweak: Vec) -> Result; /// Get the [`Self::PublicKey`] that corresponds to this private key. fn public_key(&self) -> Self::PublicKey; @@ -31,20 +29,20 @@ pub trait PrivateKey: Sized { impl PrivateKey for libsecp256k1::SecretKey { type PublicKey = libsecp256k1::PublicKey; - fn from_bytes(bytes: &PrivateKeyBytes) -> Result { - match libsecp256k1::SecretKey::parse(bytes) { + fn from_bytes(bytes: Vec) -> Result { + match libsecp256k1::SecretKey::parse_slice(&bytes) { Ok(sk) => Ok(sk), Err(_) => Err(Error::Crypto), } } - fn to_bytes(&self) -> PrivateKeyBytes { - libsecp256k1::SecretKey::serialize(self) + fn to_bytes(&self) -> Vec { + libsecp256k1::SecretKey::serialize(self).to_vec() } - fn derive_child(&self, other: PrivateKeyBytes) -> Result { + fn derive_child(&self, tweak: Vec) -> Result { let mut cpk = *self; - match cpk.tweak_add_assign(&libsecp256k1::SecretKey::parse(&other).unwrap()) { + match cpk.tweak_add_assign(&libsecp256k1::SecretKey::parse_slice(&tweak).unwrap()) { Ok(_) => Ok(cpk), Err(_) => Err(Error::Crypto), } @@ -55,14 +53,14 @@ impl PrivateKey for libsecp256k1::SecretKey { } } -impl From for libsecp256k1::SecretKey { - fn from(xprv: XPrv) -> libsecp256k1::SecretKey { +impl From for libsecp256k1::SecretKey { + fn from(xprv: XprvSecp256k1) -> libsecp256k1::SecretKey { libsecp256k1::SecretKey::from(&xprv) } } -impl From<&XPrv> for libsecp256k1::SecretKey { - fn from(xprv: &XPrv) -> libsecp256k1::SecretKey { +impl From<&XprvSecp256k1> for libsecp256k1::SecretKey { + fn from(xprv: &XprvSecp256k1) -> libsecp256k1::SecretKey { *xprv.private_key() } } @@ -71,9 +69,9 @@ impl From<&XPrv> for libsecp256k1::SecretKey { mod tests { use hex_literal::hex; - //type XPrv = crate::bip32::ExtendedPrivateKey; + //type XprvSecp256k1 = crate::bip32::ExtendedPrivateKey; - type XPrv = crate::bip32::ExtendedPrivateKey; + type XprvSecp256k1 = crate::bip32::ExtendedPrivateKey; #[test] fn secp256k1_derivation() { @@ -83,7 +81,7 @@ mod tests { ); let path = "m/0/2147483647'/1/2147483646'/2"; - let xprv = XPrv::new_from_path(seed, &path.parse().unwrap()).unwrap(); + let xprv = XprvSecp256k1::new_from_path(seed, &path.parse().unwrap()).unwrap(); assert_eq!( xprv, diff --git a/anychain-kms/src/bip32/public_key.rs b/anychain-kms/src/bip32/public_key.rs index e4ddf1f..7534c10 100644 --- a/anychain-kms/src/bip32/public_key.rs +++ b/anychain-kms/src/bip32/public_key.rs @@ -1,28 +1,23 @@ //! Trait for deriving child keys on a given type. -use crate::bip32::{KeyFingerprint, PrivateKeyBytes, Result, KEY_SIZE}; +use crate::bip32::{KeyFingerprint, Result, KEY_SIZE}; +use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE as G, edwards::EdwardsPoint, scalar::Scalar}; +use group::GroupEncoding; use ripemd::Ripemd160; use sha2::{Digest, Sha256}; -use crate::bip32::XPub; - -use crate::bip32::Error; - -/// Bytes which represent a public key. -/// -/// Includes an extra byte for an SEC1 tag. -pub type PublicKeyBytes = [u8; KEY_SIZE + 1]; +use crate::bip32::{XpubSecp256k1, Error}; /// Trait for key types which can be derived using BIP32. pub trait PublicKey: Sized { /// Initialize this key from bytes. - fn from_bytes(bytes: PublicKeyBytes) -> Result; + fn from_bytes(bytes: Vec) -> Result; /// Serialize this key as bytes. - fn to_bytes(&self) -> PublicKeyBytes; + fn to_bytes(&self) -> Vec; /// Derive a child key from a parent key and a provided tweak value. - fn derive_child(&self, other: PrivateKeyBytes) -> Result; + fn derive_child(&self, tweak: Vec) -> Result; /// Compute a 4-byte key fingerprint for this public key. /// @@ -34,53 +29,74 @@ pub trait PublicKey: Sized { } impl PublicKey for libsecp256k1::PublicKey { - fn from_bytes(bytes: PublicKeyBytes) -> Result { - match libsecp256k1::PublicKey::parse_compressed(&bytes) { + fn from_bytes(bytes: Vec) -> Result { + match libsecp256k1::PublicKey::parse_slice(&bytes, None) { Ok(pubkey) => Ok(pubkey), Err(_) => Err(Error::Crypto), } } - fn to_bytes(&self) -> PublicKeyBytes { - libsecp256k1::PublicKey::serialize_compressed(self) + fn to_bytes(&self) -> Vec { + libsecp256k1::PublicKey::serialize_compressed(self).to_vec() } - fn derive_child(&self, other: PrivateKeyBytes) -> Result { + fn derive_child(&self, tweak: Vec) -> Result { let mut cpk = *self; - match cpk.tweak_add_assign(&libsecp256k1::SecretKey::parse(&other).unwrap()) { + match cpk.tweak_add_assign(&libsecp256k1::SecretKey::parse_slice(&tweak).unwrap()) { Ok(_) => Ok(cpk), Err(_) => Err(Error::Crypto), } } } -impl From for libsecp256k1::PublicKey { - fn from(xpub: XPub) -> libsecp256k1::PublicKey { - libsecp256k1::PublicKey::from(&xpub) +impl PublicKey for ed25519_dalek::PublicKey { + fn from_bytes(bytes: Vec) -> Result { + if bytes.len() == 32 { + Ok(ed25519_dalek::PublicKey::from_bytes(&bytes) + .or(Err(crate::bip32::Error::Crypto))?) + } else if bytes.len() == 33 { + Ok(ed25519_dalek::PublicKey::from_bytes(&bytes[1..]) + .or(Err(crate::bip32::Error::Crypto))?) + } else { + Err(crate::bip32::Error::Crypto) + } + } + + fn to_bytes(&self) -> Vec { + self.as_bytes().to_vec() } -} -impl From<&XPub> for libsecp256k1::PublicKey { - fn from(xpub: &XPub) -> libsecp256k1::PublicKey { - *xpub.public_key() + fn derive_child(&self, tweak: Vec) -> Result { + let pk = self.as_bytes(); + let mut _tweak = [0u8; 32]; + _tweak.copy_from_slice(&tweak); + let point = EdwardsPoint::from_bytes(pk).unwrap(); + let tweak = &Scalar::from_bytes_mod_order(_tweak) * G; + let child = point + &tweak; + let child = child.to_bytes(); + Ok(ed25519_dalek::PublicKey::from_bytes(&child).unwrap()) } } #[cfg(test)] mod tests { + use std::str::FromStr; + use hex_literal::hex; + use crate::bip32::{extended_key::extended_public_key::XpubEd25519, DerivationPath}; + const SEED: [u8; 64] = hex!( "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a2 9f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" ); - type XPrv = crate::bip32::ExtendedPrivateKey; + type XprvSecp256k1 = crate::bip32::ExtendedPrivateKey; #[test] fn secp256k1_xprv_derivation() { let path = "m/0/2147483647'/1/2147483646'/2"; - let xprv = XPrv::new_from_path(SEED, &path.parse().unwrap()).unwrap(); + let xprv = XprvSecp256k1::new_from_path(SEED, &path.parse().unwrap()).unwrap(); assert_eq!( xprv.public_key(), @@ -91,7 +107,7 @@ mod tests { #[test] fn secp256k1_ffi_xpub_derivation() { let path = "m/0/2147483647'/1/2147483646'"; - let xprv = XPrv::new_from_path(SEED, &path.parse().unwrap()).unwrap(); + let xprv = XprvSecp256k1::new_from_path(SEED, &path.parse().unwrap()).unwrap(); let xpub = xprv.public_key().derive_child(2.into()).unwrap(); assert_eq!( @@ -99,4 +115,14 @@ mod tests { "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt".parse().unwrap() ); } + + #[test] + fn test_ed25519() { + let xpub = "xpub661MyMwAqRbcGGrNUSVKfdjbzSrBSeah6YPb99PhpDyQRiYLKtC4RaASsF1k5xEW6u2tZZ1nb3A335ZbtNh9UJtwrNorMhmumn2X3r3dEn2"; + let xpub = XpubEd25519::from_str(xpub).unwrap(); + let path = DerivationPath::from_str("m/1/2/3").unwrap(); + let child = xpub.derive_from_path(&path).unwrap(); + let child = child.public_key().as_bytes(); + println!("{:?}", child); + } } diff --git a/anychain-kms/src/bip39/mod.rs b/anychain-kms/src/bip39/mod.rs index 8d88d9c..bb3abdc 100644 --- a/anychain-kms/src/bip39/mod.rs +++ b/anychain-kms/src/bip39/mod.rs @@ -19,7 +19,7 @@ pub use seed::Seed; #[cfg(test)] mod test_mod { use super::*; - use crate::bip32::{Prefix, XPrv}; + use crate::bip32::{Prefix, XprvSecp256k1}; const VECTORS: [[&str;4];24] = [ [ "00000000000000000000000000000000", @@ -176,7 +176,7 @@ mod test_mod { assert_eq!(item[0], hex::encode(mnemonic.entropy())); assert_eq!(item[1], mnemonic.phrase()); assert_eq!(item[2], format!("{:x}", seed)); - let xprv = XPrv::new(seed).unwrap(); + let xprv = XprvSecp256k1::new(seed).unwrap(); assert_eq!(item[3], xprv.to_extended_key(Prefix::XPRV).to_string()); }) } diff --git a/anychain-kms/src/lib.rs b/anychain-kms/src/lib.rs index 80aeba6..88b1649 100644 --- a/anychain-kms/src/lib.rs +++ b/anychain-kms/src/lib.rs @@ -22,7 +22,7 @@ pub fn ecdsa_sign( #[cfg(test)] mod tests { - use crate::bip32::{ChildNumber, DerivationPath, Prefix, XPrv, XPub}; + use crate::bip32::{ChildNumber, DerivationPath, Prefix, XprvSecp256k1, XpubSecp256k1}; use crate::bip39::{Language, Mnemonic, Seed}; #[test] @@ -37,7 +37,7 @@ mod tests { println!("seed:{:X}", seed); let path: DerivationPath = "m/44'/196'/300049'/0".parse().unwrap(); - let xprv = XPrv::new_from_path(seed, &path).unwrap(); + let xprv = XprvSecp256k1::new_from_path(seed, &path).unwrap(); let _ek = xprv.to_extended_key(Prefix::XPRV); println!("xprv:{:?}", xprv); let cp: ChildNumber = 1u32.into(); @@ -57,7 +57,7 @@ mod tests { //let phrase = mnemonic.phrase(); let seed = Seed::new(&mnemonic, ""); // let path: DerivationPath = "m".parse().unwrap(); - let xprv = XPrv::new(seed).unwrap(); + let xprv = XprvSecp256k1::new(seed).unwrap(); let _secret = xprv.private_key(); //let ek = xprv.to_extended_key(Prefix::XPRV); println!("xprv:{:}", xprv.to_string(Prefix::XPRV).as_str()); @@ -71,8 +71,8 @@ mod tests { //let phrase = mnemonic.phrase(); let seed = Seed::new(&mnemonic, ""); let path: DerivationPath = "m/44'/60".parse().unwrap(); - let xprv = XPrv::new_from_path(seed, &path).unwrap(); - let xpub: XPub = xprv.public_key(); + let xprv = XprvSecp256k1::new_from_path(seed, &path).unwrap(); + let xpub: XpubSecp256k1 = xprv.public_key(); println!("{}", xpub.to_string(Prefix::XPUB)); } From 860460c7b43ff9246dcfed4903e9cdf9de322426 Mon Sep 17 00:00:00 2001 From: aya015757881 <2581015450@qq.com> Date: Fri, 21 Jun 2024 16:56:24 +0800 Subject: [PATCH 2/3] refactor: cargo fmt --- .../bip32/extended_key/extended_private_key.rs | 6 ++---- .../bip32/extended_key/extended_public_key.rs | 5 +++-- anychain-kms/src/bip32/extended_key/mod.rs | 4 ++-- anychain-kms/src/bip32/mod.rs | 5 ++++- anychain-kms/src/bip32/private_key.rs | 2 +- anychain-kms/src/bip32/public_key.rs | 18 +++++++++++------- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/anychain-kms/src/bip32/extended_key/extended_private_key.rs b/anychain-kms/src/bip32/extended_key/extended_private_key.rs index 3e108cc..e17a9b3 100644 --- a/anychain-kms/src/bip32/extended_key/extended_private_key.rs +++ b/anychain-kms/src/bip32/extended_key/extended_private_key.rs @@ -1,9 +1,8 @@ //! Extended private keys use crate::bip32::{ - ChildNumber, Depth, Error, ExtendedKey, ExtendedKeyAttrs, - ExtendedPublicKey, HmacSha512, KeyFingerprint, Prefix, - PrivateKey, PublicKey, Result, KEY_SIZE, + ChildNumber, Depth, Error, ExtendedKey, ExtendedKeyAttrs, ExtendedPublicKey, HmacSha512, + KeyFingerprint, Prefix, PrivateKey, PublicKey, Result, KEY_SIZE, }; use core::{ fmt::{self, Debug}, @@ -28,7 +27,6 @@ const BIP39_DOMAIN_SEPARATOR: [u8; 12] = [ /// Extended private secp256k1 ECDSA signing key. pub type XprvSecp256k1 = ExtendedPrivateKey; - /// Extended private keys derived using BIP32. /// /// Generic around a [`PrivateKey`] type. When the `secp256k1` feature of this diff --git a/anychain-kms/src/bip32/extended_key/extended_public_key.rs b/anychain-kms/src/bip32/extended_key/extended_public_key.rs index eb622ad..9ad7f04 100644 --- a/anychain-kms/src/bip32/extended_key/extended_public_key.rs +++ b/anychain-kms/src/bip32/extended_key/extended_public_key.rs @@ -1,7 +1,8 @@ //! Extended public keys use crate::bip32::{ - ChildNumber, DerivationPath, Error, ExtendedKey, ExtendedKeyAttrs, ExtendedPrivateKey, HmacSha512, KeyFingerprint, Prefix, PrivateKey, PublicKey, Result, KEY_SIZE + ChildNumber, DerivationPath, Error, ExtendedKey, ExtendedKeyAttrs, ExtendedPrivateKey, + HmacSha512, KeyFingerprint, Prefix, PrivateKey, PublicKey, Result, KEY_SIZE, }; use core::str::FromStr; use hmac::Mac; @@ -97,7 +98,7 @@ where let mut key_bytes = [0u8; KEY_SIZE + 1]; key_bytes.copy_from_slice(&self.to_bytes()); key_bytes - } + }, } } diff --git a/anychain-kms/src/bip32/extended_key/mod.rs b/anychain-kms/src/bip32/extended_key/mod.rs index 1c14ef5..9db74ea 100644 --- a/anychain-kms/src/bip32/extended_key/mod.rs +++ b/anychain-kms/src/bip32/extended_key/mod.rs @@ -71,7 +71,7 @@ impl FromStr for ExtendedKey { fn from_str(base58: &str) -> Result { let mut bytes = [0u8; Self::BYTE_SIZE + 4]; // with 4-byte checksum let decoded_len = bs58::decode(base58).with_check(None).into(&mut bytes)?; - + if decoded_len != Self::BYTE_SIZE { return Err(Error::Decode); } @@ -114,8 +114,8 @@ impl Drop for ExtendedKey { #[cfg(test)] mod tests { + use crate::bip32::{DerivationPath, ExtendedKey}; use crate::bip32::{Prefix, XprvSecp256k1}; - use crate::bip32::{DerivationPath, ExtendedKey,}; use alloc::string::ToString; use hex_literal::hex; diff --git a/anychain-kms/src/bip32/mod.rs b/anychain-kms/src/bip32/mod.rs index 6a54a86..c1d91de 100644 --- a/anychain-kms/src/bip32/mod.rs +++ b/anychain-kms/src/bip32/mod.rs @@ -12,7 +12,10 @@ pub use extended_key::{ attrs::ExtendedKeyAttrs, extended_private_key::ExtendedPrivateKey, extended_public_key::ExtendedPublicKey, ExtendedKey, }; -pub use extended_key::{extended_private_key::XprvSecp256k1, extended_public_key::XpubSecp256k1}; +pub use extended_key::{ + extended_private_key::XprvSecp256k1, + extended_public_key::{XpubEd25519, XpubSecp256k1}, +}; pub use prefix::Prefix; pub use private_key::PrivateKey; pub use public_key::PublicKey; diff --git a/anychain-kms/src/bip32/private_key.rs b/anychain-kms/src/bip32/private_key.rs index 00dde70..70c2a0c 100644 --- a/anychain-kms/src/bip32/private_key.rs +++ b/anychain-kms/src/bip32/private_key.rs @@ -1,6 +1,6 @@ //! Trait for deriving child keys on a given type. -use crate::bip32::{PublicKey, Result, KEY_SIZE}; +use crate::bip32::{PublicKey, Result}; use crate::bip32::{Error, XprvSecp256k1}; diff --git a/anychain-kms/src/bip32/public_key.rs b/anychain-kms/src/bip32/public_key.rs index 7534c10..fa06218 100644 --- a/anychain-kms/src/bip32/public_key.rs +++ b/anychain-kms/src/bip32/public_key.rs @@ -1,12 +1,14 @@ //! Trait for deriving child keys on a given type. -use crate::bip32::{KeyFingerprint, Result, KEY_SIZE}; -use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE as G, edwards::EdwardsPoint, scalar::Scalar}; +use crate::bip32::{KeyFingerprint, Result}; +use curve25519_dalek::{ + constants::ED25519_BASEPOINT_TABLE as G, edwards::EdwardsPoint, scalar::Scalar, +}; use group::GroupEncoding; use ripemd::Ripemd160; use sha2::{Digest, Sha256}; -use crate::bip32::{XpubSecp256k1, Error}; +use crate::bip32::Error; /// Trait for key types which can be derived using BIP32. pub trait PublicKey: Sized { @@ -52,11 +54,13 @@ impl PublicKey for libsecp256k1::PublicKey { impl PublicKey for ed25519_dalek::PublicKey { fn from_bytes(bytes: Vec) -> Result { if bytes.len() == 32 { - Ok(ed25519_dalek::PublicKey::from_bytes(&bytes) - .or(Err(crate::bip32::Error::Crypto))?) + Ok( + ed25519_dalek::PublicKey::from_bytes(&bytes) + .or(Err(crate::bip32::Error::Crypto))?, + ) } else if bytes.len() == 33 { Ok(ed25519_dalek::PublicKey::from_bytes(&bytes[1..]) - .or(Err(crate::bip32::Error::Crypto))?) + .or(Err(crate::bip32::Error::Crypto))?) } else { Err(crate::bip32::Error::Crypto) } @@ -72,7 +76,7 @@ impl PublicKey for ed25519_dalek::PublicKey { _tweak.copy_from_slice(&tweak); let point = EdwardsPoint::from_bytes(pk).unwrap(); let tweak = &Scalar::from_bytes_mod_order(_tweak) * G; - let child = point + &tweak; + let child = point + tweak; let child = child.to_bytes(); Ok(ed25519_dalek::PublicKey::from_bytes(&child).unwrap()) } From 05bfc0f02b00746bb7cb22ff81052fbafaae01bb Mon Sep 17 00:00:00 2001 From: aya015757881 <2581015450@qq.com> Date: Fri, 21 Jun 2024 16:56:51 +0800 Subject: [PATCH 3/3] refactor: version to 0.1.7 --- Cargo.lock | 2 +- anychain-kms/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index abb49a0..5b9a388 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,7 +240,7 @@ dependencies = [ [[package]] name = "anychain-kms" -version = "0.1.6" +version = "0.1.7" dependencies = [ "anyhow", "bs58 0.4.0", diff --git a/anychain-kms/Cargo.toml b/anychain-kms/Cargo.toml index f7cf324..0440a94 100644 --- a/anychain-kms/Cargo.toml +++ b/anychain-kms/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "anychain-kms" description = "A Rust library providing Key Management Schema for AnyChain. Handles general security and signature algorithms." -version = "0.1.6" +version = "0.1.7" keywords = ["cryptography", "security", "signature", "algorithm"] # Workspace inherited keys