use ed25519_zebra::{batch, Signature, VerificationKey};
use rand_core::OsRng;
use std::convert::TryFrom;
use std::convert::TryInto;
use crate::errors::{CryptoError, CryptoResult};
pub const MESSAGE_MAX_LEN: usize = 128 * 1024;
pub const BATCH_MAX_LEN: usize = 256;
pub const EDDSA_PUBKEY_LEN: usize = 32;
pub fn ed25519_verify(message: &[u8], signature: &[u8], public_key: &[u8]) -> CryptoResult<bool> {
check_message_length(message)?;
let signature = read_signature(signature)?;
let pubkey = read_pubkey(public_key)?;
match VerificationKey::try_from(pubkey)
.and_then(|vk| vk.verify(&Signature::from(signature), &message))
{
Ok(()) => Ok(true),
Err(_) => Ok(false),
}
}
pub fn ed25519_batch_verify(
messages: &[&[u8]],
signatures: &[&[u8]],
public_keys: &[&[u8]],
) -> CryptoResult<bool> {
let messages_len = messages.len();
let signatures_len = signatures.len();
let public_keys_len = public_keys.len();
let mut messages = messages.to_vec();
let mut public_keys = public_keys.to_vec();
if messages_len == signatures_len && messages_len == public_keys_len { } else if messages_len == 1 && signatures_len == public_keys_len {
messages = messages.repeat(signatures_len);
} else if public_keys_len == 1 && messages_len == signatures_len {
public_keys = public_keys.repeat(messages_len);
} else {
return Err(CryptoError::batch_err(
"Mismatched / erroneous number of messages / signatures / public keys",
));
}
debug_assert_eq!(messages.len(), signatures_len);
debug_assert_eq!(messages.len(), public_keys.len());
let mut batch = batch::Verifier::new();
for ((&message, &signature), &public_key) in messages
.iter()
.zip(signatures.iter())
.zip(public_keys.iter())
{
check_message_length(message)?;
let signature = read_signature(signature)?;
let pubkey = read_pubkey(public_key)?;
batch.queue((pubkey.into(), signature.into(), message));
}
match batch.verify(&mut OsRng) {
Ok(()) => Ok(true),
Err(_) => Ok(false),
}
}
struct InvalidEd25519SignatureFormat;
impl From<InvalidEd25519SignatureFormat> for CryptoError {
fn from(_original: InvalidEd25519SignatureFormat) -> Self {
CryptoError::invalid_signature_format()
}
}
fn read_signature(data: &[u8]) -> Result<[u8; 64], InvalidEd25519SignatureFormat> {
data.try_into().map_err(|_| InvalidEd25519SignatureFormat)
}
struct InvalidEd25519PubkeyFormat;
impl From<InvalidEd25519PubkeyFormat> for CryptoError {
fn from(_original: InvalidEd25519PubkeyFormat) -> Self {
CryptoError::invalid_pubkey_format()
}
}
fn read_pubkey(data: &[u8]) -> Result<[u8; 32], InvalidEd25519PubkeyFormat> {
data.try_into().map_err(|_| InvalidEd25519PubkeyFormat)
}
struct MessageTooLong {
limit: usize,
actual: usize,
}
impl From<MessageTooLong> for CryptoError {
fn from(original: MessageTooLong) -> Self {
CryptoError::message_too_long(original.limit, original.actual)
}
}
fn check_message_length(message: &[u8]) -> Result<(), MessageTooLong> {
if message.len() > MESSAGE_MAX_LEN {
Err(MessageTooLong {
limit: MESSAGE_MAX_LEN,
actual: message.len(),
})
} else {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use ed25519_zebra::SigningKey;
use serde::Deserialize;
const MSG: &str = "Hello World!";
const COSMOS_ED25519_MSG: &str = "";
const COSMOS_ED25519_PRIVATE_KEY_HEX: &str =
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60";
const COSMOS_ED25519_PUBLIC_KEY_HEX: &str =
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a";
const COSMOS_ED25519_SIGNATURE_HEX: &str = "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b";
const COSMOS_ED25519_TESTS_JSON: &str = "./testdata/ed25519_tests.json";
#[derive(Deserialize, Debug)]
struct Encoded {
#[serde(rename = "privkey")]
private_key: String,
#[serde(rename = "pubkey")]
public_key: String,
message: String,
signature: String,
}
fn read_cosmos_sigs() -> Vec<Encoded> {
use std::fs::File;
use std::io::BufReader;
let file = File::open(COSMOS_ED25519_TESTS_JSON).unwrap();
let reader = BufReader::new(file);
serde_json::from_reader(reader).unwrap()
}
#[test]
fn test_ed25519_verify() {
let message = MSG.as_bytes();
let secret_key = SigningKey::new(&mut OsRng);
let signature = secret_key.sign(&message);
let public_key = VerificationKey::from(&secret_key);
let signature_bytes: [u8; 64] = signature.into();
let public_key_bytes: [u8; 32] = public_key.into();
assert!(ed25519_verify(&message, &signature_bytes, &public_key_bytes).unwrap());
let bad_message = [message, b"\0"].concat();
assert!(!ed25519_verify(&bad_message, &signature_bytes, &public_key_bytes).unwrap());
let other_secret_key = SigningKey::new(&mut OsRng);
let other_public_key = VerificationKey::from(&other_secret_key);
let other_public_key_bytes: [u8; 32] = other_public_key.into();
assert!(!ed25519_verify(&message, &signature_bytes, &other_public_key_bytes).unwrap());
}
#[test]
fn test_cosmos_ed25519_verify() {
let secret_key = SigningKey::try_from(
hex::decode(COSMOS_ED25519_PRIVATE_KEY_HEX)
.unwrap()
.as_slice(),
)
.unwrap();
let public_key = VerificationKey::try_from(
hex::decode(COSMOS_ED25519_PUBLIC_KEY_HEX)
.unwrap()
.as_slice(),
)
.unwrap();
let signature = secret_key.sign(&COSMOS_ED25519_MSG.as_bytes());
let signature_bytes: [u8; 64] = signature.into();
let public_key_bytes: [u8; 32] = public_key.into();
assert_eq!(
signature_bytes,
hex::decode(&COSMOS_ED25519_SIGNATURE_HEX)
.unwrap()
.as_slice()
);
assert!(ed25519_verify(
&COSMOS_ED25519_MSG.as_bytes(),
&signature_bytes,
&public_key_bytes
)
.unwrap());
}
#[test]
fn test_cosmos_extra_ed25519_verify() {
let codes = read_cosmos_sigs();
for (i, encoded) in (1..).zip(codes) {
let message = hex::decode(&encoded.message).unwrap();
let signature = hex::decode(&encoded.signature).unwrap();
let public_key = hex::decode(&encoded.public_key).unwrap();
assert!(
ed25519_verify(&message, &signature, &public_key).unwrap(),
"verify() failed (test case {})",
i
);
}
}
#[test]
fn test_cosmos_ed25519_batch_verify() {
let codes = read_cosmos_sigs();
let mut messages: Vec<Vec<u8>> = vec![];
let mut signatures: Vec<Vec<u8>> = vec![];
let mut public_keys: Vec<Vec<u8>> = vec![];
for encoded in codes {
let message = hex::decode(&encoded.message).unwrap();
messages.push(message);
let signature = hex::decode(&encoded.signature).unwrap();
signatures.push(signature);
let public_key = hex::decode(&encoded.public_key).unwrap();
public_keys.push(public_key);
}
let messages: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
let signatures: Vec<&[u8]> = signatures.iter().map(|m| m.as_slice()).collect();
let public_keys: Vec<&[u8]> = public_keys.iter().map(|m| m.as_slice()).collect();
assert!(ed25519_batch_verify(&messages, &signatures, &public_keys).unwrap());
}
#[test]
fn test_cosmos_ed25519_batch_verify_empty_works() {
let messages: Vec<&[u8]> = vec![];
let signatures: Vec<&[u8]> = vec![];
let public_keys: Vec<&[u8]> = vec![];
assert!(ed25519_batch_verify(&messages, &signatures, &public_keys).unwrap());
}
#[test]
fn test_cosmos_ed25519_batch_verify_wrong_number_of_items_errors() {
let codes = read_cosmos_sigs();
let mut messages: Vec<Vec<u8>> = vec![];
let mut signatures: Vec<Vec<u8>> = vec![];
let mut public_keys: Vec<Vec<u8>> = vec![];
for encoded in codes {
let message = hex::decode(&encoded.message).unwrap();
messages.push(message);
let signature = hex::decode(&encoded.signature).unwrap();
signatures.push(signature);
let public_key = hex::decode(&encoded.public_key).unwrap();
public_keys.push(public_key);
}
let mut messages: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
let mut signatures: Vec<&[u8]> = signatures.iter().map(|m| m.as_slice()).collect();
let mut public_keys: Vec<&[u8]> = public_keys.iter().map(|m| m.as_slice()).collect();
assert!(ed25519_batch_verify(&messages, &signatures, &public_keys).unwrap());
let msg = messages.pop().unwrap();
let res = ed25519_batch_verify(&messages, &signatures, &public_keys);
match res.unwrap_err() {
CryptoError::BatchErr { msg, .. } => assert_eq!(
msg,
"Mismatched / erroneous number of messages / signatures / public keys"
),
_ => panic!("Wrong error message"),
}
messages.push(msg);
let sig = signatures.pop().unwrap();
let res = ed25519_batch_verify(&messages, &signatures, &public_keys);
match res.unwrap_err() {
CryptoError::BatchErr { msg, .. } => assert_eq!(
msg,
"Mismatched / erroneous number of messages / signatures / public keys"
),
_ => panic!("Wrong error message"),
}
signatures.push(sig);
let pubkey = public_keys.pop().unwrap();
let res = ed25519_batch_verify(&messages, &signatures, &public_keys);
match res.unwrap_err() {
CryptoError::BatchErr { msg, .. } => assert_eq!(
msg,
"Mismatched / erroneous number of messages / signatures / public keys"
),
_ => panic!("Wrong error message"),
}
public_keys.push(pubkey);
messages.push(messages[0]);
let res = ed25519_batch_verify(&messages, &signatures, &public_keys);
match res.unwrap_err() {
CryptoError::BatchErr { msg, .. } => assert_eq!(
msg,
"Mismatched / erroneous number of messages / signatures / public keys"
),
_ => panic!("Wrong error message"),
}
messages.pop();
signatures.push(signatures[0]);
let res = ed25519_batch_verify(&messages, &signatures, &public_keys);
match res.unwrap_err() {
CryptoError::BatchErr { msg, .. } => assert_eq!(
msg,
"Mismatched / erroneous number of messages / signatures / public keys"
),
_ => panic!("Wrong error message"),
}
signatures.pop();
public_keys.push(public_keys[0]);
let res = ed25519_batch_verify(&messages, &signatures, &public_keys);
match res.unwrap_err() {
CryptoError::BatchErr { msg, .. } => assert_eq!(
msg,
"Mismatched / erroneous number of messages / signatures / public keys"
),
_ => panic!("Wrong error message"),
}
}
#[test]
fn test_cosmos_ed25519_batch_verify_one_msg_different_number_of_sigs_pubkeys_errors() {
let codes = read_cosmos_sigs();
let mut messages: Vec<Vec<u8>> = vec![];
let mut signatures: Vec<Vec<u8>> = vec![];
let mut public_keys: Vec<Vec<u8>> = vec![];
for encoded in codes {
let message = hex::decode(&encoded.message).unwrap();
messages.push(message);
let signature = hex::decode(&encoded.signature).unwrap();
signatures.push(signature);
let public_key = hex::decode(&encoded.public_key).unwrap();
public_keys.push(public_key);
}
let mut messages: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
let mut signatures: Vec<&[u8]> = signatures.iter().map(|m| m.as_slice()).collect();
let mut public_keys: Vec<&[u8]> = public_keys.iter().map(|m| m.as_slice()).collect();
assert!(ed25519_batch_verify(&messages, &signatures, &public_keys).unwrap());
messages.truncate(1);
assert!(!ed25519_batch_verify(&messages, &signatures, &public_keys).unwrap());
let sig = signatures.pop().unwrap();
let res = ed25519_batch_verify(&messages, &signatures, &public_keys);
match res.unwrap_err() {
CryptoError::BatchErr { msg, .. } => assert_eq!(
msg,
"Mismatched / erroneous number of messages / signatures / public keys"
),
_ => panic!("Wrong error message"),
}
signatures.push(sig);
let pubkey = public_keys.pop().unwrap();
let res = ed25519_batch_verify(&messages, &signatures, &public_keys);
match res.unwrap_err() {
CryptoError::BatchErr { msg, .. } => assert_eq!(
msg,
"Mismatched / erroneous number of messages / signatures / public keys"
),
_ => panic!("Wrong error message"),
}
public_keys.push(pubkey);
}
#[test]
fn test_cosmos_ed25519_batch_verify_one_pubkey_different_number_of_msgs_sigs_errors() {
let codes = read_cosmos_sigs();
let mut messages: Vec<Vec<u8>> = vec![];
let mut signatures: Vec<Vec<u8>> = vec![];
let mut public_keys: Vec<Vec<u8>> = vec![];
for encoded in codes {
let message = hex::decode(&encoded.message).unwrap();
messages.push(message);
let signature = hex::decode(&encoded.signature).unwrap();
signatures.push(signature);
let public_key = hex::decode(&encoded.public_key).unwrap();
public_keys.push(public_key);
}
let mut messages: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
let mut signatures: Vec<&[u8]> = signatures.iter().map(|m| m.as_slice()).collect();
let mut public_keys: Vec<&[u8]> = public_keys.iter().map(|m| m.as_slice()).collect();
assert!(ed25519_batch_verify(&messages, &signatures, &public_keys).unwrap());
public_keys.truncate(1);
assert!(!ed25519_batch_verify(&messages, &signatures, &public_keys).unwrap());
let sig = signatures.pop().unwrap();
let res = ed25519_batch_verify(&messages, &signatures, &public_keys);
match res.unwrap_err() {
CryptoError::BatchErr { msg, .. } => assert_eq!(
msg,
"Mismatched / erroneous number of messages / signatures / public keys"
),
_ => panic!("Wrong error message"),
}
signatures.push(sig);
let msg = messages.pop().unwrap();
let res = ed25519_batch_verify(&messages, &signatures, &public_keys);
match res.unwrap_err() {
CryptoError::BatchErr { msg, .. } => assert_eq!(
msg,
"Mismatched / erroneous number of messages / signatures / public keys"
),
_ => panic!("Wrong error message"),
}
messages.push(msg);
}
#[test]
fn test_cosmos_ed25519_batch_verify_one_msg_zero_sigs_pubkeys_works() {
let codes = read_cosmos_sigs();
let mut messages: Vec<Vec<u8>> = vec![];
let signatures: Vec<&[u8]> = vec![];
let public_keys: Vec<&[u8]> = vec![];
for encoded in codes[..1].iter() {
let message = hex::decode(&encoded.message).unwrap();
messages.push(message);
}
let messages: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
assert!(ed25519_batch_verify(&messages, &signatures, &public_keys).unwrap());
}
#[test]
fn test_cosmos_ed25519_batch_verify_one_pubkey_zero_msgs_sigs_works() {
let codes = read_cosmos_sigs();
let messages: Vec<&[u8]> = vec![];
let signatures: Vec<&[u8]> = vec![];
let mut public_keys: Vec<Vec<u8>> = vec![];
for encoded in codes[..1].iter() {
let public_key = hex::decode(&encoded.public_key).unwrap();
public_keys.push(public_key);
}
let public_keys: Vec<&[u8]> = public_keys.iter().map(|m| m.as_slice()).collect();
assert!(ed25519_batch_verify(&messages, &signatures, &public_keys).unwrap());
}
}