use exonum_sodiumoxide::crypto::sign::{
gen_keypair, keypair_from_seed, PublicKey, SecretKey, Seed, SEEDBYTES,
};
use hex_buffer_serde::Hex;
use pwbox::{sodium::Sodium, ErasedPwBox, Eraser, RestoredPwBox, Suite};
use rand::thread_rng;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
enum PublicKeyHex {}
impl Hex<PublicKey> for PublicKeyHex {
type Error = String;
fn create_bytes(value: &PublicKey) -> Cow<'_, [u8]> {
Cow::Borrowed(&value.0)
}
fn from_bytes(bytes: &[u8]) -> Result<PublicKey, String> {
PublicKey::from_slice(bytes).ok_or_else(|| "invalid public key".to_owned())
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Keypair<T> {
#[serde(with = "PublicKeyHex")]
public_key: PublicKey,
secret_key: T,
}
impl Keypair<ErasedPwBox> {
pub fn restore(&self, eraser: &Eraser) -> Keypair<RestoredPwBox> {
assert_eq!(
self.secret_key.len(),
SEEDBYTES,
"incorrect length of encrypted data"
);
Keypair {
public_key: self.public_key,
secret_key: eraser.restore(&self.secret_key).unwrap(),
}
}
}
impl Keypair<RestoredPwBox> {
pub fn decrypt(&self, password: impl AsRef<[u8]>) -> Keypair<SecretKey> {
let mut seed = Seed([0; SEEDBYTES]);
self.secret_key.open_into(&mut seed.0, password).unwrap();
let (public_key, secret_key) = keypair_from_seed(&seed);
assert_eq!(
public_key, self.public_key,
"restored secret key does not match public key"
);
Keypair {
public_key: self.public_key,
secret_key,
}
}
}
impl Keypair<SecretKey> {
#[allow(clippy::new_without_default)] pub fn new() -> Self {
let (public_key, secret_key) = gen_keypair();
Keypair {
public_key,
secret_key,
}
}
pub fn encrypt(&self, password: impl AsRef<[u8]>, eraser: &Eraser) -> Keypair<ErasedPwBox> {
let seed = &self.secret_key[..SEEDBYTES];
let pwbox = Sodium::build_box(&mut thread_rng())
.seal(password, seed)
.unwrap();
let pwbox = eraser.erase(&pwbox).unwrap();
Keypair {
public_key: self.public_key,
secret_key: pwbox,
}
}
}
fn main() {
let mut eraser = Eraser::new();
eraser.add_suite::<Sodium>();
let keypair = Keypair::new();
println!(
"Original secret key: {}",
hex::encode(&keypair.secret_key[..])
);
let password = "correct horse battery staple";
let keypair = keypair.encrypt(password, &eraser);
let toml = toml::to_string_pretty(&keypair).unwrap();
println!(
"======== Keypair ========\n{}======== End keypair ========",
toml
);
let keypair: Keypair<ErasedPwBox> = toml::from_str(&toml).unwrap();
let keypair = keypair.restore(&eraser).decrypt(password);
println!(
"Restored secret key: {}",
hex::encode(&keypair.secret_key[..])
);
}