mirror of
https://github.com/saymrwulf/anza-cryptography-source.git
synced 2026-09-04 20:24:04 +00:00
[feat] impl legacy dalek verification method for ed25519 (#8)
* Initial commit * skeleton * refactor and merge curve and ed crates * fmt * ci * fmt again * ci * Update bench.rs * fix ubuntu * implement dalek api * clean up
This commit is contained in:
parent
b801651332
commit
bccf7e13c0
7 changed files with 211 additions and 98 deletions
48
Cargo.lock
generated
48
Cargo.lock
generated
|
|
@ -132,9 +132,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.57"
|
||||
version = "1.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
|
||||
checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
|
|
@ -645,9 +645,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.91"
|
||||
version = "0.3.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
|
||||
checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
|
|
@ -807,9 +807,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proptest"
|
||||
version = "1.10.0"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532"
|
||||
checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bit-vec",
|
||||
|
|
@ -1132,12 +1132,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.11.0-rc.5"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024"
|
||||
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"cpufeatures 0.3.0",
|
||||
"digest 0.11.2",
|
||||
]
|
||||
|
||||
|
|
@ -1191,7 +1191,7 @@ dependencies = [
|
|||
"rustcrypto-ff",
|
||||
"rustcrypto-group",
|
||||
"serde",
|
||||
"sha2 0.11.0-rc.5",
|
||||
"sha2 0.11.0",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
|
@ -1338,9 +1338,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.114"
|
||||
version = "0.2.115"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
|
||||
checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
|
|
@ -1351,9 +1351,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.114"
|
||||
version = "0.2.115"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
|
||||
checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
|
@ -1361,9 +1361,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.114"
|
||||
version = "0.2.115"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
|
||||
checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
|
|
@ -1374,9 +1374,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.114"
|
||||
version = "0.2.115"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
|
||||
checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
|
@ -1417,9 +1417,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.91"
|
||||
version = "0.3.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9"
|
||||
checksum = "84cde8507f4d7cfcb1185b8cb5890c494ffea65edbe1ba82cfd63661c805ed94"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
|
@ -1633,18 +1633,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.47"
|
||||
version = "0.8.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
|
||||
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.47"
|
||||
version = "0.8.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
|
||||
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
|||
11
Cargo.toml
11
Cargo.toml
|
|
@ -5,17 +5,11 @@ members = [
|
|||
]
|
||||
resolver = "2"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 2
|
||||
|
||||
|
||||
[workspace.dependencies]
|
||||
bincode = "1"
|
||||
cc = "1.0"
|
||||
cfg-if = "1"
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
# Keep the dependency key as "curve25519" so that all existing `use curve25519::…`
|
||||
# references in source files remain valid; the actual package is solanacurve25519.
|
||||
curve25519 = { path = "curve25519/solana-ed25519", package = "solana-ed25519" }
|
||||
curve25519-dalek-derive = "0.1.1"
|
||||
cpufeatures = "0.2.17"
|
||||
|
|
@ -38,4 +32,7 @@ serde = { version = "1.0", default-features = false, features = ["derive"] }
|
|||
sha2 = { version = "0.11.0-rc.3", default-features = false }
|
||||
subtle = { version = "2.6.0", default-features = false, features = ["const-generics"] }
|
||||
which = "4.4"
|
||||
zeroize = { version = "1", default-features = false, features = ["derive"] }
|
||||
zeroize = { version = "1", default-features = false, features = ["derive"] }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 2
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
# curve25519 (curve25519-sol)
|
||||
# curve25519 (`solana-ed25519`)
|
||||
|
||||
**A pure-Rust implementation of group operations on Ristretto and Curve25519, forked from
|
||||
[curve25519-dalek] with HEEA scalar decomposition and a reduced backend set.**
|
||||
|
||||
> For the original curve25519-dalek documentation see [README_dalek.md](README_dalek.md).
|
||||
|
||||
This crate is part of the [curve25519-sol](../README.md) workspace.
|
||||
This crate is part of the [cryptography](https://github.com/anza-xyz/cryptography/) workspace.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -56,10 +56,10 @@ verification.
|
|||
|
||||
> For the original ed25519-zebra documentation see [README_zebra.md](README_zebra.md).
|
||||
|
||||
### `verify_heea`: fast-path signature verification
|
||||
### `verify_zebra`: fast-path signature verification
|
||||
|
||||
A new method `VerificationKey::verify_heea` sits alongside the existing `verify`.
|
||||
Both accept the same arguments and produce identical results — `verify_heea` is a
|
||||
A new method `VerificationKey::verify_zebra` sits alongside the existing `verify`.
|
||||
Both accept the same arguments and produce identical results — `verify_zebra` is a
|
||||
**drop-in accelerated replacement** for `verify`.
|
||||
|
||||
The HEEA method (TCHES 2025) transforms the standard 2-point MSM:
|
||||
|
|
@ -93,7 +93,39 @@ See [ZIP 215] for full details.
|
|||
## Use
|
||||
|
||||
```toml
|
||||
curve25519-sol = { git = "https://github.com/zz-sol/ed25519-sol" }
|
||||
curve25519 = { package = "solana-ed25519", git = "https://github.com/anza-xyz/cryptography" }
|
||||
```
|
||||
|
||||
### Ed25519 signing and verification
|
||||
|
||||
```rust,no_run
|
||||
use core::convert::TryFrom;
|
||||
use curve25519::ed_sigs::{SigningKey, VerificationKey};
|
||||
|
||||
let msg = b"solana-ed25519";
|
||||
|
||||
// Generate key and sign
|
||||
let sk = SigningKey::new(rand::rng());
|
||||
let sig = sk.sign(msg);
|
||||
let vk = VerificationKey::from(&sk);
|
||||
|
||||
// Standard ZIP-215 verification (from ed25519-zebra)
|
||||
vk.verify(&sig, msg).expect("valid signature");
|
||||
|
||||
// HEEA-accelerated verification (same result, ~15% faster)
|
||||
vk.verify_zebra(&sig, msg).expect("valid signature");
|
||||
```
|
||||
|
||||
### Batch verification
|
||||
|
||||
```rust,ignore
|
||||
use curve25519::ed_sigs::batch;
|
||||
|
||||
let mut verifier = batch::Verifier::new();
|
||||
for (vk_bytes, sig, msg) in items {
|
||||
verifier.queue((vk_bytes, sig, msg));
|
||||
}
|
||||
verifier.verify(rand::rng()).expect("all valid");
|
||||
```
|
||||
|
||||
### Ed25519 signing and verification
|
||||
|
|
@ -109,11 +141,8 @@ let sk = SigningKey::new(rand::rng());
|
|||
let sig = sk.sign(msg);
|
||||
let vk = VerificationKey::from(&sk);
|
||||
|
||||
// Standard ZIP-215 verification (from ed25519-zebra)
|
||||
// Standard ZIP-215 verification with heea acceleration
|
||||
vk.verify(&sig, msg).expect("valid signature");
|
||||
|
||||
// HEEA-accelerated verification (same result, ~15% faster)
|
||||
vk.verify_heea(&sig, msg).expect("valid signature");
|
||||
```
|
||||
|
||||
### Batch verification
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
|
||||
use curve25519::ed_sigs::*;
|
||||
use ed25519::signature::Verifier as _;
|
||||
use ed25519_dalek::VerifyingKey as DalekVerifyingKey;
|
||||
use ed25519_zebra::VerificationKey as ZebraVerificationKey;
|
||||
|
||||
fn sigs_with_distinct_pubkeys() -> impl Iterator<Item = (VerificationKeyBytes, Signature)> {
|
||||
std::iter::repeat_with(|| {
|
||||
|
|
@ -20,6 +24,23 @@ fn sigs_with_same_pubkey() -> impl Iterator<Item = (VerificationKeyBytes, Signat
|
|||
})
|
||||
}
|
||||
|
||||
fn single_verify_inputs() -> (
|
||||
VerificationKey,
|
||||
Signature,
|
||||
ZebraVerificationKey,
|
||||
DalekVerifyingKey,
|
||||
) {
|
||||
let sk = SigningKey::new(rand::rng());
|
||||
let vk = VerificationKey::from(&sk);
|
||||
let sig = sk.sign(b"");
|
||||
let vk_bytes: [u8; 32] = vk.into();
|
||||
|
||||
let zebra_vk = ZebraVerificationKey::try_from(vk_bytes).expect("zebra verification key");
|
||||
let dalek_vk = DalekVerifyingKey::from_bytes(&vk_bytes).expect("dalek verification key");
|
||||
|
||||
(vk, sig, zebra_vk, dalek_vk)
|
||||
}
|
||||
|
||||
fn bench_batch_verify(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("Batch Verification");
|
||||
for n in [8usize, 16, 24, 32, 40, 48, 56, 64].iter() {
|
||||
|
|
@ -74,21 +95,31 @@ fn bench_batch_verify(c: &mut Criterion) {
|
|||
fn bench_single_verify(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("Single Verification");
|
||||
|
||||
group.bench_function("ed25519", |b| {
|
||||
let sk = SigningKey::new(rand::rng());
|
||||
let vk = VerificationKey::from(&sk);
|
||||
let sig = sk.sign(b"");
|
||||
group.bench_function("local_verify_zebra", |b| {
|
||||
let (vk, sig, _, _) = single_verify_inputs();
|
||||
b.iter(|| {
|
||||
let _ = vk.verify(&sig, b"");
|
||||
let _ = vk.verify_zebra(&sig, b"");
|
||||
})
|
||||
});
|
||||
|
||||
group.bench_function("ed25519_hEEA", |b| {
|
||||
let sk = SigningKey::new(rand::rng());
|
||||
let vk = VerificationKey::from(&sk);
|
||||
let sig = sk.sign(b"");
|
||||
group.bench_function("local_verify_dalek", |b| {
|
||||
let (vk, sig, _, _) = single_verify_inputs();
|
||||
b.iter(|| {
|
||||
let _ = vk.verify_heea(&sig, b"");
|
||||
let _ = vk.verify_dalek(&sig, b"");
|
||||
})
|
||||
});
|
||||
|
||||
group.bench_function("crates_io_ed25519_zebra", |b| {
|
||||
let (_, sig, zebra_vk, _) = single_verify_inputs();
|
||||
b.iter(|| {
|
||||
let _ = zebra_vk.verify(&sig, b"");
|
||||
})
|
||||
});
|
||||
|
||||
group.bench_function("crates_io_ed25519_dalek", |b| {
|
||||
let (_, sig, _, dalek_vk) = single_verify_inputs();
|
||||
b.iter(|| {
|
||||
let _ = dalek_vk.verify(b"", &sig);
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ impl Item {
|
|||
/// the message.
|
||||
pub fn verify_single(self) -> Result<(), Error> {
|
||||
VerificationKey::try_from(self.vk_bytes)
|
||||
.and_then(|vk| vk.verify_prehashed(&self.sig, self.k))
|
||||
.and_then(|vk| vk.verify_zebra_prehashed(&self.sig, self.k))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
use crate::ed_sigs::SigningKey;
|
||||
use crate::ed_sigs::VerificationKey;
|
||||
#[cfg(feature = "std")]
|
||||
use crate::ed_sigs::tests::small_order::SMALL_ORDER_SIGS;
|
||||
#[cfg(feature = "std")]
|
||||
use core::convert::TryFrom;
|
||||
#[cfg(feature = "std")]
|
||||
use ed25519::Signature;
|
||||
|
||||
#[test]
|
||||
fn test_verify_heea_invalid_signature() {
|
||||
|
|
@ -13,17 +19,21 @@ fn test_verify_heea_invalid_signature() {
|
|||
// Try to verify with different message
|
||||
let wrong_msg = b"Different message";
|
||||
|
||||
let result_standard = verification_key.verify(&signature, wrong_msg);
|
||||
let result_heea = verification_key.verify_heea(&signature, wrong_msg);
|
||||
let result_default = verification_key.verify(&signature, wrong_msg);
|
||||
let result_zebra = verification_key.verify_zebra(&signature, wrong_msg);
|
||||
let result_dalek = verification_key.verify_dalek(&signature, wrong_msg);
|
||||
|
||||
// Both should fail
|
||||
assert!(
|
||||
result_standard.is_err(),
|
||||
"Standard verification should fail for wrong message"
|
||||
result_default.is_err(),
|
||||
"Default verification should fail for wrong message"
|
||||
);
|
||||
assert!(
|
||||
result_heea.is_err(),
|
||||
"heea verification should fail for wrong message"
|
||||
result_zebra.is_err(),
|
||||
"Zebra verification should fail for wrong message"
|
||||
);
|
||||
assert!(
|
||||
result_dalek.is_err(),
|
||||
"Dalek verification should fail for wrong message"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -38,18 +48,56 @@ fn test_verify_heea_multiple_signatures() {
|
|||
let msg = format!("Message number {}", i);
|
||||
let signature = signing_key.sign(msg.as_bytes());
|
||||
|
||||
let result_standard = verification_key.verify(&signature, msg.as_bytes());
|
||||
let result_heea = verification_key.verify_heea(&signature, msg.as_bytes());
|
||||
let result_default = verification_key.verify(&signature, msg.as_bytes());
|
||||
let result_zebra = verification_key.verify_zebra(&signature, msg.as_bytes());
|
||||
let result_dalek = verification_key.verify_dalek(&signature, msg.as_bytes());
|
||||
|
||||
assert!(
|
||||
result_standard.is_ok(),
|
||||
"Standard verification should succeed for signature {}",
|
||||
result_default.is_ok(),
|
||||
"Default verification should succeed for signature {}",
|
||||
i
|
||||
);
|
||||
assert!(
|
||||
result_heea.is_ok(),
|
||||
"heea verification should succeed for signature {}",
|
||||
result_zebra.is_ok(),
|
||||
"Zebra verification should succeed for signature {}",
|
||||
i
|
||||
);
|
||||
assert!(
|
||||
result_dalek.is_ok(),
|
||||
"Dalek verification should succeed for signature {}",
|
||||
i
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_verification_matches_zebra() {
|
||||
let mut rng = rand::rng();
|
||||
let signing_key = SigningKey::new(&mut rng);
|
||||
let verification_key = VerificationKey::from(&signing_key);
|
||||
let msg = b"default verification mode";
|
||||
let signature = signing_key.sign(msg);
|
||||
|
||||
assert_eq!(
|
||||
verification_key.verify(&signature, msg),
|
||||
verification_key.verify_zebra(&signature, msg)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[test]
|
||||
fn test_verify_dalek_matches_legacy_edge_cases() {
|
||||
for case in SMALL_ORDER_SIGS.iter() {
|
||||
let sig = Signature::from(case.sig_bytes);
|
||||
let vk = VerificationKey::try_from(case.vk_bytes).unwrap();
|
||||
let result = vk.verify_dalek(&sig, b"Zcash");
|
||||
|
||||
assert_eq!(
|
||||
result.is_ok(),
|
||||
case.valid_legacy,
|
||||
"dalek-compatible verification mismatch for vk={} sig={}",
|
||||
hex::encode(case.vk_bytes),
|
||||
hex::encode(case.sig_bytes)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -237,8 +237,20 @@ impl Verifier<Signature> for VerificationKey {
|
|||
}
|
||||
|
||||
impl VerificationKey {
|
||||
fn challenge_scalar(&self, signature: &Signature, msg: &[u8]) -> Scalar {
|
||||
Scalar::from_hash(
|
||||
Sha512::default()
|
||||
.chain(&signature.r_bytes()[..])
|
||||
.chain(&self.A_bytes.0[..])
|
||||
.chain(msg),
|
||||
)
|
||||
}
|
||||
|
||||
/// Verify a purported `signature` on the given `msg`.
|
||||
///
|
||||
/// This is the default verification mode and uses the HEEA-accelerated
|
||||
/// verification path with Zebra / ZIP-215 semantics.
|
||||
///
|
||||
/// ## Zcash-specific consensus properties
|
||||
///
|
||||
/// Ed25519 checks are described in [§5.4.5][ps] of the Zcash protocol specification and in
|
||||
|
|
@ -259,16 +271,10 @@ impl VerificationKey {
|
|||
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#concreteed25519
|
||||
/// [ZIP215]: https://zips.z.cash/zip-0215
|
||||
pub fn verify(&self, signature: &Signature, msg: &[u8]) -> Result<(), Error> {
|
||||
let k = Scalar::from_hash(
|
||||
Sha512::default()
|
||||
.chain(&signature.r_bytes()[..])
|
||||
.chain(&self.A_bytes.0[..])
|
||||
.chain(msg),
|
||||
);
|
||||
self.verify_prehashed(signature, k)
|
||||
self.verify_zebra(signature, msg)
|
||||
}
|
||||
|
||||
/// Verify a signature using the heea half-size scalar optimization.
|
||||
/// Verify a signature using HEEA with Zebra / ZIP-215 semantics.
|
||||
///
|
||||
/// This implements the algorithm from "Accelerating EdDSA Signature Verification
|
||||
/// with Faster Scalar Size Halving" (TCHES 2025).
|
||||
|
|
@ -285,15 +291,16 @@ impl VerificationKey {
|
|||
/// τs_lo B + τs_hi (2^128 B) = τR + ρA
|
||||
/// which can be done via 4-variable MSM with half-size scalars.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn verify_heea(&self, signature: &Signature, msg: &[u8]) -> Result<(), Error> {
|
||||
// Compute the hash scalar h (called k in the standard implementation)
|
||||
let h = Scalar::from_hash(
|
||||
Sha512::default()
|
||||
.chain(&signature.r_bytes()[..])
|
||||
.chain(&self.A_bytes.0[..])
|
||||
.chain(msg),
|
||||
);
|
||||
pub fn verify_zebra(&self, signature: &Signature, msg: &[u8]) -> Result<(), Error> {
|
||||
self.verify_zebra_prehashed(signature, self.challenge_scalar(signature, msg))
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn verify_zebra_prehashed(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
h: Scalar,
|
||||
) -> Result<(), Error> {
|
||||
// Generate half-size scalars ρ and τ such that ρ ≡ τh (mod ℓ)
|
||||
// in order to have rho and tau approximately half the size of h
|
||||
// it is possible that we compute ρ ≡ -τh (mod ℓ)
|
||||
|
|
@ -323,7 +330,6 @@ impl VerificationKey {
|
|||
// Compute the multi-scalar multiplication
|
||||
let result = EdwardsPoint::vartime_triple_scalar_mul_basepoint(&tau, &neg_R, &rho, &A, &ts);
|
||||
|
||||
// Check if [8] τs B + [8] τ (-R) + [8] ρ (-A) == 0
|
||||
if result.mul_by_cofactor().is_identity() {
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
@ -331,26 +337,28 @@ impl VerificationKey {
|
|||
}
|
||||
}
|
||||
|
||||
/// Verify a signature with a prehashed `k` value. Note that this is not the
|
||||
/// same as "prehashing" in RFC8032.
|
||||
/// Verify a signature with exact `ed25519-dalek`-style byte-level behavior.
|
||||
///
|
||||
/// This recomputes the expected canonical `R` encoding and compares it to the
|
||||
/// signature's `R` bytes, matching dalek's ordinary verification behavior.
|
||||
///
|
||||
/// Note that exact dalek-compatible behavior is incompatible with the HEEA
|
||||
/// transformed equation because the transformed check does not preserve the
|
||||
/// original `R` encoding needed for the byte comparison.
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn verify_prehashed(&self, signature: &Signature, k: Scalar) -> Result<(), Error> {
|
||||
// `s_bytes` MUST represent an integer less than the prime `l`.
|
||||
pub fn verify_dalek(&self, signature: &Signature, msg: &[u8]) -> Result<(), Error> {
|
||||
self.verify_dalek_prehashed(signature, self.challenge_scalar(signature, msg))
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn verify_dalek_prehashed(&self, signature: &Signature, h: Scalar) -> Result<(), Error> {
|
||||
let s = Option::<Scalar>::from(Scalar::from_canonical_bytes(*signature.s_bytes()))
|
||||
.ok_or(Error::InvalidSignature)?;
|
||||
// `R_bytes` MUST be an encoding of a point on the twisted Edwards form of Curve25519.
|
||||
let R = CompressedEdwardsY(*signature.r_bytes())
|
||||
.decompress()
|
||||
.ok_or(Error::InvalidSignature)?;
|
||||
// We checked the encoding of A_bytes when constructing `self`.
|
||||
|
||||
// [8][s]B = [8]R + [8][k]A
|
||||
// <=> [8]R = [8][s]B - [8][k]A
|
||||
// <=> 0 = [8](R - ([s]B - [k]A))
|
||||
// <=> 0 = [8](R - R') where R' = [s]B - [k]A
|
||||
let R_prime = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &self.minus_A, &s);
|
||||
let expected_R =
|
||||
EdwardsPoint::vartime_double_scalar_mul_basepoint(&h, &self.minus_A, &s).compress();
|
||||
|
||||
if (R - R_prime).mul_by_cofactor().is_identity() {
|
||||
if expected_R.as_bytes() == signature.r_bytes() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidSignature)
|
||||
|
|
|
|||
Loading…
Reference in a new issue