use ed25519_dalek::{self as ed25519, Signer as _, Verifier as _};
use rand::RngCore;
use zeroize::Zeroize;
use std::{convert::TryFrom, fmt};
use crate::{Error, Result};
pub struct Keypair {
key_pair: ed25519::Keypair,
}
impl fmt::Debug for Keypair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Keypair")
.field("public", &self.key_pair.public)
.finish()
}
}
impl From<Keypair> for SecretKey {
fn from(val: Keypair) -> SecretKey {
SecretKey {
secret_key: val.key_pair.secret,
}
}
}
impl From<SecretKey> for Keypair {
fn from(val: SecretKey) -> Keypair {
let secret: ed25519::ExpandedSecretKey = (&val.secret_key).into();
let public = ed25519::PublicKey::from(&secret);
let key_pair = ed25519::Keypair {
secret: val.secret_key,
public,
};
Keypair { key_pair }
}
}
impl Keypair {
pub fn generate() -> Result<Keypair> {
Ok(Keypair::from(SecretKey::generate()?))
}
pub fn to_public_key(&self) -> PublicKey {
PublicKey {
public_key: self.key_pair.public,
}
}
pub fn to_secret_key(&self) -> Result<SecretKey> {
match SecretKey::from_bytes(&mut self.key_pair.secret.to_bytes()) {
Ok(secret_key) => Ok(secret_key),
Err(err) => {
let msg = format!("to secret key");
err_at!(DecodeError, Err(err), msg)
}
}
}
pub fn encode(&self) -> [u8; 64] {
self.key_pair.to_bytes()
}
pub fn decode(kp: &mut [u8]) -> Result<Keypair> {
match ed25519::Keypair::from_bytes(kp) {
Ok(key_pair) => {
kp.zeroize();
Ok(Keypair { key_pair })
}
Err(err) => {
let msg = format!("Ed25519 keypair");
err_at!(DecodeError, Err(err), msg)
}
}
}
pub fn sign(&self, msg: &[u8]) -> Result<Vec<u8>> {
Ok(self.key_pair.sign(msg).to_bytes().to_vec())
}
pub fn try_clone(&self) -> Result<Self> {
let secret = {
let mut sk_bytes = self.key_pair.secret.to_bytes();
match SecretKey::from_bytes(&mut sk_bytes) {
Ok(val) => Ok(val.secret_key),
Err(err) => {
let msg = format!("try_clone ed25519::SecretKey");
err_at!(DecodeError, Err(err), msg)
}
}?
};
let public = {
let pk_bytes = self.key_pair.public.to_bytes();
match ed25519::PublicKey::from_bytes(&pk_bytes) {
Ok(public_key) => Ok(public_key),
Err(err) => {
let msg = format!("try_clone ed25519::PublicKey");
err_at!(DecodeError, Err(err), msg)
}
}?
};
let key_pair = ed25519::Keypair { secret, public };
Ok(Keypair { key_pair })
}
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct PublicKey {
public_key: ed25519::PublicKey,
}
impl PublicKey {
pub fn verify(&self, msg: &[u8], signature: &[u8]) -> bool {
ed25519::Signature::try_from(signature)
.and_then(|s| self.public_key.verify(msg, &s))
.is_ok()
}
pub fn encode(&self) -> [u8; 32] {
self.public_key.to_bytes()
}
pub fn decode(k: &[u8]) -> Result<PublicKey> {
match ed25519::PublicKey::from_bytes(k) {
Ok(public_key) => Ok(PublicKey { public_key }),
Err(err) => {
let msg = format!("Ed25519 public key");
err_at!(DecodeError, Err(err), msg)
}
}
}
}
pub struct SecretKey {
secret_key: ed25519::SecretKey,
}
impl AsRef<[u8]> for SecretKey {
fn as_ref(&self) -> &[u8] {
self.secret_key.as_bytes()
}
}
impl fmt::Debug for SecretKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SecretKey")
}
}
impl SecretKey {
pub fn generate() -> Result<SecretKey> {
let secret_key = {
let mut bytes = [0u8; 32];
rand::thread_rng().fill_bytes(&mut bytes);
match ed25519::SecretKey::from_bytes(&bytes) {
Ok(secret_key) => Ok(secret_key),
Err(err) => {
let msg = format!("Ed25519 generate bad length");
err_at!(BadInput, Err(err), msg)
}
}?
};
Ok(SecretKey { secret_key })
}
pub fn from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result<SecretKey> {
let sk_bytes = sk_bytes.as_mut();
let secret_key = match ed25519::SecretKey::from_bytes(&*sk_bytes) {
Ok(secret_key) => Ok(secret_key),
Err(err) => {
let msg = format!("Ed25519 secret key");
err_at!(DecodeError, Err(err), msg)
}
}?;
sk_bytes.zeroize();
Ok(SecretKey { secret_key })
}
pub fn try_clone(&self) -> Result<Self> {
let mut sk_bytes = self.secret_key.to_bytes();
match Self::from_bytes(&mut sk_bytes) {
Ok(val) => Ok(val),
Err(err) => {
let msg = format!("try_clone ed25519::SecretKey");
err_at!(DecodeError, Err(err), msg)
}
}
}
}
#[cfg(test)]
#[path = "ed25519_test.rs"]
mod ed25519_test;