use crate::{consts::*, error::Error, metadata, yubikey::YubiKey};
use des::{
block_cipher_trait::{generic_array::GenericArray, BlockCipher},
TdesEde3,
};
use getrandom::getrandom;
use hmac::Hmac;
use log::error;
use pbkdf2::pbkdf2;
use sha1::Sha1;
use std::convert::{TryFrom, TryInto};
use zeroize::{Zeroize, Zeroizing};
const DEFAULT_MGM_KEY: [u8; DES_LEN_3DES] = [
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
];
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[allow(non_camel_case_types)]
pub enum MgmType {
Manual = 0,
Derived = 1,
Protected = 2,
}
#[derive(Clone)]
pub struct MgmKey([u8; DES_LEN_3DES]);
impl MgmKey {
pub fn generate() -> Result<Self, Error> {
let mut key_bytes = [0u8; DES_LEN_3DES];
if getrandom(&mut key_bytes).is_err() {
return Err(Error::RandomnessError);
}
MgmKey::new(key_bytes)
}
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, Error> {
bytes.as_ref().try_into()
}
pub fn new(key_bytes: [u8; DES_LEN_3DES]) -> Result<Self, Error> {
if is_weak_key(&key_bytes) {
error!(
"blacklisting key '{:?}' since it's weak (with odd parity)",
&key_bytes
);
return Err(Error::KeyError);
}
Ok(MgmKey(key_bytes))
}
pub fn get_derived(yubikey: &mut YubiKey, pin: &[u8]) -> Result<Self, Error> {
let txn = yubikey.begin_transaction()?;
let data = metadata::read(&txn, TAG_ADMIN)?;
let salt = metadata::get_item(&data, TAG_ADMIN_SALT)?;
if salt.len() != CB_ADMIN_SALT {
error!(
"derived MGM salt exists, but is incorrect size: {} (expected {})",
salt.len(),
CB_ADMIN_SALT
);
return Err(Error::GenericError);
}
let mut mgm = [0u8; DES_LEN_3DES];
pbkdf2::<Hmac<Sha1>>(pin, &salt, ITER_MGM_PBKDF2, &mut mgm);
MgmKey::from_bytes(mgm)
}
pub fn get_protected(yubikey: &mut YubiKey) -> Result<Self, Error> {
let txn = yubikey.begin_transaction()?;
let data = metadata::read(&txn, TAG_PROTECTED).map_err(|e| {
error!("could not read protected data (err: {:?})", e);
e
})?;
let item = metadata::get_item(&data, TAG_PROTECTED_MGM).map_err(|e| {
error!("could not read protected MGM from metadata (err: {:?})", e);
e
})?;
if item.len() != DES_LEN_3DES {
error!(
"protected data contains MGM, but is the wrong size: {} (expected {})",
item.len(),
DES_LEN_3DES
);
return Err(Error::AuthenticationError);
}
MgmKey::from_bytes(item)
}
pub fn set(&self, yubikey: &mut YubiKey, touch: Option<u8>) -> Result<(), Error> {
let txn = yubikey.begin_transaction()?;
txn.set_mgm_key(&self, touch)
}
pub fn set_protected(&self, yubikey: &mut YubiKey) -> Result<(), Error> {
let mut data = Zeroizing::new(vec![0u8; CB_BUF_MAX]);
let max_size = yubikey.obj_size_max();
let txn = yubikey.begin_transaction()?;
txn.set_mgm_key(self, None).map_err(|e| {
error!("could not set new derived mgm key, err = {}", e);
e
})?;
let buffer = match metadata::read(&txn, TAG_PROTECTED) {
Ok(b) => b,
Err(_) => {
Zeroizing::new(vec![])
}
};
let mut cb_data = buffer.len();
data[..cb_data].copy_from_slice(&buffer);
if let Err(e) = metadata::set_item(
data.as_mut_slice(),
&mut cb_data,
CB_OBJ_MAX,
TAG_PROTECTED_MGM,
self.as_ref(),
) {
error!("could not set protected mgm item, err = {:?}", e);
} else {
metadata::write(&txn, TAG_PROTECTED, &data, max_size).map_err(|e| {
error!("could not write protected data, err = {:?}", e);
e
})?;
}
cb_data = data.len();
let mut flags_1 = [0u8; 1];
if let Ok(buffer) = metadata::read(&txn, TAG_ADMIN) {
if let Ok(item) = metadata::get_item(&buffer, TAG_ADMIN_FLAGS_1) {
if item.len() == flags_1.len() {
flags_1.copy_from_slice(item);
} else {
error!(
"admin data flags are an incorrect size: {} (expected {})",
item.len(),
flags_1.len()
);
}
} else {
error!("admin data exists, but flags are not present");
}
if let Err(e) =
metadata::set_item(&mut data, &mut cb_data, CB_OBJ_MAX, TAG_ADMIN_SALT, &[])
{
error!("could not unset derived mgm salt (err = {})", e)
}
} else {
cb_data = 0;
}
flags_1[0] |= ADMIN_FLAGS_1_PROTECTED_MGM;
if let Err(e) = metadata::set_item(
data.as_mut_slice(),
&mut cb_data,
CB_OBJ_MAX,
TAG_ADMIN_FLAGS_1,
&flags_1,
) {
error!("could not set admin flags item, err = {}", e);
} else if let Err(e) = metadata::write(&txn, TAG_ADMIN, &data[..cb_data], max_size) {
error!("could not write admin data, err = {}", e);
}
Ok(())
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub(crate) fn encrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] {
let mut output = input.to_owned();
TdesEde3::new(GenericArray::from_slice(&self.0))
.encrypt_block(GenericArray::from_mut_slice(&mut output));
output
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub(crate) fn decrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] {
let mut output = input.to_owned();
TdesEde3::new(GenericArray::from_slice(&self.0))
.encrypt_block(GenericArray::from_mut_slice(&mut output));
output
}
}
impl AsRef<[u8; DES_LEN_3DES]> for MgmKey {
fn as_ref(&self) -> &[u8; DES_LEN_3DES] {
&self.0
}
}
impl Default for MgmKey {
fn default() -> Self {
MgmKey(DEFAULT_MGM_KEY)
}
}
impl Drop for MgmKey {
fn drop(&mut self) {
self.0.zeroize();
}
}
impl<'a> TryFrom<&'a [u8]> for MgmKey {
type Error = Error;
fn try_from(key_bytes: &'a [u8]) -> Result<Self, Error> {
Self::new(key_bytes.try_into().map_err(|_| Error::SizeError)?)
}
}
const WEAK_DES_KEYS: &[[u8; DES_LEN_DES]] = &[
[0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01],
[0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE],
[0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E],
[0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1],
[0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE],
[0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01],
[0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1],
[0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E],
[0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1],
[0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01],
[0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE],
[0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E],
[0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E],
[0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01],
[0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE],
[0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1],
];
fn is_weak_key(key: &[u8; DES_LEN_3DES]) -> bool {
let mut tmp = Zeroizing::new([0u8; DES_LEN_3DES]);
for i in 0..DES_LEN_3DES {
let mut c = key[i] & 0xFE;
c = (c & 0x55) + ((c >> 1) & 0x55);
c = (c & 0x33) + ((c >> 2) & 0x33);
c = (c & 0x0F) + ((c >> 4) & 0x0F);
tmp[i] = (key[i] & 0xFE) | (if c & 0x01 == 0x01 { 0x00 } else { 0x01 });
}
let mut is_weak = false;
for weak_key in WEAK_DES_KEYS.iter() {
if weak_key == &tmp[0..DES_LEN_DES]
|| weak_key == &tmp[DES_LEN_DES..2 * DES_LEN_DES]
|| weak_key == &tmp[2 * DES_LEN_DES..3 * DES_LEN_DES]
{
is_weak = true;
break;
}
}
is_weak
}