Skip to content

Commit

Permalink
Simplify k generation following RFC 6979
Browse files Browse the repository at this point in the history
  • Loading branch information
tcoratger committed Mar 15, 2024
1 parent 400deb6 commit a4c04cf
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 88 deletions.
112 changes: 83 additions & 29 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 4 additions & 6 deletions starknet-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@ exclude = ["test-data/**"]
starknet-crypto-codegen = { version = "0.3.2", path = "../starknet-crypto-codegen" }
starknet-curve = { version = "0.4.1", path = "../starknet-curve" }
starknet-ff = { version = "0.3.6", path = "../starknet-ff", default-features = false }
crypto-bigint = { version = "0.5.1", default-features = false, features = ["generic-array", "zeroize"] }
hmac = { version = "0.12.1", default-features = false }
num-bigint = { version = "0.4.3", default-features = false }
num-integer = { version = "0.1.45", default-features = false }
num-traits = { version = "0.2.15", default-features = false }
rfc6979 = { version = "0.4.0", default-features = false }
sha2 = { version = "0.10.6", default-features = false }
zeroize = { version = "1.6.0", default-features = false }
rfc6979 = { version = "0.5.0-pre.3", default-features = false }
subtle = { version = "2.5.0", default-features = false }
sha2 = { version = "0.11.0-pre.3", default-features = false }
hex-literal = { version = "0.4.1", default-features = false }
hex = { version = "0.4.3", default-features = false, optional = true }

[features]
Expand All @@ -36,7 +35,6 @@ signature-display = ["dep:hex", "alloc"]
[dev-dependencies]
criterion = { version = "0.4.0", default-features = false }
hex = "0.4.3"
hex-literal = "0.4.1"
serde = { version = "1.0.160", features = ["derive"] }
serde_json = "1.0.96"

Expand Down
72 changes: 19 additions & 53 deletions starknet-crypto/src/rfc6979.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
use crypto_bigint::{ArrayEncoding, ByteArray, Integer, U256};
use hmac::digest::Digest;
use sha2::digest::{crypto_common::BlockSizeUser, FixedOutputReset, HashMarker};
use zeroize::{Zeroize, Zeroizing};

use crate::FieldElement;
use hex_literal::hex;

const EC_ORDER: U256 =
U256::from_be_hex("0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f");
const EC_ORDER: [u8; 32] = hex!("0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f");

/// Deterministically generate ephemeral scalar `k` based on RFC 6979.
///
Expand All @@ -20,61 +15,32 @@ pub fn generate_k(
private_key: &FieldElement,
seed: Option<&FieldElement>,
) -> FieldElement {
// The message hash padding as implemented in `cairo-lang` is not needed here. The hash is
// padded in `cairo-lang` only to make sure the lowest 4 bits won't get truncated, but here it's
// never getting truncated anyways.
let message_hash = U256::from_be_slice(&message_hash.to_bytes_be()).to_be_byte_array();
let private_key = U256::from_be_slice(&private_key.to_bytes_be());

let seed_bytes = match seed {
Some(seed) => seed.to_bytes_be(),
None => [0u8; 32],
};
// Convert seed to bytes
let seed_bytes = seed.map_or([0u8; 32], |s| s.to_bytes_be());

// Find the index of the first non-zero byte in the seed
let mut first_non_zero_index = 32;
for (ind, element) in seed_bytes.iter().enumerate() {
if *element != 0u8 {
for (ind, &element) in seed_bytes.iter().enumerate() {
if element != 0u8 {
first_non_zero_index = ind;
break;
}
}

let k = generate_k_shifted::<sha2::Sha256, _>(
&private_key,
&EC_ORDER,
&message_hash,
&seed_bytes[first_non_zero_index..],
// Convert GenericArray to [u8; 32]
let mut k_bytes = [0u8; 32];
k_bytes.copy_from_slice(
rfc6979::generate_k::<sha2::Sha256, rfc6979::consts::U32>(
(&private_key.to_bytes_be()).into(),
&EC_ORDER.into(),
(&message_hash.to_bytes_be()).into(),
&seed_bytes[first_non_zero_index..],
)
.as_slice(),
);

let mut buffer = [0u8; 32];
buffer[..].copy_from_slice(&k.to_be_byte_array()[..]);

FieldElement::from_bytes_be(&buffer).unwrap()
}

// Modified from upstream `rfc6979::generate_k` with a hard-coded right bit shift. The more
// idiomatic way of doing this seems to be to implement `U252` which handles bit truncation
// interally.
// TODO: change to use upstream `generate_k` directly.
#[inline]
fn generate_k_shifted<D, I>(x: &I, n: &I, h: &ByteArray<I>, data: &[u8]) -> Zeroizing<I>
where
D: Default + Digest + BlockSizeUser + FixedOutputReset + HashMarker,
I: ArrayEncoding + Integer + Zeroize,
{
let mut x = x.to_be_byte_array();
let mut hmac_drbg = rfc6979::HmacDrbg::<D>::new(&x, h, data);
x.zeroize();

loop {
let mut bytes = ByteArray::<I>::default();
hmac_drbg.fill_bytes(&mut bytes);
let k = I::from_be_byte_array(bytes) >> 4;

if (!k.is_zero() & k.ct_lt(n)).into() {
return Zeroizing::new(k);
}
}
// Convert bytes to FieldElement
FieldElement::from_bytes_be(&k_bytes).unwrap()
}

#[cfg(test)]
Expand Down

0 comments on commit a4c04cf

Please sign in to comment.