use crate::{
consts::CB_OBJ_MAX,
error::{Error, Result},
piv::SlotId,
serialization::*,
transaction::Transaction,
yubikey::YubiKey,
Buffer,
};
use log::error;
use x509_cert::{
builder::{Builder, CertificateBuilder, Profile},
der::{self, referenced::OwnedToRef, Decode, Encode},
name::Name,
serial_number::SerialNumber,
spki::{SubjectPublicKeyInfoOwned, SubjectPublicKeyInfoRef},
time::Validity,
};
use zeroize::Zeroizing;
const TAG_CERT: u8 = 0x70;
const TAG_CERT_COMPRESS: u8 = 0x71;
const TAG_CERT_LRC: u8 = 0xFE;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CertInfo {
Uncompressed,
Gzip,
}
impl TryFrom<u8> for CertInfo {
type Error = Error;
fn try_from(value: u8) -> Result<Self> {
match value {
0x00 => Ok(CertInfo::Uncompressed),
0x01 => Ok(CertInfo::Gzip),
_ => Err(Error::InvalidObject),
}
}
}
impl From<CertInfo> for u8 {
fn from(certinfo: CertInfo) -> u8 {
match certinfo {
CertInfo::Uncompressed => 0x00,
CertInfo::Gzip => 0x01,
}
}
}
#[derive(Clone, Debug)]
pub struct Certificate {
pub cert: x509_cert::Certificate,
}
impl Certificate {
pub fn generate_self_signed<F, KT: yubikey_signer::KeyType>(
yubikey: &mut YubiKey,
key: SlotId,
serial: SerialNumber,
validity: Validity,
subject: Name,
subject_pki: SubjectPublicKeyInfoOwned,
extensions: F,
) -> Result<Self>
where
F: FnOnce(&mut CertificateBuilder<'_, yubikey_signer::Signer<'_, KT>>) -> der::Result<()>,
{
let signer = yubikey_signer::Signer::new(yubikey, key, subject_pki.owned_to_ref())?;
let mut builder = CertificateBuilder::new(
Profile::Manual { issuer: None },
serial,
validity,
subject,
subject_pki,
&signer,
)
.map_err(|_| Error::KeyError)?;
extensions(&mut builder)?;
let cert = builder.build().map_err(|_| Error::KeyError)?;
let cert = Self { cert };
cert.write(yubikey, key, CertInfo::Uncompressed)?;
Ok(cert)
}
pub fn read(yubikey: &mut YubiKey, slot: SlotId) -> Result<Self> {
let txn = yubikey.begin_transaction()?;
let buf = read_certificate(&txn, slot)?;
if buf.is_empty() {
return Err(Error::InvalidObject);
}
Self::from_bytes(buf)
}
pub fn write(&self, yubikey: &mut YubiKey, slot: SlotId, certinfo: CertInfo) -> Result<()> {
let txn = yubikey.begin_transaction()?;
let data = self.cert.to_der().map_err(|_| Error::InvalidObject)?;
write_certificate(&txn, slot, Some(&data), certinfo)
}
#[cfg(feature = "untested")]
pub fn delete(yubikey: &mut YubiKey, slot: SlotId) -> Result<()> {
let txn = yubikey.begin_transaction()?;
write_certificate(&txn, slot, None, CertInfo::Uncompressed)
}
pub fn from_bytes(cert: impl Into<Buffer>) -> Result<Self> {
let cert = cert.into();
if cert.is_empty() {
error!("certificate cannot be empty");
return Err(Error::SizeError);
}
x509_cert::Certificate::from_der(&cert)
.map(|cert| Self { cert })
.map_err(|_| Error::InvalidObject)
}
pub fn issuer(&self) -> String {
self.cert.tbs_certificate.issuer.to_string()
}
pub fn subject(&self) -> String {
self.cert.tbs_certificate.subject.to_string()
}
pub fn subject_pki(&self) -> SubjectPublicKeyInfoRef<'_> {
self.cert
.tbs_certificate
.subject_public_key_info
.owned_to_ref()
}
}
pub(crate) fn read_certificate(txn: &Transaction<'_>, slot: SlotId) -> Result<Buffer> {
let object_id = slot.object_id();
let buf = match txn.fetch_object(object_id) {
Ok(b) => b,
Err(_) => {
return Ok(Zeroizing::new(vec![]));
}
};
if buf[0] == TAG_CERT {
Tlv::parse_single(buf, TAG_CERT).or_else(|_| {
Ok(Zeroizing::new(vec![]))
})
} else {
Ok(buf)
}
}
pub(crate) fn write_certificate(
txn: &Transaction<'_>,
slot: SlotId,
data: Option<&[u8]>,
certinfo: CertInfo,
) -> Result<()> {
let object_id = slot.object_id();
if let Some(data) = data {
let mut buf = [0u8; CB_OBJ_MAX];
let mut offset = Tlv::write(&mut buf, TAG_CERT, data)?;
offset += Tlv::write(&mut buf[offset..], TAG_CERT_COMPRESS, &[certinfo.into()])?;
offset += Tlv::write(&mut buf[offset..], TAG_CERT_LRC, &[])?;
txn.save_object(object_id, &buf[..offset])
} else {
txn.save_object(object_id, &[])
}
}
pub mod yubikey_signer {
use crate::{
error::{Error, Result},
piv::AlgorithmId,
piv::{sign_data, SlotId},
YubiKey,
};
use der::{
asn1::{Any, OctetString},
oid::db::rfc5912,
Encode, Sequence,
};
use sha2::{Digest, Sha256, Sha384};
use signature::Keypair;
use std::{cell::RefCell, fmt, io::Write, marker::PhantomData};
use x509_cert::spki::{
self, AlgorithmIdentifierOwned, DynSignatureAlgorithmIdentifier, EncodePublicKey,
SignatureBitStringEncoding, SubjectPublicKeyInfoRef,
};
type SigResult<T> = core::result::Result<T, signature::Error>;
pub trait KeyType {
type Error: Into<signature::Error> + fmt::Debug;
type Signature: SignatureBitStringEncoding
+ for<'s> TryFrom<&'s [u8], Error = Self::Error>
+ fmt::Debug;
type VerifyingKey: EncodePublicKey
+ DynSignatureAlgorithmIdentifier
+ Clone
+ From<Self::PublicKey>;
type PublicKey: for<'a> TryFrom<SubjectPublicKeyInfoRef<'a>, Error = spki::Error>;
const ALGORITHM: AlgorithmId;
fn prepare(input: &[u8]) -> SigResult<Vec<u8>>;
fn read_signature(input: &[u8]) -> SigResult<Self::Signature>;
}
impl KeyType for p256::NistP256 {
const ALGORITHM: AlgorithmId = AlgorithmId::EccP256;
type Error = ecdsa::Error;
type Signature = p256::ecdsa::DerSignature;
type VerifyingKey = p256::ecdsa::VerifyingKey;
type PublicKey = p256::ecdsa::VerifyingKey;
fn prepare(input: &[u8]) -> SigResult<Vec<u8>> {
Ok(Sha256::digest(input).to_vec())
}
fn read_signature(input: &[u8]) -> SigResult<Self::Signature> {
Self::Signature::from_bytes(input)
}
}
impl KeyType for p384::NistP384 {
const ALGORITHM: AlgorithmId = AlgorithmId::EccP384;
type Error = ecdsa::Error;
type Signature = p384::ecdsa::DerSignature;
type VerifyingKey = p384::ecdsa::VerifyingKey;
type PublicKey = p384::ecdsa::VerifyingKey;
fn prepare(input: &[u8]) -> SigResult<Vec<u8>> {
Ok(Sha384::digest(input).to_vec())
}
fn read_signature(input: &[u8]) -> SigResult<Self::Signature> {
Self::Signature::from_bytes(input)
}
}
pub trait RsaLength {
const BIT_LENGTH: usize;
const ALGORITHM: AlgorithmId;
}
pub struct Rsa1024;
impl RsaLength for Rsa1024 {
const BIT_LENGTH: usize = 1024;
const ALGORITHM: AlgorithmId = AlgorithmId::Rsa1024;
}
pub struct Rsa2048;
impl RsaLength for Rsa2048 {
const BIT_LENGTH: usize = 2048;
const ALGORITHM: AlgorithmId = AlgorithmId::Rsa2048;
}
pub struct YubiRsa<N: RsaLength> {
_len: PhantomData<N>,
}
impl<N: RsaLength> KeyType for YubiRsa<N> {
type Error = signature::Error;
type Signature = rsa::pkcs1v15::Signature;
type VerifyingKey = rsa::pkcs1v15::VerifyingKey<Sha256>;
type PublicKey = rsa::RsaPublicKey;
const ALGORITHM: AlgorithmId = N::ALGORITHM;
fn prepare(input: &[u8]) -> SigResult<Vec<u8>> {
let hashed = Sha256::digest(input).to_vec();
OctetString::new(hashed)
.map_err(|e| e.into())
.and_then(Self::emsa_pkcs1_1_5)
.map_err(signature::Error::from_source)
}
fn read_signature(input: &[u8]) -> SigResult<Self::Signature> {
Self::Signature::try_from(input)
}
}
impl<N: RsaLength> YubiRsa<N> {
fn emsa_pkcs1_1_5(digest: OctetString) -> Result<Vec<u8>> {
#[derive(Debug, Sequence)]
struct DigestInfo {
digest_algorithm: AlgorithmIdentifierOwned,
digest: OctetString,
}
let em_len = N::BIT_LENGTH / 8;
let null = Any::null();
let t = DigestInfo {
digest_algorithm: AlgorithmIdentifierOwned {
oid: rfc5912::ID_SHA_256,
parameters: Some(null),
},
digest,
};
let t = t.to_der()?;
let ps = vec![0xff; em_len - t.len() - 3];
assert!(ps.len() >= 8, "spec violation");
let mut out = Vec::with_capacity(em_len);
out.write(&[0x00, 0x01]).map_err(|_| Error::MemoryError)?;
out.write(&ps).map_err(|_| Error::MemoryError)?;
out.write(&[0x00]).map_err(|_| Error::MemoryError)?;
out.write(&t).map_err(|_| Error::MemoryError)?;
Ok(out)
}
}
pub struct Signer<'y, KT: KeyType> {
yubikey: RefCell<&'y mut YubiKey>,
key: SlotId,
public_key: KT::VerifyingKey,
}
impl<'y, KT: KeyType> Signer<'y, KT> {
pub fn new(
yubikey: &'y mut YubiKey,
key: SlotId,
subject_pki: SubjectPublicKeyInfoRef<'_>,
) -> Result<Self> {
let public_key = KT::PublicKey::try_from(subject_pki).map_err(|_| Error::ParseError)?;
let public_key = public_key.into();
Ok(Self {
yubikey: RefCell::new(yubikey),
key,
public_key,
})
}
}
impl<'y, KT: KeyType> Keypair for Signer<'y, KT> {
type VerifyingKey = KT::VerifyingKey;
fn verifying_key(&self) -> <Self as Keypair>::VerifyingKey {
self.public_key.clone()
}
}
impl<'y, KT: KeyType> DynSignatureAlgorithmIdentifier for Signer<'y, KT> {
fn signature_algorithm_identifier(&self) -> spki::Result<AlgorithmIdentifierOwned> {
self.verifying_key().signature_algorithm_identifier()
}
}
impl<'y, KT: KeyType> signature::Signer<KT::Signature> for Signer<'y, KT> {
fn try_sign(&self, msg: &[u8]) -> SigResult<KT::Signature> {
let data = KT::prepare(msg)?;
let out = sign_data(
&mut self.yubikey.borrow_mut(),
&data,
KT::ALGORITHM,
self.key,
)
.map_err(signature::Error::from_source)?;
let out = KT::read_signature(&out)?;
Ok(out)
}
}
}