use ring::digest;
use yubikey_piv::{MgmKey, YubiKey};
use yubikey_piv::policy::{PinPolicy, TouchPolicy};
use yubikey_piv::key::{attest, AlgorithmId, sign_data as yk_sign_data, SlotId};
use yubikey_piv::certificate::{Certificate, PublicKeyInfo};
#[derive(Debug)]
pub enum Error {
Unprovisioned,
WrongKeyType,
Unsupported,
InvalidManagementKey,
InternalYubiKeyError(yubikey_piv::error::Error),
}
pub fn configured(yk: &mut YubiKey, slot: SlotId) -> Result<PublicKeyInfo, Error> {
match yubikey_piv::certificate::Certificate::read(yk, slot) {
Ok(cert) => {Ok(cert.subject_pki().clone())},
Err(e) => Err(Error::InternalYubiKeyError(e)),
}
}
fn subject(yk: &mut YubiKey, slot: SlotId) -> Result<String, Error> {
match yubikey_piv::certificate::Certificate::read(yk, slot) {
Ok(cert) => {Ok(cert.subject().to_owned())},
Err(e) => Err(Error::InternalYubiKeyError(e)),
}
}
pub fn fetch_certificate(slot: SlotId) -> Result<Vec<u8>, Error> {
let mut yk = match YubiKey::open() {
Ok(yk) => yk,
Err(e) => return Err(Error::InternalYubiKeyError(e)),
};
match yubikey_piv::certificate::Certificate::read(&mut yk, slot) {
Ok(cert) => {Ok(cert.as_ref().to_vec())},
Err(e) => Err(Error::InternalYubiKeyError(e)),
}
}
pub fn fetch_pubkey(slot: SlotId) -> Result<PublicKeyInfo, Error> {
let mut yubikey = match YubiKey::open() {
Ok(yk) => yk,
Err(e) => return Err(Error::InternalYubiKeyError(e)),
};
configured(&mut yubikey, slot)
}
pub fn fetch_subject(slot: SlotId) -> Result<String, Error> {
let mut yubikey = match YubiKey::open() {
Ok(yk) => yk,
Err(e) => return Err(Error::InternalYubiKeyError(e)),
};
subject(&mut yubikey, slot)
}
pub fn fetch_attestation(slot: SlotId) -> Option<Vec<u8>> {
let mut yubikey = match YubiKey::open() {
Ok(yk) => yk,
Err(_e) => return None,
};
match attest(&mut yubikey, slot) {
Ok(buf) => Some(buf.to_vec()),
Err(_) => None
}
}
pub fn provision(pin: &[u8], mgm_key: &[u8], slot: SlotId, subject: &str, alg: AlgorithmId, require_touch: TouchPolicy) -> Result<PublicKeyInfo, Error> {
let mut yk = match YubiKey::open() {
Ok(yk) => yk,
Err(e) => return Err(Error::InternalYubiKeyError(e)),
};
match yk.verify_pin(pin) {
Ok(_) => (),
Err(e) => {
println!("Error in verify pin: {}", e);
return Err(Error::InternalYubiKeyError(e))
},
}
let mgm_key = match MgmKey::from_bytes(mgm_key) {
Ok(mgm) => mgm,
Err(_) => return Err(Error::InvalidManagementKey),
};
match yk.authenticate(mgm_key) {
Ok(_) => (),
Err(e) => {
println!("Error in MGM Key Authentication: {}", e);
return Err(Error::InternalYubiKeyError(e));
},
}
let key_info = match yubikey_piv::key::generate(&mut yk, slot, alg, PinPolicy::Never, require_touch) {
Ok(ki) => ki,
Err(e) => {
println!("Error in provisioning new key: {}", e);
return Err(Error::InternalYubiKeyError(e));
},
};
if let Err(e) = Certificate::generate_self_signed(
&mut yk,
slot,
[0u8; 20],
None,
subject.to_string(),
key_info,
) {
return Err(Error::InternalYubiKeyError(e));
}
configured(&mut yk, slot)
}
pub fn sign_data(data: &[u8], alg: AlgorithmId, slot: SlotId) -> Result<Vec<u8>, Error> {
let mut yk = match YubiKey::open() {
Ok(yk) => yk,
Err(e) => return Err(Error::InternalYubiKeyError(e)),
};
let slot_alg = match configured(&mut yk, slot) {
Ok(PublicKeyInfo::EcP256(_)) => AlgorithmId::EccP256,
Ok(PublicKeyInfo::EcP384(_)) => AlgorithmId::EccP384,
Ok(_) => AlgorithmId::Rsa2048, Err(_) => return Err(Error::Unprovisioned),
};
if slot_alg != alg {
return Err(Error::WrongKeyType);
}
let hash = match slot_alg {
AlgorithmId::EccP256 => digest::digest(&digest::SHA256, data).as_ref().to_vec(),
AlgorithmId::EccP384 => digest::digest(&digest::SHA384, data).as_ref().to_vec(),
_ => return Err(Error::Unsupported),
};
match yk_sign_data(&mut yk, &hash[..], alg, slot) {
Ok(sig) => Ok(sig.to_vec()),
Err(e) => Err(Error::InternalYubiKeyError(e)),
}
}