use std::{borrow::ToOwned, convert::Into, str};
use aes::cipher::{BlockDecryptMut, KeyIvInit, block_padding};
use pbkdf2::pbkdf2_hmac;
use snafu::ResultExt;
use crate::{
Which,
error::{self, Result, Utf8Snafu},
};
type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
#[derive(Clone)]
#[derive(Debug)]
#[derive(Default)]
#[derive(PartialEq, Eq)]
pub struct Decrypter {
pass_v10: Vec<u8>,
}
impl Decrypter {
const K_SALT: &'static [u8] = b"saltysalt";
const K_ENCRYPTION_ITERATIONS: u32 = 1003;
const K_ENCRYPTION_VERSION_PREFIX: &'static [u8] = b"v10";
}
impl Decrypter {
pub async fn build(safe_storage: &str, safe_name: &str) -> Result<Self> {
let pass_v10 = Self::get_pass(safe_storage, safe_name).await?;
Ok(Self { pass_v10 })
}
async fn get_pass(safe_storage: &str, safe_name: &str) -> Result<Vec<u8>> {
let safe_storage: &'static str =
unsafe { std::mem::transmute::<&str, &'static str>(safe_storage) };
let safe_name: &'static str =
unsafe { std::mem::transmute::<&str, &'static str>(safe_name) };
tokio::task::spawn_blocking(|| {
let entry =
keyring::Entry::new(safe_storage, safe_name).context(error::KeyringSnafu)?;
entry
.get_secret()
.context(error::KeyringSnafu)
})
.await
.context(error::TaskSnafu)?
}
pub fn decrypt(&self, ciphertext: &mut [u8], which: Which) -> Result<String> {
if !ciphertext.starts_with(Self::K_ENCRYPTION_VERSION_PREFIX) {
return Ok(String::from_utf8_lossy(ciphertext).to_string());
}
let prefix_len = Self::K_ENCRYPTION_VERSION_PREFIX.len();
let mut key = [0_u8; 16];
let iv = [b' '; 16];
pbkdf2_hmac::<sha1::Sha1>(
&self.pass_v10,
Self::K_SALT,
Self::K_ENCRYPTION_ITERATIONS,
&mut key,
);
let decrypter = Aes128CbcDec::new(&key.into(), &iv.into());
decrypter
.decrypt_padded_mut::<block_padding::Pkcs7>(&mut ciphertext[prefix_len..])
.context(error::UnpaddingSnafu)
.map(|res| {
match which {
Which::Cookie => res.get(32..).map_or_else(
|| {
std::hint::cold_path();
str::from_utf8(res)
},
|slice| {
str::from_utf8(slice).or_else(|_| {
std::hint::cold_path();
str::from_utf8(res)
})
},
),
Which::Login => str::from_utf8(res),
}
.map(ToOwned::to_owned) })?
.context(Utf8Snafu)
}
}