-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is used to calculate session keys for Yubico HSM.
- Loading branch information
Showing
6 changed files
with
267 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
//! YubiHSM Auth protocol | ||
//! | ||
//! YubiHSM Auth is a YubiKey CCID application that stores the long-lived | ||
//! credentials used to establish secure sessions with a YubiHSM 2. The secure | ||
//! session protocol is based on Secure Channel Protocol 3 (SCP03). | ||
use crate::{ | ||
error::{Error, Result}, | ||
transaction::Transaction, | ||
YubiKey, | ||
}; | ||
use std::str::FromStr; | ||
use zeroize::Zeroizing; | ||
|
||
/// Yubikey HSM Auth Applet ID | ||
pub(crate) const APPLET_ID: &[u8] = &[0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x07, 0x01]; | ||
/// Yubikey HSM Auth Applet Name | ||
pub(crate) const APPLET_NAME: &str = "YubiHSM"; | ||
|
||
/// AES key size in bytes. SCP03 theoretically supports other key sizes, but | ||
/// the YubiHSM 2 does not. Since this crate is somewhat specialized to the `YubiHSM 2` (at least for now) | ||
/// we hardcode to 128-bit for simplicity. | ||
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; | ||
|
||
/// Label associated with a secret on the Yubikey. | ||
pub struct Label(pub(crate) Vec<u8>); | ||
|
||
impl FromStr for Label { | ||
type Err = Error; | ||
|
||
fn from_str(input: &str) -> Result<Self> { | ||
let buf = input.as_bytes(); | ||
|
||
if (1..=64).contains(&buf.len()) { | ||
Ok(Self(buf.to_vec())) | ||
} else { | ||
Err(Error::ParseError) | ||
} | ||
} | ||
} | ||
|
||
/// [`Context`] holds the various challenges used for the authentication. | ||
/// | ||
/// This is used as part of the key derivation for the session keys. | ||
pub struct Context(pub(crate) [u8; 16]); | ||
|
||
impl Context { | ||
/// Creates a [`Context`] from its components | ||
pub fn new(host_challenge: [u8; 8], hsm_challenge: [u8; 8]) -> Self { | ||
let mut out = Self::zeroed(); | ||
out.0[..8].copy_from_slice(&host_challenge[..]); | ||
out.0[8..].copy_from_slice(&hsm_challenge[..]); | ||
|
||
out | ||
} | ||
|
||
fn zeroed() -> Self { | ||
Self([0u8; 16]) | ||
} | ||
} | ||
|
||
/// Exclusive access to the Hsmauth applet. | ||
pub struct HsmAuth<'y> { | ||
client: &'y mut YubiKey, | ||
} | ||
|
||
impl<'y> HsmAuth<'y> { | ||
pub(crate) fn new(client: &'y mut YubiKey) -> Result<Self> { | ||
Transaction::new(&mut client.card)?.select_application( | ||
APPLET_ID, | ||
APPLET_NAME, | ||
"failed selecting YkHSM auth application", | ||
)?; | ||
|
||
Ok(Self { client }) | ||
} | ||
|
||
/// Calculate session key with the specified key. | ||
pub fn calculate( | ||
&mut self, | ||
label: Label, | ||
context: Context, | ||
password: &[u8], | ||
) -> Result<SessionKeys> { | ||
Transaction::new(&mut self.client.card)?.calculate( | ||
self.client.version, | ||
label, | ||
context, | ||
password, | ||
) | ||
} | ||
} | ||
|
||
impl<'y> Drop for HsmAuth<'y> { | ||
fn drop(&mut self) { | ||
// Revert to PIV application on drop | ||
Transaction::new(&mut self.client.card) | ||
.unwrap() | ||
.select_piv_application() | ||
.unwrap() | ||
} | ||
} | ||
|
||
/// The sessions keys after negociation via SCP03. | ||
#[derive(Default, Debug)] | ||
pub struct SessionKeys { | ||
/// Session encryption key (S-ENC) | ||
pub enc_key: Zeroizing<[u8; KEY_SIZE]>, | ||
/// Session Command MAC key (S-MAC) | ||
pub mac_key: Zeroizing<[u8; KEY_SIZE]>, | ||
/// Session Respose MAC key (S-RMAC) | ||
pub rmac_key: Zeroizing<[u8; KEY_SIZE]>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.