Skip to content

Commit a4c04cf

Browse files
committed
Simplify k generation following RFC 6979
1 parent 400deb6 commit a4c04cf

File tree

3 files changed

+106
-88
lines changed

3 files changed

+106
-88
lines changed

Cargo.lock

Lines changed: 83 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

starknet-crypto/Cargo.toml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@ exclude = ["test-data/**"]
1717
starknet-crypto-codegen = { version = "0.3.2", path = "../starknet-crypto-codegen" }
1818
starknet-curve = { version = "0.4.1", path = "../starknet-curve" }
1919
starknet-ff = { version = "0.3.6", path = "../starknet-ff", default-features = false }
20-
crypto-bigint = { version = "0.5.1", default-features = false, features = ["generic-array", "zeroize"] }
21-
hmac = { version = "0.12.1", default-features = false }
2220
num-bigint = { version = "0.4.3", default-features = false }
2321
num-integer = { version = "0.1.45", default-features = false }
2422
num-traits = { version = "0.2.15", default-features = false }
25-
rfc6979 = { version = "0.4.0", default-features = false }
26-
sha2 = { version = "0.10.6", default-features = false }
27-
zeroize = { version = "1.6.0", default-features = false }
23+
rfc6979 = { version = "0.5.0-pre.3", default-features = false }
24+
subtle = { version = "2.5.0", default-features = false }
25+
sha2 = { version = "0.11.0-pre.3", default-features = false }
26+
hex-literal = { version = "0.4.1", default-features = false }
2827
hex = { version = "0.4.3", default-features = false, optional = true }
2928

3029
[features]
@@ -36,7 +35,6 @@ signature-display = ["dep:hex", "alloc"]
3635
[dev-dependencies]
3736
criterion = { version = "0.4.0", default-features = false }
3837
hex = "0.4.3"
39-
hex-literal = "0.4.1"
4038
serde = { version = "1.0.160", features = ["derive"] }
4139
serde_json = "1.0.96"
4240

starknet-crypto/src/rfc6979.rs

Lines changed: 19 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
use crypto_bigint::{ArrayEncoding, ByteArray, Integer, U256};
2-
use hmac::digest::Digest;
3-
use sha2::digest::{crypto_common::BlockSizeUser, FixedOutputReset, HashMarker};
4-
use zeroize::{Zeroize, Zeroizing};
5-
61
use crate::FieldElement;
2+
use hex_literal::hex;
73

8-
const EC_ORDER: U256 =
9-
U256::from_be_hex("0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f");
4+
const EC_ORDER: [u8; 32] = hex!("0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f");
105

116
/// Deterministically generate ephemeral scalar `k` based on RFC 6979.
127
///
@@ -20,61 +15,32 @@ pub fn generate_k(
2015
private_key: &FieldElement,
2116
seed: Option<&FieldElement>,
2217
) -> FieldElement {
23-
// The message hash padding as implemented in `cairo-lang` is not needed here. The hash is
24-
// padded in `cairo-lang` only to make sure the lowest 4 bits won't get truncated, but here it's
25-
// never getting truncated anyways.
26-
let message_hash = U256::from_be_slice(&message_hash.to_bytes_be()).to_be_byte_array();
27-
let private_key = U256::from_be_slice(&private_key.to_bytes_be());
28-
29-
let seed_bytes = match seed {
30-
Some(seed) => seed.to_bytes_be(),
31-
None => [0u8; 32],
32-
};
18+
// Convert seed to bytes
19+
let seed_bytes = seed.map_or([0u8; 32], |s| s.to_bytes_be());
3320

21+
// Find the index of the first non-zero byte in the seed
3422
let mut first_non_zero_index = 32;
35-
for (ind, element) in seed_bytes.iter().enumerate() {
36-
if *element != 0u8 {
23+
for (ind, &element) in seed_bytes.iter().enumerate() {
24+
if element != 0u8 {
3725
first_non_zero_index = ind;
3826
break;
3927
}
4028
}
4129

42-
let k = generate_k_shifted::<sha2::Sha256, _>(
43-
&private_key,
44-
&EC_ORDER,
45-
&message_hash,
46-
&seed_bytes[first_non_zero_index..],
30+
// Convert GenericArray to [u8; 32]
31+
let mut k_bytes = [0u8; 32];
32+
k_bytes.copy_from_slice(
33+
rfc6979::generate_k::<sha2::Sha256, rfc6979::consts::U32>(
34+
(&private_key.to_bytes_be()).into(),
35+
&EC_ORDER.into(),
36+
(&message_hash.to_bytes_be()).into(),
37+
&seed_bytes[first_non_zero_index..],
38+
)
39+
.as_slice(),
4740
);
4841

49-
let mut buffer = [0u8; 32];
50-
buffer[..].copy_from_slice(&k.to_be_byte_array()[..]);
51-
52-
FieldElement::from_bytes_be(&buffer).unwrap()
53-
}
54-
55-
// Modified from upstream `rfc6979::generate_k` with a hard-coded right bit shift. The more
56-
// idiomatic way of doing this seems to be to implement `U252` which handles bit truncation
57-
// interally.
58-
// TODO: change to use upstream `generate_k` directly.
59-
#[inline]
60-
fn generate_k_shifted<D, I>(x: &I, n: &I, h: &ByteArray<I>, data: &[u8]) -> Zeroizing<I>
61-
where
62-
D: Default + Digest + BlockSizeUser + FixedOutputReset + HashMarker,
63-
I: ArrayEncoding + Integer + Zeroize,
64-
{
65-
let mut x = x.to_be_byte_array();
66-
let mut hmac_drbg = rfc6979::HmacDrbg::<D>::new(&x, h, data);
67-
x.zeroize();
68-
69-
loop {
70-
let mut bytes = ByteArray::<I>::default();
71-
hmac_drbg.fill_bytes(&mut bytes);
72-
let k = I::from_be_byte_array(bytes) >> 4;
73-
74-
if (!k.is_zero() & k.ct_lt(n)).into() {
75-
return Zeroizing::new(k);
76-
}
77-
}
42+
// Convert bytes to FieldElement
43+
FieldElement::from_bytes_be(&k_bytes).unwrap()
7844
}
7945

8046
#[cfg(test)]

0 commit comments

Comments
 (0)