#![allow(clippy::integer_arithmetic)]
use crate::{
hash::{CryptoHash, CryptoHasher},
traits::*,
};
use anyhow::{anyhow, Result};
use core::convert::TryFrom;
use diem_crypto_derive::{DeserializeKey, SerializeKey, SilentDebug, SilentDisplay};
use mirai_annotations::*;
use serde::Serialize;
use std::{cmp::Ordering, fmt};
pub use ed25519_dalek;
pub const ED25519_PRIVATE_KEY_LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
pub const ED25519_PUBLIC_KEY_LENGTH: usize = ed25519_dalek::PUBLIC_KEY_LENGTH;
pub const ED25519_SIGNATURE_LENGTH: usize = ed25519_dalek::SIGNATURE_LENGTH;
const L: [u8; 32] = [
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
];
#[derive(DeserializeKey, SerializeKey, SilentDebug, SilentDisplay)]
pub struct Ed25519PrivateKey(ed25519_dalek::SecretKey);
#[cfg(feature = "assert-private-keys-not-cloneable")]
static_assertions::assert_not_impl_any!(Ed25519PrivateKey: Clone);
#[cfg(any(test, feature = "cloneable-private-keys"))]
impl Clone for Ed25519PrivateKey {
fn clone(&self) -> Self {
let serialized: &[u8] = &(self.to_bytes());
Ed25519PrivateKey::try_from(serialized).unwrap()
}
}
#[derive(DeserializeKey, Clone, SerializeKey)]
pub struct Ed25519PublicKey(ed25519_dalek::PublicKey);
#[cfg(mirai)]
use crate::tags::ValidatedPublicKeyTag;
#[cfg(not(mirai))]
struct ValidatedPublicKeyTag {}
#[derive(DeserializeKey, Clone, SerializeKey)]
pub struct Ed25519Signature(ed25519_dalek::Signature);
impl Ed25519PrivateKey {
pub const LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
pub fn to_bytes(&self) -> [u8; ED25519_PRIVATE_KEY_LENGTH] {
self.0.to_bytes()
}
fn from_bytes_unchecked(
bytes: &[u8],
) -> std::result::Result<Ed25519PrivateKey, CryptoMaterialError> {
match ed25519_dalek::SecretKey::from_bytes(bytes) {
Ok(dalek_secret_key) => Ok(Ed25519PrivateKey(dalek_secret_key)),
Err(_) => Err(CryptoMaterialError::DeserializationError),
}
}
fn sign_arbitrary_message(&self, message: &[u8]) -> Ed25519Signature {
let secret_key: &ed25519_dalek::SecretKey = &self.0;
let public_key: Ed25519PublicKey = self.into();
let expanded_secret_key: ed25519_dalek::ExpandedSecretKey =
ed25519_dalek::ExpandedSecretKey::from(secret_key);
let sig = expanded_secret_key.sign(message.as_ref(), &public_key.0);
Ed25519Signature(sig)
}
}
impl Ed25519PublicKey {
pub fn to_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_LENGTH] {
self.0.to_bytes()
}
pub(crate) fn from_bytes_unchecked(
bytes: &[u8],
) -> std::result::Result<Ed25519PublicKey, CryptoMaterialError> {
match ed25519_dalek::PublicKey::from_bytes(bytes) {
Ok(dalek_public_key) => Ok(Ed25519PublicKey(dalek_public_key)),
Err(_) => Err(CryptoMaterialError::DeserializationError),
}
}
#[cfg(test)]
pub(crate) fn from_x25519_public_bytes(
x25519_bytes: &[u8],
negative: bool,
) -> Result<Self, CryptoMaterialError> {
if x25519_bytes.len() != 32 {
return Err(CryptoMaterialError::DeserializationError);
}
let key_bits = {
let mut bits = [0u8; 32];
bits.copy_from_slice(x25519_bytes);
bits
};
let mtg_point = curve25519_dalek::montgomery::MontgomeryPoint(key_bits);
let sign = if negative { 1u8 } else { 0u8 };
let ed_point = mtg_point
.to_edwards(sign)
.ok_or(CryptoMaterialError::DeserializationError)?;
Ed25519PublicKey::try_from(&ed_point.compress().as_bytes()[..])
}
}
impl Ed25519Signature {
pub const LENGTH: usize = ed25519_dalek::SIGNATURE_LENGTH;
pub fn to_bytes(&self) -> [u8; ED25519_SIGNATURE_LENGTH] {
self.0.to_bytes()
}
pub(crate) fn from_bytes_unchecked(
bytes: &[u8],
) -> std::result::Result<Ed25519Signature, CryptoMaterialError> {
match ed25519_dalek::Signature::try_from(bytes) {
Ok(dalek_signature) => Ok(Ed25519Signature(dalek_signature)),
Err(_) => Err(CryptoMaterialError::DeserializationError),
}
}
#[cfg(any(test, feature = "fuzzing"))]
pub fn dummy_signature() -> Self {
Self::from_bytes_unchecked(&[0u8; Self::LENGTH]).unwrap()
}
pub fn check_malleability(bytes: &[u8]) -> std::result::Result<(), CryptoMaterialError> {
if bytes.len() != ED25519_SIGNATURE_LENGTH {
return Err(CryptoMaterialError::WrongLengthError);
}
if !check_s_lt_l(&bytes[32..]) {
return Err(CryptoMaterialError::CanonicalRepresentationError);
}
Ok(())
}
}
impl PrivateKey for Ed25519PrivateKey {
type PublicKeyMaterial = Ed25519PublicKey;
}
impl SigningKey for Ed25519PrivateKey {
type VerifyingKeyMaterial = Ed25519PublicKey;
type SignatureMaterial = Ed25519Signature;
fn sign<T: CryptoHash + Serialize>(&self, message: &T) -> Ed25519Signature {
Ed25519PrivateKey::sign_arbitrary_message(self, signing_message(message).as_ref())
}
#[cfg(any(test, feature = "fuzzing"))]
fn sign_arbitrary_message(&self, message: &[u8]) -> Ed25519Signature {
Ed25519PrivateKey::sign_arbitrary_message(self, message)
}
}
impl Uniform for Ed25519PrivateKey {
fn generate<R>(rng: &mut R) -> Self
where
R: ::rand::RngCore + ::rand::CryptoRng,
{
Ed25519PrivateKey(ed25519_dalek::SecretKey::generate(rng))
}
}
impl PartialEq<Self> for Ed25519PrivateKey {
fn eq(&self, other: &Self) -> bool {
self.to_bytes() == other.to_bytes()
}
}
impl Eq for Ed25519PrivateKey {}
impl TryFrom<&[u8]> for Ed25519PrivateKey {
type Error = CryptoMaterialError;
fn try_from(bytes: &[u8]) -> std::result::Result<Ed25519PrivateKey, CryptoMaterialError> {
Ed25519PrivateKey::from_bytes_unchecked(bytes)
}
}
impl Length for Ed25519PrivateKey {
fn length(&self) -> usize {
Self::LENGTH
}
}
impl ValidCryptoMaterial for Ed25519PrivateKey {
fn to_bytes(&self) -> Vec<u8> {
self.to_bytes().to_vec()
}
}
impl Genesis for Ed25519PrivateKey {
fn genesis() -> Self {
let mut buf = [0u8; ED25519_PRIVATE_KEY_LENGTH];
buf[ED25519_PRIVATE_KEY_LENGTH - 1] = 1;
Self::try_from(buf.as_ref()).unwrap()
}
}
impl From<&Ed25519PrivateKey> for Ed25519PublicKey {
fn from(private_key: &Ed25519PrivateKey) -> Self {
let secret: &ed25519_dalek::SecretKey = &private_key.0;
let public: ed25519_dalek::PublicKey = secret.into();
Ed25519PublicKey(public)
}
}
impl PublicKey for Ed25519PublicKey {
type PrivateKeyMaterial = Ed25519PrivateKey;
}
impl std::hash::Hash for Ed25519PublicKey {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let encoded_pubkey = self.to_bytes();
state.write(&encoded_pubkey);
}
}
impl PartialEq for Ed25519PublicKey {
fn eq(&self, other: &Ed25519PublicKey) -> bool {
self.to_bytes() == other.to_bytes()
}
}
impl Eq for Ed25519PublicKey {}
impl VerifyingKey for Ed25519PublicKey {
type SigningKeyMaterial = Ed25519PrivateKey;
type SignatureMaterial = Ed25519Signature;
}
impl fmt::Display for Ed25519PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", hex::encode(&self.0.as_bytes()))
}
}
impl fmt::Debug for Ed25519PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Ed25519PublicKey({})", self)
}
}
impl TryFrom<&[u8]> for Ed25519PublicKey {
type Error = CryptoMaterialError;
fn try_from(bytes: &[u8]) -> std::result::Result<Ed25519PublicKey, CryptoMaterialError> {
if bytes.len() != ED25519_PUBLIC_KEY_LENGTH {
return Err(CryptoMaterialError::WrongLengthError);
}
let mut bits = [0u8; ED25519_PUBLIC_KEY_LENGTH];
bits.copy_from_slice(&bytes[..ED25519_PUBLIC_KEY_LENGTH]);
let compressed = curve25519_dalek::edwards::CompressedEdwardsY(bits);
let point = compressed
.decompress()
.ok_or(CryptoMaterialError::DeserializationError)?;
if point.is_small_order() {
return Err(CryptoMaterialError::SmallSubgroupError);
}
let public_key = Ed25519PublicKey::from_bytes_unchecked(bytes)?;
add_tag!(&public_key, ValidatedPublicKeyTag); Ok(public_key)
}
}
impl Length for Ed25519PublicKey {
fn length(&self) -> usize {
ED25519_PUBLIC_KEY_LENGTH
}
}
impl ValidCryptoMaterial for Ed25519PublicKey {
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}
}
impl Signature for Ed25519Signature {
type VerifyingKeyMaterial = Ed25519PublicKey;
type SigningKeyMaterial = Ed25519PrivateKey;
fn verify<T: CryptoHash + Serialize>(
&self,
message: &T,
public_key: &Ed25519PublicKey,
) -> Result<()> {
precondition!(has_tag!(public_key, ValidatedPublicKeyTag));
let mut bytes = <T::Hasher as CryptoHasher>::seed().to_vec();
bcs::serialize_into(&mut bytes, &message)
.map_err(|_| CryptoMaterialError::SerializationError)?;
Self::verify_arbitrary_msg(self, &bytes, public_key)
}
fn verify_arbitrary_msg(&self, message: &[u8], public_key: &Ed25519PublicKey) -> Result<()> {
precondition!(has_tag!(public_key, ValidatedPublicKeyTag));
Ed25519Signature::check_malleability(&self.to_bytes())?;
public_key
.0
.verify_strict(message, &self.0)
.map_err(|e| anyhow!("{}", e))
.and(Ok(()))
}
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}
#[cfg(feature = "batch")]
fn batch_verify<T: CryptoHash + Serialize>(
message: &T,
keys_and_signatures: Vec<(Self::VerifyingKeyMaterial, Self)>,
) -> Result<()> {
for (_, sig) in keys_and_signatures.iter() {
Ed25519Signature::check_malleability(&sig.to_bytes())?
}
let mut message_bytes = <T::Hasher as CryptoHasher>::seed().to_vec();
bcs::serialize_into(&mut message_bytes, &message)
.map_err(|_| CryptoMaterialError::SerializationError)?;
let batch_argument = keys_and_signatures
.iter()
.map(|(key, signature)| (key.0, signature.0));
let (dalek_public_keys, dalek_signatures): (Vec<_>, Vec<_>) = batch_argument.unzip();
let message_ref = &(&message_bytes)[..];
let messages = vec![message_ref; dalek_signatures.len()];
ed25519_dalek::verify_batch(&messages[..], &dalek_signatures[..], &dalek_public_keys[..])
.map_err(|e| anyhow!("{}", e))?;
Ok(())
}
}
impl Length for Ed25519Signature {
fn length(&self) -> usize {
ED25519_SIGNATURE_LENGTH
}
}
impl ValidCryptoMaterial for Ed25519Signature {
fn to_bytes(&self) -> Vec<u8> {
self.to_bytes().to_vec()
}
}
impl std::hash::Hash for Ed25519Signature {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let encoded_signature = self.to_bytes();
state.write(&encoded_signature);
}
}
impl TryFrom<&[u8]> for Ed25519Signature {
type Error = CryptoMaterialError;
fn try_from(bytes: &[u8]) -> std::result::Result<Ed25519Signature, CryptoMaterialError> {
Ed25519Signature::check_malleability(bytes)?;
Ed25519Signature::from_bytes_unchecked(bytes)
}
}
impl PartialEq for Ed25519Signature {
fn eq(&self, other: &Ed25519Signature) -> bool {
self.to_bytes()[..] == other.to_bytes()[..]
}
}
impl Eq for Ed25519Signature {}
impl fmt::Display for Ed25519Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", hex::encode(&self.0.to_bytes()[..]))
}
}
impl fmt::Debug for Ed25519Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Ed25519Signature({})", self)
}
}
fn check_s_lt_l(s: &[u8]) -> bool {
for i in (0..32).rev() {
match s[i].cmp(&L[i]) {
Ordering::Less => return true,
Ordering::Greater => return false,
_ => {}
}
}
false
}
#[cfg(any(test, feature = "fuzzing"))]
use crate::test_utils::{self, KeyPair};
#[cfg(any(test, feature = "fuzzing"))]
pub fn keypair_strategy() -> impl Strategy<Value = KeyPair<Ed25519PrivateKey, Ed25519PublicKey>> {
test_utils::uniform_keypair_strategy::<Ed25519PrivateKey, Ed25519PublicKey>()
}
#[cfg(any(test, feature = "fuzzing"))]
use proptest::prelude::*;
#[cfg(any(test, feature = "fuzzing"))]
impl proptest::arbitrary::Arbitrary for Ed25519PublicKey {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
crate::test_utils::uniform_keypair_strategy::<Ed25519PrivateKey, Ed25519PublicKey>()
.prop_map(|v| v.public_key)
.boxed()
}
}