forked from parallaxsecond/rust-tss-esapi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
web_authn.rs
147 lines (136 loc) · 5.09 KB
/
web_authn.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use crate::{
handles::KeyHandle,
interface_types::session_handles::AuthSession,
structures::{
Attest, Data, EccScheme, PcrSelectionList, Public, RsaScheme, Signature, SignatureScheme,
},
traits::Marshall,
Context, Error, Result,
WrapperErrorKind::InternalError,
};
use ciborium::{cbor, value::Value};
use picky_asn1_x509::SubjectPublicKeyInfo;
use serde_bytes::Bytes;
use sha2::{Digest, Sha256};
use std::convert::{TryFrom, TryInto};
/// WebAuthn TPM Attestation Statement
///
/// Represents a key attestation token as described in [section 8.3](https://www.w3.org/TR/webauthn-2/#sctn-tpm-attestation)
/// of the WebAuthn spec. The token can be (de)serialized to the format described in the spec.
#[derive(Debug)]
pub struct TpmStatement {
attestation: Attest,
signature: Signature,
public_area: Public,
ak_kid: Vec<u8>,
}
impl TpmStatement {
/// Generate a new attestation token for the attested key
pub fn new(
context: &mut Context,
attested_key: KeyHandle,
attesting_key: KeyHandle,
nonce: Data,
) -> Result<TpmStatement> {
// Get the signing scheme of the attesting key
let (attesting_key_public_area, _, _) = context.read_public(attesting_key)?;
let signing_scheme = get_sig_scheme(&attesting_key_public_area)?;
let ak_kid = get_kid(attesting_key_public_area)?;
// Generate the TPM-native attestation token
let (attestation, signature) = context.execute_with_sessions(
(
Some(AuthSession::Password),
Some(AuthSession::Password),
None,
),
|ctx| ctx.certify(attested_key.into(), attesting_key, nonce, signing_scheme),
)?;
// Get public metadata of attested key (`pubArea` in WebAuthn spec)
let (attested_key_public_area, _, _) = context.read_public(attested_key)?;
Ok(TpmStatement {
attestation,
signature,
public_area: attested_key_public_area,
ak_kid,
})
}
/// Encodes the token in the format defined by the spec
pub fn encode(&self) -> Result<Value> {
let sig = self.signature.clone().marshall()?;
let pub_area = self.public_area.clone().marshall()?;
let cert_info = self.attestation.clone().marshall()?;
cbor!({
"tpmVer" => "2.0",
"sig" => &Bytes::new(&sig[..]),
"kid" => &Bytes::new(&self.ak_kid[..]),
"pubArea" => &Bytes::new(&pub_area[..]),
"certInfo" => &Bytes::new(&cert_info[..]),
})
.or(Err(Error::local_error(InternalError)))
}
}
#[derive(Debug)]
pub struct TpmPlatStmt {
attestation: Attest,
signature: Signature,
ak_kid: Vec<u8>,
}
impl TpmPlatStmt {
pub fn new(
context: &mut Context,
key: KeyHandle,
nonce: Vec<u8>,
selection_list: PcrSelectionList,
) -> Result<TpmPlatStmt> {
let (key_public_area, _, _) = context.read_public(key)?;
let signing_scheme = get_sig_scheme(&key_public_area)?;
let ak_kid = get_kid(key_public_area)?;
let (attestation, signature) = context
.execute_with_session(Some(AuthSession::Password), |ctx| {
ctx.quote(key, nonce.try_into()?, signing_scheme, selection_list)
})?;
Ok(TpmPlatStmt {
attestation,
signature,
ak_kid,
})
}
pub fn encode(&self) -> Result<Value> {
let sig = self.signature.clone().marshall()?;
let attest_info = self.attestation.clone().marshall()?;
cbor!({
"tpmVer" => "2.0",
"sig" => &Bytes::new(&sig[..]),
"kid" => &Bytes::new(&self.ak_kid[..]),
"attestInfo" => &Bytes::new(&attest_info[..]),
})
.or(Err(Error::local_error(InternalError)))
}
}
fn get_sig_scheme(public_area: &Public) -> Result<SignatureScheme> {
match public_area {
Public::Rsa { parameters, .. } => match parameters.rsa_scheme() {
RsaScheme::RsaSsa(hash_scheme) => Ok(SignatureScheme::RsaSsa { hash_scheme }),
RsaScheme::RsaPss(hash_scheme) => Ok(SignatureScheme::RsaPss { hash_scheme }),
_ => Err(Error::local_error(InternalError)),
},
Public::Ecc { parameters, .. } => match parameters.ecc_scheme() {
EccScheme::EcDsa(hash_scheme) => Ok(SignatureScheme::EcDsa { hash_scheme }),
_ => Err(Error::local_error(InternalError)),
},
_ => Err(Error::local_error(InternalError)),
}
}
fn get_kid(public_area: Public) -> Result<Vec<u8>> {
let subject_public_key_info = SubjectPublicKeyInfo::try_from(public_area)?;
let encoded_key = picky_asn1_der::to_vec(&subject_public_key_info)
.map_err(|_| Error::WrapperError(crate::WrapperErrorKind::InvalidParam))?;
// Create key ID
let mut hasher = Sha256::new();
hasher.update(&encoded_key);
hasher.update(b"parsec-aik");
// Mark ID as "random" (starting with 0x01 tag)
let mut key_id = vec![0x01_u8];
key_id.append(&mut hasher.finalize().to_vec());
Ok(key_id)
}