use crate::cesr::signing::cipher::Cipher;
use crate::cesr::signing::{
cix_var_qb2_dex, cix_var_strm_dex, ed25519_sk_to_x25519_sk, Salter, Signer,
};
use crate::cesr::{mtr_dex, BaseMatter, Parsable};
use crate::errors::MatterError;
use crate::Matter;
use sodiumoxide::crypto::box_::SecretKey;
use sodiumoxide::crypto::sealedbox;
use sodiumoxide::crypto::sign::ed25519;
use std::any::Any;
#[derive(Debug, Clone)]
pub struct Decrypter {
base: BaseMatter,
}
impl Decrypter {
pub fn new(
raw: Option<&[u8]>,
code: Option<&str>,
seed: Option<&[u8]>,
) -> Result<Self, MatterError> {
let code = code.unwrap_or(mtr_dex::X25519_PRIVATE);
let raw = match raw {
Some(r) => r,
None => {
if seed.is_some() {
let signer = Signer::from_qb64b(&mut seed.unwrap().to_vec(), None)?;
if signer.code() != mtr_dex::ED25519_SEED {
return Err(MatterError::InvalidCode(format!(
"Unsupported signing seed derivation code = {}",
signer.code()
)));
}
let mut sigkey = Vec::new();
sigkey.extend_from_slice(signer.raw());
sigkey.extend_from_slice(signer.verfer().raw());
let ed_sk = ed25519::SecretKey::from_slice(&sigkey).unwrap();
let sk = ed25519_sk_to_x25519_sk(&ed_sk)?;
&sk.as_ref().to_vec()
} else {
return Err(MatterError::ValueError(
"Either seed or raw must be provided".to_string(),
));
}
}
};
let base = BaseMatter::new(Some(&raw), Some(code), None, None)?;
if base.code() != mtr_dex::X25519_PRIVATE {
return Err(MatterError::InvalidCode(format!(
"Unsupported decrypter code = {}",
base.code()
)));
}
Ok(Decrypter { base })
}
pub fn from_qb64(qb64: &str) -> Result<Self, MatterError> {
let base = BaseMatter::from_qb64(qb64)?;
let decrypter = Decrypter { base };
if decrypter.code() != mtr_dex::X25519_PRIVATE {
return Err(MatterError::InvalidCode(format!(
"Unsupported decrypter code = {}",
decrypter.code()
)));
}
Ok(decrypter)
}
pub fn decrypt(
&self,
cipher: Option<&Cipher>,
qb64: Option<&str>,
qb2: Option<&[u8]>,
transferable: Option<bool>,
bare: Option<bool>,
) -> Result<Box<dyn Any>, MatterError> {
let transferable = transferable.unwrap_or(false);
let bare = bare.unwrap_or(false);
let cipher = if let Some(c) = cipher {
c.clone()
} else if let Some(q) = qb64 {
Cipher::from_qb64(q)?
} else if let Some(q) = qb2 {
let mut qb2_vec = q.to_vec();
Cipher::from_qb2(&mut qb2_vec, Some(true))?
} else {
return Err(MatterError::EmptyMaterialError(
"qb64, qb2, or cipher must be provided".to_string(),
));
};
self.decrypt_x25519(&cipher, transferable, bare)
}
fn decrypt_x25519(
&self,
cipher: &Cipher,
transferable: bool,
bare: bool,
) -> Result<Box<dyn Any>, MatterError> {
let private_key = SecretKey::from_slice(self.raw())
.ok_or_else(|| MatterError::InvalidKey("Invalid X25519 private key".to_string()))?;
let public_key = private_key.public_key();
let mut plain = sealedbox::open(cipher.raw(), &public_key, &private_key)
.map_err(|_| MatterError::VerificationError("Decryption failed".to_string()))?;
if bare {
return Ok(Box::new(plain));
}
match cipher.code() {
mtr_dex::X25519_CIPHER_SALT => {
if cix_var_qb2_dex::TUPLE.contains(&cipher.code()) {
let salter = Salter::from_qb2(&mut plain, None)?;
Ok(Box::new(salter))
} else {
let salter = Salter::from_qb64b(&mut plain, None)?;
Ok(Box::new(salter))
}
}
mtr_dex::X25519_CIPHER_SEED => {
if cix_var_qb2_dex::TUPLE.contains(&cipher.code()) {
let signer = Signer::from_qb2(&mut plain, None)?;
Ok(Box::new(signer))
} else {
let signer =
Signer::from_qb64b_and_transferable(&mut plain, None, transferable)?;
Ok(Box::new(signer))
}
}
code if cix_var_strm_dex::TUPLE.contains(&code) => {
Err(MatterError::InvalidCode(format!(
"Stream cipher code = {} not supported yet",
cipher.code()
)))
}
_ => Err(MatterError::InvalidCode(format!(
"Unsupported cipher code = {}",
cipher.code()
))),
}
}
}
impl Parsable for Decrypter {
fn from_qb64b(data: &mut Vec<u8>, strip: Option<bool>) -> Result<Self, MatterError> {
let base = BaseMatter::from_qb64b(data, strip)?;
if base.code() != mtr_dex::X25519_PRIVATE {
return Err(MatterError::InvalidCode(format!(
"Unsupported decrypter code = {}",
base.code()
)));
}
Ok(Decrypter { base })
}
fn from_qb2(data: &mut Vec<u8>, strip: Option<bool>) -> Result<Self, MatterError> {
let base = BaseMatter::from_qb2(data, strip)?;
if base.code() != mtr_dex::X25519_PRIVATE {
return Err(MatterError::InvalidCode(format!(
"Unsupported decrypter code = {}",
base.code()
)));
}
Ok(Decrypter { base })
}
}
impl Matter for Decrypter {
fn code(&self) -> &str {
self.base.code()
}
fn raw(&self) -> &[u8] {
self.base.raw()
}
fn qb64(&self) -> String {
self.base.qb64()
}
fn qb64b(&self) -> Vec<u8> {
self.base.qb64b()
}
fn qb2(&self) -> Vec<u8> {
self.base.qb2()
}
fn soft(&self) -> &str {
self.base.soft()
}
fn full_size(&self) -> usize {
self.base.full_size()
}
fn size(&self) -> usize {
self.base.size()
}
fn is_transferable(&self) -> bool {
self.base.is_transferable()
}
fn is_digestive(&self) -> bool {
self.base.is_digestive()
}
fn is_prefixive(&self) -> bool {
self.base.is_prefixive()
}
fn is_special(&self) -> bool {
self.base.is_special()
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cesr::signing::ed25519_pk_to_x25519_pk;
use crate::cesr::signing::encrypter::Encrypter;
use sodiumoxide::crypto::sign;
#[test]
fn test_decrypter() {
sodiumoxide::init().expect("Sodium initialization failed");
let seed = [
0x18, 0x3B, 0x30, 0xC4, 0x0F, 0x2A, 0x76, 0x46, 0xFA, 0xE3, 0xA2, 0x45, 0x65, 0x65,
0x1F, 0x96, 0x6F, 0xCE, 0x29, 0x47, 0x85, 0xE3, 0x58, 0x86, 0xDA, 0x04, 0xF0, 0xDC,
0xDE, 0x06, 0xC0, 0x2B,
];
let signer = Signer::new(Some(&seed), Some(mtr_dex::ED25519_SEED), Some(true)).unwrap();
assert_eq!(signer.verfer().code(), mtr_dex::ED25519);
assert!(signer.verfer().is_transferable()); let seedqb64b = signer.qb64b();
assert_eq!(seedqb64b, b"ABg7MMQPKnZG-uOiRWVlH5ZvzilHheNYhtoE8NzeBsAr");
let matter_seedqb64b =
BaseMatter::new(Some(&seed), Some(mtr_dex::ED25519_SEED), None, None)
.unwrap()
.qb64b();
assert_eq!(seedqb64b, matter_seedqb64b);
let salt_raw = [
0x36, 0x08, 0x64, 0x0D, 0xA1, 0xBB, 0x39, 0x8D, 0x70, 0x8D, 0xA0, 0xC0, 0x13, 0x4A,
0x87, 0x72,
];
let salter = Salter::new(Some(&salt_raw), Some(mtr_dex::SALT_128), None).unwrap();
assert_eq!(salter.code(), mtr_dex::SALT_128);
let saltqb64b = salter.qb64b();
assert_eq!(saltqb64b, b"0AA2CGQNobs5jXCNoMATSody");
let matter_saltqb64b =
BaseMatter::new(Some(&salt_raw), Some(mtr_dex::SALT_128), None, None)
.unwrap()
.qb64b();
assert_eq!(saltqb64b, matter_saltqb64b);
let cryptseed = [
0x68, 0x2C, 0x23, 0x7C, 0x8A, 0x70, 0x22, 0x12, 0xC4, 0x33, 0x74, 0x32, 0xA6, 0xE1,
0x18, 0x19, 0xF0, 0x66, 0x32, 0x2C, 0x79, 0xC4, 0xC2, 0x31, 0x40, 0xF5, 0x40, 0x15,
0x2E, 0xA2, 0x1A, 0xCF,
];
let cryptsigner =
Signer::new(Some(&cryptseed), Some(mtr_dex::ED25519_SEED), Some(true)).unwrap();
let seed_key = sign::Seed::from_slice(&cryptseed).unwrap();
let (verkey, sigkey) = sign::keypair_from_seed(&seed_key);
let ed_pk = sign::PublicKey::from_slice(verkey.as_ref()).unwrap();
let ed_sk = sign::SecretKey::from_slice(sigkey.as_ref()).unwrap();
let pubkey = ed25519_pk_to_x25519_pk(&ed_pk).unwrap();
let prikey = ed25519_sk_to_x25519_sk(&ed_sk).unwrap();
let result = Decrypter::new(None, None, None);
assert!(result.is_err());
let encrypter = Encrypter::new(Some(pubkey.as_ref()), Some(mtr_dex::X25519), None).unwrap();
assert_eq!(encrypter.code(), mtr_dex::X25519);
assert_eq!(
encrypter.qb64(),
"CAF7Wr3XNq5hArcOuBJzaY6Nd23jgtUVI6KDfb3VngkR"
);
assert_eq!(encrypter.raw(), pubkey.as_ref());
let seedcipher = encrypter
.encrypt(Some(&seedqb64b), None, Some(mtr_dex::X25519_CIPHER_SEED))
.unwrap();
assert_eq!(seedcipher.code(), mtr_dex::X25519_CIPHER_SEED);
let decrypter =
Decrypter::new(Some(prikey.as_ref()), Some(mtr_dex::X25519_PRIVATE), None).unwrap();
assert_eq!(decrypter.code(), mtr_dex::X25519_PRIVATE);
assert_eq!(
decrypter.qb64(),
"OLCFxqMz1z1UUS0TEJnvZP_zXHcuYdQsSGBWdOZeY5VQ"
);
assert_eq!(decrypter.raw(), prikey.as_ref());
let designer = decrypter
.decrypt(
None,
Some(&seedcipher.qb64()),
None,
Some(signer.verfer().is_transferable()),
Some(false),
)
.unwrap();
let designer = designer.downcast_ref::<Signer>().unwrap();
assert_eq!(designer.qb64b(), seedqb64b);
assert_eq!(designer.code(), mtr_dex::ED25519_SEED);
assert_eq!(designer.verfer().code(), mtr_dex::ED25519);
assert!(designer.verfer().is_transferable());
let plain = decrypter
.decrypt(
None,
Some(&seedcipher.qb64()),
None,
Some(signer.verfer().is_transferable()),
Some(true),
)
.unwrap();
let plain = plain.downcast_ref::<Vec<u8>>().unwrap();
assert_eq!(plain, &seedqb64b);
let designer = decrypter
.decrypt(
Some(&seedcipher),
None,
None,
Some(signer.verfer().is_transferable()),
Some(false),
)
.unwrap();
let designer = designer.downcast_ref::<Signer>().unwrap();
assert_eq!(designer.qb64b(), seedqb64b);
assert_eq!(designer.code(), mtr_dex::ED25519_SEED);
assert_eq!(designer.verfer().code(), mtr_dex::ED25519);
assert!(designer.verfer().is_transferable());
let saltcipher = encrypter
.encrypt(
Some(saltqb64b.as_ref()),
None,
Some(mtr_dex::X25519_CIPHER_SALT),
)
.unwrap();
assert_eq!(saltcipher.code(), mtr_dex::X25519_CIPHER_SALT);
let desalter = decrypter
.decrypt(None, Some(&saltcipher.qb64()), None, None, Some(false))
.unwrap();
let desalter = desalter.downcast_ref::<Salter>().unwrap();
assert_eq!(desalter.qb64b(), saltqb64b);
assert_eq!(desalter.code(), mtr_dex::SALT_128);
let plain = decrypter
.decrypt(None, Some(&saltcipher.qb64()), None, None, Some(true))
.unwrap();
let plain = plain.downcast_ref::<Vec<u8>>().unwrap();
assert_eq!(plain, &saltqb64b);
let desalter = decrypter
.decrypt(Some(&saltcipher), None, None, None, Some(false))
.unwrap();
let desalter = desalter.downcast_ref::<Salter>().unwrap();
assert_eq!(desalter.qb64b(), saltqb64b);
assert_eq!(desalter.code(), mtr_dex::SALT_128);
let cipherseed = "PM9jOGWNYfjM_oLXJNaQ8UlFSAV5ACjsUY7J16xfzrlpc9Ve3A5WYrZ4o_NHtP5lhp78Usspl9fyFdnCdItNd5JyqZ6dt8SXOt6TOqOCs-gy0obrwFkPPqBvVkEw";
let designer = decrypter
.decrypt(
None,
Some(cipherseed),
None,
Some(signer.verfer().is_transferable()),
Some(false),
)
.unwrap();
let designer = designer.downcast_ref::<Signer>().unwrap();
assert_eq!(designer.qb64b(), seedqb64b);
assert_eq!(designer.code(), mtr_dex::ED25519_SEED);
assert_eq!(designer.verfer().code(), mtr_dex::ED25519);
let ciphersalt = "1AAHjlR2QR9J5Et67Wy-ZaVdTryN6T6ohg44r73GLRPnHw-5S3ABFkhWyIwLOI6TXUB_5CT13S8JvknxLxBaF8ANPK9FSOPD8tYu";
let desalter = decrypter
.decrypt(None, Some(ciphersalt), None, None, Some(false))
.unwrap();
let desalter = desalter.downcast_ref::<Salter>().unwrap();
assert_eq!(desalter.qb64b(), saltqb64b);
assert_eq!(desalter.code(), mtr_dex::SALT_128);
let decrypter = Decrypter::new(None, None, Some(cryptsigner.qb64b().as_ref())).unwrap();
assert_eq!(decrypter.code(), mtr_dex::X25519_PRIVATE);
assert_eq!(
decrypter.qb64(),
"OLCFxqMz1z1UUS0TEJnvZP_zXHcuYdQsSGBWdOZeY5VQ"
);
assert_eq!(decrypter.raw(), prikey.as_ref());
let desalter = decrypter
.decrypt(None, Some(&saltcipher.qb64()), None, None, Some(false))
.unwrap();
let desalter = desalter.downcast_ref::<Salter>().unwrap();
assert_eq!(desalter.qb64b(), saltqb64b);
assert_eq!(desalter.code(), mtr_dex::SALT_128);
}
#[test]
fn test_decrypter_parsable() -> Result<(), MatterError> {
sodiumoxide::init().expect("Sodium initialization failed");
let seed_key = sign::gen_keypair();
let prikey = ed25519_sk_to_x25519_sk(&seed_key.1)?;
let decrypter = Decrypter::new(Some(prikey.as_ref()), Some(mtr_dex::X25519_PRIVATE), None)?;
let qb64 = decrypter.qb64();
let parsed_decrypter = Decrypter::from_qb64(&qb64)?;
assert_eq!(parsed_decrypter.raw(), decrypter.raw());
let mut qb64b = decrypter.qb64b();
let parsed_decrypter = Decrypter::from_qb64b(&mut qb64b, Some(true))?;
assert_eq!(parsed_decrypter.raw(), decrypter.raw());
Ok(())
}
#[test]
fn test_decrypter_error_cases() {
let result = Decrypter::new(None, None, None);
assert!(result.is_err());
let invalid_key = [0u8; 31]; let result = Decrypter::new(Some(&invalid_key), Some(mtr_dex::X25519_PRIVATE), None);
assert!(result.is_err());
let valid_key = [0u8; 32];
let result = Decrypter::new(Some(&valid_key), Some(mtr_dex::ED25519), None);
assert!(result.is_err());
let result = Decrypter::from_qb64("invalid-qb64-format");
assert!(result.is_err());
}
}