use crate::{Keypair, KeypairInfo, Scrypt, ss58};
use anyhow::{Result, anyhow};
use base64::{Engine as _, engine::general_purpose::STANDARD};
use rand::RngCore;
use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct Keystore {
pub encoded: String,
#[serde(default)]
pub encoding: Encoding,
pub address: String,
#[serde(default)]
pub meta: Meta,
}
impl Keystore {
const NONCE_LENGTH: usize = 24;
pub fn encrypt(keypair: Keypair, passphrase: Option<&[u8]>) -> Result<Self> {
let info = KeypairInfo::from(keypair);
if let Some(passphrase) = passphrase {
Self::encrypt_scrypt(info, passphrase)
} else {
Self::encrypt_none(info)
}
}
pub fn encrypt_scrypt(info: KeypairInfo, passphrase: &[u8]) -> Result<Self> {
let mut encoded = Vec::new();
let scrypt = Scrypt::default();
let passwd = scrypt.passwd(passphrase)?;
encoded.extend_from_slice(&scrypt.encode());
let mut nonce = [0; Self::NONCE_LENGTH];
rand::thread_rng().fill_bytes(&mut nonce);
encoded.extend_from_slice(&nonce);
let encrypted = nacl::secret_box::pack(&info.encode(), &nonce, &passwd[..32])
.map_err(|e| anyhow!("{e:?}"))?;
encoded.extend_from_slice(&encrypted);
Ok(Self {
encoded: STANDARD.encode(&encoded),
address: ss58::encode(&info.public)?,
encoding: Encoding::scrypt(),
..Default::default()
})
}
pub fn encrypt_none(info: KeypairInfo) -> Result<Self> {
Ok(Self {
encoded: STANDARD.encode(info.encode()),
address: ss58::encode(&info.public)?,
..Default::default()
})
}
pub fn decrypt(&self, passphrase: Option<&[u8]>) -> Result<Keypair> {
if let Some(passphrase) = passphrase {
if !self.encoding.is_scrypt() {
return Err(anyhow!(
"unsupported key deriven function {}.",
self.encoding.ty[0]
));
}
self.decrypt_scrypt(passphrase)
} else {
if self.encoding.is_xsalsa20_poly1305() {
return Err(anyhow!("password required to decode encrypted data."));
}
self.decrypt_none()
}
}
pub fn decrypt_scrypt(&self, passphrase: &[u8]) -> Result<Keypair> {
let decoded = self.decoded()?;
let mut encoded_scrypt = [0; Scrypt::ENCODED_LENGTH];
encoded_scrypt.copy_from_slice(&decoded[..Scrypt::ENCODED_LENGTH]);
let passwd = Scrypt::decode(encoded_scrypt).passwd(passphrase)?;
let encrypted = &decoded[Scrypt::ENCODED_LENGTH..];
let secret = nacl::secret_box::open(
&encrypted[Self::NONCE_LENGTH..],
&encrypted[..Self::NONCE_LENGTH],
&passwd[..32],
)
.map_err(|e| anyhow!("{e:?}"))?;
KeypairInfo::decode(&secret[..KeypairInfo::ENCODED_LENGTH])?.into_keypair()
}
pub fn decrypt_none(&self) -> Result<Keypair> {
KeypairInfo::decode(&self.decoded()?)?.into_keypair()
}
pub fn with_name(mut self, name: &str) -> Self {
self.meta.name = name.to_owned();
self
}
fn decoded(&self) -> Result<Vec<u8>> {
STANDARD.decode(&self.encoded).map_err(Into::into)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Encoding {
pub content: (String, String),
#[serde(rename = "type")]
pub ty: Vec<String>,
pub version: String,
}
impl Encoding {
pub fn none() -> Self {
Self {
content: ("pkcs8".into(), "sr25519".into()),
ty: vec!["none".into()],
version: "3".to_string(),
}
}
pub fn scrypt() -> Self {
Self {
content: ("pkcs8".into(), "sr25519".into()),
ty: vec!["scrypt".into(), "xsalsa20-poly1305".into()],
..Default::default()
}
}
pub fn is_scrypt(&self) -> bool {
self.ty.first() == Some(&"scrypt".into())
}
pub fn is_xsalsa20_poly1305(&self) -> bool {
self.ty.get(1) == Some(&"xsalsa20-poly1305".into())
}
}
impl Default for Encoding {
fn default() -> Self {
Self::none()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Meta {
pub name: String,
#[serde(rename = "whenCreated")]
pub when_created: u128,
}
impl Default for Meta {
fn default() -> Self {
Self {
name: "".into(),
when_created: SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("time went backwards")
.as_millis(),
}
}
}