diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 68d7bbd..03b9cd0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -7,6 +7,7 @@ on: env: CARGO_TERM_COLOR: always CARGO_NET_GIT_FETCH_WITH_CLI: true + RUSTFLAGS: -D warnings jobs: build-bare: diff --git a/Cargo.lock b/Cargo.lock index f742113..818d3bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -164,14 +164,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cggmp21-keygen" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e4deddd2b4d08a454d34897e1ad20bb23fea47b5df4d9856d46ab918beb6e28" +checksum = "035c720da632d7303d2286f9ba4923dfba646f99465695ef21fc72b9e330476f" dependencies = [ "digest", "displaydoc", "generic-ec", "generic-ec-zkp", + "hd-wallet", "hex", "key-share", "rand_core", @@ -179,7 +180,6 @@ dependencies = [ "serde", "serde_with", "sha2", - "slip-10", "thiserror", "udigest", ] @@ -529,9 +529,9 @@ dependencies = [ [[package]] name = "generic-ec" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cc122ac7a2ebc45550f766cd23a9040f6d15db440230b0888e5645e8eb2cb4" +checksum = "750adbb28dfd2ed99334e36f16d878a8620839df430c0c4b0f745c3c48c2e22e" dependencies = [ "curve25519-dalek", "digest", @@ -580,9 +580,9 @@ dependencies = [ [[package]] name = "generic-ec-zkp" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f360d09b920f154726c654363cef62ba3497c8037b82f94aa17df215fc05d6f9" +checksum = "ec6e906724b1f70cd13e7e2455165300f1ef2fd200e40aa6207a046a36af839d" dependencies = [ "generic-array", "generic-ec", @@ -628,13 +628,13 @@ dependencies = [ "digest", "futures", "generic-ec", + "hd-wallet", "k256", "key-share", "rand_core", "round-based", "serde", "sha2", - "slip-10", "static_assertions", ] @@ -671,6 +671,19 @@ dependencies = [ "subtle", ] +[[package]] +name = "hd-wallet" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4111172d55e2217d689df0fc9038cbd65826dd842ac5df9a04363accfa0769a3" +dependencies = [ + "generic-array", + "generic-ec", + "hmac", + "sha2", + "subtle", +] + [[package]] name = "hex" version = "0.4.3" @@ -734,18 +747,18 @@ dependencies = [ [[package]] name = "key-share" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1057564dad5e55cedefc355e51c6c669ccb124f343b80d02b9d6eaf9471e042e" +checksum = "8ea364cb2397405d8c79afd3de173ca7e2e1d83a4ddd94d359263480ad96f06f" dependencies = [ "displaydoc", "generic-ec", "generic-ec-zkp", + "hd-wallet", "hex", "rand_core", "serde", "serde_with", - "slip-10", "thiserror", ] @@ -1321,9 +1334,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "udigest" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a435a16abb7029ba807a45242367b087dd767e87e2e5ebc5f0e4189ea114a23" +checksum = "81cd61fa9fb78569e9fe34acf0048fd8cb9ebdbacc47af740745487287043ff0" dependencies = [ "digest", "udigest-derive", @@ -1331,9 +1344,9 @@ dependencies = [ [[package]] name = "udigest-derive" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6296c12e792dbc59565a58920d8d1842997ad5a72ddf2a51f70d70bd7af2ba" +checksum = "603329303137e0d59238ee4d6b9c085eada8e2a9d20666f3abd9dadf8f8543f4" dependencies = [ "proc-macro2", "quote", diff --git a/givre/Cargo.toml b/givre/Cargo.toml index 541304a..d4fc96d 100644 --- a/givre/Cargo.toml +++ b/givre/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cggmp21-keygen = { version = "0.3", default-features = false, optional = true } -key-share = { version = "0.4.1", default-features = false } +cggmp21-keygen = { version = "0.4", default-features = false, optional = true } +key-share = { version = "0.5", default-features = false } generic-ec = { version = "0.4", default-features = false, features = ["alloc"] } @@ -22,7 +22,7 @@ sha2 = { version = "0.10", default-features = false, optional = true } serde = { version = "1", default-features = false, features = ["derive"], optional = true } -slip-10 = { version = "0.4", default-features = false, optional = true } +hd-wallet = { version = "0.5", default-features = false, optional = true } [dev-dependencies] rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } @@ -36,7 +36,7 @@ std = [ "cggmp21-keygen?/std", "round-based?/std", "serde?/std", - "slip-10?/std" + "hd-wallet?/std" ] cggmp21-keygen = ["dep:cggmp21-keygen"] @@ -46,11 +46,11 @@ serde = ["dep:serde", "key-share/serde"] spof = ["key-share/spof"] -hd-wallets = ["slip-10", "key-share/hd-wallets", "cggmp21-keygen?/hd-wallets"] +hd-wallet = ["dep:hd-wallet", "key-share/hd-wallet", "cggmp21-keygen?/hd-wallet"] taproot = ["sha2"] all-ciphersuites = ["ciphersuite-secp256k1", "ciphersuite-ed25519", "ciphersuite-bitcoin"] -ciphersuite-secp256k1 = ["generic-ec/curve-secp256k1", "k256", "sha2", "static_assertions"] -ciphersuite-ed25519 = ["generic-ec/curve-ed25519", "sha2"] -ciphersuite-bitcoin = ["ciphersuite-secp256k1", "taproot"] +ciphersuite-secp256k1 = ["generic-ec/curve-secp256k1", "hd-wallet?/curve-secp256k1", "k256", "sha2", "static_assertions"] +ciphersuite-ed25519 = ["generic-ec/curve-ed25519", "hd-wallet?/curve-ed25519", "sha2"] +ciphersuite-bitcoin = ["ciphersuite-secp256k1", "hd-wallet?/curve-secp256k1", "taproot"] diff --git a/givre/src/ciphersuite.rs b/givre/src/ciphersuite.rs index 631df3d..340d71c 100644 --- a/givre/src/ciphersuite.rs +++ b/givre/src/ciphersuite.rs @@ -53,6 +53,10 @@ pub trait Ciphersuite: Sized + Clone + Copy + core::fmt::Debug { /// Indicates that the ciphersuite outputs taproot-compatible signatures const IS_TAPROOT: bool = false; + /// HD derivation algorithm recommended to be used with this ciphersuite + #[cfg(feature = "hd-wallet")] + type HdAlgo: hd_wallet::HdWallet; + /// `H1` hash function as defined in the draft /// /// Accepts a list of bytestring, that'll be concatenated before hashing. diff --git a/givre/src/ciphersuite/bitcoin.rs b/givre/src/ciphersuite/bitcoin.rs index 4bdbac7..d493cec 100644 --- a/givre/src/ciphersuite/bitcoin.rs +++ b/givre/src/ciphersuite/bitcoin.rs @@ -17,6 +17,9 @@ impl Ciphersuite for Bitcoin { const IS_TAPROOT: bool = true; + #[cfg(feature = "hd-wallet")] + type HdAlgo = hd_wallet::Slip10; + fn h1(msg: &[&[u8]]) -> generic_ec::Scalar { Secp256k1::h1(msg) } diff --git a/givre/src/ciphersuite/ed25519.rs b/givre/src/ciphersuite/ed25519.rs index 8cb7df3..dbec0e4 100644 --- a/givre/src/ciphersuite/ed25519.rs +++ b/givre/src/ciphersuite/ed25519.rs @@ -14,6 +14,9 @@ impl Ciphersuite for Ed25519 { type Digest = sha2::Sha512; type MultiscalarMul = generic_ec::multiscalar::Dalek; + #[cfg(feature = "hd-wallet")] + type HdAlgo = hd_wallet::Edwards; + fn h1(msg: &[&[u8]]) -> generic_ec::Scalar { let mut hash = sha2::Sha512::new() .chain_update(Self::NAME) diff --git a/givre/src/ciphersuite/secp256k1.rs b/givre/src/ciphersuite/secp256k1.rs index 6af3c33..81a4ee3 100644 --- a/givre/src/ciphersuite/secp256k1.rs +++ b/givre/src/ciphersuite/secp256k1.rs @@ -14,6 +14,9 @@ impl Ciphersuite for Secp256k1 { type Digest = sha2::Sha256; type MultiscalarMul = generic_ec::multiscalar::Default; + #[cfg(feature = "hd-wallet")] + type HdAlgo = hd_wallet::Slip10; + fn h1(msg: &[&[u8]]) -> generic_ec::Scalar { hash_to_scalar(msg, &[Self::NAME.as_bytes(), b"rho"]) } diff --git a/givre/src/lib.rs b/givre/src/lib.rs index 46d5863..88d81b7 100644 --- a/givre/src/lib.rs +++ b/givre/src/lib.rs @@ -174,6 +174,8 @@ extern crate std; extern crate alloc; pub use generic_ec; +#[cfg(feature = "hd-wallet")] +pub use hd_wallet; #[cfg(feature = "full-signing")] pub use round_based; @@ -202,6 +204,9 @@ pub mod key_share { } mod error { + // It's currently only needed in full signing, but better keep it always in the code + #![cfg_attr(not(feature = "full-signing"), allow(dead_code, unused_imports))] + #[cfg(feature = "std")] pub use std::error::Error as StdError; diff --git a/givre/src/signing/aggregate.rs b/givre/src/signing/aggregate.rs index 4a4edb7..53145c5 100644 --- a/givre/src/signing/aggregate.rs +++ b/givre/src/signing/aggregate.rs @@ -22,7 +22,7 @@ use super::{round1::PublicCommitments, round2::SigShare, utils}; serde(bound = "") )] /// Schnorr Signature -pub struct Signature { +pub struct Signature { /// $R$ component of the signature pub r: crate::ciphersuite::NormalizedPoint>, /// $z$ component of the signature @@ -129,16 +129,43 @@ impl<'a, C: Ciphersuite> AggregateOptions<'a, C> { /// Specifies HD derivation path /// + /// Uses default HD derivation algorithm defined for the ciphersuite in [`Ciphersuite::HdAlgo`]. + /// If you need to use another algorithm, use [`AggregateOptions::set_derivation_path_with_algo`]. + /// /// If called twice, the second call overwrites the first. /// /// Returns error if the key doesn't support HD derivation, or if the path is invalid - #[cfg(feature = "hd-wallets")] + #[cfg(feature = "hd-wallet")] pub fn set_derivation_path( self, path: impl IntoIterator, - ) -> Result>::Error>> + ) -> Result< + Self, + crate::key_share::HdError<>::Error>, + > + where + hd_wallet::NonHardenedIndex: TryFrom, + { + self.set_derivation_path_with_algo::(path) + } + + /// Specifies HD derivation path + /// + /// Uses HD derivation algorithm defined by [`hd_wallet::HdWallet`] trait. + /// + /// If called twice, the second call overwrites the first. + /// + /// Returns error if the key doesn't support HD derivation, or if the path is invalid + #[cfg(feature = "hd-wallet")] + pub fn set_derivation_path_with_algo, Index>( + self, + path: impl IntoIterator, + ) -> Result< + Self, + crate::key_share::HdError<>::Error>, + > where - slip_10::NonHardenedIndex: TryFrom, + hd_wallet::NonHardenedIndex: TryFrom, { use crate::key_share::HdError; @@ -146,8 +173,8 @@ impl<'a, C: Ciphersuite> AggregateOptions<'a, C> { .key_info .extended_public_key() .ok_or(HdError::DisabledHd)?; - let additive_shift = - utils::derive_additive_shift(public_key, path).map_err(HdError::InvalidPath)?; + let additive_shift = utils::derive_additive_shift::(public_key, path) + .map_err(HdError::InvalidPath)?; Ok(self.dangerous_set_hd_additive_shift(additive_shift)) } @@ -156,6 +183,7 @@ impl<'a, C: Ciphersuite> AggregateOptions<'a, C> { /// /// CAUTION: additive shift MUST BE derived from the extended public key obtained from /// the key share which is used for signing by calling [`utils::derive_additive_shift`]. + #[cfg(feature = "hd-wallet")] pub(crate) fn dangerous_set_hd_additive_shift( mut self, hd_additive_shift: Scalar, diff --git a/givre/src/signing/full_signing.rs b/givre/src/signing/full_signing.rs index 26b3392..4f2bf0c 100644 --- a/givre/src/signing/full_signing.rs +++ b/givre/src/signing/full_signing.rs @@ -64,16 +64,43 @@ impl<'a, C: Ciphersuite> SigningBuilder<'a, C> { /// Specifies HD derivation path /// + /// Uses default HD derivation algorithm defined for the ciphersuite in [`Ciphersuite::HdAlgo`]. + /// If you need to use another algorithm, use [`SigningBuilder::set_derivation_path_with_algo`]. + /// /// If called twice, the second call overwrites the first. /// /// Returns error if the key doesn't support HD derivation, or if the path is invalid - #[cfg(feature = "hd-wallets")] + #[cfg(feature = "hd-wallet")] pub fn set_derivation_path( + self, + path: impl IntoIterator, + ) -> Result< + Self, + crate::key_share::HdError<>::Error>, + > + where + hd_wallet::NonHardenedIndex: TryFrom, + { + self.set_derivation_path_with_algo::(path) + } + + /// Specifies HD derivation path + /// + /// Uses HD derivation algorithm defined by [`hd_wallet::HdWallet`] trait. + /// + /// If called twice, the second call overwrites the first. + /// + /// Returns error if the key doesn't support HD derivation, or if the path is invalid + #[cfg(feature = "hd-wallet")] + pub fn set_derivation_path_with_algo, Index>( mut self, path: impl IntoIterator, - ) -> Result>::Error>> + ) -> Result< + Self, + crate::key_share::HdError<>::Error>, + > where - slip_10::NonHardenedIndex: TryFrom, + hd_wallet::NonHardenedIndex: TryFrom, { use crate::key_share::HdError; @@ -81,8 +108,8 @@ impl<'a, C: Ciphersuite> SigningBuilder<'a, C> { .key_share .extended_public_key() .ok_or(HdError::DisabledHd)?; - let additive_shift = - utils::derive_additive_shift(public_key, path).map_err(HdError::InvalidPath)?; + let additive_shift = utils::derive_additive_shift::(public_key, path) + .map_err(HdError::InvalidPath)?; self.hd_additive_shift = Some(additive_shift); Ok(self) } @@ -167,6 +194,7 @@ impl<'a, C: Ciphersuite> SigningBuilder<'a, C> { } } +#[allow(clippy::too_many_arguments)] async fn signing( party: M, rng: &mut (impl RngCore + CryptoRng), @@ -220,11 +248,11 @@ where let mut options = crate::signing::round2::SigningOptions::::new(key_share, nonces, msg, &signers_list); - #[cfg(feature = "hd-wallets")] + #[cfg(feature = "hd-wallet")] if let Some(additive_shift) = hd_additive_shift { options = options.dangerous_set_hd_additive_shift(additive_shift); } - if cfg!(not(feature = "hd-wallets")) && hd_additive_shift.is_some() { + if cfg!(not(feature = "hd-wallet")) && hd_additive_shift.is_some() { return Err(Bug::AdditiveShiftWithoutHdFeature.into()); } #[cfg(feature = "taproot")] @@ -259,7 +287,7 @@ where let key_info: &KeyInfo<_> = key_share.as_ref(); let mut options = crate::signing::aggregate::AggregateOptions::new(key_info, &signers_list, msg); - #[cfg(feature = "hd-wallets")] + #[cfg(feature = "hd-wallet")] if let Some(additive_shift) = hd_additive_shift { options = options.dangerous_set_hd_additive_shift(additive_shift); } @@ -281,9 +309,11 @@ enum SigningOutput { /// Interactive Signing error #[derive(Debug)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub struct FullSigningError(Reason); #[derive(Debug)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] enum Reason { NonTaprootCiphersuite, NOverflowsU16, @@ -296,6 +326,7 @@ enum Reason { } #[derive(Debug)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] enum IoError { Send(Box), Recv(Box), @@ -311,6 +342,7 @@ impl IoError { } #[derive(Debug)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] enum Bug { UnexpectedOutput, AdditiveShiftWithoutHdFeature, diff --git a/givre/src/signing/round2.rs b/givre/src/signing/round2.rs index 75c367a..621b57c 100644 --- a/givre/src/signing/round2.rs +++ b/givre/src/signing/round2.rs @@ -82,16 +82,43 @@ impl<'a, C: Ciphersuite> SigningOptions<'a, C> { /// Specifies HD derivation path /// + /// Uses default HD derivation algorithm defined for the ciphersuite in [`Ciphersuite::HdAlgo`]. + /// If you need to use another algorithm, use [`SigningOptions::set_derivation_path_with_algo`]. + /// /// If called twice, the second call overwrites the first. /// /// Returns error if the key doesn't support HD derivation, or if the path is invalid - #[cfg(feature = "hd-wallets")] + #[cfg(feature = "hd-wallet")] pub fn set_derivation_path( self, path: impl IntoIterator, - ) -> Result>::Error>> + ) -> Result< + Self, + crate::key_share::HdError<>::Error>, + > + where + hd_wallet::NonHardenedIndex: TryFrom, + { + self.set_derivation_path_with_algo::(path) + } + + /// Specifies HD derivation path + /// + /// Uses HD derivation algorithm defined by [`hd_wallet::HdWallet`] trait. + /// + /// If called twice, the second call overwrites the first. + /// + /// Returns error if the key doesn't support HD derivation, or if the path is invalid + #[cfg(feature = "hd-wallet")] + pub fn set_derivation_path_with_algo, Index>( + self, + path: impl IntoIterator, + ) -> Result< + Self, + crate::key_share::HdError<>::Error>, + > where - slip_10::NonHardenedIndex: TryFrom, + hd_wallet::NonHardenedIndex: TryFrom, { use crate::key_share::HdError; @@ -99,8 +126,8 @@ impl<'a, C: Ciphersuite> SigningOptions<'a, C> { .key_share .extended_public_key() .ok_or(HdError::DisabledHd)?; - let additive_shift = - utils::derive_additive_shift(public_key, path).map_err(HdError::InvalidPath)?; + let additive_shift = utils::derive_additive_shift::(public_key, path) + .map_err(HdError::InvalidPath)?; Ok(self.dangerous_set_hd_additive_shift(additive_shift)) } @@ -108,6 +135,7 @@ impl<'a, C: Ciphersuite> SigningOptions<'a, C> { /// /// CAUTION: additive shift MUST BE derived from the extended public key obtained from /// the key share which is used for signing by calling [`utils::derive_additive_shift`]. + #[cfg(feature = "hd-wallet")] pub(crate) fn dangerous_set_hd_additive_shift( mut self, hd_additive_shift: Scalar, @@ -423,6 +451,7 @@ fn normalize_key_share( pub struct SigningError(Reason); #[derive(Debug)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] enum Reason { #[cfg(feature = "taproot")] MissingTaprootMerkleRoot, diff --git a/givre/src/signing/utils.rs b/givre/src/signing/utils.rs index 7df9de7..078919f 100644 --- a/givre/src/signing/utils.rs +++ b/givre/src/signing/utils.rs @@ -129,19 +129,19 @@ pub fn share_preimage( } } -#[cfg(feature = "hd-wallets")] -pub fn derive_additive_shift( - mut epub: slip_10::ExtendedPublicKey, +#[cfg(feature = "hd-wallet")] +pub fn derive_additive_shift, Index>( + mut epub: hd_wallet::ExtendedPublicKey, path: impl IntoIterator, -) -> Result, >::Error> +) -> Result, >::Error> where - slip_10::NonHardenedIndex: TryFrom, + hd_wallet::NonHardenedIndex: TryFrom, { let mut additive_shift = Scalar::::zero(); for child_index in path { - let child_index: slip_10::NonHardenedIndex = child_index.try_into()?; - let shift = slip_10::derive_public_shift(&epub, child_index); + let child_index: hd_wallet::NonHardenedIndex = child_index.try_into()?; + let shift = Hd::derive_public_shift(&epub, child_index); additive_shift += shift.shift; epub = shift.child_public_key; diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 6495a04..112020d 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -7,7 +7,7 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -givre = { path = "../givre", features = ["all-ciphersuites", "cggmp21-keygen", "spof", "hd-wallets", "full-signing"] } +givre = { path = "../givre", features = ["all-ciphersuites", "cggmp21-keygen", "spof", "hd-wallet", "full-signing"] } generic-tests = "0.1" test-case = "3.3" diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 9f51e95..6c38f41 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result}; use givre::{ - generic_ec::{NonZero, Point}, + generic_ec::{Curve, NonZero, Point}, signing::aggregate::Signature, Ciphersuite, }; @@ -10,9 +10,6 @@ pub trait ExternalVerifier: Ciphersuite { /// the message needs to be an output of a hash function with fixed size const REQUIRED_MESSAGE_SIZE: Option = None; - /// Indicates whether external lib supports HD derivation - const SUPPORTS_HD: bool = false; - /// Verifies signature using external library /// /// Takes arguments: @@ -43,21 +40,28 @@ pub struct InvalidSignature; impl ExternalVerifier for givre::ciphersuite::Ed25519 { fn verify_sig( pk: &NonZero>, - _chain_code: Option<[u8; 32]>, + chain_code: Option<[u8; 32]>, hd_derivation_path: &[u32], taproot_merkle_root: Option>, sig: &Signature, msg: &[u8], ) -> Result<()> { - if !hd_derivation_path.is_empty() { - anyhow::bail!("HD derivation is not supported by ed25519_dalek") - } if taproot_merkle_root.is_some() { anyhow::bail!("taproot is not compatible with EdDSA") } + let pk = if !hd_derivation_path.is_empty() { + derive_child_key::<_, Self::HdAlgo>( + pk, + chain_code.context("missing chain code")?, + hd_derivation_path, + )? + } else { + *pk + }; + let pk = ed25519::VerifyingKey::from_bytes( - &Self::serialize_point(pk) + &Self::serialize_point(&pk) .as_bytes() .try_into() .expect("wrong size of pk"), @@ -76,16 +80,13 @@ impl ExternalVerifier for givre::ciphersuite::Secp256k1 { fn verify_sig( _pk: &NonZero>, _chain_code: Option<[u8; 32]>, - hd_derivation_path: &[u32], + _hd_derivation_path: &[u32], taproot_merkle_root: Option>, _sig: &Signature, _msg: &[u8], ) -> Result<()> { - if !hd_derivation_path.is_empty() { - anyhow::bail!("HD derivation is not supported by ed25519_dalek") - } if taproot_merkle_root.is_some() { - anyhow::bail!("taproot is not compatible with EdDSA") + anyhow::bail!("taproot is not compatible with Secp256k1 ciphersuite") } // No external verifier for secp256k1 ciphersuite @@ -123,7 +124,7 @@ impl ExternalVerifier for givre::ciphersuite::Bitcoin { let child_index = bitcoin::bip32::ChildNumber::from_normal_idx(child_index) .context("only non-hardened derivation is supported")?; xpub = xpub - .ckd_pub(&secp256k1::SECP256K1, child_index) + .ckd_pub(secp256k1::SECP256K1, child_index) .context("child derivation")?; } @@ -137,7 +138,7 @@ impl ExternalVerifier for givre::ciphersuite::Bitcoin { .map(bitcoin::TapNodeHash::assume_hidden); let (pk, _) = - bitcoin::key::TapTweak::tap_tweak(pk, &secp256k1::SECP256K1, taproot_merkle_root); + bitcoin::key::TapTweak::tap_tweak(pk, secp256k1::SECP256K1, taproot_merkle_root); let mut signature = [0u8; 64]; assert_eq!(signature.len(), Signature::::serialized_len()); @@ -168,7 +169,29 @@ pub fn random_taproot_merkle_root(rng: &mut impl rand::Rng) -> Option<[u8; 32]> /// between 0 to 3 indexes pub fn random_hd_path(rng: &mut impl rand::Rng) -> Vec { let len = rng.gen_range(0..=3); - std::iter::repeat_with(|| rng.gen_range(0..slip_10::H)) + std::iter::repeat_with(|| rng.gen_range(0..givre::hd_wallet::H)) .take(len) .collect() } + +/// Derives an HD child key using `HdAlgo` algorithm +/// +/// It's not desirable to use this function. In the tests, we should rather use other libraries +/// from what we use in `givre` implementation itself. However, not all derivation methods have +/// other libraries than `hd_wallet` +pub fn derive_child_key>( + parent_pk: &NonZero>, + parent_child_code: [u8; 32], + derivation_path: &[u32], +) -> Result>> { + let parent_epk = givre::hd_wallet::ExtendedPublicKey { + public_key: **parent_pk, + chain_code: parent_child_code, + }; + let child_epk = HdAlgo::try_derive_child_public_key_with_path( + &parent_epk, + derivation_path.iter().map(|i| (*i).try_into()), + ) + .context("invalid path")?; + NonZero::from_point(child_epk.public_key).context("derived key is zero") +} diff --git a/tests/tests/it/interactive.rs b/tests/tests/it/interactive.rs index 320849f..0465865 100644 --- a/tests/tests/it/interactive.rs +++ b/tests/tests/it/interactive.rs @@ -36,11 +36,12 @@ mod generic { if let Some(t) = t { givre::keygen::(eid, j, n) .set_threshold(t) - .hd_wallet(C::SUPPORTS_HD) + .hd_wallet(true) .start(&mut rng, party_threshold) .await } else { givre::keygen(eid, j, n) + .hd_wallet(true) .start(&mut rng, party_nonthreshold) .await } @@ -63,11 +64,7 @@ mod generic { println!("message to sign: {}", hex::encode(msg)); // HD derivation path - let derivation_path = if C::SUPPORTS_HD { - givre_tests::random_hd_path(&mut rng) - } else { - vec![] - }; + let derivation_path = givre_tests::random_hd_path(&mut rng); println!("HD path: {derivation_path:?}"); // Taproot merkle root diff --git a/tests/tests/it/main.rs b/tests/tests/it/main.rs index 510e406..79019d7 100644 --- a/tests/tests/it/main.rs +++ b/tests/tests/it/main.rs @@ -56,11 +56,7 @@ mod generic { println!("message to sign: {}", hex::encode(&message)); // HD derivation path - let derivation_path = if C::SUPPORTS_HD { - givre_tests::random_hd_path(&mut rng) - } else { - vec![] - }; + let derivation_path = givre_tests::random_hd_path(&mut rng); println!("HD path: {derivation_path:?}"); // Taproot merkle root diff --git a/wasm/no_std/Cargo.lock b/wasm/no_std/Cargo.lock index 1ab05c7..f7533ca 100644 --- a/wasm/no_std/Cargo.lock +++ b/wasm/no_std/Cargo.lock @@ -37,14 +37,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cggmp21-keygen" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e4deddd2b4d08a454d34897e1ad20bb23fea47b5df4d9856d46ab918beb6e28" +checksum = "035c720da632d7303d2286f9ba4923dfba646f99465695ef21fc72b9e330476f" dependencies = [ "digest", "displaydoc", "generic-ec", "generic-ec-zkp", + "hd-wallet", "hex", "key-share", "rand_core", @@ -52,7 +53,6 @@ dependencies = [ "serde", "serde_with", "sha2", - "slip-10", "udigest", ] @@ -318,9 +318,9 @@ dependencies = [ [[package]] name = "generic-ec" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cc122ac7a2ebc45550f766cd23a9040f6d15db440230b0888e5645e8eb2cb4" +checksum = "750adbb28dfd2ed99334e36f16d878a8620839df430c0c4b0f745c3c48c2e22e" dependencies = [ "curve25519-dalek", "digest", @@ -369,9 +369,9 @@ dependencies = [ [[package]] name = "generic-ec-zkp" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f360d09b920f154726c654363cef62ba3497c8037b82f94aa17df215fc05d6f9" +checksum = "ec6e906724b1f70cd13e7e2455165300f1ef2fd200e40aa6207a046a36af839d" dependencies = [ "generic-array", "generic-ec", @@ -388,13 +388,13 @@ dependencies = [ "cggmp21-keygen", "digest", "generic-ec", + "hd-wallet", "k256", "key-share", "rand_core", "round-based", "serde", "sha2", - "slip-10", "static_assertions", ] @@ -416,6 +416,19 @@ dependencies = [ "subtle", ] +[[package]] +name = "hd-wallet" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4111172d55e2217d689df0fc9038cbd65826dd842ac5df9a04363accfa0769a3" +dependencies = [ + "generic-array", + "generic-ec", + "hmac", + "sha2", + "subtle", +] + [[package]] name = "hex" version = "0.4.3" @@ -458,18 +471,18 @@ dependencies = [ [[package]] name = "key-share" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1057564dad5e55cedefc355e51c6c669ccb124f343b80d02b9d6eaf9471e042e" +checksum = "8ea364cb2397405d8c79afd3de173ca7e2e1d83a4ddd94d359263480ad96f06f" dependencies = [ "displaydoc", "generic-ec", "generic-ec-zkp", + "hd-wallet", "hex", "rand_core", "serde", "serde_with", - "slip-10", ] [[package]] @@ -717,19 +730,6 @@ dependencies = [ "digest", ] -[[package]] -name = "slip-10" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c50b6de806a216bce1d98e9107e03f60b54abcfbe6be3935a528ab57c19f6a" -dependencies = [ - "generic-array", - "generic-ec", - "hmac", - "sha2", - "subtle", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -813,9 +813,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "udigest" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a435a16abb7029ba807a45242367b087dd767e87e2e5ebc5f0e4189ea114a23" +checksum = "81cd61fa9fb78569e9fe34acf0048fd8cb9ebdbacc47af740745487287043ff0" dependencies = [ "digest", "udigest-derive", @@ -823,9 +823,9 @@ dependencies = [ [[package]] name = "udigest-derive" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6296c12e792dbc59565a58920d8d1842997ad5a72ddf2a51f70d70bd7af2ba" +checksum = "603329303137e0d59238ee4d6b9c085eada8e2a9d20666f3abd9dadf8f8543f4" dependencies = [ "proc-macro2", "quote", diff --git a/wasm/no_std/Cargo.toml b/wasm/no_std/Cargo.toml index dc42626..dc70f98 100644 --- a/wasm/no_std/Cargo.toml +++ b/wasm/no_std/Cargo.toml @@ -14,6 +14,6 @@ features = [ "serde", "cggmp21-keygen", "full-signing", - "hd-wallets", + "hd-wallet", "taproot", ]