use crate::{
config::{
TokenInteractions,
secured_config::{unlock_code_decrypt, unlock_code_encrypt},
},
errors::OpenVTCError,
openpgp_card::open_card,
};
use byteorder::{BigEndian, ByteOrder};
use openpgp_card::ocard::KeyType;
use openpgp_card_rpgp::CardSlot;
use pgp::{
composed::PlainSessionKey,
crypto::public_key::PublicKeyAlgorithm,
ser::Serialize,
types::{DecryptionKey, EncryptionKey, EskType, Password, PkeskBytes},
};
use rand::{Rng, rngs::OsRng};
use secrecy::SecretString;
use std::io::BufReader;
use tracing::{info, warn};
use zeroize::Zeroize;
fn generate_checksum(bytes: &[u8]) -> [u8; 2] {
let sum = (bytes.iter().map(|v| u32::from(*v)).sum::<u32>() & 0xffff) as u16;
let mut res = [0u8; 2];
BigEndian::write_u16(&mut res[..], sum);
res
}
pub fn token_encrypt(
token_id: &str,
data: &[u8],
touch_prompt: &(dyn Fn() + Send + Sync),
) -> Result<(Vec<u8>, Vec<u8>), OpenVTCError> {
let mut card = open_card(token_id).map_err(|e| {
OpenVTCError::Token(format!("Couldn't use hardware token ({token_id}): {e}"))
})?;
let mut card = card.transaction().map_err(|e| {
OpenVTCError::Token(format!(
"Couldn't create hardware token transaction - encrypt: {e}"
))
})?;
let cs =
CardSlot::init_from_card(&mut card, KeyType::Decryption, touch_prompt).map_err(|e| {
OpenVTCError::Token(format!("Couldn't init decryption key from token: {e}"))
})?;
let mut seed: [u8; 32] = [0; 32];
OsRng.fill(&mut seed);
let mut seed_augmented: [u8; 35] = [0; 35];
let cksum = generate_checksum(&seed);
seed_augmented[1..33].copy_from_slice(&seed);
seed_augmented[33] = cksum[0];
seed_augmented[34] = cksum[1];
let pk = cs.public_key();
let esk = pk
.encrypt(OsRng, &seed_augmented, EskType::V6)
.map_err(|e| OpenVTCError::Encrypt(format!("Couldn't encrypt config data: {e}")))?;
let encrypted = unlock_code_encrypt(&seed, data)?;
seed.zeroize();
seed_augmented.zeroize();
Ok((
esk.to_bytes().map_err(|e| {
OpenVTCError::Encrypt(format!("Couldn't convert encrypted ESK to bytes: {e}"))
})?,
encrypted,
))
}
pub fn token_decrypt<T>(
user_pin: &SecretString,
token_id: &str,
esk: &[u8],
data: &[u8],
touch_prompt: &T,
) -> Result<Vec<u8>, OpenVTCError>
where
T: TokenInteractions + Send + Sync,
{
info!("Unlocking hardware token");
let mut card = open_card(token_id)?;
let mut card = card.transaction().map_err(|e| {
OpenVTCError::Token(format!(
"Couldn't create hardware token transaction - decrypt: {e}"
))
})?;
card.verify_user_pin(user_pin.to_owned())
.map_err(|_| OpenVTCError::TokenBadPin)?;
card.as_user_card(None)
.map_err(|e| OpenVTCError::Token(format!("Couldn't unlock user mode on token: {e}")))?;
let binding = || touch_prompt.touch_notify();
let cs = CardSlot::init_from_card(&mut card, KeyType::Decryption, &binding).map_err(|e| {
OpenVTCError::Token(format!("Couldn't init decryption key from token: {e}"))
})?;
touch_prompt.touch_completed();
let raw_br = BufReader::new(esk);
let pk_esk =
PkeskBytes::try_from_reader(&PublicKeyAlgorithm::ECDH, 6, raw_br).map_err(|e| {
OpenVTCError::Decrypt(format!("Couldn't convert public-key ESK. Reason: {e}"))
})?;
let plain_session_key = cs
.decrypt(&Password::empty(), &pk_esk, EskType::V6)
.map_err(|e| OpenVTCError::Token(format!("Couldn't decrypt data, reason: {e}")))?
.map_err(|e| OpenVTCError::Token(format!("Session key decryption failed: {e}")))?;
let decrypted_esk = match &plain_session_key {
PlainSessionKey::V6 { key } => key.as_ref(),
PlainSessionKey::V3_4 { key, .. } => key.as_ref(),
PlainSessionKey::V5 { key } => key.as_ref(),
};
if decrypted_esk.len() != 32 {
warn!(
"Invalid ESK length ({}) received! Expected 32",
decrypted_esk.len()
);
return Err(OpenVTCError::Decrypt(format!(
"Decrypted ESK has invalid length ({})! Expected 32",
decrypted_esk.len()
)));
}
unlock_code_decrypt(
decrypted_esk
.first_chunk::<32>()
.ok_or_else(|| OpenVTCError::Decrypt("Decrypted ESK is not 32 bytes".to_string()))?,
data,
)
}