mod privkey;
mod rpgp;
use std::fmt::{Debug, Formatter};
use std::sync::Mutex;
use openpgp_card::ocard::crypto::Hash;
use openpgp_card::ocard::KeyType;
use openpgp_card::state::Transaction;
use openpgp_card::Card;
use pgp::crypto::checksum;
use pgp::crypto::ecc_curve::ECCCurve;
use pgp::crypto::hash::HashAlgorithm;
use pgp::crypto::public_key::PublicKeyAlgorithm;
use pgp::crypto::sym::SymmetricKeyAlgorithm;
use pgp::packet::PublicKey;
use pgp::types::{
EcdsaPublicParams, KeyId, KeyTrait, Mpi, PublicKeyTrait, PublicParams, SecretKeyTrait,
};
pub use privkey::UploadableKey;
use rand::{CryptoRng, Rng};
pub use rpgp::{
decrypt, fp_from_pub, make_certificate, public_key_material_and_fp_to_key,
public_key_material_to_key,
};
use crate::rpgp::map_card_err;
pub struct CardSlot<'cs, 't> {
tx: Mutex<&'cs mut Card<Transaction<'t>>>,
key_type: KeyType,
public_key: PublicKey,
touch_prompt: &'cs (dyn Fn() + Send + Sync),
}
impl<'cs, 't> CardSlot<'cs, 't> {
pub fn with_public_key(
tx: &'cs mut Card<Transaction<'t>>,
key_type: KeyType,
public_key: PublicKey,
touch_prompt: &'cs (dyn Fn() + Send + Sync),
) -> Result<Self, pgp::errors::Error> {
Ok(Self {
tx: Mutex::new(tx),
public_key,
key_type,
touch_prompt,
})
}
pub fn init_from_card(
tx: &'cs mut Card<Transaction<'t>>,
key_type: KeyType,
touch_prompt: &'cs (dyn Fn() + Send + Sync),
) -> Result<Self, pgp::errors::Error> {
let pk = rpgp::pubkey_from_card(tx, key_type)?;
Self::with_public_key(tx, key_type, pk, touch_prompt)
}
pub fn public_key(&self) -> &PublicKey {
&self.public_key
}
pub fn key_type(&self) -> KeyType {
self.key_type
}
fn touch_required(&self, tx: &mut Card<Transaction<'_>>) -> bool {
if let Ok(Some(uif)) = tx.user_interaction_flag(self.key_type) {
uif.touch_policy().touch_required()
} else {
false
}
}
}
impl Debug for CardSlot<'_, '_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "CardSlot for {:?}", self.public_key)?;
Ok(())
}
}
impl KeyTrait for CardSlot<'_, '_> {
fn fingerprint(&self) -> Vec<u8> {
self.public_key.fingerprint()
}
fn key_id(&self) -> KeyId {
self.public_key.key_id()
}
fn algorithm(&self) -> PublicKeyAlgorithm {
self.public_key.algorithm()
}
}
impl PublicKeyTrait for CardSlot<'_, '_> {
fn verify_signature(
&self,
hash: HashAlgorithm,
data: &[u8],
sig: &[Mpi],
) -> pgp::errors::Result<()> {
self.public_key.verify_signature(hash, data, sig)
}
fn encrypt<R: CryptoRng + Rng>(
&self,
rng: &mut R,
plain: &[u8],
) -> pgp::errors::Result<Vec<Mpi>> {
self.public_key.encrypt(rng, plain)
}
fn to_writer_old(&self, writer: &mut impl std::io::Write) -> pgp::errors::Result<()> {
self.public_key.to_writer_old(writer)
}
}
pub struct Unlocked;
impl SecretKeyTrait for CardSlot<'_, '_> {
type PublicKey = PublicKey;
type Unlocked = Self;
fn unlock<F, G, T>(&self, _pw: F, work: G) -> pgp::errors::Result<T>
where
F: FnOnce() -> String,
G: FnOnce(&Self::Unlocked) -> pgp::errors::Result<T>,
{
work(self)
}
fn create_signature<F>(
&self,
_key_pw: F,
hash: HashAlgorithm,
data: &[u8],
) -> pgp::errors::Result<Vec<Mpi>>
where
F: FnOnce() -> String,
{
let mut tx = self.tx.lock().unwrap();
let hash = match self.public_key.algorithm() {
PublicKeyAlgorithm::RSA => match hash {
HashAlgorithm::SHA2_256 => Hash::SHA256(data.try_into().expect("FIXME")),
HashAlgorithm::SHA2_384 => Hash::SHA384(data.try_into().expect("FIXME")),
HashAlgorithm::SHA2_512 => Hash::SHA512(data.try_into().expect("FIXME")),
_ => {
return Err(pgp::errors::Error::Unimplemented(format!(
"Unsupported HashAlgorithm for RSA: {:?}",
hash
)))
}
},
PublicKeyAlgorithm::ECDSA => Hash::ECDSA({
match self.public_key.public_params() {
PublicParams::ECDSA(EcdsaPublicParams::P256 { .. }) => &data[..32],
PublicParams::ECDSA(EcdsaPublicParams::P384 { .. }) => &data[..48],
PublicParams::ECDSA(EcdsaPublicParams::P521 { .. }) => &data[..64],
_ => data,
}
}),
PublicKeyAlgorithm::EdDSA => Hash::EdDSA(data),
_ => {
return Err(pgp::errors::Error::Unimplemented(format!(
"Unsupported PublicKeyAlgorithm for signature creation: {:?}",
self.public_key.algorithm()
)))
}
};
if self.touch_required(&mut tx) {
(self.touch_prompt)();
}
let sig = match self.key_type {
KeyType::Signing => tx.card().signature_for_hash(hash).map_err(map_card_err)?,
KeyType::Authentication => tx
.card()
.authenticate_for_hash(hash)
.map_err(map_card_err)?,
_ => unimplemented!(),
};
let mpis = match self.public_key.algorithm() {
PublicKeyAlgorithm::RSA => vec![sig.into()],
PublicKeyAlgorithm::ECDSA => {
let mid = sig.len() / 2;
vec![
Mpi::from_raw_slice(&sig[..mid]),
Mpi::from_raw_slice(&sig[mid..]),
]
}
PublicKeyAlgorithm::EdDSA => {
assert_eq!(sig.len(), 64); vec![
Mpi::from_raw_slice(&sig[..32]),
Mpi::from_raw_slice(&sig[32..]),
]
}
_ => unimplemented!(), };
Ok(mpis)
}
fn public_key(&self) -> Self::PublicKey {
self.public_key.clone()
}
fn public_params(&self) -> &PublicParams {
self.public_key.public_params()
}
}
impl CardSlot<'_, '_> {
pub fn decrypt(&self, mpis: &[Mpi]) -> pgp::errors::Result<(Vec<u8>, SymmetricKeyAlgorithm)> {
let mut tx = self.tx.lock().unwrap();
let decrypted_key = match self.public_key.public_params() {
PublicParams::RSA { .. } => {
let ciphertext = mpis[0].as_bytes();
let cryptogram = openpgp_card::ocard::crypto::Cryptogram::RSA(ciphertext);
if self.touch_required(&mut tx) {
(self.touch_prompt)();
}
tx.card().decipher(cryptogram).expect("decipher")
}
PublicParams::ECDH {
curve,
alg_sym,
hash,
..
} => {
let ciphertext = mpis[0].as_bytes();
let encrypted_session_key = mpis[2].as_bytes();
let ciphertext = if *curve == ECCCurve::Curve25519 {
assert_eq!(
ciphertext[0], 0x40,
"Unexpected shape of Cv25519 encrypted data"
);
&ciphertext[1..]
} else {
ciphertext
};
let cryptogram = openpgp_card::ocard::crypto::Cryptogram::ECDH(ciphertext);
if self.touch_required(&mut tx) {
(self.touch_prompt)();
}
let shared_secret: [u8; 32] = tx
.card()
.decipher(cryptogram)
.expect("decipher")
.try_into()
.unwrap();
let encrypted_key_len: usize =
mpis[1].first().copied().map(Into::into).unwrap_or(0);
let decrypted_key: Vec<u8> = pgp::crypto::ecdh::derive_session_key(
shared_secret,
encrypted_session_key,
encrypted_key_len,
&(curve.oid(), *alg_sym, *hash),
&self.public_key.fingerprint(),
)?;
decrypted_key
}
_ => unimplemented!(),
};
let dec_len = decrypted_key.len();
let (sessionkey, checksum) = (
&decrypted_key[1..dec_len - 2],
&decrypted_key[dec_len - 2..],
);
checksum::simple(checksum, sessionkey)?;
let session_key_algorithm = decrypted_key[0].into();
Ok((sessionkey.to_vec(), session_key_algorithm))
}
}