diff --git a/Cargo.toml b/Cargo.toml index 7b437b1..a29376e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,10 @@ repository = "https://github.com/LFDT-Lockness/hd-wallet" [dependencies] generic-ec = { version = "0.4", default-features = false } -hmac = { version = "0.12", default-features = false } -sha2 = { version = "0.10", default-features = false } -subtle = { version = "2", default-features = false } -generic-array = "0.14" +hmac = { version = "0.12", default-features = false, optional = true } +sha2 = { version = "0.10", default-features = false, optional = true } +subtle = { version = "2", default-features = false, optional = true } +generic-array = { version = "0.14", default-features = false, optional = true } serde = { version = "1", default-features = false, features = ["derive"], optional = true } @@ -27,13 +27,24 @@ rug = "1.26" generic-ec = { version = "0.4", default-features = false, features = ["curve-stark"] } [features] +default = [] std = [] -curve-secp256k1 = ["generic-ec/curve-secp256k1"] -curve-secp256r1 = ["generic-ec/curve-secp256r1"] -curve-ed25519 = ["generic-ec/curve-ed25519"] +# Adds support of secp256k1 curve to slip10 derivation +curve-secp256k1 = ["generic-ec/curve-secp256k1", "slip10"] +# Adds support of secp256r1 curve to slip10 derivation +curve-secp256r1 = ["generic-ec/curve-secp256r1", "slip10"] +# Enables Edwards-specific derivation +curve-ed25519 = ["generic-ec/curve-ed25519", "edwards"] all-curves = ["curve-secp256k1", "curve-secp256r1", "curve-ed25519"] serde = ["dep:serde", "generic-ec/serde"] +# Enables Slip10 derivation +slip10 = ["hmac", "sha2", "generic-array", "subtle"] +# Enables Edwards-specific derivation +edwards = ["hmac", "sha2", "generic-array", "curve-ed25519"] +# Enables Stark-specific derivation +stark = [] + [[test]] name = "slip10_test_vector" required-features = ["curve-secp256k1", "curve-secp256r1"] diff --git a/src/edwards.rs b/src/edwards.rs new file mode 100644 index 0000000..bef64be --- /dev/null +++ b/src/edwards.rs @@ -0,0 +1,110 @@ +//! Edwards HD derivation +//! +//! This module provides [`Edwards`] derivation as well as aliases for calling +//! `>::*` methods for convenience when you don't need to support +//! generic HD derivation algorithm. +//! +//! See [`Edwards`] docs to learn more about the derivation method. + +use generic_ec::{curves, Point, Scalar}; +use hmac::Mac; + +use crate::{ + DeriveShift, DerivedShift, ExtendedKeyPair, ExtendedPublicKey, HardenedIndex, NonHardenedIndex, +}; + +type HmacSha512 = hmac::Hmac; + +/// HD derivation for Ed25519 curve +/// +/// This type of derivation isn't defined in any known to us standards, but it can be often +/// found in other libraries. It is secure and efficient (much more efficient than using +/// [`Slip10Like`](Slip10Like), for instance). +/// +/// ## Example +/// ```rust +/// use hd_wallet::{HdWallet, Edwards, curves::Ed25519}; +/// +/// # fn load_key() -> hd_wallet::ExtendedKeyPair { +/// # hd_wallet::ExtendedSecretKey { +/// # secret_key: generic_ec::SecretScalar::random(&mut rand::rngs::OsRng), +/// # chain_code: rand::Rng::gen(&mut rand::rngs::OsRng), +/// # }.into() +/// # } +/// # +/// let parent_key: hd_wallet::ExtendedKeyPair = load_key(); +/// +/// let child_key_pair = Edwards::derive_child_key_pair_with_path( +/// &parent_key, +/// [1 + hd_wallet::H, 10], +/// ); +/// # Ok::<(), Box>(()) +/// ``` +pub struct Edwards; + +impl DeriveShift for Edwards { + fn derive_public_shift( + parent_public_key: &ExtendedPublicKey, + child_index: NonHardenedIndex, + ) -> DerivedShift { + let hmac = HmacSha512::new_from_slice(&parent_public_key.chain_code) + .expect("this never fails: hmac can handle keys of any size"); + let i = hmac + .clone() + .chain_update(parent_public_key.public_key.to_bytes(true)) + // we append 0 byte to the public key for compatibility with other libs + .chain_update([0x00]) + .chain_update(child_index.to_be_bytes()) + .finalize() + .into_bytes(); + Self::calculate_shift(parent_public_key, i) + } + + fn derive_hardened_shift( + parent_key: &ExtendedKeyPair, + child_index: HardenedIndex, + ) -> DerivedShift { + let hmac = HmacSha512::new_from_slice(parent_key.chain_code()) + .expect("this never fails: hmac can handle keys of any size"); + let i = hmac + .clone() + .chain_update([0x00]) + .chain_update(parent_key.secret_key.secret_key.as_ref().to_be_bytes()) + .chain_update(child_index.to_be_bytes()) + .finalize() + .into_bytes(); + Self::calculate_shift(&parent_key.public_key, i) + } +} + +impl Edwards { + fn calculate_shift( + parent_public_key: &ExtendedPublicKey, + i: hmac::digest::Output, + ) -> DerivedShift { + let (i_left, i_right) = split_into_two_halves(&i); + + let shift = Scalar::from_be_bytes_mod_order(i_left); + let child_pk = parent_public_key.public_key + Point::generator() * shift; + + DerivedShift { + shift, + child_public_key: ExtendedPublicKey { + public_key: child_pk, + chain_code: (*i_right).into(), + }, + } + } +} + +/// Splits array `I` of 64 bytes into two arrays `I_L = I[..32]` and `I_R = I[32..]` +fn split_into_two_halves( + i: &generic_array::GenericArray, +) -> ( + &generic_array::GenericArray, + &generic_array::GenericArray, +) { + generic_array::sequence::Split::split(i) +} + +super::create_aliases!(Edwards, edwards, hd_wallet::curves::Ed25519); diff --git a/src/lib.rs b/src/lib.rs index 7ee9f68..696d471 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,44 +67,24 @@ #![cfg_attr(not(feature = "std"), no_std)] #![forbid(missing_docs, unsafe_code)] +#![cfg_attr(not(test), forbid(unused_crate_dependencies))] use core::ops; -use generic_array::{ - typenum::{U32, U64}, - GenericArray, -}; use generic_ec::{Curve, Point, Scalar, SecretScalar}; -use hmac::Mac; pub use generic_ec::curves; +#[cfg(feature = "edwards")] +pub mod edwards; pub mod errors; +#[cfg(feature = "slip10")] pub mod slip10; -/// Slip10-like HD derivation -/// -/// This module provides aliases for calling `>::*` methods for convenience -/// when you don't need to support generic HD derivation algorithm. -/// -/// See [`Slip10Like`] docs to learn more about the derivation method. -pub mod slip10_like { - pub use crate::Slip10Like; - super::create_aliases!(Slip10Like, slip10_like); -} - -/// Edwards HD derivation -/// -/// This module provides aliases for calling `>::*` methods for convenience -/// when you don't need to support generic HD derivation algorithm. -/// -/// See [`Edwards`] docs to learn more about the derivation method. -pub mod edwards { - pub use crate::Edwards; - super::create_aliases!(Edwards, edwards, hd_wallet::curves::Ed25519); -} - -type HmacSha512 = hmac::Hmac; +#[cfg(feature = "edwards")] +pub use edwards::Edwards; +#[cfg(feature = "slip10")] +pub use slip10::Slip10; /// Beginning of hardened child indexes /// @@ -369,6 +349,7 @@ impl<'de, E: Curve> serde::Deserialize<'de> for ExtendedKeyPair { /// * `$m` - current module, module where these functions will appear. Used in doc /// tests only /// * `$e` - curve supported by this HD derivation, used in doc tests only +#[cfg(any(feature = "slip10", feature = "edwards", feature = "stark"))] macro_rules! create_aliases { ($t:ty, $m:expr) => { $crate::create_aliases!($t, $m, hd_wallet::curves::Secp256k1); }; ($t:ty, $m:expr, $e:ty) => { @@ -604,6 +585,7 @@ macro_rules! create_aliases { } }; } +#[cfg(any(feature = "slip10", feature = "edwards", feature = "stark"))] pub(crate) use create_aliases; /// HD derivation @@ -830,257 +812,3 @@ pub trait DeriveShift { child_index: HardenedIndex, ) -> DerivedShift; } - -/// SLIP10-like HD wallet derivation -/// -/// `Slip10Like` is generalization of [`Slip10`], which is defined for any curve that meets -/// constraints listed below. -/// -/// When `Slip10Like` is instantiated with secp256k1 or secp256r1 curves, it follows exactly -/// SLIP10 derivation rules. -/// -/// ## Constraints -/// `Slip10Like` must be used with curves which operate on 32 bytes scalars. -/// -/// `Slip10Like` is not recommended to be used with curves with order significantly lower -/// than $2^{256}$ (e.g. ed25519) as it worsens the performance. -/// -/// ### Ed25519 curve -/// Although `Slip10Like` will work on ed25519 curve, we do not recommend using it, because: -/// 1. it's confusing as ed25519 curve is defined in SLIP10, however, -/// `Slip10Like` will not follow SLIP10 standard -/// 2. it's quite inefficient -/// -/// Prefer using [`Edwards`] derivation method for ed25519 curve. -pub struct Slip10Like; - -impl DeriveShift for Slip10Like { - fn derive_public_shift( - parent_public_key: &ExtendedPublicKey, - child_index: NonHardenedIndex, - ) -> DerivedShift { - let hmac = HmacSha512::new_from_slice(&parent_public_key.chain_code) - .expect("this never fails: hmac can handle keys of any size"); - let i = hmac - .clone() - .chain_update(parent_public_key.public_key.to_bytes(true)) - .chain_update(child_index.to_be_bytes()) - .finalize() - .into_bytes(); - Self::calculate_shift(&hmac, parent_public_key, *child_index, i) - } - - fn derive_hardened_shift( - parent_key: &ExtendedKeyPair, - child_index: HardenedIndex, - ) -> DerivedShift { - let hmac = HmacSha512::new_from_slice(parent_key.chain_code()) - .expect("this never fails: hmac can handle keys of any size"); - let i = hmac - .clone() - .chain_update([0x00]) - .chain_update(parent_key.secret_key.secret_key.as_ref().to_be_bytes()) - .chain_update(child_index.to_be_bytes()) - .finalize() - .into_bytes(); - Self::calculate_shift(&hmac, &parent_key.public_key, *child_index, i) - } -} - -impl Slip10Like { - fn calculate_shift( - hmac: &HmacSha512, - parent_public_key: &ExtendedPublicKey, - child_index: u32, - mut i: hmac::digest::Output, - ) -> DerivedShift { - loop { - let (i_left, i_right) = split_into_two_halves(&i); - - if let Ok(shift) = Scalar::::from_be_bytes(i_left) { - let child_pk = parent_public_key.public_key + Point::generator() * shift; - if !child_pk.is_zero() { - return DerivedShift { - shift, - child_public_key: ExtendedPublicKey { - public_key: child_pk, - chain_code: (*i_right).into(), - }, - }; - } - } - - i = hmac - .clone() - .chain_update([0x01]) - .chain_update(i_right) - .chain_update(child_index.to_be_bytes()) - .finalize() - .into_bytes() - } - } -} - -/// [SLIP10][slip10-spec] HD wallet derivation -/// -/// Performs HD derivation as defined in the spec. Only supports secp256k1 and secp256r1 curves. -/// -/// ## Limitations -/// We do not support SLIP10 instantiated with ed25519 or curve25519 due to the limitations. -/// Ed25519 and curve25519 are special-cases in SLIP10 standard, they only support hardened -/// derivation, and they operate on EdDSA and X25519 private keys instead of elliptic points -/// and scalars as in other cases. This library only supports HD derivations in which -/// secret keys are represented as scalars and public keys as points, see [`ExtendedSecretKey`] -/// and [`ExtendedPublicKey`]. -/// -/// If you need HD derivation on Ed25519 curve, we recommend using [`Edwards`] HD derivation, -/// which supports both hardened and non-hardened derivation. -/// -/// ## Master key derivation from the seed -/// [`slip10::derive_master_key`] can be used to derive a master key from the seed as defined -/// in the spec. -/// -/// ## Example -/// Derive a master key from the seed, and then derive a child key m/1H/10: -/// ```rust -/// use hd_wallet::{HdWallet, Slip10, curves::Secp256k1}; -/// -/// let seed = b"16-64 bytes of high entropy".as_slice(); -/// let master_key = hd_wallet::slip10::derive_master_key::(seed)?; -/// let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key); -/// -/// let child_key_pair = Slip10::derive_child_key_pair_with_path( -/// &master_key_pair, -/// [1 + hd_wallet::H, 10], -/// ); -/// # Ok::<(), Box>(()) -/// ``` -/// -/// ## SLIP10-like derivation -/// SLIP10 is only defined for a few curves, but it can be extended to support any curve. -/// See [`Slip10Like`] if you need other curves than is supported by SLIP10. -/// -/// [slip10-spec]: https://github.com/satoshilabs/slips/blob/master/slip-0010.md -pub struct Slip10; - -#[cfg(feature = "curve-secp256k1")] -impl DeriveShift for Slip10 { - fn derive_public_shift( - parent_public_key: &ExtendedPublicKey, - child_index: NonHardenedIndex, - ) -> DerivedShift { - Slip10Like::derive_public_shift(parent_public_key, child_index) - } - fn derive_hardened_shift( - parent_key: &ExtendedKeyPair, - child_index: HardenedIndex, - ) -> DerivedShift { - Slip10Like::derive_hardened_shift(parent_key, child_index) - } -} -#[cfg(feature = "curve-secp256r1")] -impl DeriveShift for Slip10 { - fn derive_public_shift( - parent_public_key: &ExtendedPublicKey, - child_index: NonHardenedIndex, - ) -> DerivedShift { - Slip10Like::derive_public_shift(parent_public_key, child_index) - } - fn derive_hardened_shift( - parent_key: &ExtendedKeyPair, - child_index: HardenedIndex, - ) -> DerivedShift { - Slip10Like::derive_hardened_shift(parent_key, child_index) - } -} - -/// Splits array `I` of 64 bytes into two arrays `I_L = I[..32]` and `I_R = I[32..]` -fn split_into_two_halves( - i: &GenericArray, -) -> (&GenericArray, &GenericArray) { - generic_array::sequence::Split::split(i) -} - -/// HD derivation for Ed25519 curve -/// -/// This type of derivation isn't defined in any known to us standards, but it can be often -/// found in other libraries. It is secure and efficient (much more efficient than using -/// [`Slip10Like`](Slip10Like), for instance). -/// -/// ## Example -/// ```rust -/// use hd_wallet::{HdWallet, Edwards, curves::Ed25519}; -/// -/// # fn load_key() -> hd_wallet::ExtendedKeyPair { -/// # hd_wallet::ExtendedSecretKey { -/// # secret_key: generic_ec::SecretScalar::random(&mut rand::rngs::OsRng), -/// # chain_code: rand::Rng::gen(&mut rand::rngs::OsRng), -/// # }.into() -/// # } -/// # -/// let parent_key: hd_wallet::ExtendedKeyPair = load_key(); -/// -/// let child_key_pair = Edwards::derive_child_key_pair_with_path( -/// &parent_key, -/// [1 + hd_wallet::H, 10], -/// ); -/// # Ok::<(), Box>(()) -/// ``` -pub struct Edwards; - -#[cfg(feature = "curve-ed25519")] -impl DeriveShift for Edwards { - fn derive_public_shift( - parent_public_key: &ExtendedPublicKey, - child_index: NonHardenedIndex, - ) -> DerivedShift { - let hmac = HmacSha512::new_from_slice(&parent_public_key.chain_code) - .expect("this never fails: hmac can handle keys of any size"); - let i = hmac - .clone() - .chain_update(parent_public_key.public_key.to_bytes(true)) - // we append 0 byte to the public key for compatibility with other libs - .chain_update([0x00]) - .chain_update(child_index.to_be_bytes()) - .finalize() - .into_bytes(); - Self::calculate_shift(parent_public_key, i) - } - - fn derive_hardened_shift( - parent_key: &ExtendedKeyPair, - child_index: HardenedIndex, - ) -> DerivedShift { - let hmac = HmacSha512::new_from_slice(parent_key.chain_code()) - .expect("this never fails: hmac can handle keys of any size"); - let i = hmac - .clone() - .chain_update([0x00]) - .chain_update(parent_key.secret_key.secret_key.as_ref().to_be_bytes()) - .chain_update(child_index.to_be_bytes()) - .finalize() - .into_bytes(); - Self::calculate_shift(&parent_key.public_key, i) - } -} - -#[cfg(feature = "curve-ed25519")] -impl Edwards { - fn calculate_shift( - parent_public_key: &ExtendedPublicKey, - i: hmac::digest::Output, - ) -> DerivedShift { - let (i_left, i_right) = split_into_two_halves(&i); - - let shift = Scalar::from_be_bytes_mod_order(i_left); - let child_pk = parent_public_key.public_key + Point::generator() * shift; - - DerivedShift { - shift, - child_public_key: ExtendedPublicKey { - public_key: child_pk, - chain_code: (*i_right).into(), - }, - } - } -} diff --git a/src/slip10.rs b/src/slip10.rs index 2db3ee9..c0b88ff 100644 --- a/src/slip10.rs +++ b/src/slip10.rs @@ -12,9 +12,187 @@ //! [slip10-spec]: https://github.com/satoshilabs/slips/blob/master/slip-0010.md //! [bip32-spec]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki +use generic_ec::{Curve, Point, Scalar}; use hmac::Mac as _; -pub use crate::Slip10; +use crate::{ + DeriveShift, DerivedShift, ExtendedKeyPair, ExtendedPublicKey, HardenedIndex, NonHardenedIndex, +}; + +type HmacSha512 = hmac::Hmac; + +/// SLIP10-like HD wallet derivation +/// +/// `Slip10Like` is generalization of [`Slip10`], which is defined for any curve that meets +/// constraints listed below. +/// +/// When `Slip10Like` is instantiated with secp256k1 or secp256r1 curves, it follows exactly +/// SLIP10 derivation rules. +/// +/// ## Constraints +/// `Slip10Like` must be used with curves which operate on 32 bytes scalars. +/// +/// `Slip10Like` is not recommended to be used with curves with order significantly lower +/// than $2^{256}$ (e.g. ed25519) as it worsens the performance. +/// +/// ### Ed25519 curve +/// Although `Slip10Like` will work on ed25519 curve, we do not recommend using it, because: +/// 1. it's confusing as ed25519 curve is defined in SLIP10, however, +/// `Slip10Like` will not follow SLIP10 standard +/// 2. it's quite inefficient +/// +/// Prefer using [`Edwards`] derivation method for ed25519 curve. +pub struct Slip10Like; + +impl DeriveShift for Slip10Like { + fn derive_public_shift( + parent_public_key: &ExtendedPublicKey, + child_index: NonHardenedIndex, + ) -> DerivedShift { + let hmac = HmacSha512::new_from_slice(&parent_public_key.chain_code) + .expect("this never fails: hmac can handle keys of any size"); + let i = hmac + .clone() + .chain_update(parent_public_key.public_key.to_bytes(true)) + .chain_update(child_index.to_be_bytes()) + .finalize() + .into_bytes(); + Self::calculate_shift(&hmac, parent_public_key, *child_index, i) + } + + fn derive_hardened_shift( + parent_key: &ExtendedKeyPair, + child_index: HardenedIndex, + ) -> DerivedShift { + let hmac = HmacSha512::new_from_slice(parent_key.chain_code()) + .expect("this never fails: hmac can handle keys of any size"); + let i = hmac + .clone() + .chain_update([0x00]) + .chain_update(parent_key.secret_key.secret_key.as_ref().to_be_bytes()) + .chain_update(child_index.to_be_bytes()) + .finalize() + .into_bytes(); + Self::calculate_shift(&hmac, &parent_key.public_key, *child_index, i) + } +} + +impl Slip10Like { + fn calculate_shift( + hmac: &HmacSha512, + parent_public_key: &ExtendedPublicKey, + child_index: u32, + mut i: hmac::digest::Output, + ) -> DerivedShift { + loop { + let (i_left, i_right) = split_into_two_halves(&i); + + if let Ok(shift) = Scalar::::from_be_bytes(i_left) { + let child_pk = parent_public_key.public_key + Point::generator() * shift; + if !child_pk.is_zero() { + return DerivedShift { + shift, + child_public_key: ExtendedPublicKey { + public_key: child_pk, + chain_code: (*i_right).into(), + }, + }; + } + } + + i = hmac + .clone() + .chain_update([0x01]) + .chain_update(i_right) + .chain_update(child_index.to_be_bytes()) + .finalize() + .into_bytes() + } + } +} + +/// Splits array `I` of 64 bytes into two arrays `I_L = I[..32]` and `I_R = I[32..]` +fn split_into_two_halves( + i: &generic_array::GenericArray, +) -> ( + &generic_array::GenericArray, + &generic_array::GenericArray, +) { + generic_array::sequence::Split::split(i) +} + +/// [SLIP10][slip10-spec] HD wallet derivation +/// +/// Performs HD derivation as defined in the spec. Only supports secp256k1 and secp256r1 curves. +/// +/// ## Limitations +/// We do not support SLIP10 instantiated with ed25519 or curve25519 due to the limitations. +/// Ed25519 and curve25519 are special-cases in SLIP10 standard, they only support hardened +/// derivation, and they operate on EdDSA and X25519 private keys instead of elliptic points +/// and scalars as in other cases. This library only supports HD derivations in which +/// secret keys are represented as scalars and public keys as points, see [`ExtendedSecretKey`] +/// and [`ExtendedPublicKey`]. +/// +/// If you need HD derivation on Ed25519 curve, we recommend using [`Edwards`] HD derivation, +/// which supports both hardened and non-hardened derivation. +/// +/// ## Master key derivation from the seed +/// [`slip10::derive_master_key`] can be used to derive a master key from the seed as defined +/// in the spec. +/// +/// ## Example +/// Derive a master key from the seed, and then derive a child key m/1H/10: +/// ```rust +/// use hd_wallet::{HdWallet, Slip10, curves::Secp256k1}; +/// +/// let seed = b"16-64 bytes of high entropy".as_slice(); +/// let master_key = hd_wallet::slip10::derive_master_key::(seed)?; +/// let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key); +/// +/// let child_key_pair = Slip10::derive_child_key_pair_with_path( +/// &master_key_pair, +/// [1 + hd_wallet::H, 10], +/// ); +/// # Ok::<(), Box>(()) +/// ``` +/// +/// ## SLIP10-like derivation +/// SLIP10 is only defined for a few curves, but it can be extended to support any curve. +/// See [`Slip10Like`] if you need other curves than is supported by SLIP10. +/// +/// [slip10-spec]: https://github.com/satoshilabs/slips/blob/master/slip-0010.md +pub struct Slip10; + +#[cfg(feature = "curve-secp256k1")] +impl DeriveShift for Slip10 { + fn derive_public_shift( + parent_public_key: &ExtendedPublicKey, + child_index: NonHardenedIndex, + ) -> DerivedShift { + Slip10Like::derive_public_shift(parent_public_key, child_index) + } + fn derive_hardened_shift( + parent_key: &ExtendedKeyPair, + child_index: HardenedIndex, + ) -> DerivedShift { + Slip10Like::derive_hardened_shift(parent_key, child_index) + } +} +#[cfg(feature = "curve-secp256r1")] +impl DeriveShift for Slip10 { + fn derive_public_shift( + parent_public_key: &ExtendedPublicKey, + child_index: NonHardenedIndex, + ) -> DerivedShift { + Slip10Like::derive_public_shift(parent_public_key, child_index) + } + fn derive_hardened_shift( + parent_key: &ExtendedKeyPair, + child_index: HardenedIndex, + ) -> DerivedShift { + Slip10Like::derive_hardened_shift(parent_key, child_index) + } +} /// Marker for a curve supported by SLIP10 specs and this library /// @@ -79,12 +257,12 @@ pub fn derive_master_key_with_curve_tag( return Err(crate::errors::InvalidLength); } - let hmac = crate::HmacSha512::new_from_slice(curve_tag) + let hmac = HmacSha512::new_from_slice(curve_tag) .expect("this never fails: hmac can handle keys of any size"); let mut i = hmac.clone().chain_update(seed).finalize().into_bytes(); loop { - let (i_left, i_right) = crate::split_into_two_halves(&i); + let (i_left, i_right) = split_into_two_halves(&i); if let Ok(mut sk) = generic_ec::Scalar::::from_be_bytes(i_left) { if !bool::from(subtle::ConstantTimeEq::ct_eq(