// -*- mode: rust; -*- // // This file is part of ed25519-dalek. // Copyright (c) 2017-2019 isis lovecruft // See LICENSE for licensing information. // // Authors: // - isis agora lovecruft //! Integration tests for ed25519-dalek. #![allow(clippy::items_after_test_module)] use ed25519_dalek::*; use hex::FromHex; #[cfg(feature = "digest")] use hex_literal::hex; #[cfg(test)] mod vectors { use super::*; use curve25519_dalek::{ constants::ED25519_BASEPOINT_POINT, edwards::{CompressedEdwardsY, EdwardsPoint}, scalar::Scalar, traits::IsIdentity, }; use sha2::{digest::Digest, Sha512}; use std::{ convert::TryFrom, fs::File, io::{BufRead, BufReader}, ops::Neg, }; // TESTVECTORS is taken from sign.input.gz in agl's ed25519 Golang // package. It is a selection of test cases from // http://ed25519.cr.yp.to/python/sign.input #[test] fn against_reference_implementation() { // TestGolden let mut line: String; let mut lineno: usize = 0; let f = File::open("TESTVECTORS"); if f.is_err() { println!( "This test is only available when the code has been cloned \ from the git repository, since the TESTVECTORS file is large \ and is therefore not included within the distributed crate." ); panic!(); } let file = BufReader::new(f.unwrap()); for l in file.lines() { lineno += 1; line = l.unwrap(); let parts: Vec<&str> = line.split(':').collect(); assert_eq!(parts.len(), 5, "wrong number of fields in line {}", lineno); let sec_bytes: Vec = FromHex::from_hex(parts[0]).unwrap(); let pub_bytes: Vec = FromHex::from_hex(parts[1]).unwrap(); let msg_bytes: Vec = FromHex::from_hex(parts[2]).unwrap(); let sig_bytes: Vec = FromHex::from_hex(parts[3]).unwrap(); let sec_bytes = &sec_bytes[..SECRET_KEY_LENGTH].try_into().unwrap(); let pub_bytes = &pub_bytes[..PUBLIC_KEY_LENGTH].try_into().unwrap(); let signing_key = SigningKey::from_bytes(sec_bytes); let expected_verifying_key = VerifyingKey::from_bytes(pub_bytes).unwrap(); assert_eq!(expected_verifying_key, signing_key.verifying_key()); // The signatures in the test vectors also include the message // at the end, but we just want R and S. let sig1: Signature = Signature::try_from(&sig_bytes[..64]).unwrap(); let sig2: Signature = signing_key.sign(&msg_bytes); assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); assert!( signing_key.verify(&msg_bytes, &sig2).is_ok(), "Signature verification failed on line {}", lineno ); assert!( expected_verifying_key .verify_strict(&msg_bytes, &sig2) .is_ok(), "Signature strict verification failed on line {}", lineno ); } } // From https://tools.ietf.org/html/rfc8032#section-7.3 #[cfg(feature = "digest")] #[test] fn ed25519ph_rf8032_test_vector_prehash() { let sec_bytes = hex!("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42"); let pub_bytes = hex!("ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf"); let msg_bytes = hex!("616263"); let sig_bytes = hex!("98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406"); let signing_key = SigningKey::from_bytes(&sec_bytes); let expected_verifying_key = VerifyingKey::from_bytes(&pub_bytes).unwrap(); assert_eq!(expected_verifying_key, signing_key.verifying_key()); let sig1 = Signature::try_from(&sig_bytes[..]).unwrap(); let mut prehash_for_signing = Sha512::default(); let mut prehash_for_verifying = Sha512::default(); prehash_for_signing.update(&msg_bytes[..]); prehash_for_verifying.update(&msg_bytes[..]); let sig2: Signature = signing_key .sign_prehashed(prehash_for_signing, None) .unwrap(); assert!( sig1 == sig2, "Original signature from test vectors doesn't equal signature produced:\ \noriginal:\n{:?}\nproduced:\n{:?}", sig1, sig2 ); assert!( signing_key .verify_prehashed(prehash_for_verifying.clone(), None, &sig2) .is_ok(), "Could not verify ed25519ph signature!" ); assert!( expected_verifying_key .verify_prehashed_strict(prehash_for_verifying, None, &sig2) .is_ok(), "Could not strict-verify ed25519ph signature!" ); } // // The remaining items in this mod are for the repudiation tests // // Taken from curve25519_dalek::constants::EIGHT_TORSION[4] const EIGHT_TORSION_4: [u8; 32] = [ 236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, ]; // Computes the prehashed or non-prehashed challenge, depending on whether context is given fn compute_challenge( message: &[u8], pub_key: &EdwardsPoint, signature_r: &EdwardsPoint, context: Option<&[u8]>, ) -> Scalar { let mut h = Sha512::default(); if let Some(c) = context { h.update(b"SigEd25519 no Ed25519 collisions"); h.update([1]); h.update([c.len() as u8]); h.update(c); } h.update(signature_r.compress().as_bytes()); h.update(&pub_key.compress().as_bytes()[..]); h.update(message); Scalar::from_hash(h) } fn serialize_signature(r: &EdwardsPoint, s: &Scalar) -> Vec { [&r.compress().as_bytes()[..], &s.as_bytes()[..]].concat() } const WEAK_PUBKEY: CompressedEdwardsY = CompressedEdwardsY(EIGHT_TORSION_4); // Pick a random Scalar fn non_null_scalar() -> Scalar { let mut rng = rand::rngs::OsRng; let mut s_candidate = Scalar::random(&mut rng); while s_candidate == Scalar::ZERO { s_candidate = Scalar::random(&mut rng); } s_candidate } fn pick_r(s: Scalar) -> EdwardsPoint { let r0 = s * ED25519_BASEPOINT_POINT; // Pick a torsion point of order 2 r0 + WEAK_PUBKEY.decompress().unwrap().neg() } // Tests that verify_strict() rejects small-order pubkeys. We test this by explicitly // constructing a pubkey-signature pair that verifies with respect to two distinct messages. // This should be accepted by verify(), but rejected by verify_strict(). #[test] fn repudiation() { let message1 = b"Send 100 USD to Alice"; let message2 = b"Send 100000 USD to Alice"; let mut s: Scalar = non_null_scalar(); let pubkey = WEAK_PUBKEY.decompress().unwrap(); let mut r = pick_r(s); // Find an R such that // H(R || A || M₁) · A == A == H(R || A || M₂) · A // This happens with high probability when A is low order. while !(pubkey.neg() + compute_challenge(message1, &pubkey, &r, None) * pubkey) .is_identity() || !(pubkey.neg() + compute_challenge(message2, &pubkey, &r, None) * pubkey) .is_identity() { // We pick an s and let R = sB - A where B is the basepoint s = non_null_scalar(); r = pick_r(s); } // At this point, both verification equations hold: // sB = R + H(R || A || M₁) · A // = R + H(R || A || M₂) · A // Check that this is true let signature = serialize_signature(&r, &s); let vk = VerifyingKey::from_bytes(pubkey.compress().as_bytes()).unwrap(); let sig = Signature::try_from(&signature[..]).unwrap(); assert!(vk.verify(message1, &sig).is_ok()); assert!(vk.verify(message2, &sig).is_ok()); // Check that this public key appears as weak assert!(vk.is_weak()); // Now check that the sigs fail under verify_strict. This is because verify_strict rejects // small order pubkeys. assert!(vk.verify_strict(message1, &sig).is_err()); assert!(vk.verify_strict(message2, &sig).is_err()); } // Identical to repudiation() above, but testing verify_prehashed against // verify_prehashed_strict. See comments above for a description of what's happening. #[cfg(feature = "digest")] #[test] fn repudiation_prehash() { let message1 = Sha512::new().chain_update(b"Send 100 USD to Alice"); let message2 = Sha512::new().chain_update(b"Send 100000 USD to Alice"); let message1_bytes = message1.clone().finalize(); let message2_bytes = message2.clone().finalize(); let mut s: Scalar = non_null_scalar(); let pubkey = WEAK_PUBKEY.decompress().unwrap(); let mut r = pick_r(s); let context_str = Some(&b"edtest"[..]); while !(pubkey.neg() + compute_challenge(&message1_bytes, &pubkey, &r, context_str) * pubkey) .is_identity() || !(pubkey.neg() + compute_challenge(&message2_bytes, &pubkey, &r, context_str) * pubkey) .is_identity() { s = non_null_scalar(); r = pick_r(s); } // Check that verify_prehashed succeeds on both sigs let signature = serialize_signature(&r, &s); let vk = VerifyingKey::from_bytes(pubkey.compress().as_bytes()).unwrap(); let sig = Signature::try_from(&signature[..]).unwrap(); assert!(vk .verify_prehashed(message1.clone(), context_str, &sig) .is_ok()); assert!(vk .verify_prehashed(message2.clone(), context_str, &sig) .is_ok()); // Check that verify_prehashed_strict fails on both sigs assert!(vk .verify_prehashed_strict(message1.clone(), context_str, &sig) .is_err()); assert!(vk .verify_prehashed_strict(message2.clone(), context_str, &sig) .is_err()); } } #[cfg(feature = "rand_core")] mod integrations { use super::*; use rand::rngs::OsRng; #[cfg(feature = "digest")] use sha2::Sha512; use std::collections::HashMap; #[test] fn sign_verify() { // TestSignVerify let good: &[u8] = "test message".as_bytes(); let bad: &[u8] = "wrong message".as_bytes(); let mut csprng = OsRng; let signing_key: SigningKey = SigningKey::generate(&mut csprng); let verifying_key = signing_key.verifying_key(); let good_sig: Signature = signing_key.sign(good); let bad_sig: Signature = signing_key.sign(bad); // Check that an honestly generated public key is not weak assert!(!verifying_key.is_weak()); assert!( signing_key.verify(good, &good_sig).is_ok(), "Verification of a valid signature failed!" ); assert!( verifying_key.verify_strict(good, &good_sig).is_ok(), "Strict verification of a valid signature failed!" ); assert!( signing_key.verify(good, &bad_sig).is_err(), "Verification of a signature on a different message passed!" ); assert!( verifying_key.verify_strict(good, &bad_sig).is_err(), "Strict verification of a signature on a different message passed!" ); assert!( signing_key.verify(bad, &good_sig).is_err(), "Verification of a signature on a different message passed!" ); assert!( verifying_key.verify_strict(bad, &good_sig).is_err(), "Strict verification of a signature on a different message passed!" ); } #[cfg(feature = "digest")] #[test] fn ed25519ph_sign_verify() { let good: &[u8] = b"test message"; let bad: &[u8] = b"wrong message"; let mut csprng = OsRng; // ugh… there's no `impl Copy for Sha512`… i hope we can all agree these are the same hashes let mut prehashed_good1: Sha512 = Sha512::default(); prehashed_good1.update(good); let mut prehashed_good2: Sha512 = Sha512::default(); prehashed_good2.update(good); let mut prehashed_good3: Sha512 = Sha512::default(); prehashed_good3.update(good); let mut prehashed_bad1: Sha512 = Sha512::default(); prehashed_bad1.update(bad); let mut prehashed_bad2: Sha512 = Sha512::default(); prehashed_bad2.update(bad); let context: &[u8] = b"testing testing 1 2 3"; let signing_key: SigningKey = SigningKey::generate(&mut csprng); let verifying_key = signing_key.verifying_key(); let good_sig: Signature = signing_key .sign_prehashed(prehashed_good1, Some(context)) .unwrap(); let bad_sig: Signature = signing_key .sign_prehashed(prehashed_bad1, Some(context)) .unwrap(); assert!( signing_key .verify_prehashed(prehashed_good2.clone(), Some(context), &good_sig) .is_ok(), "Verification of a valid signature failed!" ); assert!( verifying_key .verify_prehashed_strict(prehashed_good2, Some(context), &good_sig) .is_ok(), "Strict verification of a valid signature failed!" ); assert!( signing_key .verify_prehashed(prehashed_good3.clone(), Some(context), &bad_sig) .is_err(), "Verification of a signature on a different message passed!" ); assert!( verifying_key .verify_prehashed_strict(prehashed_good3, Some(context), &bad_sig) .is_err(), "Strict verification of a signature on a different message passed!" ); assert!( signing_key .verify_prehashed(prehashed_bad2.clone(), Some(context), &good_sig) .is_err(), "Verification of a signature on a different message passed!" ); assert!( verifying_key .verify_prehashed_strict(prehashed_bad2, Some(context), &good_sig) .is_err(), "Strict verification of a signature on a different message passed!" ); } #[cfg(feature = "batch")] #[test] fn verify_batch_seven_signatures() { let messages: [&[u8]; 7] = [ b"Watch closely everyone, I'm going to show you how to kill a god.", b"I'm not a cryptographer I just encrypt a lot.", b"Still not a cryptographer.", b"This is a test of the tsunami alert system. This is only a test.", b"Fuck dumbin' it down, spit ice, skip jewellery: Molotov cocktails on me like accessories.", b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.", b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ]; let mut csprng = OsRng; let mut signing_keys: Vec = Vec::new(); let mut signatures: Vec = Vec::new(); for msg in messages { let signing_key: SigningKey = SigningKey::generate(&mut csprng); signatures.push(signing_key.sign(msg)); signing_keys.push(signing_key); } let verifying_keys: Vec = signing_keys.iter().map(|key| key.verifying_key()).collect(); let result = verify_batch(&messages, &signatures, &verifying_keys); assert!(result.is_ok()); } #[test] fn public_key_hash_trait_check() { let mut csprng = OsRng {}; let secret: SigningKey = SigningKey::generate(&mut csprng); let public_from_secret: VerifyingKey = (&secret).into(); let mut m = HashMap::new(); m.insert(public_from_secret, "Example_Public_Key"); m.insert(public_from_secret, "Updated Value"); let (k, &v) = m.get_key_value(&public_from_secret).unwrap(); assert_eq!(k, &public_from_secret); assert_eq!(v, "Updated Value"); assert_eq!(m.len(), 1usize); let second_secret: SigningKey = SigningKey::generate(&mut csprng); let public_from_second_secret: VerifyingKey = (&second_secret).into(); assert_ne!(public_from_secret, public_from_second_secret); m.insert(public_from_second_secret, "Second public key"); let (k, &v) = m.get_key_value(&public_from_second_secret).unwrap(); assert_eq!(k, &public_from_second_secret); assert_eq!(v, "Second public key"); assert_eq!(m.len(), 2usize); } } #[cfg(all(test, feature = "serde"))] #[derive(Debug, serde::Serialize, serde::Deserialize)] #[serde(crate = "serde")] struct Demo { signing_key: SigningKey, } #[cfg(all(test, feature = "serde"))] mod serialisation { #![allow(clippy::zero_prefixed_literal)] use super::*; // The size for bincode to serialize the length of a byte array. static BINCODE_INT_LENGTH: usize = 8; static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [ 130, 039, 155, 015, 062, 076, 188, 063, 124, 122, 026, 251, 233, 253, 225, 220, 014, 041, 166, 120, 108, 035, 254, 077, 160, 083, 172, 058, 219, 042, 086, 120, ]; static SECRET_KEY_BYTES: [u8; SECRET_KEY_LENGTH] = [ 062, 070, 027, 163, 092, 182, 011, 003, 077, 234, 098, 004, 011, 127, 079, 228, 243, 187, 150, 073, 201, 137, 076, 022, 085, 251, 152, 002, 241, 042, 072, 054, ]; /// Signature with the above signing_key of a blank message. static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [ 010, 126, 151, 143, 157, 064, 047, 001, 196, 140, 179, 058, 226, 152, 018, 102, 160, 123, 080, 016, 210, 086, 196, 028, 053, 231, 012, 157, 169, 019, 158, 063, 045, 154, 238, 007, 053, 185, 227, 229, 079, 108, 213, 080, 124, 252, 084, 167, 216, 085, 134, 144, 129, 149, 041, 081, 063, 120, 126, 100, 092, 059, 050, 011, ]; #[test] fn serialize_deserialize_signature_bincode() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES); let encoded_signature: Vec = bincode::serialize(&signature).unwrap(); let decoded_signature: Signature = bincode::deserialize(&encoded_signature).unwrap(); assert_eq!(signature, decoded_signature); } #[test] fn serialize_deserialize_signature_json() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES); let encoded_signature = serde_json::to_string(&signature).unwrap(); let decoded_signature: Signature = serde_json::from_str(&encoded_signature).unwrap(); assert_eq!(signature, decoded_signature); } #[test] fn serialize_deserialize_verifying_key_bincode() { let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); let encoded_verifying_key: Vec = bincode::serialize(&verifying_key).unwrap(); let decoded_verifying_key: VerifyingKey = bincode::deserialize(&encoded_verifying_key).unwrap(); assert_eq!( &PUBLIC_KEY_BYTES[..], &encoded_verifying_key[encoded_verifying_key.len() - PUBLIC_KEY_LENGTH..] ); assert_eq!(verifying_key, decoded_verifying_key); } #[test] fn serialize_deserialize_verifying_key_json() { let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); let encoded_verifying_key = serde_json::to_string(&verifying_key).unwrap(); let decoded_verifying_key: VerifyingKey = serde_json::from_str(&encoded_verifying_key).unwrap(); assert_eq!(verifying_key, decoded_verifying_key); } #[test] fn serialize_deserialize_verifying_key_json_too_long() { // derived from `serialize_deserialize_verifying_key_json` test // trailing zero elements makes key too long (34 bytes) let encoded_verifying_key_too_long = "[130,39,155,15,62,76,188,63,124,122,26,251,233,253,225,220,14,41,166,120,108,35,254,77,160,83,172,58,219,42,86,120,0,0]"; let de_err = serde_json::from_str::(encoded_verifying_key_too_long) .unwrap_err() .to_string(); assert!( de_err.contains("invalid length 34"), "expected invalid length error, got: {de_err}", ); } #[test] fn serialize_deserialize_verifying_key_json_too_short() { // derived from `serialize_deserialize_verifying_key_json` test let encoded_verifying_key_too_long = "[130,39,155,15]"; let de_err = serde_json::from_str::(encoded_verifying_key_too_long) .unwrap_err() .to_string(); assert!( de_err.contains("invalid length 4"), "expected invalid length error, got: {de_err}" ); } #[test] fn serialize_deserialize_signing_key_bincode() { let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES); let encoded_signing_key: Vec = bincode::serialize(&signing_key).unwrap(); let decoded_signing_key: SigningKey = bincode::deserialize(&encoded_signing_key).unwrap(); #[allow(clippy::needless_range_loop)] for i in 0..SECRET_KEY_LENGTH { assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]); } } #[test] fn serialize_deserialize_signing_key_json() { let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES); let encoded_signing_key = serde_json::to_string(&signing_key).unwrap(); let decoded_signing_key: SigningKey = serde_json::from_str(&encoded_signing_key).unwrap(); #[allow(clippy::needless_range_loop)] for i in 0..SECRET_KEY_LENGTH { assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]); } } #[test] fn serialize_deserialize_signing_key_json_too_long() { // derived from `serialize_deserialize_signing_key_json` test // trailing zero elements makes key too long (34 bytes) let encoded_signing_key_too_long = "[62,70,27,163,92,182,11,3,77,234,98,4,11,127,79,228,243,187,150,73,201,137,76,22,85,251,152,2,241,42,72,54,0,0]"; let de_err = serde_json::from_str::(encoded_signing_key_too_long) .unwrap_err() .to_string(); assert!( de_err.contains("invalid length 34"), "expected invalid length error, got: {de_err}", ); } #[test] fn serialize_deserialize_signing_key_json_too_short() { // derived from `serialize_deserialize_signing_key_json` test let encoded_signing_key_too_long = "[62,70,27,163]"; let de_err = serde_json::from_str::(encoded_signing_key_too_long) .unwrap_err() .to_string(); assert!( de_err.contains("invalid length 4"), "expected invalid length error, got: {de_err}" ); } #[test] fn serialize_deserialize_signing_key_toml() { let demo = Demo { signing_key: SigningKey::from_bytes(&SECRET_KEY_BYTES), }; println!("\n\nWrite to toml"); let demo_toml = toml::to_string(&demo).unwrap(); println!("{}", demo_toml); let demo_toml_rebuild: Result = toml::from_str(&demo_toml); println!("{:?}", demo_toml_rebuild); } #[test] fn serialize_verifying_key_size() { let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); assert_eq!( bincode::serialized_size(&verifying_key).unwrap() as usize, BINCODE_INT_LENGTH + PUBLIC_KEY_LENGTH ); } #[test] fn serialize_signature_size() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES); assert_eq!( bincode::serialized_size(&signature).unwrap() as usize, SIGNATURE_LENGTH ); } #[test] fn serialize_signing_key_size() { let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES); assert_eq!( bincode::serialized_size(&signing_key).unwrap() as usize, BINCODE_INT_LENGTH + SECRET_KEY_LENGTH ); } }