use crate::base64_data::Base64UrlSafeData;
use crate::error::*;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::convert::TryFrom;
#[cfg(feature = "wasm")]
use js_sys::{Array, ArrayBuffer, Object, Uint8Array};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
pub type UserId = Vec<u8>;
pub type Counter = u32;
pub type Aaguid = Vec<u8>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Challenge(pub Vec<u8>);
impl Into<Base64UrlSafeData> for Challenge {
fn into(self) -> Base64UrlSafeData {
Base64UrlSafeData(self.0)
}
}
impl From<Base64UrlSafeData> for Challenge {
fn from(d: Base64UrlSafeData) -> Self {
Challenge(d.0)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ECDSACurve {
SECP256R1 = 1,
SECP384R1 = 2,
SECP521R1 = 3,
}
impl TryFrom<i128> for ECDSACurve {
type Error = WebauthnError;
fn try_from(u: i128) -> Result<Self, Self::Error> {
match u {
1 => Ok(ECDSACurve::SECP256R1),
2 => Ok(ECDSACurve::SECP384R1),
3 => Ok(ECDSACurve::SECP521R1),
_ => Err(WebauthnError::COSEKeyECDSAInvalidCurve),
}
}
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum COSEContentType {
ECDSA_SHA256 = -7,
ECDSA_SHA384 = -35,
ECDSA_SHA512 = -36,
RS256 = -257,
RS384 = -258,
RS512 = -259,
PS256 = -37,
PS384 = -38,
PS512 = -39,
EDDSA = -8,
INSECURE_RS1 = -65535,
}
impl TryFrom<i128> for COSEContentType {
type Error = WebauthnError;
fn try_from(i: i128) -> Result<Self, Self::Error> {
match i {
-7 => Ok(COSEContentType::ECDSA_SHA256),
-35 => Ok(COSEContentType::ECDSA_SHA384),
-36 => Ok(COSEContentType::ECDSA_SHA512),
-257 => Ok(COSEContentType::RS256),
-258 => Ok(COSEContentType::RS384),
-259 => Ok(COSEContentType::RS512),
-37 => Ok(COSEContentType::PS256),
-38 => Ok(COSEContentType::PS384),
-39 => Ok(COSEContentType::PS512),
-8 => Ok(COSEContentType::EDDSA),
-65535 => Ok(COSEContentType::INSECURE_RS1),
_ => Err(WebauthnError::COSEKeyECDSAContentType),
}
}
}
impl From<&COSEContentType> for i64 {
fn from(c: &COSEContentType) -> Self {
match c {
COSEContentType::ECDSA_SHA256 => -7,
COSEContentType::ECDSA_SHA384 => -35,
COSEContentType::ECDSA_SHA512 => -6,
COSEContentType::RS256 => -257,
COSEContentType::RS384 => -258,
COSEContentType::RS512 => -259,
COSEContentType::PS256 => -37,
COSEContentType::PS384 => -38,
COSEContentType::PS512 => -39,
COSEContentType::EDDSA => -8,
COSEContentType::INSECURE_RS1 => -65535,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct COSEEC2Key {
pub curve: ECDSACurve,
pub x: [u8; 32],
pub y: [u8; 32],
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct COSERSAKey {
pub n: Vec<u8>,
pub e: [u8; 3],
}
#[allow(non_camel_case_types)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum COSEKeyType {
EC_OKP,
EC_EC2(COSEEC2Key),
RSA(COSERSAKey),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct COSEKey {
pub type_: COSEContentType,
pub key: COSEKeyType,
}
pub type CredentialID = Vec<u8>;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Credential {
pub cred_id: CredentialID,
pub cred: COSEKey,
pub counter: u32,
pub verified: bool,
}
impl Credential {
pub(crate) fn new(
acd: &AttestedCredentialData,
ck: COSEKey,
counter: u32,
verified: bool,
) -> Self {
Credential {
cred_id: acd.credential_id.clone(),
cred: ck,
counter,
verified,
}
}
}
impl PartialEq<Credential> for Credential {
fn eq(&self, c: &Credential) -> bool {
self.cred_id == c.cred_id
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[allow(non_camel_case_types)]
#[serde(rename_all = "lowercase")]
pub enum UserVerificationPolicy {
Required,
#[serde(rename = "preferred")]
Preferred_DO_NOT_USE,
Discouraged,
}
pub(crate) type JSONExtensions = BTreeMap<String, String>;
#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RelyingParty {
pub name: String,
pub id: String,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct User {
pub id: Base64UrlSafeData,
pub name: String,
pub display_name: String,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct PubKeyCredParams {
#[serde(rename = "type")]
pub type_: String,
pub alg: i64,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct AllowCredentials {
#[serde(rename = "type")]
pub type_: String,
pub id: Base64UrlSafeData,
#[serde(skip_serializing_if = "Option::is_none")]
pub transports: Option<Vec<String>>,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicKeyCredentialCreationOptions {
pub rp: RelyingParty,
pub user: User,
pub challenge: Base64UrlSafeData,
pub pub_key_cred_params: Vec<PubKeyCredParams>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attestation: Option<AttestationConveyancePreference>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exclude_credentials: Option<Vec<PublicKeyCredentialDescriptor>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub authenticator_selection: Option<AuthenticatorSelectionCriteria>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extensions: Option<JSONExtensions>,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticatorSelectionCriteria {
#[serde(skip_serializing_if = "Option::is_none")]
pub authenticator_attachment: Option<AuthenticatorAttachment>,
pub require_resident_key: bool,
pub user_verification: UserVerificationPolicy,
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
pub enum AuthenticatorAttachment {
#[serde(rename = "platform")]
Platform,
#[serde(rename = "cross-platform")]
CrossPlatform,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AttestationConveyancePreference {
None,
Indirect,
Direct,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct PublicKeyCredentialDescriptor {
#[serde(rename = "type")]
type_: String,
id: Base64UrlSafeData,
transports: Option<Vec<AuthenticatorTransport>>,
}
impl PublicKeyCredentialDescriptor {
pub fn from_bytes(bytes: Vec<u8>) -> Self {
Self {
type_: "public-key".to_string(),
id: Base64UrlSafeData(bytes),
transports: None,
}
}
}
#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "lowercase")]
#[allow(unused)]
pub enum AuthenticatorTransport {
Usb,
Nfc,
Ble,
Internal,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreationChallengeResponse {
pub public_key: PublicKeyCredentialCreationOptions,
}
#[cfg(feature = "wasm")]
impl Into<web_sys::CredentialCreationOptions> for CreationChallengeResponse {
fn into(self) -> web_sys::CredentialCreationOptions {
let chal = Uint8Array::from(self.public_key.challenge.0.as_slice());
let userid = Uint8Array::from(self.public_key.user.id.0.as_slice());
let jsv = JsValue::from_serde(&self).unwrap();
let pkcco = js_sys::Reflect::get(&jsv, &JsValue::from("publicKey")).unwrap();
js_sys::Reflect::set(&pkcco, &JsValue::from("challenge"), &chal);
let user = js_sys::Reflect::get(&pkcco, &JsValue::from("user")).unwrap();
js_sys::Reflect::set(&user, &JsValue::from("id"), &userid);
web_sys::CredentialCreationOptions::from(jsv)
}
}
#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicKeyCredentialRequestOptions {
pub challenge: Base64UrlSafeData,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<u32>,
pub rp_id: String,
pub allow_credentials: Vec<AllowCredentials>,
pub user_verification: UserVerificationPolicy,
pub extensions: Option<JSONExtensions>,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RequestChallengeResponse {
pub public_key: PublicKeyCredentialRequestOptions,
}
#[cfg(feature = "wasm")]
impl Into<web_sys::CredentialRequestOptions> for RequestChallengeResponse {
fn into(self) -> web_sys::CredentialRequestOptions {
let chal = Uint8Array::from(self.public_key.challenge.0.as_slice());
let allow_creds: Array = self
.public_key
.allow_credentials
.iter()
.map(|ac| {
let obj = Object::new();
js_sys::Reflect::set(
&obj,
&JsValue::from("type"),
&JsValue::from_str(ac.type_.as_str()),
);
js_sys::Reflect::set(
&obj,
&JsValue::from("id"),
&Uint8Array::from(ac.id.0.as_slice()),
);
if let Some(transports) = &ac.transports {
let tarray: Array = transports
.iter()
.map(|s| JsValue::from_str(s.as_str()))
.collect();
js_sys::Reflect::set(&obj, &JsValue::from("transports"), &tarray);
}
obj
})
.collect();
let jsv = JsValue::from_serde(&self).unwrap();
let pkcco = js_sys::Reflect::get(&jsv, &JsValue::from("publicKey")).unwrap();
js_sys::Reflect::set(&pkcco, &JsValue::from("challenge"), &chal);
js_sys::Reflect::set(&pkcco, &JsValue::from("allowCredentials"), &allow_creds);
web_sys::CredentialRequestOptions::from(jsv)
}
}
impl RequestChallengeResponse {
pub(crate) fn new(
challenge: Challenge,
timeout: u32,
relaying_party: String,
allow_credentials: Vec<AllowCredentials>,
user_verification_policy: UserVerificationPolicy,
) -> Self {
RequestChallengeResponse {
public_key: PublicKeyCredentialRequestOptions {
challenge: challenge.into(),
timeout: Some(timeout),
rp_id: relaying_party,
allow_credentials,
user_verification: user_verification_policy,
extensions: None,
},
}
}
}
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct CollectedClientData {
#[serde(rename = "type")]
pub type_: String,
pub challenge: Base64UrlSafeData,
pub origin: String,
#[serde(rename = "tokenBinding")]
pub token_binding: Option<TokenBinding>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TokenBinding {
pub status: String,
pub id: Option<String>,
}
impl TryFrom<&Vec<u8>> for CollectedClientData {
type Error = WebauthnError;
fn try_from(data: &Vec<u8>) -> Result<CollectedClientData, WebauthnError> {
let ccd: CollectedClientData =
serde_json::from_slice(&data).map_err(WebauthnError::ParseJSONFailure)?;
Ok(ccd)
}
}
#[derive(Debug)]
pub(crate) struct AttestedCredentialData {
pub(crate) aaguid: Aaguid,
pub(crate) credential_id: CredentialID,
pub(crate) credential_pk: serde_cbor::Value,
}
#[derive(Debug)]
pub struct AuthenticatorData {
pub(crate) rp_id_hash: Vec<u8>,
pub(crate) counter: u32,
pub(crate) user_present: bool,
pub(crate) user_verified: bool,
pub(crate) acd: Option<AttestedCredentialData>,
pub(crate) extensions: Option<()>,
}
fn cbor_parser(i: &[u8]) -> nom::IResult<&[u8], serde_cbor::Value> {
let v: serde_cbor::Value = serde_cbor::from_slice(&i[0..])
.map_err(|_| nom::Err::Failure(nom::Context::Code(i, nom::ErrorKind::Custom(1))))?;
let encoded = serde_cbor::to_vec(&v)
.map_err(|_| nom::Err::Failure(nom::Context::Code(i, nom::ErrorKind::Custom(2))))?;
let cred_len = encoded.len();
Ok((&i[cred_len..], v))
}
named!( extensions_parser<&[u8], ()>,
do_parse!(
(())
)
);
named!( acd_parser<&[u8], AttestedCredentialData>,
do_parse!(
aaguid: take!(16) >>
cred_id_len: u16!(nom::Endianness::Big) >>
cred_id: take!(cred_id_len) >>
cred_pk: cbor_parser >>
(AttestedCredentialData {
aaguid: aaguid.to_vec(),
credential_id: cred_id.to_vec(),
credential_pk: cred_pk,
})
)
);
named!( authenticator_data_flags<&[u8], (bool, bool, bool, bool)>,
bits!(
do_parse!(
exten_pres: map!(take_bits!(u8, 1), |i| i != 0) >>
acd_pres: map!(take_bits!(u8, 1), |i| i != 0) >>
take_bits!(u8, 1) >>
take_bits!(u8, 1) >>
take_bits!(u8, 1) >>
u_ver: map!(take_bits!(u8, 1), |i| i != 0) >>
take_bits!(u8, 1) >>
u_pres: map!(take_bits!(u8, 1), |i| i != 0) >>
((exten_pres, acd_pres, u_ver, u_pres))
)
)
);
named!( authenticator_data_parser<&[u8], AuthenticatorData>,
do_parse!(
rp_id_hash: take!(32) >>
data_flags: authenticator_data_flags >>
counter: u32!(nom::Endianness::Big) >>
acd: cond!(data_flags.1, acd_parser) >>
extensions: cond!(data_flags.0, extensions_parser) >>
(AuthenticatorData {
rp_id_hash: rp_id_hash.to_vec(),
counter,
user_verified: data_flags.2,
user_present: data_flags.3,
acd,
extensions,
})
)
);
impl TryFrom<&Vec<u8>> for AuthenticatorData {
type Error = WebauthnError;
fn try_from(auth_data_bytes: &Vec<u8>) -> Result<Self, Self::Error> {
authenticator_data_parser(auth_data_bytes.as_slice())
.map_err(|e| {
log::debug!("nom -> {:?}", e);
WebauthnError::ParseNOMFailure
})
.map(|(_, ad)| ad)
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttestationObjectInner<'a> {
pub(crate) auth_data: &'a [u8],
pub(crate) fmt: String,
pub(crate) att_stmt: serde_cbor::Value,
}
#[derive(Debug)]
pub struct AttestationObject {
pub(crate) auth_data: AuthenticatorData,
pub(crate) auth_data_bytes: Vec<u8>,
pub(crate) fmt: String,
pub(crate) att_stmt: serde_cbor::Value,
}
impl TryFrom<&[u8]> for AttestationObject {
type Error = WebauthnError;
fn try_from(data: &[u8]) -> Result<AttestationObject, WebauthnError> {
let aoi: AttestationObjectInner =
serde_cbor::from_slice(&data).map_err(WebauthnError::ParseCBORFailure)?;
let auth_data_bytes: Vec<u8> = aoi.auth_data.iter().copied().collect();
let auth_data = AuthenticatorData::try_from(&auth_data_bytes)?;
Ok(AttestationObject {
fmt: aoi.fmt.clone(),
auth_data,
auth_data_bytes,
att_stmt: aoi.att_stmt,
})
}
}
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct AuthenticatorAttestationResponseRaw {
#[serde(rename = "attestationObject")]
pub attestation_object: Base64UrlSafeData,
#[serde(rename = "clientDataJSON")]
pub client_data_json: Base64UrlSafeData,
}
pub(crate) struct AuthenticatorAttestationResponse {
pub(crate) attestation_object: AttestationObject,
pub(crate) client_data_json: CollectedClientData,
pub(crate) client_data_json_bytes: Vec<u8>,
}
impl TryFrom<&AuthenticatorAttestationResponseRaw> for AuthenticatorAttestationResponse {
type Error = WebauthnError;
fn try_from(aarr: &AuthenticatorAttestationResponseRaw) -> Result<Self, Self::Error> {
let ccdj = CollectedClientData::try_from(aarr.client_data_json.as_ref())?;
let ao = AttestationObject::try_from(aarr.attestation_object.as_ref())?;
Ok(AuthenticatorAttestationResponse {
attestation_object: ao,
client_data_json: ccdj,
client_data_json_bytes: aarr.client_data_json.clone().into(),
})
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct RegisterPublicKeyCredential {
pub id: String,
#[serde(rename = "rawId")]
pub raw_id: Base64UrlSafeData,
pub response: AuthenticatorAttestationResponseRaw,
#[serde(rename = "type")]
pub type_: String,
}
#[cfg(feature = "wasm")]
impl From<web_sys::PublicKeyCredential> for RegisterPublicKeyCredential {
fn from(data: web_sys::PublicKeyCredential) -> RegisterPublicKeyCredential {
let data_raw_id =
Uint8Array::new(&js_sys::Reflect::get(&data, &JsValue::from("rawId")).unwrap())
.to_vec();
let data_response = js_sys::Reflect::get(&data, &JsValue::from("response")).unwrap();
let data_response_attestation_object = Uint8Array::new(
&js_sys::Reflect::get(&data_response, &JsValue::from("attestationObject")).unwrap(),
)
.to_vec();
let data_response_client_data_json = Uint8Array::new(
&js_sys::Reflect::get(&data_response, &JsValue::from("clientDataJSON")).unwrap(),
)
.to_vec();
let data_raw_id_b64 = Base64UrlSafeData(data_raw_id);
let data_response_attestation_object_b64 =
Base64UrlSafeData(data_response_attestation_object);
let data_response_client_data_json_b64 = Base64UrlSafeData(data_response_client_data_json);
RegisterPublicKeyCredential {
id: format!("{}", data_raw_id_b64),
raw_id: data_raw_id_b64,
type_: "public-key".to_string(),
response: AuthenticatorAttestationResponseRaw {
attestation_object: data_response_attestation_object_b64,
client_data_json: data_response_client_data_json_b64,
},
}
}
}
#[derive(Debug)]
pub(crate) struct AuthenticatorAssertionResponse {
pub(crate) authenticator_data: AuthenticatorData,
pub(crate) authenticator_data_bytes: Vec<u8>,
pub(crate) client_data: CollectedClientData,
pub(crate) client_data_bytes: Vec<u8>,
pub(crate) signature: Vec<u8>,
pub(crate) user_handle: Option<Vec<u8>>,
}
impl TryFrom<&AuthenticatorAssertionResponseRaw> for AuthenticatorAssertionResponse {
type Error = WebauthnError;
fn try_from(aarr: &AuthenticatorAssertionResponseRaw) -> Result<Self, Self::Error> {
Ok(AuthenticatorAssertionResponse {
authenticator_data: AuthenticatorData::try_from(aarr.authenticator_data.as_ref())?,
authenticator_data_bytes: aarr.authenticator_data.clone().into(),
client_data: CollectedClientData::try_from(aarr.client_data_json.as_ref())?,
client_data_bytes: aarr.client_data_json.clone().into(),
signature: aarr.signature.clone().into(),
user_handle: aarr.user_handle.clone().map(|uh| uh.into()),
})
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct AuthenticatorAssertionResponseRaw {
#[serde(rename = "authenticatorData")]
pub authenticator_data: Base64UrlSafeData,
#[serde(rename = "clientDataJSON")]
pub client_data_json: Base64UrlSafeData,
pub signature: Base64UrlSafeData,
#[serde(rename = "userHandle")]
pub user_handle: Option<Base64UrlSafeData>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct PublicKeyCredential {
pub id: String,
#[serde(rename = "rawId")]
pub raw_id: Base64UrlSafeData,
pub response: AuthenticatorAssertionResponseRaw,
#[serde(rename = "type")]
pub type_: String,
}
#[cfg(feature = "wasm")]
impl From<web_sys::PublicKeyCredential> for PublicKeyCredential {
fn from(data: web_sys::PublicKeyCredential) -> PublicKeyCredential {
let data_raw_id =
Uint8Array::new(&js_sys::Reflect::get(&data, &JsValue::from("rawId")).unwrap())
.to_vec();
let data_response = js_sys::Reflect::get(&data, &JsValue::from("response")).unwrap();
let data_response_authenticator_data = Uint8Array::new(
&js_sys::Reflect::get(&data_response, &JsValue::from("authenticatorData")).unwrap(),
)
.to_vec();
let data_response_signature = Uint8Array::new(
&js_sys::Reflect::get(&data_response, &JsValue::from("signature")).unwrap(),
)
.to_vec();
let data_response_user_handle =
&js_sys::Reflect::get(&data_response, &JsValue::from("userHandle")).unwrap();
let data_response_user_handle = if data_response_user_handle.is_undefined() {
None
} else {
Some(Uint8Array::new(data_response_user_handle).to_vec())
};
let data_response_client_data_json = Uint8Array::new(
&js_sys::Reflect::get(&data_response, &JsValue::from("clientDataJSON")).unwrap(),
)
.to_vec();
let data_raw_id_b64 = Base64UrlSafeData(data_raw_id);
let data_response_client_data_json_b64 = Base64UrlSafeData(data_response_client_data_json);
let data_response_authenticator_data_b64 =
Base64UrlSafeData(data_response_authenticator_data);
let data_response_signature_b64 = Base64UrlSafeData(data_response_signature);
let data_response_user_handle_b64 = data_response_user_handle.map(|d| Base64UrlSafeData(d));
PublicKeyCredential {
id: format!("{}", data_raw_id_b64),
raw_id: data_raw_id_b64,
type_: "public-key".to_string(),
response: AuthenticatorAssertionResponseRaw {
authenticator_data: data_response_authenticator_data_b64,
client_data_json: data_response_client_data_json_b64,
signature: data_response_signature_b64,
user_handle: data_response_user_handle_b64,
},
}
}
}
pub const TPM_GENERATED_VALUE: u32 = 0xff544347;
#[derive(Debug, PartialEq)]
#[repr(u16)]
pub enum TpmSt {
RspCommand = 0x00c4,
Null = 0x8000,
NoSessions = 0x8001,
Sessions = 0x8002,
ReservedA = 0x8003,
ReservedB = 0x8004,
AttestNV = 0x8014,
AttestCommandAudit = 0x8015,
AttestSessionAudit = 0x8016,
AttestCertify = 0x8017,
AttestQuote = 0x8018,
AttestTime = 0x8019,
AttestCreation = 0x801a,
ReservedC = 0x801b,
Creation = 0x8021,
Verified = 0x8022,
AuthSecret = 0x8023,
Hashcheck = 0x8024,
AuthSigned = 0x8025,
FUManifest = 0x8029,
}
impl TpmSt {
fn new(v: u16) -> Option<Self> {
match v {
0x00c4 => Some(TpmSt::RspCommand),
0x8000 => Some(TpmSt::Null),
0x8001 => Some(TpmSt::NoSessions),
0x8002 => Some(TpmSt::Sessions),
0x8003 => Some(TpmSt::ReservedA),
0x8004 => Some(TpmSt::ReservedB),
0x8014 => Some(TpmSt::AttestNV),
0x8015 => Some(TpmSt::AttestCommandAudit),
0x8016 => Some(TpmSt::AttestSessionAudit),
0x8017 => Some(TpmSt::AttestCertify),
0x8018 => Some(TpmSt::AttestQuote),
0x8019 => Some(TpmSt::AttestTime),
0x801a => Some(TpmSt::AttestCreation),
0x801b => Some(TpmSt::ReservedC),
0x8021 => Some(TpmSt::Creation),
0x8022 => Some(TpmSt::Verified),
0x8023 => Some(TpmSt::AuthSecret),
0x8024 => Some(TpmSt::Hashcheck),
0x8025 => Some(TpmSt::AuthSigned),
0x8029 => Some(TpmSt::FUManifest),
_ => None,
}
}
}
#[derive(Debug)]
pub struct TpmsClockInfo {
clock: u64,
reset_count: u32,
restart_count: u32,
safe: bool,
}
named!( tpmsclockinfo_parser<&[u8], TpmsClockInfo>,
do_parse!(
clock: u64!(nom::Endianness::Big) >>
reset_count: u32!(nom::Endianness::Big) >>
restart_count: u32!(nom::Endianness::Big) >>
safe: switch!(take!(1),
[0] => value!(false) |
[1] => value!(true)
) >>
(TpmsClockInfo {
clock, reset_count, restart_count, safe
})
)
);
#[derive(Debug)]
pub enum Tpm2bName {
None,
Handle(u32),
Digest(Vec<u8>),
}
#[derive(Debug)]
pub enum TpmuAttest {
AttestCertify(Tpm2bName, Tpm2bName),
Invalid,
}
#[derive(Debug)]
pub struct TpmsAttest {
pub magic: u32,
pub type_: TpmSt,
pub qualified_signer: Tpm2bName,
pub extra_data: Option<Vec<u8>>,
pub clock_info: TpmsClockInfo,
pub firmware_version: u64,
pub typeattested: TpmuAttest,
}
named!( tpm2b_name<&[u8], Tpm2bName>,
switch!(u16!(nom::Endianness::Big),
0 => value!(Tpm2bName::None) |
4 => map!(u32!(nom::Endianness::Big), Tpm2bName::Handle) |
size => map!(take!(size), |d| Tpm2bName::Digest(d.to_vec()))
)
);
named!( tpm2b_data<&[u8], Option<Vec<u8>>>,
switch!(u16!(nom::Endianness::Big),
0 => value!(None) |
size => map!(take!(size), |d| Some(d.to_vec()))
)
);
named! ( tpmuattestcertify<&[u8], TpmuAttest>,
do_parse!(
name: tpm2b_name >>
qualified_name: tpm2b_name >>
(
TpmuAttest::AttestCertify(name, qualified_name)
)
)
);
named!( tpmsattest_parser<&[u8], TpmsAttest>,
do_parse!(
magic: verify!(u32!(nom::Endianness::Big), |x| x == TPM_GENERATED_VALUE) >>
type_: map_opt!(u16!(nom::Endianness::Big), TpmSt::new) >>
qualified_signer: tpm2b_name >>
extra_data: tpm2b_data >>
clock_info: tpmsclockinfo_parser >>
firmware_version: u64!(nom::Endianness::Big) >>
typeattested: tpmuattestcertify >>
(TpmsAttest {
magic, type_, qualified_signer, extra_data, clock_info, firmware_version, typeattested
})
)
);
impl TryFrom<&[u8]> for TpmsAttest {
type Error = WebauthnError;
fn try_from(data: &[u8]) -> Result<TpmsAttest, WebauthnError> {
tpmsattest_parser(data)
.map_err(|e| {
log::debug!("{:?}", e);
WebauthnError::ParseNOMFailure
})
.map(|(_, v)| v)
}
}
#[derive(Debug, Clone, Copy)]
#[repr(u16)]
pub enum TpmAlgId {
Error = 0x0000,
Rsa = 0x0001,
Sha1 = 0x0004,
Hmac = 0x0005,
Aes = 0x0006,
Sha256 = 0x000B,
Sha384 = 0x000C,
Sha512 = 0x000D,
Null = 0x0010,
RsaSSA = 0x0014,
RsaPSS = 0x0016,
Ecdsa = 0x0018,
Ecdaa = 0x001A,
Ecc = 0x0023,
}
impl TpmAlgId {
fn new(v: u16) -> Option<Self> {
match v {
0x0000 => Some(TpmAlgId::Error),
0x0001 => Some(TpmAlgId::Rsa),
0x0004 => Some(TpmAlgId::Sha1),
0x0005 => Some(TpmAlgId::Hmac),
0x0006 => Some(TpmAlgId::Aes),
0x000B => Some(TpmAlgId::Sha256),
0x000C => Some(TpmAlgId::Sha384),
0x000D => Some(TpmAlgId::Sha512),
0x0010 => Some(TpmAlgId::Null),
0x0014 => Some(TpmAlgId::RsaSSA),
0x0016 => Some(TpmAlgId::RsaPSS),
0x0018 => Some(TpmAlgId::Ecdsa),
0x001A => Some(TpmAlgId::Ecdaa),
0x0023 => Some(TpmAlgId::Ecc),
_ => None,
}
}
}
#[derive(Debug)]
pub struct TpmtSymDefObject {
algorithm: TpmAlgId,
}
fn parse_tpmtsymdefobject(input: &[u8]) -> nom::IResult<&[u8], Option<TpmtSymDefObject>> {
let (data, algorithm) = map_opt!(input, u16!(nom::Endianness::Big), TpmAlgId::new)?;
match algorithm {
TpmAlgId::Null => Ok((data, None)),
_ => Err(nom::Err::Failure(nom::Context::Code(
input,
nom::ErrorKind::Custom(2),
))),
}
}
#[derive(Debug)]
pub struct TpmtRsaScheme {
algorithm: TpmAlgId,
}
fn parse_tpmtrsascheme(input: &[u8]) -> nom::IResult<&[u8], Option<TpmtRsaScheme>> {
let (data, algorithm) = map_opt!(input, u16!(nom::Endianness::Big), TpmAlgId::new)?;
match algorithm {
TpmAlgId::Null => Ok((data, None)),
_ => Err(nom::Err::Failure(nom::Context::Code(
input,
nom::ErrorKind::Custom(2),
))),
}
}
#[derive(Debug)]
pub struct TpmsRsaParms {
symmetric: Option<TpmtSymDefObject>,
scheme: Option<TpmtRsaScheme>,
keybits: u16,
pub exponent: u32,
}
named!( tpmsrsaparms_parser<&[u8], TpmsRsaParms>,
do_parse!(
symmetric: parse_tpmtsymdefobject >>
scheme: parse_tpmtrsascheme >>
keybits: u16!(nom::Endianness::Big) >>
exponent: u32!(nom::Endianness::Big) >>
(TpmsRsaParms {
symmetric, scheme, keybits, exponent
})
)
);
#[derive(Debug)]
pub enum TpmuPublicParms {
Rsa(TpmsRsaParms),
}
fn parse_tpmupublicparms(input: &[u8], alg: TpmAlgId) -> nom::IResult<&[u8], TpmuPublicParms> {
match alg {
TpmAlgId::Rsa => {
tpmsrsaparms_parser(input).map(|(data, inner)| (data, TpmuPublicParms::Rsa(inner)))
}
_ => Err(nom::Err::Failure(nom::Context::Code(
input,
nom::ErrorKind::Custom(2),
))),
}
}
#[derive(Debug)]
pub enum TpmuPublicId {
Rsa(Vec<u8>),
}
named!( tpmsrsapublickey_parser<&[u8], Vec<u8>>,
switch!(u16!(nom::Endianness::Big),
0 => value!(Vec::new()) |
size => map!(take!(size), |d| d.to_vec())
)
);
fn parse_tpmupublicid(input: &[u8], alg: TpmAlgId) -> nom::IResult<&[u8], TpmuPublicId> {
match alg {
TpmAlgId::Rsa => {
tpmsrsapublickey_parser(input).map(|(data, inner)| (data, TpmuPublicId::Rsa(inner)))
}
_ => Err(nom::Err::Failure(nom::Context::Code(
input,
nom::ErrorKind::Custom(2),
))),
}
}
#[derive(Debug)]
pub struct TpmtPublic {
pub type_: TpmAlgId,
pub name_alg: TpmAlgId,
pub object_attributes: u32,
pub auth_policy: Option<Vec<u8>>,
pub parameters: TpmuPublicParms,
pub unique: TpmuPublicId,
}
impl TryFrom<&[u8]> for TpmtPublic {
type Error = WebauthnError;
fn try_from(data: &[u8]) -> Result<TpmtPublic, WebauthnError> {
tpmtpublic_parser(data)
.map_err(|e| {
log::debug!("{:?}", e);
WebauthnError::ParseNOMFailure
})
.map(|(_, v)| v)
}
}
named!( tpm2b_digest<&[u8], Option<Vec<u8>>>,
switch!(u16!(nom::Endianness::Big),
0 => value!(None) |
size => map!(take!(size), |d| Some(d.to_vec()))
)
);
named!( tpmtpublic_parser<&[u8], TpmtPublic>,
do_parse!(
type_: map_opt!(u16!(nom::Endianness::Big), TpmAlgId::new) >>
name_alg: map_opt!(u16!(nom::Endianness::Big), TpmAlgId::new) >>
object_attributes: u32!(nom::Endianness::Big) >>
auth_policy: tpm2b_digest >>
parameters: call!(parse_tpmupublicparms, type_) >>
unique: call!(parse_tpmupublicid, type_) >>
(TpmtPublic {
type_, name_alg, object_attributes, auth_policy, parameters, unique
})
)
);
#[derive(Debug)]
pub enum TpmtSignature {
RawSignature(Vec<u8>),
}
impl TryFrom<&[u8]> for TpmtSignature {
type Error = WebauthnError;
fn try_from(data: &[u8]) -> Result<TpmtSignature, WebauthnError> {
tpmtsignature_parser(data)
.map_err(|e| {
log::debug!("{:?}", e);
WebauthnError::ParseNOMFailure
})
.map(|(_, v)| v)
}
}
fn tpmtsignature_parser(input: &[u8]) -> nom::IResult<&[u8], TpmtSignature> {
let (_data, algorithm) = map!(input, u16!(nom::Endianness::Big), TpmAlgId::new)?;
match algorithm {
None => Ok((&[], TpmtSignature::RawSignature(Vec::from(input)))),
_ => Err(nom::Err::Failure(nom::Context::Code(
input,
nom::ErrorKind::Custom(2),
))),
}
}
#[cfg(test)]
mod tests {
use super::{
AttestationObject, RegisterPublicKeyCredential, TpmsAttest, TpmtPublic, TpmtSignature,
TPM_GENERATED_VALUE,
};
use serde_json;
use std::convert::TryFrom;
#[test]
fn deserialise_register_response() {
let x = r#"
{ "id":"4oiUggKcrpRIlB-cFzFbfkx_BNeM7UAnz3wO7ZpT4I2GL_n-g8TICyJTHg11l0wyc-VkQUVnJ0yM08-1D5oXnw",
"rawId":"4oiUggKcrpRIlB-cFzFbfkx_BNeM7UAnz3wO7ZpT4I2GL_n-g8TICyJTHg11l0wyc-VkQUVnJ0yM08-1D5oXnw",
"response":{
"attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjEEsoXtJryKJQ28wPgFmAwoh5SXSZuIJJnQzgBqP1AcaBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQOKIlIICnK6USJQfnBcxW35MfwTXjO1AJ898Du2aU-CNhi_5_oPEyAsiUx4NdZdMMnPlZEFFZydMjNPPtQ-aF5-lAQIDJiABIVggFo08FM4Je1yfCSuPsxP6h0zvlJSjfocUk75EvXw2oSMiWCArRwLD8doar0bACWS1PgVJKzp_wStyvOkTd4NlWHW8rQ",
"clientDataJSON":"eyJjaGFsbGVuZ2UiOiJwZENXRDJWamRMSVkzN2VSYTVfazdhS3BqdkF2VmNOY04ycVozMjk0blpVIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovLzEyNy4wLjAuMTo4MDgwIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9"
},
"type":"public-key"
}
"#;
let _y: RegisterPublicKeyCredential = serde_json::from_str(x).unwrap();
}
#[test]
fn deserialise_attestation_object() {
let raw_ao = base64::decode(
"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjEEsoXtJryKJQ28wPgFmAwoh5SXSZuIJJnQzgBqP1AcaBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQCgxaVISCxE+DrcxP5/+aPM88CTI+04J+o61SK6mnepjGZYv062AbtydzWmbAxF00VSAyp0ImP94uoy+0y7w9yilAQIDJiABIVggGT9woA+UoX+jBxuiHQpdkm0kCVh75WTj3TXl4zLJuzoiWCBKiCneKgWJgWiwrZedNwl06GTaXyaGrYS4bPbBraInyg=="
).unwrap();
let _ao = AttestationObject::try_from(raw_ao.as_slice()).unwrap();
}
#[test]
fn deserialise_tpms_attest() {
let data: Vec<u8> = vec![
255, 84, 67, 71,
128, 23,
0, 34,
0, 11, 174, 74, 152, 70, 1, 87, 191, 156, 96, 74, 177, 221, 37, 132, 6, 8, 101, 35,
124, 216, 85, 173, 85, 195, 115, 137, 194, 247, 145, 61, 82, 40,
0, 20,
234, 98, 144, 49, 146, 39, 99, 47, 44, 82, 115, 48, 64, 40, 152, 224, 227, 42, 63,
133,
0, 0, 0, 2, 219, 215, 137, 38,
187, 106, 183, 8,
100, 145, 106, 200,
1,
86, 5, 220, 81, 118, 234, 131, 141,
0, 34,
0, 11, 239, 53, 112, 255, 253, 12, 189, 168, 16, 253, 10, 149, 108, 7, 31, 212, 143,
21, 153, 7, 7, 153, 99, 73, 205, 97, 90, 110, 182, 120, 4, 250, 0, 34, 0, 11, 249, 72,
224, 84, 16, 96, 147, 197, 167, 195, 110, 181, 77, 207, 147, 16, 34, 64, 139, 185, 120,
190, 196, 209, 213, 29, 1, 136, 76, 235, 223, 247,
];
let tpms_attest = TpmsAttest::try_from(data.as_slice()).unwrap();
println!("{:?}", tpms_attest);
assert!(tpms_attest.magic == TPM_GENERATED_VALUE);
}
#[test]
fn deserialise_tpmt_public() {
let data: Vec<u8> = vec![
0, 1, 0, 11, 0, 6, 4, 114, 0, 32, 157, 255, 203, 243, 108, 56, 58, 230, 153, 251, 152,
104, 220, 109, 203, 137, 215, 21, 56, 132, 190, 40, 3, 146, 44, 18, 65, 88, 191, 173,
34, 174, 0, 16, 0, 16, 8, 0, 0, 0, 0, 0, 1, 0, 220, 20, 243, 114, 251, 142, 90, 236,
17, 204, 181, 223, 8, 72, 230, 209, 122, 44, 90, 55, 96, 134, 69, 16, 125, 139, 112,
81, 154, 230, 133, 211, 129, 37, 75, 208, 222, 70, 210, 239, 209, 188, 152, 93, 222,
222, 154, 169, 217, 160, 90, 243, 135, 151, 25, 87, 240, 178, 106, 119, 150, 89, 23,
223, 158, 88, 107, 72, 101, 61, 184, 132, 19, 110, 144, 107, 22, 178, 252, 206, 50,
207, 11, 177, 137, 35, 139, 68, 212, 148, 121, 249, 50, 35, 89, 52, 47, 26, 23, 6, 15,
115, 155, 127, 59, 168, 208, 196, 78, 125, 205, 0, 98, 43, 223, 233, 65, 137, 103, 2,
227, 35, 81, 107, 247, 230, 186, 111, 27, 4, 57, 42, 220, 32, 29, 181, 159, 6, 176,
182, 94, 191, 222, 212, 235, 60, 101, 83, 86, 217, 203, 151, 251, 254, 219, 204, 195,
10, 74, 147, 5, 27, 167, 127, 117, 149, 245, 157, 92, 124, 2, 196, 214, 107, 246, 228,
171, 229, 100, 212, 67, 88, 215, 75, 33, 183, 199, 51, 171, 210, 213, 65, 45, 96, 96,
226, 29, 130, 254, 58, 92, 252, 133, 207, 105, 63, 156, 208, 149, 142, 9, 83, 1, 193,
217, 244, 35, 137, 43, 138, 137, 140, 82, 231, 195, 145, 213, 230, 185, 245, 104, 105,
62, 142, 124, 34, 9, 157, 167, 188, 243, 112, 104, 248, 63, 50, 19, 53, 173, 69, 12,
39, 252, 9, 69, 223,
];
let tpmt_public = TpmtPublic::try_from(data.as_slice()).unwrap();
println!("{:?}", tpmt_public);
}
#[test]
fn deserialise_tpmt_signature() {
let data: Vec<u8> = vec![
5, 3, 162, 216, 151, 57, 210, 103, 145, 121, 161, 186, 63, 232, 221, 255, 89, 37, 17,
59, 155, 241, 77, 30, 35, 201, 30, 140, 84, 214, 250, 185, 47, 248, 58, 89, 177, 187,
231, 202, 220, 45, 167, 126, 243, 194, 94, 33, 39, 205, 163, 51, 40, 171, 35, 118, 196,
244, 247, 143, 166, 193, 223, 94, 244, 157, 121, 220, 22, 94, 163, 15, 151, 223, 214,
131, 105, 202, 40, 16, 176, 11, 154, 102, 100, 212, 174, 103, 166, 92, 90, 154, 224,
20, 165, 106, 127, 53, 91, 230, 217, 199, 172, 195, 203, 242, 41, 158, 64, 252, 65, 9,
155, 160, 63, 40, 94, 94, 64, 145, 173, 71, 85, 173, 2, 199, 18, 148, 88, 223, 93, 154,
203, 197, 170, 142, 35, 249, 146, 107, 146, 2, 14, 54, 39, 151, 181, 10, 176, 216, 117,
25, 196, 2, 205, 159, 140, 155, 56, 89, 87, 31, 135, 93, 97, 78, 95, 176, 228, 72, 237,
130, 171, 23, 66, 232, 35, 115, 218, 105, 168, 6, 253, 121, 161, 129, 44, 78, 252, 44,
11, 23, 172, 66, 37, 214, 113, 128, 28, 33, 209, 66, 34, 32, 196, 153, 80, 87, 243,
162, 7, 25, 62, 252, 243, 174, 31, 168, 98, 123, 100, 2, 143, 134, 36, 154, 236, 18,
128, 175, 185, 189, 177, 51, 53, 216, 190, 43, 63, 35, 84, 14, 64, 249, 23, 9, 125,
147, 160, 176, 137, 30, 174, 245, 148, 189,
];
let tpmt_sig = TpmtSignature::try_from(data.as_slice()).unwrap();
println!("{:?}", tpmt_sig);
}
}