use asmcrypto::ecdsa_batch::recover_addresses_batch;
use asmcrypto::ecdsa_scalar::{
recover_address as our_recover_address, recover_public_key as our_recover_pubkey,
};
use criterion::{Criterion, black_box, criterion_group, criterion_main};
struct TestVector {
hash: [u8; 32],
r: [u8; 32],
s: [u8; 32],
v: u8, }
fn make_test_vector() -> TestVector {
use k256::ecdsa::{SigningKey, signature::hazmat::PrehashSigner};
let sk = SigningKey::from_slice(&[0x01u8; 32]).expect("valid private key");
let hash = [0x42u8; 32]; let (sig, recid): (k256::ecdsa::Signature, k256::ecdsa::RecoveryId) =
PrehashSigner::sign_prehash(&sk, &hash).expect("signing failed");
let r: [u8; 32] = sig.r().to_bytes().into();
let s: [u8; 32] = sig.s().to_bytes().into();
TestVector {
hash,
r,
s,
v: recid.to_byte(),
}
}
mod bench_k256 {
use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
pub fn recover_pubkey(hash: &[u8; 32], r: &[u8; 32], s: &[u8; 32], v: u8) -> [u8; 65] {
let sig = Signature::from_scalars(k256::FieldBytes::from(*r), k256::FieldBytes::from(*s))
.expect("k256 from_scalars");
let recid = RecoveryId::try_from(v).expect("recovery id");
let vk = VerifyingKey::recover_from_prehash(hash, &sig, recid).expect("k256 recovery");
let ep = vk.to_encoded_point(false);
ep.as_bytes().try_into().expect("65 bytes")
}
}
mod bench_secp256k1 {
use secp256k1::{
SECP256K1,
ecdsa::{RecoverableSignature, RecoveryId},
};
pub fn recover_pubkey(hash: &[u8; 32], r: &[u8; 32], s: &[u8; 32], v: u8) -> [u8; 65] {
let msg = secp256k1::Message::from_digest(*hash);
let recid = RecoveryId::from_i32(v as i32).expect("recovery id");
let mut compact = [0u8; 64];
compact[..32].copy_from_slice(r);
compact[32..].copy_from_slice(s);
let sig = RecoverableSignature::from_compact(&compact, recid).expect("secp256k1 sig");
let pk = SECP256K1
.recover_ecdsa(&msg, &sig)
.expect("secp256k1 recovery");
pk.serialize_uncompressed()
}
}
fn bench_ecdsa(c: &mut Criterion) {
let tv = make_test_vector();
{
let mut g = c.benchmark_group("ecdsa/recover_public_key");
g.bench_function("asmcrypto", |b| {
b.iter(|| {
our_recover_pubkey(
black_box(&tv.hash),
black_box(&tv.r),
black_box(&tv.s),
black_box(tv.v),
)
})
});
g.bench_function("k256", |b| {
b.iter(|| {
bench_k256::recover_pubkey(
black_box(&tv.hash),
black_box(&tv.r),
black_box(&tv.s),
black_box(tv.v),
)
})
});
g.bench_function("secp256k1", |b| {
b.iter(|| {
bench_secp256k1::recover_pubkey(
black_box(&tv.hash),
black_box(&tv.r),
black_box(&tv.s),
black_box(tv.v),
)
})
});
g.finish();
}
{
let mut g = c.benchmark_group("ecdsa/recover_address");
g.bench_function("asmcrypto", |b| {
b.iter(|| {
our_recover_address(
black_box(&tv.hash),
black_box(&tv.r),
black_box(&tv.s),
black_box(tv.v),
)
})
});
g.bench_function("k256+sha3", |b| {
b.iter(|| {
use sha3::Digest as _;
let pk = bench_k256::recover_pubkey(
black_box(&tv.hash),
black_box(&tv.r),
black_box(&tv.s),
black_box(tv.v),
);
let h: [u8; 32] = sha3::Keccak256::digest(&pk[1..]).into();
let mut addr = [0u8; 20];
addr.copy_from_slice(&h[12..]);
addr
})
});
g.bench_function("secp256k1+sha3", |b| {
b.iter(|| {
use sha3::Digest as _;
let pk = bench_secp256k1::recover_pubkey(
black_box(&tv.hash),
black_box(&tv.r),
black_box(&tv.s),
black_box(tv.v),
);
let h: [u8; 32] = sha3::Keccak256::digest(&pk[1..]).into();
let mut addr = [0u8; 20];
addr.copy_from_slice(&h[12..]);
addr
})
});
g.finish();
}
}
fn bench_ecdsa_batch(c: &mut Criterion) {
let tv = make_test_vector();
let hashes = [&tv.hash; 8];
let rs = [&tv.r; 8];
let ss = [&tv.s; 8];
let vs = [tv.v; 8];
let mut g = c.benchmark_group("ecdsa/recover_address_x8");
g.bench_function("asmcrypto-batch", |b| {
b.iter(|| {
recover_addresses_batch(
black_box(hashes),
black_box(rs),
black_box(ss),
black_box(vs),
)
})
});
g.bench_function("asmcrypto-scalar-x8", |b| {
b.iter(|| {
std::array::from_fn::<[u8; 20], 8, _>(|i| {
our_recover_address(
black_box(hashes[i]),
black_box(rs[i]),
black_box(ss[i]),
black_box(vs[i]),
)
.unwrap_or([0u8; 20])
})
})
});
g.bench_function("secp256k1-x8", |b| {
b.iter(|| {
std::array::from_fn::<[u8; 20], 8, _>(|i| {
use sha3::Digest as _;
let pk = bench_secp256k1::recover_pubkey(
black_box(hashes[i]),
black_box(rs[i]),
black_box(ss[i]),
black_box(vs[i]),
);
let h: [u8; 32] = sha3::Keccak256::digest(&pk[1..]).into();
let mut addr = [0u8; 20];
addr.copy_from_slice(&h[12..]);
addr
})
})
});
g.finish();
}
criterion_group!(benches, bench_ecdsa, bench_ecdsa_batch);
criterion_main!(benches);