From b7f727a5c67f364ffd66ccb0f206fc0dd1a0407f Mon Sep 17 00:00:00 2001 From: Denis Varlakov Date: Wed, 17 Jul 2024 12:14:08 +0200 Subject: [PATCH] Second attempt to implement HD wallets Signed-off-by: Denis Varlakov --- givre/src/key_share.rs | 352 ++++++----------------------------------- 1 file changed, 47 insertions(+), 305 deletions(-) diff --git a/givre/src/key_share.rs b/givre/src/key_share.rs index 82f21bb..9cc88e1 100644 --- a/givre/src/key_share.rs +++ b/givre/src/key_share.rs @@ -2,9 +2,9 @@ //! //! This module re-exports type definitions from [`key_share`] crate. -#![allow(non_snake_case)] +#![allow(missing_docs)] // TODO: remove -use generic_ec::{Curve, NonZero, Point, Scalar, SecretScalar}; +use generic_ec::Curve; #[doc(inline)] pub use key_share::{ @@ -16,332 +16,74 @@ pub use key_share::{ #[doc(inline)] pub use key_share::reconstruct_secret_key; -use crate::ciphersuite::NormalizedPoint; - -mod sealed { - pub trait Sealed {} -} - -/// Any data structure that provides the same information as validated [`KeyInfo`] -pub trait KeyInfoLike: sealed::Sealed { - /// Curve type - type Curve: Curve; - - /// Public key corresponding to shared secret key - /// - /// Corresponds to [`KeyInfo::shared_public_key`] - fn shared_public_key(&self) -> Point; - - /// Returns public share of j-th signer - /// - /// Corresponds to [`DirtyKeyInfo::public_shares`] - fn public_share(&self, j: u16) -> Option>; - // /// Returns iterator over public shares of all signers, from signer with index - // /// `0` to signer with index `n-1` - // fn public_shares(&self) -> impl Iterator>>; - - /// Returns signing threshold - /// - /// Corresponds to [`KeyInfo::min_signers`] - fn min_signers(&self) -> u16; - /// Returns share preimage of j-th signer - /// - /// Corresponds to [`DirtyKeyInfo::share_preimage`] - fn share_preimage(&self, j: u16) -> Option>>; - - /// Indicates if it's an additive key share - /// - /// Returns: - /// * `true` if it's additive key share (implies that `min_signers == n`) - /// * `false` if it's polynomial key share (threshold can be anything in range `2 <= min_signers <= n`) - fn is_additive(&self) -> bool; - - /// Returns the chain code - /// - /// Corresponds to [`DirtyKeyInfo::chain_code`] - #[cfg(feature = "hd-wallets")] - fn chain_code(&self) -> Option; - - /// Returns a key share corresponding to the derived child key - /// - /// Returns error if the key share is not HD-capable, or if path is invalid - #[cfg(feature = "hd-wallets")] - fn with_hd_path( - self, - path: impl IntoIterator, - ) -> Result< - WithAdditiveShift, - HdError<>::Error>, - > - where - slip_10::NonHardenedIndex: TryFrom, - Self: Sized, - { - let epub = self.extended_public_key().ok_or(HdError::DisabledHd)?; - let shift = derive_additive_shift(epub, path).map_err(HdError::InvalidPath)?; - Ok(WithAdditiveShift::new(self, shift)) - } +pub struct With, K> { + inner: T::Out, } -/// Any data structure that provides the same information as validated [`KeyShare`] -/// -/// This trait provides read-only access to key share fields. [`KeyShare`] obviously is -/// the core structure that implements this trait. It may be composed with other wrappers -/// like [`HdChild`] to "modify" the key share. -pub trait KeyShareLike: KeyInfoLike { - /// Index of local party in key generation protocol - /// - /// Corresponds to [`DirtyKeyShare::i`] - fn i(&self) -> u16; - - /// Secret share - /// - /// Corresponds to [`DirtyKeyShare::x`] - fn x(&self) -> &SecretScalar; - - /// Returns the extended public key, if HD support was enabled - #[cfg(feature = "hd-wallets")] - fn extended_public_key(&self) -> Option> { - Some(slip_10::ExtendedPublicKey { - public_key: self.shared_public_key(), - chain_code: self.chain_code()?, - }) - } -} - -#[cfg(feature = "hd-wallets")] -fn derive_additive_shift( - mut epub: slip_10::ExtendedPublicKey, - path: impl IntoIterator, -) -> Result, >::Error> -where - slip_10::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); - - additive_shift += shift.shift; - epub = shift.child_public_key; +impl, K> core::ops::Deref for With { + type Target = T::Out; + fn deref(&self) -> &Self::Target { + &self.inner } - - Ok(additive_shift) } -impl sealed::Sealed for KeyShare {} -impl KeyInfoLike for KeyShare { - type Curve = E; +pub mod transformations { + use generic_ec::Curve; - fn shared_public_key(&self) -> Point { - *self.shared_public_key - } - fn public_share(&self, j: u16) -> Option> { - self.public_shares.get(usize::from(j)).map(|s| **s) - } + use super::{DirtyKeyShare, KeyShare}; - fn min_signers(&self) -> u16 { - self.min_signers() + pub trait TryTransform: super::sealed::Sealed { + type Out; + type Err; + fn transform(self, value: K) -> Result; } - fn share_preimage(&self, j: u16) -> Option>> { - (**self).share_preimage(j) + pub struct HdDerivation

{ + pub path: P, } + impl

super::sealed::Sealed for HdDerivation

{} + impl TryTransform> for HdDerivation

+ where + E: Curve, + P: IntoIterator, + slip_10::NonHardenedIndex: TryFrom, + { + type Out = DirtyKeyShare; + type Err = DeriveChildError<>::Error>; - fn is_additive(&self) -> bool { - self.vss_setup.is_none() + fn transform(self, value: KeyShare) -> Result { + todo!() + } } - fn chain_code(&self) -> Option { - self.chain_code - } -} -impl KeyShareLike for KeyShare { - fn i(&self) -> u16 { - self.i + pub struct DeriveChildError(DeriveChildReason); + enum DeriveChildReason { + DisabledHd, + InvalidPath(E), + InvalidChildKey, } - fn x(&self) -> &SecretScalar { - &self.x - } + pub(crate) struct Normalization; } -/// Normalizes the key share -/// -/// Makes sure that shared public key is normalized. Negates the shared secret -/// key if necessary. -/// -/// It's not exposed in public API, it's rather used internally during the -/// signing process. -pub(crate) struct WithNormalization { - key_share: K, - /// Indicates whether the shared secret key needs to be negated - needs_negation: bool, - normalized_pk: NormalizedPoint>, - normalized_share: Option>, +mod sealed { + pub trait Sealed {} } -impl WithNormalization -where - E: generic_ec::Curve, - C: crate::Ciphersuite, - K: KeyShareLike, +/// Any valid key share +pub trait AnyKeyShare: + core::ops::Deref> + sealed::Sealed { - pub fn new(key_share: K) -> Self { - let (pk, needs_negation) = - match NormalizedPoint::try_normalize(key_share.shared_public_key()) { - Ok(pk) => (pk, false), - Err(pk) => (pk, true), - }; - let share = if needs_negation { - Some(SecretScalar::new(&mut -key_share.x().as_ref())) - } else { - None - }; - Self { - key_share, - normalized_pk: pk, - normalized_share: share, - needs_negation, - } - } - - pub fn normalized_pk(&self) -> NormalizedPoint> { - self.normalized_pk - } } -impl sealed::Sealed for WithNormalization where C: crate::Ciphersuite {} -impl KeyInfoLike for WithNormalization -where - E: generic_ec::Curve, - C: crate::Ciphersuite, - K: KeyShareLike, -{ - type Curve = E; - - fn shared_public_key(&self) -> Point { - *self.normalized_pk() - } - - fn public_share(&self, j: u16) -> Option> { - if !self.needs_negation { - self.key_share.public_share(j) - } else { - self.key_share.public_share(j).map(|s| -s) - } - } - - fn min_signers(&self) -> u16 { - self.key_share.min_signers() - } - - fn share_preimage(&self, j: u16) -> Option>> { - self.key_share.share_preimage(j) - } - - fn is_additive(&self) -> bool { - self.key_share.is_additive() - } +impl sealed::Sealed for KeyShare {} +impl AnyKeyShare for KeyShare {} - fn chain_code(&self) -> Option { - self.key_share.chain_code() - } -} -impl KeyShareLike for WithNormalization +impl, K> sealed::Sealed for With {} +impl AnyKeyShare for With where - E: generic_ec::Curve, - C: crate::Ciphersuite, - K: KeyShareLike, + E: Curve, + T: transformations::TryTransform>, + K: AnyKeyShare, { - fn i(&self) -> u16 { - self.key_share.i() - } - - fn x(&self) -> &SecretScalar { - self.normalized_share - .as_ref() - .unwrap_or_else(|| self.key_share.x()) - } -} - -/// Adds additive shift to the key share -pub struct WithAdditiveShift { - shift: Scalar, - Shift: Point, - shifted_x: Option>, - /// When it's `None`, chain code is inherited from the `key_share` - new_chain_code: Option, - key_share: K, -} - -impl> WithAdditiveShift { - fn new(key_share: K, shift: Scalar) -> Self { - let Shift = Point::generator() * shift; - let shifted_x = if !key_share.is_additive() || key_share.i() == 0 { - // * For polynomial key share, we add an additive shift to each share - // * For additive key share, we add the shift only to the first key share - Some(SecretScalar::new(&mut (key_share.x() + shift))) - } else { - None - }; - Self { - shift, - Shift, - shifted_x, - new_chain_code: None, - key_share, - } - } - - fn set_chain_code(mut self, chain_code: slip_10::ChainCode) -> Self { - self.new_chain_code = Some(chain_code); - self - } -} - -impl sealed::Sealed for WithAdditiveShift {} -impl> KeyShareLike for WithAdditiveShift { - type Curve = E; - - fn shared_public_key(&self) -> Point { - self.key_share.shared_public_key() + self.Shift - } - - fn public_share(&self, j: u16) -> Option> { - // * For polynomial key share, we add an additive shift to each share - // * For additive key share, we add the shift only to the first key share - if !self.key_share.is_additive() || j == 0 { - self.key_share.public_share(j).map(|s| s + self.Shift) - } else { - self.key_share.public_share(j) - } - } - - fn min_signers(&self) -> u16 { - self.key_share.min_signers() - } - - fn share_preimage(&self, j: u16) -> Option>> { - self.key_share.share_preimage(j) - } - - fn is_additive(&self) -> bool { - self.key_share.is_additive() - } - - fn chain_code(&self) -> Option { - self.new_chain_code.or_else(|| self.key_share.chain_code()) - } - - fn i(&self) -> u16 { - self.key_share.i() - } - - fn x(&self) -> &SecretScalar { - self.shifted_x - .as_ref() - .unwrap_or_else(|| self.key_share.x()) - } }