Skip to content

Commit dc23dae

Browse files
authored
Split extensions into unsigned extensions and signed extensions inside AuthenticatorData (#99)
- `impl Serialize for AttestedCredentialData` and `impl<T> Serialize for AuthenticatorData<T>` actually doesn't work the way it was intended, because it needs to be wrapped inside some other data type, that we don't have. So I moved that code to a stand-alone function that returns the `Vec<u8>` directly. - Some things are not yet implemented, like appid, largeblob-write.
1 parent b017fd5 commit dc23dae

File tree

8 files changed

+249
-195
lines changed

8 files changed

+249
-195
lines changed

libwebauthn/examples/prf_test.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use tokio::sync::mpsc::Receiver;
1313
use tracing_subscriber::{self, EnvFilter};
1414

1515
use libwebauthn::ops::webauthn::{
16-
GetAssertionHmacOrPrfInput, GetAssertionHmacOrPrfOutput, GetAssertionRequest,
17-
GetAssertionRequestExtensions, PRFValue, UserVerificationRequirement,
16+
GetAssertionHmacOrPrfInput, GetAssertionRequest, GetAssertionRequestExtensions, PRFValue,
17+
UserVerificationRequirement,
1818
};
1919
use libwebauthn::pin::PinRequestReason;
2020
use libwebauthn::proto::ctap2::{Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialType};
@@ -178,15 +178,15 @@ async fn run_success_test(
178178
println!(
179179
"{num}. result of {printoutput}: {:?}",
180180
assertion
181-
.authenticator_data
182-
.extensions
181+
.unsigned_extensions_output
183182
.as_ref()
184-
.map(|e| match &e.hmac_or_prf {
185-
GetAssertionHmacOrPrfOutput::None => String::from("ERROR: No PRF output"),
186-
GetAssertionHmacOrPrfOutput::HmacGetSecret(..) =>
187-
String::from("ERROR: Got HMAC instead of PRF output"),
188-
GetAssertionHmacOrPrfOutput::Prf { enabled: _, result } =>
189-
hex::encode(result.first),
183+
.map(|e| if let Some(prf) = &e.prf {
184+
let results = prf.results.as_ref().map(|r| hex::encode(r.first)).unwrap();
185+
format!("Found PRF results: {}", results)
186+
} else if e.hmac_get_secret.is_some() {
187+
String::from("ERROR: Got HMAC instead of PRF output")
188+
} else {
189+
String::from("ERROR: No PRF output")
190190
})
191191
.unwrap_or(String::from("ERROR: No extensions returned"))
192192
);

libwebauthn/src/fido.rs

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,22 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
22
use cosey::PublicKey;
33
use serde::{
44
de::{DeserializeOwned, Error as DesError, Visitor},
5-
ser::Error as SerError,
6-
Deserialize, Deserializer, Serialize, Serializer,
5+
Deserialize, Deserializer, Serialize,
76
};
87
use serde_bytes::ByteBuf;
98
use std::{
109
fmt,
1110
io::{Cursor, Read},
1211
marker::PhantomData,
1312
};
14-
use tracing::warn;
15-
16-
use crate::proto::{
17-
ctap2::{Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialType},
18-
CtapError,
13+
use tracing::{error, warn};
14+
15+
use crate::{
16+
proto::{
17+
ctap2::{Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialType},
18+
CtapError,
19+
},
20+
webauthn::{Error, PlatformError},
1921
};
2022

2123
#[derive(Debug, PartialEq, Eq)]
@@ -62,28 +64,6 @@ pub struct AttestedCredentialData {
6264
pub credential_public_key: PublicKey,
6365
}
6466

65-
impl Serialize for AttestedCredentialData {
66-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
67-
where
68-
S: Serializer,
69-
{
70-
// Name | Length
71-
// --------------------------------
72-
// aaguid | 16
73-
// credentialIdLenght | 2
74-
// credentialId | L
75-
// credentialPublicKey | variable
76-
let mut res = self.aaguid.to_vec();
77-
res.write_u16::<BigEndian>(self.credential_id.len() as u16)
78-
.map_err(SerError::custom)?;
79-
res.extend(&self.credential_id);
80-
let cose_encoded_public_key =
81-
serde_cbor::to_vec(&self.credential_public_key).map_err(SerError::custom)?;
82-
res.extend(cose_encoded_public_key);
83-
serializer.serialize_bytes(&res)
84-
}
85-
}
86-
8767
impl From<&AttestedCredentialData> for Ctap2PublicKeyCredentialDescriptor {
8868
fn from(data: &AttestedCredentialData) -> Self {
8969
Self {
@@ -103,14 +83,11 @@ pub struct AuthenticatorData<T> {
10383
pub extensions: Option<T>,
10484
}
10585

106-
impl<T> Serialize for AuthenticatorData<T>
86+
impl<T> AuthenticatorData<T>
10787
where
10888
T: Clone + Serialize,
10989
{
110-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
111-
where
112-
S: Serializer,
113-
{
90+
pub fn to_response_bytes(&self) -> Result<Vec<u8>, Error> {
11491
// Name | Length
11592
// -----------------------------------
11693
// rpIdHash | 32
@@ -121,14 +98,46 @@ where
12198
let mut res = self.rp_id_hash.to_vec();
12299
res.push(self.flags.bits());
123100
res.write_u32::<BigEndian>(self.signature_count)
124-
.map_err(SerError::custom)?;
101+
.map_err(|e| {
102+
error!("Failed to create AuthenticatorData output vec at signature_count: {e:?}");
103+
Error::Platform(PlatformError::InvalidDeviceResponse)
104+
})?;
105+
125106
if let Some(att_data) = &self.attested_credential {
126-
res.extend(serde_cbor::to_vec(att_data).map_err(SerError::custom)?);
107+
// Name | Length
108+
// --------------------------------
109+
// aaguid | 16
110+
// credentialIdLenght | 2
111+
// credentialId | L
112+
// credentialPublicKey | variable
113+
res.extend(att_data.aaguid);
114+
res.write_u16::<BigEndian>(att_data.credential_id.len() as u16)
115+
.map_err(|e| {
116+
error!(
117+
"Failed to create AuthenticatorData output vec at attested_credential.credential_id: {e:?}"
118+
);
119+
Error::Platform(PlatformError::InvalidDeviceResponse)
120+
})?;
121+
res.extend(&att_data.credential_id);
122+
let cose_encoded_public_key =
123+
serde_cbor::to_vec(&att_data.credential_public_key)
124+
.map_err(|e| {
125+
error!(
126+
"Failed to create AuthenticatorData output vec at attested_credential.credential_public_key: {e:?}"
127+
);
128+
Error::Platform(PlatformError::InvalidDeviceResponse)
129+
})?;
130+
res.extend(cose_encoded_public_key);
127131
}
128-
if let Some(extensions) = &self.extensions {
129-
res.extend(serde_cbor::to_vec(extensions).map_err(SerError::custom)?);
132+
133+
if self.extensions.is_some() || self.flags.contains(AuthenticatorDataFlags::EXTENSION_DATA)
134+
{
135+
res.extend(serde_cbor::to_vec(&self.extensions).map_err(|e| {
136+
error!("Failed to create AuthenticatorData output vec at extensions: {e:?}");
137+
Error::Platform(PlatformError::InvalidDeviceResponse)
138+
})?);
130139
}
131-
serializer.serialize_bytes(&res)
140+
Ok(res)
132141
}
133142
}
134143

libwebauthn/src/ops/u2f.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ impl UpgradableResponse<MakeCredentialResponse, MakeCredentialRequest> for Regis
148148
attestation_statement,
149149
enterprise_attestation: None,
150150
large_blob_key: None,
151-
unsigned_extension_output: None,
152151
};
153152
Ok(resp.into_make_credential_output(request, None))
154153
}
@@ -195,7 +194,6 @@ impl UpgradableResponse<GetAssertionResponse, SignRequest> for SignResponse {
195194
credentials_count: None,
196195
user_selected: None,
197196
large_blob_key: None,
198-
unsigned_extension_outputs: None,
199197
enterprise_attestation: None,
200198
attestation_statement: None,
201199
};

0 commit comments

Comments
 (0)