use anyhow::{Result, bail};
use bip39::Mnemonic;
use blake2::{Blake2s, Digest, digest, digest::typenum::ToInt};
use ed25519_dalek::{Signer, Verifier};
use rand::{CryptoRng, RngCore, SeedableRng, rngs::StdRng};
use serde::{Deserialize, Serialize};
use std::fmt;
use zeroize::Zeroizing;
const ENTROPY_LEN: usize = 16;
type Entropy = [u8; ENTROPY_LEN];
pub struct SigningKey {
key: ed25519_dalek::SigningKey,
entropy: Zeroizing<Entropy>,
}
type SigHasher = Blake2s<digest::consts::U32>;
impl Default for SigningKey {
fn default() -> Self {
let mut rng = StdRng::from_os_rng();
Self::from_crypto_rng(&mut rng)
}
}
impl SigningKey {
pub fn from_phrase<S>(phrase: S) -> Result<Self>
where
S: AsRef<str>,
{
let mnemonic = Mnemonic::from_phrase(phrase.as_ref(), Default::default())?;
if mnemonic.entropy().len() != ENTROPY_LEN {
bail!("Invalid passphrase length");
}
let mut entropy = Entropy::default();
entropy.copy_from_slice(mnemonic.entropy());
Ok(Self::from_entropy(entropy))
}
pub fn sign<T>(&self, msg: &T) -> Signature
where
T: Serialize,
{
let mut hasher = SigHasher::new();
bincode::serialize_into(&mut hasher, msg).expect("should serialize to hasher");
Signature(self.key.sign(&hasher.finalize()))
}
pub fn phrase(&self) -> String {
Mnemonic::from_entropy(self.entropy.as_ref(), Default::default())
.unwrap()
.phrase()
.to_string()
}
pub fn verifying_key(&self) -> VerifyingKey {
VerifyingKey(self.key.verifying_key())
}
fn from_crypto_rng<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let mut entropy = Entropy::default();
rng.fill_bytes(&mut entropy);
Self::from_entropy(entropy)
}
fn from_entropy(entropy: Entropy) -> Self {
let key_hash = SigHasher::digest(entropy);
let key = ed25519_dalek::SigningKey::from_bytes(&key_hash.into());
let entropy = Zeroizing::new(entropy);
Self { key, entropy }
}
}
impl fmt::Debug for SigningKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"SigningKey({})",
bs58::encode(self.key.as_bytes()).into_string()
)
}
}
#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct Signature(ed25519_dalek::Signature);
impl fmt::Debug for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Signature({})",
bs58::encode(&self.0.to_bytes()).into_string()
)
}
}
#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct VerifyingKey(ed25519_dalek::VerifyingKey);
impl VerifyingKey {
pub fn verify<T>(&self, msg: &T, signature: &Signature) -> bool
where
T: Serialize,
{
let mut hasher = SigHasher::new();
bincode::serialize_into(&mut hasher, msg).expect("should serialize to hasher");
self.0.verify(&hasher.finalize(), &signature.0).is_ok()
}
pub fn peer_id(&self) -> PeerId {
let mut hasher = Blake2s::<digest::consts::U16>::new();
hasher.update(self.0.as_bytes());
PeerId(hasher.finalize().into())
}
}
impl fmt::Debug for VerifyingKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"VerifyingKey({})",
bs58::encode(self.0.as_bytes()).into_string()
)
}
}
#[derive(Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub struct PeerId([u8; digest::consts::U16::INT]);
impl PeerId {
pub fn digits(&self) -> String {
self.0
.iter()
.fold(String::with_capacity(32), |mut output, b| {
output.push_str(&format!("{b:02X}"));
output
})
}
}
impl fmt::Debug for PeerId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PeerId({})", self.digits())
}
}
impl fmt::Display for PeerId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.digits())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keypair_phrase() {
let sk = SigningKey::default();
let from_phrase = SigningKey::from_phrase(sk.phrase()).unwrap();
assert_eq!(sk.key, from_phrase.key);
}
#[test]
fn sign() {
#[derive(Serialize)]
struct Point {
x: f32,
y: f32,
}
let msg = Point { x: 10.2, y: 4.3 };
let sk = SigningKey::default();
let sig = sk.sign(&msg);
let vk = sk.verifying_key();
assert!(vk.verify(&msg, &sig));
let msg = Point { x: 10.2001, y: 4.3 };
assert!(!vk.verify(&msg, &sig));
}
}