From 91a850c1bfd1de0176fedb27b2aa8e859a715a8f Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Fri, 30 Jun 2023 13:48:11 -0700 Subject: [PATCH] implement put credential method --- src/apdu.rs | 5 +++ src/consts.rs | 5 +++ src/hsmauth.rs | 37 ++++++++++++++++++++-- src/transaction.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 119 insertions(+), 4 deletions(-) diff --git a/src/apdu.rs b/src/apdu.rs index 43036457..954e9f34 100644 --- a/src/apdu.rs +++ b/src/apdu.rs @@ -207,6 +207,9 @@ pub enum Ins { /// YubiHSM Auth // List credentials ListCredentials, + /// YubiHSM Auth // Put credential + PutCredential, + /// Other/unrecognized instruction codes Other(u8), } @@ -233,6 +236,7 @@ impl Ins { Ins::GetSerial => 0xf8, Ins::GetMetadata => 0xf7, // Yubihsm auth + Ins::PutCredential => 0x01, Ins::Calculate => 0x03, Ins::GetHostChallenge => 0x04, Ins::ListCredentials => 0x05, @@ -244,6 +248,7 @@ impl Ins { impl From for Ins { fn from(code: u8) -> Self { match code { + 0x01 => Ins::PutCredential, 0x03 => Ins::Calculate, 0x04 => Ins::GetHostChallenge, 0x05 => Ins::ListCredentials, diff --git a/src/consts.rs b/src/consts.rs index 5e06591b..2b8e8aad 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -22,4 +22,9 @@ pub(crate) const TAG_PROTECTED_MGM: u8 = 0x89; // YubiHSM Auth pub(crate) const TAG_LABEL: u8 = 0x71; pub(crate) const TAG_PW: u8 = 0x73; +pub(crate) const TAG_ALGO: u8 = 0x74; +pub(crate) const TAG_KEY_ENC: u8 = 0x75; +pub(crate) const TAG_KEY_MAC: u8 = 0x76; pub(crate) const TAG_CONTEXT: u8 = 0x77; +pub(crate) const TAG_TOUCH: u8 = 0x7a; +pub(crate) const TAG_MGMKEY: u8 = 0x7b; diff --git a/src/hsmauth.rs b/src/hsmauth.rs index 4079222f..b01e49a5 100644 --- a/src/hsmauth.rs +++ b/src/hsmauth.rs @@ -32,6 +32,17 @@ pub(crate) const KEY_SIZE: usize = 16; /// Password to authenticate to the Yubikey HSM Auth Applet has a max length of 16 pub(crate) const PW_LEN: usize = 16; +pub struct MgmKey(pub [u8; PW_LEN]); + +impl Default for MgmKey { + fn default() -> Self { + // https://docs.yubico.com/yesdk/users-manual/application-yubihsm-auth/commands/change-management-key.html + // The default value of the management key is all zeros: + // 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Self([0; PW_LEN]) + } +} + /// Label associated with a secret on the Yubikey. #[derive(Clone)] pub struct Label(pub(crate) Vec); @@ -116,6 +127,27 @@ impl HsmAuth { Transaction::new(&mut self.client.card)?.list_credentials(self.client.version) } + /// Put credential + pub fn put_credential( + &mut self, + mgmkey: Option, + label: Label, + password: &[u8], + enc_key: [u8; KEY_SIZE], + mac_key: [u8; KEY_SIZE], + touch: bool, + ) -> Result<()> { + Transaction::new(&mut self.client.card)?.put_credential( + self.client.version, + mgmkey.unwrap_or(MgmKey::default()), + label, + password, + enc_key, + mac_key, + touch, + ) + } + /// Retun the inner `YubiKey` pub fn into_inner(mut self) -> Result { Transaction::new(&mut self.client.card)?.select_piv_application()?; @@ -124,12 +156,13 @@ impl HsmAuth { } #[derive(Debug)] +#[repr(u8)] /// Algorithm for the credentials pub enum Algorithm { /// AES 128 keys - Aes128, + Aes128 = 38, /// EC P256 - EcP256, + EcP256 = 39, } impl fmt::Display for Algorithm { diff --git a/src/transaction.rs b/src/transaction.rs index 93fd610c..1e41a7f9 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -3,9 +3,12 @@ use crate::{ apdu::Response, apdu::{Apdu, Ins, StatusWords}, - consts::{CB_BUF_MAX, CB_OBJ_MAX, TAG_CONTEXT, TAG_LABEL, TAG_PW}, + consts::{ + CB_BUF_MAX, CB_OBJ_MAX, TAG_ALGO, TAG_CONTEXT, TAG_KEY_ENC, TAG_KEY_MAC, TAG_LABEL, + TAG_MGMKEY, TAG_PW, TAG_TOUCH, + }, error::{Error, Result}, - hsmauth::{self, Challenge, Context, Credential, Label, SessionKeys}, + hsmauth::{self, Algorithm, Challenge, Context, Credential, Label, SessionKeys}, otp, piv::{self, AlgorithmId, SlotId}, serialization::*, @@ -636,4 +639,73 @@ impl<'tx> Transaction<'tx> { let data = response.data(); Credential::parse_list(data) } + + /// Adds a credential to YubiHSM Auth applet + pub fn put_credential( + &mut self, + version: Version, + mgmkey: hsmauth::MgmKey, + label: Label, + password: &[u8], + enc_key: [u8; hsmauth::KEY_SIZE], + mac_key: [u8; hsmauth::KEY_SIZE], + touch: bool, + ) -> Result<()> { + // YubiHSM was introduced by firmware 5.4.3 + // https://docs.yubico.com/yesdk/users-manual/application-yubihsm-auth/yubihsm-auth-overview.html + if version + < (Version { + major: 5, + minor: 4, + patch: 3, + }) + { + return Err(Error::NotSupported); + } + + let mut data = [0u8; CB_BUF_MAX]; + let mut len = data.len(); + let mut data_remaining = &mut data[..]; + + let offset = Tlv::write(data_remaining, TAG_MGMKEY, &mgmkey.0)?; + data_remaining = &mut data_remaining[offset..]; + + let offset = Tlv::write(data_remaining, TAG_LABEL, &label.0)?; + data_remaining = &mut data_remaining[offset..]; + + let offset = Tlv::write(data_remaining, TAG_ALGO, &[Algorithm::Aes128 as u8])?; + data_remaining = &mut data_remaining[offset..]; + + let offset = Tlv::write(data_remaining, TAG_KEY_ENC, &enc_key)?; + data_remaining = &mut data_remaining[offset..]; + + let offset = Tlv::write(data_remaining, TAG_KEY_MAC, &mac_key)?; + data_remaining = &mut data_remaining[offset..]; + + let mut password = password.to_vec(); + password.resize(hsmauth::PW_LEN, 0); + + let offset = Tlv::write(data_remaining, TAG_PW, &password)?; + data_remaining = &mut data_remaining[offset..]; + + let offset = Tlv::write(data_remaining, TAG_TOUCH, &[touch as u8])?; + data_remaining = &mut data_remaining[offset..]; + + len -= data_remaining.len(); + + let response = Apdu::new(Ins::PutCredential) + .params(0x00, 0x00) + .data(&data[..len]) + .transmit(self, 2)?; + + if !response.is_success() { + error!( + "Unable to store credential: {:04x}", + response.status_words().code() + ); + return Err(Error::GenericError); + } + + Ok(()) + } }