Skip to content

Commit 8b2a4ef

Browse files
authored
Merge pull request #5946 from hugocaillard/chore/update-curve25519-dalek
chore: upgrade curve25519-dalek to v4
2 parents be3bcae + b9a6895 commit 8b2a4ef

File tree

11 files changed

+112
-86
lines changed

11 files changed

+112
-86
lines changed

Cargo.lock

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

stacks-common/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ features = ["arbitrary_precision", "unbounded_depth"]
5050
workspace = true
5151

5252
[dependencies.curve25519-dalek]
53-
version = "=2.0.0"
53+
version = "4.1.3"
5454
features = ["serde"]
5555

5656
[dependencies.time]

stacks-common/src/util/vrf.rs

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use std::{error, fmt};
2626

2727
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
2828
use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint};
29-
use curve25519_dalek::scalar::Scalar as ed25519_Scalar;
29+
use curve25519_dalek::scalar::{clamp_integer, Scalar as ed25519_Scalar};
3030
use rand;
3131
use sha2::{Digest, Sha512};
3232

@@ -181,6 +181,7 @@ impl VRFPublicKey {
181181
pub enum Error {
182182
InvalidPublicKey,
183183
InvalidDataError,
184+
InvalidHashPoints,
184185
OSRNGError(rand::Error),
185186
}
186187

@@ -189,6 +190,7 @@ impl fmt::Display for Error {
189190
match *self {
190191
Error::InvalidPublicKey => write!(f, "Invalid public key"),
191192
Error::InvalidDataError => write!(f, "No data could be found"),
193+
Error::InvalidHashPoints => write!(f, "VRF hash points did not yield a valid scalar"),
192194
Error::OSRNGError(ref e) => fmt::Display::fmt(e, f),
193195
}
194196
}
@@ -199,6 +201,7 @@ impl error::Error for Error {
199201
match *self {
200202
Error::InvalidPublicKey => None,
201203
Error::InvalidDataError => None,
204+
Error::InvalidHashPoints => None,
202205
Error::OSRNGError(ref e) => Some(e),
203206
}
204207
}
@@ -246,7 +249,7 @@ impl VRFProof {
246249

247250
#[allow(clippy::needless_range_loop)]
248251
pub fn check_c(c: &ed25519_Scalar) -> bool {
249-
let c_bytes = c.reduce().to_bytes();
252+
let c_bytes = c.to_bytes();
250253

251254
// upper 16 bytes of c must be 0's
252255
for c_byte in c_bytes[16..32].iter() {
@@ -281,7 +284,9 @@ impl VRFProof {
281284
// 0 32 48 80
282285
// |----------------------------|----------|---------------------------|
283286
// Gamma point c scalar s scalar
284-
let gamma_opt = CompressedEdwardsY::from_slice(&bytes[0..32]).decompress();
287+
let gamma_opt = CompressedEdwardsY::from_slice(&bytes[0..32])
288+
.ok()
289+
.and_then(|y| y.decompress());
285290
if gamma_opt.is_none() {
286291
test_debug!("Invalid Gamma");
287292
return None;
@@ -297,10 +302,14 @@ impl VRFProof {
297302

298303
c_buf[..16].copy_from_slice(&bytes[32..(16 + 32)]);
299304
s_buf[..32].copy_from_slice(&bytes[48..(32 + 48)]);
300-
let c = ed25519_Scalar::from_canonical_bytes(c_buf)?;
301-
let s = ed25519_Scalar::from_canonical_bytes(s_buf)?;
302-
303-
Some(VRFProof { Gamma: gamma, c, s })
305+
let c: Option<ed25519_Scalar> = ed25519_Scalar::from_canonical_bytes(c_buf).into();
306+
let s: Option<ed25519_Scalar> = ed25519_Scalar::from_canonical_bytes(s_buf).into();
307+
308+
Some(VRFProof {
309+
Gamma: gamma,
310+
c: c?,
311+
s: s?,
312+
})
304313
}
305314
_ => None,
306315
}
@@ -324,7 +333,7 @@ impl VRFProof {
324333
"FATAL ERROR: somehow constructed an invalid ECVRF proof"
325334
);
326335

327-
let c_bytes = self.c.reduce().to_bytes();
336+
let c_bytes = self.c.to_bytes();
328337
c_bytes_16[0..16].copy_from_slice(&c_bytes[0..16]);
329338

330339
let gamma_bytes = self.Gamma.compress().to_bytes();
@@ -386,7 +395,7 @@ impl VRF {
386395
}
387396

388397
let y = CompressedEdwardsY::from_slice(&hasher.finalize()[0..32]);
389-
if let Some(h) = y.decompress() {
398+
if let Some(h) = y.ok().and_then(|y| y.decompress()) {
390399
break h;
391400
}
392401

@@ -445,8 +454,7 @@ impl VRF {
445454
let mut h_32 = [0u8; 32];
446455
h_32.copy_from_slice(&h[0..32]);
447456

448-
let x_scalar_raw = ed25519_Scalar::from_bits(h_32);
449-
let x_scalar = x_scalar_raw.reduce(); // use the canonical scalar for the private key
457+
let x_scalar = ed25519_Scalar::from_bytes_mod_order(clamp_integer(h_32));
450458

451459
trunc_hash.copy_from_slice(&h[32..64]);
452460

@@ -469,17 +477,17 @@ impl VRF {
469477

470478
/// Convert a 16-byte string into a scalar.
471479
/// The upper 16 bytes in the resulting scalar MUST BE 0's
472-
fn ed25519_scalar_from_hash128(hash128: &[u8; 16]) -> ed25519_Scalar {
480+
fn ed25519_scalar_from_hash128(hash128: &[u8; 16]) -> Option<ed25519_Scalar> {
473481
let mut scalar_buf = [0u8; 32];
474482
scalar_buf[0..16].copy_from_slice(hash128);
475483

476-
ed25519_Scalar::from_bits(scalar_buf)
484+
ed25519_Scalar::from_canonical_bytes(scalar_buf).into()
477485
}
478486

479487
/// ECVRF proof routine
480488
/// https://tools.ietf.org/id/draft-irtf-cfrg-vrf-02.html#rfc.section.5.1
481489
#[allow(clippy::op_ref)]
482-
pub fn prove(secret: &VRFPrivateKey, alpha: &[u8]) -> VRFProof {
490+
pub fn prove(secret: &VRFPrivateKey, alpha: &[u8]) -> Option<VRFProof> {
483491
let (Y_point, x_scalar, trunc_hash) = VRF::expand_privkey(secret);
484492
let H_point = VRF::hash_to_curve(&Y_point, alpha);
485493

@@ -490,15 +498,15 @@ impl VRF {
490498
let kH_point = &k_scalar * &H_point;
491499

492500
let c_hashbuf = VRF::hash_points(&H_point, &Gamma_point, &kB_point, &kH_point);
493-
let c_scalar = VRF::ed25519_scalar_from_hash128(&c_hashbuf);
501+
let c_scalar = VRF::ed25519_scalar_from_hash128(&c_hashbuf)?;
494502

495-
let s_full_scalar = &k_scalar + &c_scalar * &x_scalar;
496-
let s_scalar = s_full_scalar.reduce();
503+
let s_scalar = &k_scalar + &c_scalar * &x_scalar;
497504

498505
// NOTE: expect() won't panic because c_scalar is guaranteed to have
499506
// its upper 16 bytes as 0
500507
VRFProof::new(Gamma_point, c_scalar, s_scalar)
501-
.expect("FATAL ERROR: upper-16 bytes of proof's C scalar are NOT 0")
508+
.inspect_err(|e| error!("FATAL: upper-16 bytes of proof's C scalar are NOT 0: {e}"))
509+
.ok()
502510
}
503511

504512
/// Given a public key, verify that the private key owner that generate the ECVRF proof did so on the given message.
@@ -509,19 +517,21 @@ impl VRF {
509517
#[allow(clippy::op_ref)]
510518
pub fn verify(Y_point: &VRFPublicKey, proof: &VRFProof, alpha: &[u8]) -> Result<bool, Error> {
511519
let H_point = VRF::hash_to_curve(Y_point, alpha);
512-
let s_reduced = proof.s().reduce();
520+
let s_reduced = proof.s();
513521
let Y_point_ed = CompressedEdwardsY(Y_point.to_bytes())
514522
.decompress()
515523
.ok_or(Error::InvalidPublicKey)?;
516524
if proof.Gamma().is_small_order() {
517525
return Err(Error::InvalidPublicKey);
518526
}
519527

520-
let U_point = &s_reduced * &ED25519_BASEPOINT_POINT - proof.c() * Y_point_ed;
521-
let V_point = &s_reduced * &H_point - proof.c() * proof.Gamma();
528+
let U_point = s_reduced * &ED25519_BASEPOINT_POINT - proof.c() * Y_point_ed;
529+
let V_point = s_reduced * &H_point - proof.c() * proof.Gamma();
522530

523531
let c_prime_hashbuf = VRF::hash_points(&H_point, proof.Gamma(), &U_point, &V_point);
524-
let c_prime = VRF::ed25519_scalar_from_hash128(&c_prime_hashbuf);
532+
let Some(c_prime) = VRF::ed25519_scalar_from_hash128(&c_prime_hashbuf) else {
533+
return Err(Error::InvalidHashPoints);
534+
};
525535

526536
// NOTE: this leverages constant-time comparison inherited from the Scalar impl
527537
Ok(c_prime == *(proof.c()))
@@ -583,7 +593,7 @@ mod tests {
583593
let privk = VRFPrivateKey::from_bytes(&proof_fixture.privkey[..]).unwrap();
584594
let expected_proof_bytes = &proof_fixture.proof[..];
585595

586-
let proof = VRF::prove(&privk, &alpha.to_vec());
596+
let proof = VRF::prove(&privk, &alpha.to_vec()).unwrap();
587597
let proof_bytes = proof.to_bytes();
588598

589599
assert_eq!(proof_bytes.to_vec(), expected_proof_bytes.to_vec());
@@ -605,7 +615,7 @@ mod tests {
605615
let mut msg = [0u8; 1024];
606616
rng.fill_bytes(&mut msg);
607617

608-
let proof = VRF::prove(&secret_key, &msg);
618+
let proof = VRF::prove(&secret_key, &msg).unwrap();
609619
let res = VRF::verify(&public_key, &proof, &msg).unwrap();
610620

611621
assert!(res);

stackslib/Cargo.toml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,6 @@ features = ["serde", "recovery"]
8383
[dependencies.ed25519-dalek]
8484
workspace = true
8585

86-
[dependencies.curve25519-dalek]
87-
version = "=2.0.0"
88-
features = ["serde"]
89-
9086
[dependencies.time]
9187
version = "0.2.23"
9288
features = ["std"]

stackslib/src/burnchains/tests/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ impl TestMiner {
241241
);
242242
match self.vrf_key_map.get(vrf_pubkey) {
243243
Some(prover_key) => {
244-
let proof = VRF::prove(prover_key, last_sortition_hash.as_bytes());
244+
let proof = VRF::prove(prover_key, last_sortition_hash.as_bytes())?;
245245
let valid = match VRF::verify(vrf_pubkey, &proof, last_sortition_hash.as_bytes()) {
246246
Ok(v) => v,
247247
Err(e) => false,

stackslib/src/chainstate/coordinator/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ fn make_genesis_block_with_recipients(
646646

647647
let parent_stacks_header = StacksHeaderInfo::regtest_genesis();
648648

649-
let proof = VRF::prove(vrf_key, sortition_tip.sortition_hash.as_bytes());
649+
let proof = VRF::prove(vrf_key, sortition_tip.sortition_hash.as_bytes()).unwrap();
650650

651651
let mut builder = StacksBlockBuilder::make_regtest_block_builder(
652652
burnchain,
@@ -909,7 +909,7 @@ fn make_stacks_block_with_input(
909909

910910
eprintln!("Build off of {:?}", &parent_stacks_header);
911911

912-
let proof = VRF::prove(vrf_key, sortition_tip.sortition_hash.as_bytes());
912+
let proof = VRF::prove(vrf_key, sortition_tip.sortition_hash.as_bytes()).unwrap();
913913

914914
let total_burn = parents_sortition.total_burn;
915915

stackslib/src/chainstate/nakamoto/tests/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1628,7 +1628,7 @@ fn test_nakamoto_block_static_verification() {
16281628
let vrf_privkey = VRFPrivateKey::new();
16291629
let vrf_pubkey = VRFPublicKey::from_private(&vrf_privkey);
16301630
let sortition_hash = SortitionHash([0x01; 32]);
1631-
let vrf_proof = VRF::prove(&vrf_privkey, sortition_hash.as_bytes());
1631+
let vrf_proof = VRF::prove(&vrf_privkey, sortition_hash.as_bytes()).unwrap();
16321632

16331633
let burn_recipient = StacksAddress::burn_address(false).to_account_principal();
16341634
let alt_recipient = StacksAddress::p2pkh(false, &StacksPublicKey::from_private(&private_key_2))

0 commit comments

Comments
 (0)