use {digest, error, private, rand, signature};
use super::ops::*;
use untrusted;
pub struct EdDSAParameters;
pub struct Ed25519KeyPair {
private_scalar: Scalar,
private_prefix: Prefix,
public_key: PublicKey,
}
pub struct Ed25519KeyPairBytes {
pub private_key: [u8; SEED_LEN],
pub public_key: [u8; PUBLIC_KEY_LEN],
}
impl<'a> Ed25519KeyPair {
pub fn generate(rng: &rand::SecureRandom)
-> Result<Ed25519KeyPair, error::Unspecified> {
Ed25519KeyPair::generate_serializable(rng).map(|(key_pair, _)| key_pair)
}
pub fn generate_serializable(rng: &rand::SecureRandom)
-> Result<(Ed25519KeyPair, Ed25519KeyPairBytes),
error::Unspecified> {
let mut seed = [0u8; SEED_LEN];
try!(rng.fill(&mut seed));
let key_pair = Ed25519KeyPair::from_seed(&seed);
let bytes = Ed25519KeyPairBytes {
private_key: seed,
public_key: key_pair.public_key,
};
Ok((key_pair, bytes))
}
pub fn from_bytes(seed: &[u8], public_key: &[u8])
-> Result<Ed25519KeyPair, error::Unspecified> {
let seed = try!(slice_as_array_ref!(seed, SEED_LEN));
let pair = Ed25519KeyPair::from_seed(seed);
if public_key != &pair.public_key[..] {
return Err(error::Unspecified);
}
Ok(pair)
}
fn from_seed(seed: &Seed) -> Ed25519KeyPair {
let h = digest::digest(&digest::SHA512, seed);
let (scalar_encoded, prefix_encoded) = h.as_ref().split_at(SCALAR_LEN);
let mut scalar = [0u8; SCALAR_LEN];
scalar.copy_from_slice(&scalar_encoded);
unsafe { GFp_ed25519_scalar_mask(&mut scalar) };
let mut prefix = [0u8; PREFIX_LEN];
prefix.copy_from_slice(prefix_encoded);
let mut a = ExtPoint::new_at_infinity();
unsafe {
GFp_x25519_ge_scalarmult_base(&mut a, &scalar);
}
Ed25519KeyPair {
private_scalar: scalar,
private_prefix: prefix,
public_key: a.into_encoded_point(),
}
}
pub fn public_key_bytes(&'a self) -> &'a [u8] {
&self.public_key
}
pub fn sign(&self, msg: &[u8]) -> signature::Signature {
let mut signature_bytes = [0u8; SIGNATURE_LEN];
{ let (signature_r, signature_s) =
signature_bytes.split_at_mut(ELEM_LEN);
let signature_r =
slice_as_array_ref_mut!(signature_r, ELEM_LEN).unwrap();
let signature_s =
slice_as_array_ref_mut!(signature_s, SCALAR_LEN).unwrap();
let nonce = {
let mut ctx = digest::Context::new(&digest::SHA512);
ctx.update(&self.private_prefix);
ctx.update(msg);
ctx.finish()
};
let nonce = digest_scalar(nonce);
let mut r = ExtPoint::new_at_infinity();
unsafe {
GFp_x25519_ge_scalarmult_base(&mut r, &nonce);
}
*signature_r = r.into_encoded_point();
let hram_digest = eddsa_digest(signature_r, &self.public_key, msg);
let hram = digest_scalar(hram_digest);
unsafe {
GFp_x25519_sc_muladd(signature_s, &hram, &self.private_scalar,
&nonce);
}
}
signature::Signature::new(signature_bytes)
}
}
pub static ED25519: EdDSAParameters = EdDSAParameters {};
impl signature::VerificationAlgorithm for EdDSAParameters {
fn verify(&self, public_key: untrusted::Input, msg: untrusted::Input,
signature: untrusted::Input) -> Result<(), error::Unspecified> {
let public_key = public_key.as_slice_less_safe();
let public_key = try!(slice_as_array_ref!(public_key, ELEM_LEN));
let (signature_r, signature_s) =
try!(signature.read_all(error::Unspecified, |input| {
let r = try!(input.skip_and_get_input(ELEM_LEN));
let r = r.as_slice_less_safe();
let s = try!(input.skip_and_get_input(SCALAR_LEN));
let s = s.as_slice_less_safe();
let s = slice_as_array_ref!(s, SCALAR_LEN).unwrap();
Ok((r, s))
}));
if (signature_s[SCALAR_LEN - 1] & 0b11100000) != 0 {
return Err(error::Unspecified);
}
let mut a = try!(ExtPoint::from_encoded_point_vartime(public_key));
a.invert_vartime();
let h_digest =
eddsa_digest(signature_r, public_key, msg.as_slice_less_safe());
let h = digest_scalar(h_digest);
let mut r = Point::new_at_infinity();
unsafe {
GFp_ge_double_scalarmult_vartime(&mut r, &h, &a, &signature_s)
};
let r_check = r.into_encoded_point();
if signature_r != r_check {
return Err(error::Unspecified);
}
Ok(())
}
}
impl private::Private for EdDSAParameters {}
fn eddsa_digest(signature_r: &[u8], public_key: &[u8], msg: &[u8])
-> digest::Digest {
let mut ctx = digest::Context::new(&digest::SHA512);
ctx.update(signature_r);
ctx.update(public_key);
ctx.update(msg);
ctx.finish()
}
fn digest_scalar(digest: digest::Digest) -> Scalar {
let mut unreduced = [0u8; digest::SHA512_OUTPUT_LEN];
unreduced.copy_from_slice(digest.as_ref());
unsafe { GFp_x25519_sc_reduce(&mut unreduced) };
let mut scalar = [0u8; SCALAR_LEN];
scalar.copy_from_slice(&unreduced[..SCALAR_LEN]);
scalar
}
extern {
fn GFp_ed25519_scalar_mask(a: &mut Scalar);
fn GFp_ge_double_scalarmult_vartime(r: &mut Point, a_coeff: &Scalar,
a: &ExtPoint, b_coeff: &Scalar);
fn GFp_x25519_ge_scalarmult_base(h: &mut ExtPoint, a: &Seed);
fn GFp_x25519_sc_muladd(s: &mut Scalar, a: &Scalar, b: &Scalar, c: &Scalar);
fn GFp_x25519_sc_reduce(s: &mut UnreducedScalar);
}
type PublicKey = [u8; PUBLIC_KEY_LEN];
const PUBLIC_KEY_LEN: usize = ELEM_LEN;
type Prefix = [u8; PREFIX_LEN];
const PREFIX_LEN: usize = digest::SHA512_OUTPUT_LEN - SCALAR_LEN;
const SIGNATURE_LEN: usize = ELEM_LEN + SCALAR_LEN;
type Seed = [u8; SEED_LEN];
const SEED_LEN: usize = 32;
#[cfg(test)]
mod tests {
use {test, rand, signature};
use super::Ed25519KeyPair;
use untrusted;
#[test]
fn test_signature_ed25519() {
test::from_file("src/ec/curve25519/ed25519_tests.txt",
|section, test_case| {
assert_eq!(section, "");
let private_key = test_case.consume_bytes("PRIV");
assert_eq!(64, private_key.len());
let public_key = test_case.consume_bytes("PUB");
assert_eq!(32, public_key.len());
let msg = test_case.consume_bytes("MESSAGE");
let expected_sig = test_case.consume_bytes("SIG");
let key_pair = Ed25519KeyPair::from_bytes(&private_key[..32],
&public_key).unwrap();
let actual_sig = key_pair.sign(&msg);
assert_eq!(&expected_sig[..], actual_sig.as_ref());
let public_key = untrusted::Input::from(&public_key);
let msg = untrusted::Input::from(&msg);
let expected_sig = untrusted::Input::from(&expected_sig);
assert!(signature::verify(&signature::ED25519, public_key,
msg, expected_sig).is_ok());
Ok(())
});
}
#[test]
fn test_ed25519_from_bytes_misuse() {
let rng = rand::SystemRandom::new();
let (_, bytes) = Ed25519KeyPair::generate_serializable(&rng).unwrap();
assert!(Ed25519KeyPair::from_bytes(&bytes.private_key,
&bytes.public_key).is_ok());
assert!(Ed25519KeyPair::from_bytes(&bytes.private_key[..31],
&bytes.public_key).is_err());
assert!(Ed25519KeyPair::from_bytes(&bytes.private_key,
&bytes.public_key[..31]).is_err());
assert!(Ed25519KeyPair::from_bytes(&bytes.public_key,
&bytes.private_key).is_err());
}
}