use crate::account_address::AccountAddress;
use anyhow::{ensure, Error, Result};
use diem_crypto::{
ed25519::{Ed25519PublicKey, Ed25519Signature},
hash::CryptoHash,
multi_ed25519::{MultiEd25519PublicKey, MultiEd25519Signature},
traits::Signature,
CryptoMaterialError, HashValue, ValidCryptoMaterial, ValidCryptoMaterialStringExt,
};
use diem_crypto_derive::{CryptoHasher, DeserializeKey, SerializeKey};
#[cfg(any(test, feature = "fuzzing"))]
use proptest_derive::Arbitrary;
use rand::{rngs::OsRng, Rng};
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt, str::FromStr};
#[derive(Debug)]
#[repr(u8)]
pub enum Scheme {
Ed25519 = 0,
MultiEd25519 = 1,
}
impl fmt::Display for Scheme {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let display = match self {
Scheme::Ed25519 => "Ed25519",
Scheme::MultiEd25519 => "MultiEd25519",
};
write!(f, "Scheme::{}", display)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum TransactionAuthenticator {
Ed25519 {
public_key: Ed25519PublicKey,
signature: Ed25519Signature,
},
MultiEd25519 {
public_key: MultiEd25519PublicKey,
signature: MultiEd25519Signature,
},
}
impl TransactionAuthenticator {
pub fn scheme(&self) -> Scheme {
match self {
Self::Ed25519 { .. } => Scheme::Ed25519,
Self::MultiEd25519 { .. } => Scheme::MultiEd25519,
}
}
pub fn ed25519(public_key: Ed25519PublicKey, signature: Ed25519Signature) -> Self {
Self::Ed25519 {
public_key,
signature,
}
}
pub fn multi_ed25519(
public_key: MultiEd25519PublicKey,
signature: MultiEd25519Signature,
) -> Self {
Self::MultiEd25519 {
public_key,
signature,
}
}
pub fn verify<T: Serialize + CryptoHash>(&self, message: &T) -> Result<()> {
match self {
Self::Ed25519 {
public_key,
signature,
} => signature.verify(message, public_key),
Self::MultiEd25519 {
public_key,
signature,
} => signature.verify(message, public_key),
}
}
pub fn public_key_bytes(&self) -> Vec<u8> {
match self {
Self::Ed25519 { public_key, .. } => public_key.to_bytes().to_vec(),
Self::MultiEd25519 { public_key, .. } => public_key.to_bytes().to_vec(),
}
}
pub fn signature_bytes(&self) -> Vec<u8> {
match self {
Self::Ed25519 { signature, .. } => signature.to_bytes().to_vec(),
Self::MultiEd25519 { signature, .. } => signature.to_bytes().to_vec(),
}
}
pub fn authentication_key_preimage(&self) -> AuthenticationKeyPreimage {
AuthenticationKeyPreimage::new(self.public_key_bytes(), self.scheme())
}
pub fn authentication_key(&self) -> AuthenticationKey {
AuthenticationKey::from_preimage(&self.authentication_key_preimage())
}
}
#[derive(
Clone,
Copy,
CryptoHasher,
Debug,
DeserializeKey,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
SerializeKey,
)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub struct AuthenticationKey([u8; AuthenticationKey::LENGTH]);
impl AuthenticationKey {
pub const fn new(bytes: [u8; Self::LENGTH]) -> Self {
Self(bytes)
}
pub const LENGTH: usize = 32;
pub fn from_preimage(preimage: &AuthenticationKeyPreimage) -> AuthenticationKey {
AuthenticationKey::new(*HashValue::sha3_256_of(&preimage.0).as_ref())
}
pub fn ed25519(public_key: &Ed25519PublicKey) -> AuthenticationKey {
Self::from_preimage(&AuthenticationKeyPreimage::ed25519(public_key))
}
pub fn multi_ed25519(public_key: &MultiEd25519PublicKey) -> Self {
Self::from_preimage(&AuthenticationKeyPreimage::multi_ed25519(public_key))
}
pub fn derived_address(&self) -> AccountAddress {
let mut array = [0u8; AccountAddress::LENGTH];
array.copy_from_slice(&self.0[Self::LENGTH - AccountAddress::LENGTH..]);
AccountAddress::new(array)
}
pub fn prefix(&self) -> [u8; AccountAddress::LENGTH] {
let mut array = [0u8; AccountAddress::LENGTH];
array.copy_from_slice(&self.0[..AccountAddress::LENGTH]);
array
}
pub fn to_vec(&self) -> Vec<u8> {
self.0.to_vec()
}
pub fn random() -> Self {
let mut rng = OsRng;
let buf: [u8; Self::LENGTH] = rng.gen();
AuthenticationKey::new(buf)
}
}
impl ValidCryptoMaterial for AuthenticationKey {
fn to_bytes(&self) -> Vec<u8> {
self.to_vec()
}
}
pub struct AuthenticationKeyPreimage(Vec<u8>);
impl AuthenticationKeyPreimage {
fn new(mut public_key_bytes: Vec<u8>, scheme: Scheme) -> Self {
public_key_bytes.push(scheme as u8);
Self(public_key_bytes)
}
pub fn ed25519(public_key: &Ed25519PublicKey) -> AuthenticationKeyPreimage {
Self::new(public_key.to_bytes().to_vec(), Scheme::Ed25519)
}
pub fn multi_ed25519(public_key: &MultiEd25519PublicKey) -> AuthenticationKeyPreimage {
Self::new(public_key.to_bytes(), Scheme::MultiEd25519)
}
pub fn into_vec(self) -> Vec<u8> {
self.0
}
}
impl fmt::Display for TransactionAuthenticator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"TransactionAuthenticator[scheme id: {:?}, public key: {}, signature: {}]",
self.scheme(),
hex::encode(&self.public_key_bytes()),
hex::encode(&self.signature_bytes())
)
}
}
impl TryFrom<&[u8]> for AuthenticationKey {
type Error = CryptoMaterialError;
fn try_from(bytes: &[u8]) -> std::result::Result<AuthenticationKey, CryptoMaterialError> {
if bytes.len() != Self::LENGTH {
return Err(CryptoMaterialError::WrongLengthError);
}
let mut addr = [0u8; Self::LENGTH];
addr.copy_from_slice(bytes);
Ok(AuthenticationKey(addr))
}
}
impl TryFrom<Vec<u8>> for AuthenticationKey {
type Error = CryptoMaterialError;
fn try_from(bytes: Vec<u8>) -> std::result::Result<AuthenticationKey, CryptoMaterialError> {
AuthenticationKey::try_from(&bytes[..])
}
}
impl FromStr for AuthenticationKey {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
ensure!(
!s.is_empty(),
"authentication key string should not be empty.",
);
let bytes_out = ::hex::decode(s)?;
let key = AuthenticationKey::try_from(bytes_out.as_slice())?;
Ok(key)
}
}
impl AsRef<[u8]> for AuthenticationKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl fmt::LowerHex for AuthenticationKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", hex::encode(&self.0))
}
}
impl fmt::Display for AuthenticationKey {
fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
write!(f, "{:#x}", self)
}
}
#[cfg(test)]
mod tests {
use crate::transaction::authenticator::AuthenticationKey;
use std::str::FromStr;
#[test]
fn test_from_str_should_not_panic_by_given_empty_string() {
assert!(AuthenticationKey::from_str("").is_err());
}
}