Skip to content

Commit d272070

Browse files
committed
Use Wscalar instead of DlogProverInput in ExtSecretKey
Now that DlogProverInput eagerly evaluates public key, it's a bad fit for ExtSecretKey since it causes unnecessary EC operations
1 parent c6f5c9a commit d272070

File tree

6 files changed

+53
-28
lines changed

6 files changed

+53
-28
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
[workspace]
32
resolver = "2"
43
members = [

ergo-lib/src/wallet/ext_pub_key.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ type HmacSha512 = Hmac<Sha512>;
2828
pub struct ExtPubKey {
2929
/// Parsed public key (EcPoint)
3030
pub public_key: EcPoint,
31-
chain_code: ChainCode,
31+
/// Chain code bytes
32+
pub chain_code: ChainCode,
3233
/// Derivation path for this extended public key
3334
pub derivation_path: DerivationPath,
3435
}

ergo-lib/src/wallet/ext_secret_key.rs

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
//! Extended private key operations according to BIP-32
2-
use core::convert::TryInto;
3-
42
use super::{
53
derivation_path::{ChildIndex, ChildIndexError, DerivationPath},
64
ext_pub_key::ExtPubKey,
@@ -28,14 +26,23 @@ type HmacSha512 = Hmac<Sha512>;
2826

2927
/// Extended secret key
3028
/// implemented according to BIP-32
31-
#[derive(PartialEq, Eq, Debug, Clone)]
29+
#[derive(PartialEq, Eq, Clone)]
3230
pub struct ExtSecretKey {
33-
/// The secret key
34-
private_input: DlogProverInput,
31+
private_input: Wscalar,
3532
chain_code: ChainCode,
3633
derivation_path: DerivationPath,
3734
}
3835

36+
impl core::fmt::Debug for ExtSecretKey {
37+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38+
f.debug_struct("ExtSecretKey")
39+
.field("private_input", &"*****") // disable debug output for secret key to prevent key leakage in logs
40+
.field("chain_code", &self.chain_code)
41+
.field("derivation_path", &self.derivation_path)
42+
.finish()
43+
}
44+
}
45+
3946
/// Extended secret key errors
4047
#[derive(Error, PartialEq, Eq, Debug, Clone)]
4148
pub enum ExtSecretKeyError {
@@ -66,8 +73,8 @@ impl ExtSecretKey {
6673
chain_code: ChainCode,
6774
derivation_path: DerivationPath,
6875
) -> Result<Self, ExtSecretKeyError> {
69-
let private_input = DlogProverInput::from_bytes(&secret_key_bytes)
70-
.ok_or(ExtSecretKeyError::ScalarEncodingError)?;
76+
let private_input =
77+
Wscalar::from_bytes(&secret_key_bytes).ok_or(ExtSecretKeyError::ScalarEncodingError)?;
7178
Ok(Self {
7279
private_input,
7380
chain_code,
@@ -82,7 +89,7 @@ impl ExtSecretKey {
8289

8390
/// Returns secret key
8491
pub fn secret_key(&self) -> SecretKey {
85-
self.private_input.clone().into()
92+
DlogProverInput::new(self.private_input.clone()).into()
8693
}
8794

8895
/// Byte representation of the underlying scalar
@@ -92,7 +99,7 @@ impl ExtSecretKey {
9299

93100
/// Public image associated with the private input
94101
pub fn public_image(&self) -> ProveDlog {
95-
self.private_input.public_image()
102+
DlogProverInput::new(self.private_input.clone()).public_image()
96103
}
97104

98105
/// Public image bytes in SEC-1 encoded & compressed format
@@ -103,12 +110,11 @@ impl ExtSecretKey {
103110
/// The extended public key associated with this secret key
104111
pub fn public_key(&self) -> Result<ExtPubKey, ExtSecretKeyError> {
105112
#[allow(clippy::unwrap_used)]
106-
Ok(ExtPubKey::new(
107-
// unwrap is safe as it is used on an Infallible result type
108-
self.public_image_bytes()?.try_into().unwrap(),
109-
self.chain_code,
110-
self.derivation_path.clone(),
111-
)?)
113+
Ok(ExtPubKey {
114+
public_key: *self.public_image().h,
115+
chain_code: self.chain_code,
116+
derivation_path: self.derivation_path.clone(),
117+
})
112118
}
113119

114120
/// Derive a child extended secret key using the provided index
@@ -127,16 +133,14 @@ impl ExtSecretKey {
127133
let mac_bytes = mac.finalize().into_bytes();
128134
let mut secret_key_bytes = [0; SecretKeyBytes::LEN];
129135
secret_key_bytes.copy_from_slice(&mac_bytes[..32]);
130-
if let Some(dlog_prover) = DlogProverInput::from_bytes(&secret_key_bytes) {
136+
if let Some(wscalar) = Wscalar::from_bytes(&secret_key_bytes) {
131137
// parse256(IL) + kpar (mod n).
132138
// via https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions
133-
let child_secret_key: DlogProverInput = Wscalar::from(
134-
dlog_prover
135-
.w
139+
let child_secret_key = Wscalar::from(
140+
wscalar
136141
.as_scalar_ref()
137-
.add(self.private_input.w.as_scalar_ref()),
138-
)
139-
.into();
142+
.add(self.private_input.as_scalar_ref()),
143+
);
140144
if child_secret_key.is_zero() {
141145
// ki == 0 case of:
142146
// > In case parse256(IL) ≥ n or ki = 0, the resulting key is invalid, and one

ergotree-interpreter/src/sigma_protocol/private_input.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ impl DlogProverInput {
107107

108108
/// byte representation of the underlying scalar
109109
pub fn to_bytes(&self) -> [u8; DlogProverInput::SIZE_BYTES] {
110-
self.w.as_scalar_ref().to_bytes().into()
110+
self.w.to_bytes()
111111
}
112112

113113
/// public key of discrete logarithm signature protocol
@@ -117,7 +117,7 @@ impl DlogProverInput {
117117

118118
/// Return true if the secret is 0
119119
pub fn is_zero(&self) -> bool {
120-
self.w.as_scalar_ref().is_zero().into()
120+
self.w.is_zero()
121121
}
122122
}
123123

ergotree-interpreter/src/sigma_protocol/wscalar.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use core::fmt::Formatter;
77

88
use derive_more::From;
99
use derive_more::Into;
10+
use elliptic_curve::PrimeField;
1011
use ergo_chain_types::Base16DecodedBytes;
1112
use ergo_chain_types::Base16EncodedBytes;
1213
use k256::elliptic_curve::generic_array::GenericArray;
@@ -31,10 +32,30 @@ use super::SOUNDNESS_BYTES;
3132
pub struct Wscalar(Scalar);
3233

3334
impl Wscalar {
35+
/// Scalar(secret key) size in bytes
36+
pub const SIZE_BYTES: usize = 32;
37+
3438
/// Returns a reference to underlying Scalar
3539
pub fn as_scalar_ref(&self) -> &Scalar {
3640
&self.0
3741
}
42+
43+
/// Attempts to parse the given byte array as an SEC-1-encoded scalar(secret key).
44+
/// Returns None if the byte array does not contain a big-endian integer in the range [0, modulus).
45+
pub fn from_bytes(bytes: &[u8; Self::SIZE_BYTES]) -> Option<Self> {
46+
k256::Scalar::from_repr((*bytes).into())
47+
.map(Wscalar::from)
48+
.into()
49+
}
50+
51+
/// Convert scalar to big-endian byte representation
52+
pub fn to_bytes(&self) -> [u8; Self::SIZE_BYTES] {
53+
self.0.to_bytes().into()
54+
}
55+
/// Return true if the scalar is 0
56+
pub fn is_zero(&self) -> bool {
57+
self.0.is_zero().into()
58+
}
3859
}
3960

4061
impl From<GroupSizedBytes> for Wscalar {

ergotree-ir/src/chain/address.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,8 +542,8 @@ impl AddressEncoder {
542542
let mut address_bytes = address.content_bytes();
543543
let mut bytes = vec![prefix_byte];
544544
bytes.append(&mut address_bytes);
545-
let mut calculated_checksum = AddressEncoder::calc_checksum(&bytes[..]).to_vec();
546-
bytes.append(&mut calculated_checksum);
545+
let calculated_checksum = AddressEncoder::calc_checksum(&bytes[..]);
546+
bytes.extend_from_slice(&calculated_checksum);
547547
bytes
548548
}
549549

0 commit comments

Comments
 (0)