use crate::AgentId;
use super::{
custodian::{CustodialAgentKey, KeyMask, UserAuthKey},
AgentIdentity,
};
use ed25519_dalek::Keypair;
use hmac::{Hmac, Mac, NewMac};
use rand::rngs::OsRng;
use scrypt::{scrypt, ScryptParams};
use serde::{Deserialize, Serialize};
use sha2::Sha512Trunc256;
use zeroize::Zeroize;
#[derive(Debug, Serialize, Deserialize)]
pub struct AgentKey {
pub keypair: Keypair,
pub email: Option<String>,
}
impl PartialEq for AgentKey {
fn eq(&self, other: &Self) -> bool {
self.keypair.secret.as_bytes().eq(other.keypair.secret.as_bytes())
&& self.keypair.public.as_bytes().eq(other.keypair.public.as_bytes())
}
}
impl AgentKey {
pub fn create(email: Option<String>) -> AgentKey {
let mut csprng = OsRng {};
let keypair = Keypair::generate(&mut csprng);
AgentKey { keypair, email }
}
pub fn hmac(&self) -> [u8; 32] {
let mut mac = Hmac::<Sha512Trunc256>::new_varkey(b"agentkey").unwrap();
mac.update(self.keypair.secret.as_bytes());
mac.update(self.keypair.public.as_bytes());
let result = mac.finalize();
result.into_bytes().into()
}
pub fn id(&self) -> AgentId {
let pubkey = self.keypair.public.as_bytes().clone();
AgentId { pubkey }
}
pub fn identity(&self) -> AgentIdentity {
let pubkey = self.keypair.public.as_bytes().clone();
AgentIdentity {
pubkey,
email: self.email.clone(),
}
}
pub fn pubkey(&self) -> [u8; 32] {
self.keypair.public.as_bytes().clone()
}
pub fn keymask(&self, passkey: &PassKey) -> KeyMask {
let mut mask = [0u8; 32];
self.keypair
.secret
.as_bytes()
.iter()
.zip(passkey.c.iter())
.enumerate()
.for_each(|(i, (bk, bc))| mask[i] = bk ^ bc);
KeyMask { mask }
}
pub fn custodial_key(&self, passkey: PassKey) -> CustodialAgentKey {
CustodialAgentKey {
pubkey: self.keypair.public.as_bytes().clone(),
mask: self.keymask(&passkey),
check: self.hmac(),
email: self.email.clone(),
}
}
pub fn from_custodial_key(custodial_key: CustodialAgentKey, passkey: PassKey) -> Result<Self, crate::Error> {
let mut secret = [0u8; 32];
custodial_key
.mask
.as_bytes()
.iter()
.zip(passkey.c.iter())
.enumerate()
.for_each(|(i, (m, p))| secret[i] = m ^ p);
let mut mac = Hmac::<Sha512Trunc256>::new_varkey(b"agentkey").unwrap();
mac.update(&secret);
mac.update(&custodial_key.pubkey);
mac.verify(&custodial_key.check)?;
Ok(Self {
keypair: Keypair {
secret: ed25519_dalek::SecretKey::from_bytes(&secret)?,
public: ed25519_dalek::PublicKey::from_bytes(&custodial_key.pubkey)?,
},
email: custodial_key.email.clone(),
})
}
}
#[derive(Zeroize)]
#[zeroize(drop)]
pub struct PassKey {
c: [u8; 32],
}
impl PassKey {
pub fn new(passphrase: &str) -> PassKey {
let salt = b"mindbase passkey";
let params = ScryptParams::recommended();
let mut dk = [0u8; 32];
scrypt(passphrase.as_bytes(), salt, ¶ms, &mut dk).expect("32 bytes always satisfy output length requirements");
PassKey { c: dk }
}
pub fn auth(&self) -> UserAuthKey {
let salt = b"mindbase authkey";
let params = ScryptParams::recommended();
let mut auth = [0u8; 32];
scrypt(&self.c, salt, ¶ms, &mut auth).expect("32 bytes always satisfy output length requirements");
UserAuthKey { auth }
}
}