use libc::c_ulonglong;
#[cfg(not(feature = "std"))]
use prelude::*;
use std::fmt;
use std::iter::repeat;
use std::mem;
pub const SEEDBYTES: usize = ffi::crypto_sign_ed25519_SEEDBYTES;
pub const SECRETKEYBYTES: usize = ffi::crypto_sign_ed25519_SECRETKEYBYTES;
pub const PUBLICKEYBYTES: usize = ffi::crypto_sign_ed25519_PUBLICKEYBYTES;
pub const SIGNATUREBYTES: usize = ffi::crypto_sign_ed25519_BYTES;
pub const SCALARMULTBYTES: usize = ffi::crypto_scalarmult_curve25519_BYTES;
new_type! {
secret Seed(SEEDBYTES);
}
new_type! {
secret SecretKey(SECRETKEYBYTES);
}
new_type! {
public PublicKey(PUBLICKEYBYTES);
}
new_type! {
public Signature(SIGNATUREBYTES);
}
pub fn gen_keypair() -> (PublicKey, SecretKey) {
unsafe {
let mut pk = [0u8; PUBLICKEYBYTES];
let mut sk = [0u8; SECRETKEYBYTES];
ffi::crypto_sign_ed25519_keypair(&mut pk, &mut sk);
(PublicKey(pk), SecretKey(sk))
}
}
pub fn keypair_from_seed(&Seed(ref seed): &Seed) -> (PublicKey, SecretKey) {
unsafe {
let mut pk = [0u8; PUBLICKEYBYTES];
let mut sk = [0u8; SECRETKEYBYTES];
ffi::crypto_sign_ed25519_seed_keypair(&mut pk, &mut sk, seed);
(PublicKey(pk), SecretKey(sk))
}
}
pub fn sign(m: &[u8], &SecretKey(ref sk): &SecretKey) -> Vec<u8> {
unsafe {
let mut sm: Vec<u8> = repeat(0u8).take(m.len() + SIGNATUREBYTES).collect();
let mut smlen = 0;
ffi::crypto_sign_ed25519(
sm.as_mut_ptr(),
&mut smlen,
m.as_ptr(),
m.len() as c_ulonglong,
sk,
);
sm.truncate(smlen as usize);
sm
}
}
pub fn verify(sm: &[u8], &PublicKey(ref pk): &PublicKey) -> Result<Vec<u8>, ()> {
unsafe {
let mut m: Vec<u8> = repeat(0u8).take(sm.len()).collect();
let mut mlen = 0;
if ffi::crypto_sign_ed25519_open(
m.as_mut_ptr(),
&mut mlen,
sm.as_ptr(),
sm.len() as c_ulonglong,
pk,
) == 0
{
m.truncate(mlen as usize);
Ok(m)
} else {
Err(())
}
}
}
pub fn sign_detached(m: &[u8], &SecretKey(ref sk): &SecretKey) -> Signature {
unsafe {
let mut sig = [0u8; SIGNATUREBYTES];
let mut siglen: c_ulonglong = 0;
ffi::crypto_sign_ed25519_detached(
&mut sig,
&mut siglen,
m.as_ptr(),
m.len() as c_ulonglong,
sk,
);
assert_eq!(siglen, SIGNATUREBYTES as c_ulonglong);
Signature(sig)
}
}
pub fn verify_detached(
&Signature(ref sig): &Signature,
m: &[u8],
&PublicKey(ref pk): &PublicKey,
) -> bool {
unsafe {
0 == ffi::crypto_sign_ed25519_verify_detached(sig, m.as_ptr(), m.len() as c_ulonglong, pk)
}
}
pub fn convert_ed_pk_to_curve25519(pk: &[u8; SCALARMULTBYTES]) -> [u8; SCALARMULTBYTES] {
let mut curve_pk = [0; SCALARMULTBYTES];
unsafe { ffi::crypto_sign_ed25519_pk_to_curve25519(&mut curve_pk, pk) }
curve_pk
}
pub fn convert_ed_sk_to_curve25519(sk: &[u8; SCALARMULTBYTES]) -> [u8; SCALARMULTBYTES] {
let mut curve_sk = [0; SCALARMULTBYTES];
unsafe { ffi::crypto_sign_ed25519_sk_to_curve25519(&mut curve_sk, sk) }
curve_sk
}
#[allow(clippy::needless_pass_by_value)]
pub fn convert_ed_keypair_to_curve25519(pk: PublicKey, sk: SecretKey) -> (PublicKey, SecretKey) {
let pk = convert_ed_pk_to_curve25519(&pk.0);
let mut secret_key = [0; SCALARMULTBYTES];
secret_key.clone_from_slice(&sk[..SCALARMULTBYTES]);
let sk = convert_ed_sk_to_curve25519(&secret_key);
let mut secret_key = [0; SECRETKEYBYTES];
secret_key.clone_from_slice(&[sk, pk].concat());
(PublicKey(pk), SecretKey(secret_key))
}
pub fn convert_sk_to_seed(sk: &SecretKey) -> Seed {
let mut seed = [0; SEEDBYTES];
unsafe {
ffi::crypto_sign_ed25519_sk_to_seed(&mut seed, &sk.0);
}
Seed(seed)
}
pub fn convert_sk_to_pk(sk: &SecretKey) -> PublicKey {
let mut pk = [0; PUBLICKEYBYTES];
unsafe {
ffi::crypto_sign_ed25519_sk_to_pk(&mut pk, &sk.0);
}
PublicKey(pk)
}
#[derive(Clone)]
pub struct State(ffi::crypto_sign_ed25519ph_state);
impl State {
#[allow(clippy::uninit_assumed_init)]
pub fn init() -> State {
unsafe {
let mut s = mem::MaybeUninit::uninit().assume_init();
ffi::crypto_sign_ed25519ph_init(&mut s);
State(s)
}
}
pub fn update(&mut self, m: &[u8]) {
unsafe {
ffi::crypto_sign_ed25519ph_update(&mut self.0, m.as_ptr(), m.len() as c_ulonglong);
}
}
pub fn finalize(&mut self, &SecretKey(ref sk): &SecretKey) -> Signature {
unsafe {
let sig = [0u8; SIGNATUREBYTES];
let mut siglen: c_ulonglong = 0;
ffi::crypto_sign_ed25519ph_final_create(&mut self.0, sig.as_ptr(), &mut siglen, sk);
assert_eq!(siglen, SIGNATUREBYTES as c_ulonglong);
Signature(sig)
}
}
pub fn verify(
&mut self,
&Signature(ref sig): &Signature,
&PublicKey(ref pk): &PublicKey,
) -> bool {
unsafe { 0 == ffi::crypto_sign_ed25519ph_final_verify(&mut self.0, sig.as_ptr(), pk) }
}
}
impl fmt::Debug for State {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ed25519 state")
}
}
impl Default for State {
fn default() -> State {
State::init()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::randombytes::randombytes;
#[test]
fn test_streaming_sign() {
for i in 0..256usize {
let (pk, sk) = gen_keypair();
let m = randombytes(i);
let mut creation_state = State::init();
creation_state.update(&m);
let sig = creation_state.finalize(&sk);
let mut validator_state = State::init();
validator_state.update(&m);
assert!(validator_state.verify(&sig, &pk));
}
}
#[test]
fn test_chunks_sign() {
let (pk, sk) = gen_keypair();
let mut creation_state = State::init();
let mut validator_state = State::init();
for i in 0..64usize {
let chunk = randombytes(i);
creation_state.update(&chunk);
validator_state.update(&chunk);
}
let sig = creation_state.finalize(&sk);
assert!(validator_state.verify(&sig, &pk));
}
#[test]
fn test_sign_verify() {
for i in 0..256usize {
let (pk, sk) = gen_keypair();
let m = randombytes(i);
let sm = sign(&m, &sk);
let m2 = verify(&sm, &pk);
assert!(Ok(m) == m2);
}
}
#[test]
fn test_sign_verify_tamper() {
for i in 0..32usize {
let (pk, sk) = gen_keypair();
let m = randombytes(i);
let mut sm = sign(&m, &sk);
for j in 0..sm.len() {
sm[j] ^= 0x20;
assert!(Err(()) == verify(&sm, &pk));
sm[j] ^= 0x20;
}
}
}
#[test]
fn test_sign_verify_detached() {
for i in 0..256usize {
let (pk, sk) = gen_keypair();
let m = randombytes(i);
let sig = sign_detached(&m, &sk);
assert!(verify_detached(&sig, &m, &pk));
}
}
#[test]
fn test_sign_verify_detached_tamper() {
for i in 0..32usize {
let (pk, sk) = gen_keypair();
let m = randombytes(i);
let Signature(mut sig) = sign_detached(&m, &sk);
for j in 0..SIGNATUREBYTES {
sig[j] ^= 0x20;
assert!(!verify_detached(&Signature(sig), &m, &pk));
sig[j] ^= 0x20;
}
}
}
#[test]
fn test_sign_verify_seed() {
use crate::randombytes::randombytes_into;
for i in 0..256usize {
let mut seedbuf = [0; 32];
randombytes_into(&mut seedbuf);
let seed = Seed(seedbuf);
let (pk, sk) = keypair_from_seed(&seed);
let m = randombytes(i);
let sm = sign(&m, &sk);
let m2 = verify(&sm, &pk);
assert!(Ok(m) == m2);
}
}
#[test]
fn test_sign_verify_tamper_seed() {
use crate::randombytes::randombytes_into;
for i in 0..32usize {
let mut seedbuf = [0; 32];
randombytes_into(&mut seedbuf);
let seed = Seed(seedbuf);
let (pk, sk) = keypair_from_seed(&seed);
let m = randombytes(i);
let mut sm = sign(&m, &sk);
for j in 0..sm.len() {
sm[j] ^= 0x20;
assert_eq!(Err(()), verify(&sm, &pk));
sm[j] ^= 0x20;
}
}
}
#[test]
fn test_vectors() {
use std::{
fs::File,
io::{BufRead, BufReader},
};
let r = BufReader::new(File::open("testvectors/ed25519.input").unwrap());
for mline in r.lines() {
let line = mline.unwrap();
let mut x = line.split(':');
let x0 = x.next().unwrap();
let x1 = x.next().unwrap();
let x2 = x.next().unwrap();
let x3 = x.next().unwrap();
let seed_bytes = hex::decode(&x0[..64]).unwrap();
assert_eq!(seed_bytes.len(), SEEDBYTES);
let mut seedbuf = [0u8; SEEDBYTES];
for (s, b) in seedbuf.iter_mut().zip(seed_bytes.iter()) {
*s = *b
}
let seed = Seed(seedbuf);
let (pk, sk) = keypair_from_seed(&seed);
let m = hex::decode(&x2).unwrap();
let sm = sign(&m, &sk);
verify(&sm, &pk).unwrap();
assert_eq!(x1, hex::encode(&pk[..]));
assert_eq!(x3, hex::encode(&sm));
}
}
#[test]
fn test_vectors_detached() {
use std::{
fs::File,
io::{BufRead, BufReader},
};
let r = BufReader::new(File::open("testvectors/ed25519.input").unwrap());
for mline in r.lines() {
let line = mline.unwrap();
let mut x = line.split(':');
let x0 = x.next().unwrap();
let x1 = x.next().unwrap();
let x2 = x.next().unwrap();
let x3 = x.next().unwrap();
let seed_bytes = hex::decode(&x0[..64]).unwrap();
assert_eq!(seed_bytes.len(), SEEDBYTES);
let mut seedbuf = [0u8; SEEDBYTES];
for (s, b) in seedbuf.iter_mut().zip(seed_bytes.iter()) {
*s = *b
}
let seed = Seed(seedbuf);
let (pk, sk) = keypair_from_seed(&seed);
let m = hex::decode(&x2).unwrap();
let sig = sign_detached(&m, &sk);
assert!(verify_detached(&sig, &m, &pk));
assert_eq!(x1, hex::encode(&pk[..]));
let sm = hex::encode(&sig[..]) + x2; assert_eq!(x3, sm);
}
}
#[cfg(feature = "serde")]
#[test]
fn test_serialisation() {
use crate::test_utils::round_trip;
for i in 0..256usize {
let (pk, sk) = gen_keypair();
let m = randombytes(i);
let sig = sign_detached(&m, &sk);
round_trip(pk);
round_trip(sk);
round_trip(sig);
}
}
#[test]
fn test_crypto_sign_ed25519_to_curve25519() {
use crate::crypto::scalarmult::curve25519::{scalarmult_base, GroupElement, Scalar};
let (pk, sk) = gen_keypair();
let (PublicKey(ref pk), SecretKey(ref sk)) = convert_ed_keypair_to_curve25519(pk, sk);
let secret_key = Scalar::from_slice(&sk[..SCALARMULTBYTES]).unwrap();
let GroupElement(public_key) = scalarmult_base(&secret_key);
assert_eq!(pk, &public_key);
}
#[test]
fn test_convert_sk_to_seed() {
let (_, sk) = gen_keypair();
let seed = convert_sk_to_seed(&sk);
let (_, sk_from_seed) = keypair_from_seed(&seed);
assert_eq!(sk, sk_from_seed);
}
#[test]
fn test_convert_sk_to_pk() {
let (pk, sk) = gen_keypair();
let pk_from_sk = convert_sk_to_pk(&sk);
assert_eq!(pk, pk_from_sk);
}
}
#[cfg(feature = "benchmarks")]
#[cfg(test)]
mod bench {
extern crate test;
use super::*;
use crate::randombytes::randombytes;
const BENCH_SIZES: [usize; 14] = [0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096];
#[bench]
fn bench_sign(b: &mut test::Bencher) {
let (_, sk) = gen_keypair();
let ms: Vec<Vec<u8>> = BENCH_SIZES.iter().map(|s| randombytes(*s)).collect();
b.iter(|| {
for m in ms.iter() {
sign(m, &sk);
}
});
}
#[bench]
fn bench_verify(b: &mut test::Bencher) {
let (pk, sk) = gen_keypair();
let sms: Vec<Vec<u8>> = BENCH_SIZES
.iter()
.map(|s| {
let m = randombytes(*s);
sign(&m, &sk)
})
.collect();
b.iter(|| {
for sm in sms.iter() {
verify(sm, &pk);
}
});
}
}