Skip to content

Commit dfa1875

Browse files
caBLE progress: Working make credential and get assertion with iOS
1 parent e27d33f commit dfa1875

File tree

10 files changed

+464
-145
lines changed

10 files changed

+464
-145
lines changed

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libwebauthn/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ tungstenite = { version = "0.20.1" }
6060
tokio-tungstenite = { version = "0.20.1", features = [
6161
"rustls-tls-native-roots",
6262
] }
63+
tokio-stream = "0.1.4"
6364
snow = { path = "../snow", features = ["p256"] }
6465

6566
[dev-dependencies]

libwebauthn/examples/webauthn_cable.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,8 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
3939
Box::new(EphemeralDeviceInfoStore::default());
4040

4141
// Create QR code
42-
let mut device = CableQrCodeDevice::new_persistent(
43-
QrCodeOperationHint::MakeCredential,
44-
&mut device_info_store,
45-
);
42+
let mut device: CableQrCodeDevice<'_> =
43+
CableQrCodeDevice::new_transient(QrCodeOperationHint::MakeCredential);
4644

4745
println!("Created QR code, awaiting for advertisement.");
4846
let qr_code = QrCode::new(device.qr_code.to_string()).unwrap();
@@ -105,6 +103,23 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
105103
timeout: TIMEOUT,
106104
};
107105

106+
// Create QR code
107+
let mut device: CableQrCodeDevice<'_> =
108+
CableQrCodeDevice::new_transient(QrCodeOperationHint::GetAssertionRequest);
109+
110+
println!("Created QR code, awaiting for advertisement.");
111+
let qr_code = QrCode::new(device.qr_code.to_string()).unwrap();
112+
let image = qr_code
113+
.render::<unicode::Dense1x2>()
114+
.dark_color(unicode::Dense1x2::Light)
115+
.light_color(unicode::Dense1x2::Dark)
116+
.build();
117+
println!("{}", image);
118+
119+
// Connect to a known device
120+
let mut channel: CableChannel = device.channel().await.unwrap();
121+
println!("Tunnel established {:?}", channel);
122+
108123
let response = loop {
109124
match channel
110125
.webauthn_get_assertion(&get_assertion, &pin_provider)

libwebauthn/src/ops/u2f.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::time::Duration;
22

33
use byteorder::{BigEndian, WriteBytesExt};
4+
use ctap_types::cose;
45
use serde_bytes::ByteBuf;
56
use serde_cbor::to_vec;
67
use sha2::{Digest, Sha256};
78
use tracing::{error, trace};
89
use x509_parser::nom::AsBytes;
9-
use ctap_types::cose;
1010

1111
use super::webauthn::MakeCredentialRequest;
1212
use crate::ops::webauthn::{GetAssertionResponse, MakeCredentialResponse};
@@ -70,14 +70,14 @@ impl UpgradableResponse<MakeCredentialResponse, MakeCredentialRequest> for Regis
7070
.expect("Not the identity point")
7171
.as_bytes(),
7272
)
73-
.unwrap();
73+
.unwrap();
7474
let y: heapless::Vec<u8, 32> = heapless::Vec::from_slice(
7575
encoded_point
7676
.y()
7777
.expect("Not identity nor compressed")
7878
.as_bytes(),
7979
)
80-
.unwrap();
80+
.unwrap();
8181
let cose_public_key = cose::PublicKey::P256Key(cose::P256PublicKey {
8282
x: x.into(),
8383
y: y.into(),
@@ -148,6 +148,9 @@ impl UpgradableResponse<MakeCredentialResponse, MakeCredentialRequest> for Regis
148148
format: String::from("fido-u2f"),
149149
authenticator_data: ByteBuf::from(auth_data),
150150
attestation_statement: attestation_statement,
151+
enterprise_attestation: None,
152+
large_blob_key: None,
153+
unsigned_extension_output: None,
151154
})
152155
}
153156
}
@@ -161,7 +164,7 @@ impl UpgradableResponse<GetAssertionResponse, SignRequest> for SignResponse {
161164
// See also Authenticator Data section of [WebAuthn].
162165
let mut flags: u8 = 0;
163166
flags |= 0b00000001; // up always set
164-
// bit 1 is unused, ignoring
167+
// bit 1 is unused, ignoring
165168

166169
// Let signCount be a 4-byte unsigned integer initialized with CTAP1/U2F response counter field.
167170
let sign_count = self.counter;
@@ -189,8 +192,12 @@ impl UpgradableResponse<GetAssertionResponse, SignRequest> for SignResponse {
189192
user: None,
190193
credentials_count: None,
191194
user_selected: None,
195+
large_blob_key: None,
196+
unsigned_extension_outputs: None,
197+
enterprise_attestation: None,
198+
attestation_statement: None,
192199
}
193-
.into();
200+
.into();
194201

195202
trace!(?upgraded_response);
196203
Ok(upgraded_response)

libwebauthn/src/proto/ctap2/cbor/request.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::proto::ctap2::model::Ctap2CommandCode;
99
use crate::proto::ctap2::model::Ctap2GetAssertionRequest;
1010
use crate::proto::ctap2::model::Ctap2MakeCredentialRequest;
1111

12-
#[derive(Debug)]
12+
#[derive(Debug, Clone)]
1313
pub struct CborRequest {
1414
pub command: Ctap2CommandCode,
1515
pub encoded_data: Vec<u8>,

libwebauthn/src/proto/ctap2/cbor/response.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@ use std::convert::{TryFrom, TryInto};
44
use std::io::{Error as IOError, ErrorKind as IOErrorKind};
55
use tracing::error;
66

7-
#[derive(Debug)]
7+
#[derive(Debug, Clone)]
88
pub struct CborResponse {
99
pub status_code: CtapError,
1010
pub data: Option<Vec<u8>>,
1111
}
1212

13+
impl CborResponse {
14+
pub fn new_success_from_slice(slice: &[u8]) -> Self {
15+
Self {
16+
status_code: CtapError::Ok,
17+
data: Some(slice.to_vec()),
18+
}
19+
}
20+
}
21+
1322
impl TryFrom<&Vec<u8>> for CborResponse {
1423
type Error = IOError;
1524
fn try_from(packet: &Vec<u8>) -> Result<Self, Self::Error> {

libwebauthn/src/proto/ctap2/model.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
use std::collections::BTreeMap;
12
use std::collections::HashMap;
23
use std::convert::TryFrom;
34
use std::io::Cursor as IOCursor;
45

56
use byteorder::{BigEndian, ReadBytesExt};
7+
use ctap_types::ctap2::make_credential::AttestationStatement;
8+
use futures::future::Map;
69
use num_enum::{IntoPrimitive, TryFromPrimitive};
710
use serde_bytes::ByteBuf;
11+
use serde_cbor::Value;
812
use serde_derive::{Deserialize, Serialize};
913
use serde_indexed::{DeserializeIndexed, SerializeIndexed};
1014
use serde_repr::{Deserialize_repr, Serialize_repr};
@@ -279,12 +283,20 @@ pub struct TpmAttestationStmt {
279283
pub public_area: ByteBuf,
280284
}
281285

286+
#[derive(Debug, Clone, Deserialize)]
287+
pub struct AppleAnonymousAttestationStmt {
288+
#[serde(rename = "x5c")]
289+
pub certificates: Vec<ByteBuf>,
290+
}
291+
282292
#[derive(Debug, Clone, Deserialize)]
283293
#[serde(untagged)]
284294
pub enum Ctap2AttestationStatement {
285295
PackedOrAndroid(PackedAttestationStmt),
286296
Tpm(TpmAttestationStmt),
287297
FidoU2F(FidoU2fAttestationStmt),
298+
AppleAnonymous(AppleAnonymousAttestationStmt),
299+
None(BTreeMap<Value, Value>),
288300
}
289301

290302
// https://www.w3.org/TR/webauthn/#authenticatormakecredential
@@ -368,6 +380,15 @@ pub struct Ctap2MakeCredentialResponse {
368380
pub format: String,
369381
pub authenticator_data: ByteBuf,
370382
pub attestation_statement: Ctap2AttestationStatement,
383+
384+
#[serde(skip_serializing_if = "Option::is_none")]
385+
pub enterprise_attestation: Option<bool>,
386+
387+
#[serde(skip_serializing_if = "Option::is_none")]
388+
pub large_blob_key: Option<ByteBuf>,
389+
390+
#[serde(skip_serializing_if = "Option::is_none")]
391+
pub unsigned_extension_output: Option<BTreeMap<Value, Value>>,
371392
}
372393

373394
// https://www.w3.org/TR/webauthn/#op-get-assertion
@@ -435,6 +456,18 @@ pub struct Ctap2GetAssertionResponse {
435456
#[serde(default)]
436457
#[serde(skip_serializing_if = "Option::is_none")]
437458
pub user_selected: Option<bool>,
459+
#[serde(default)]
460+
#[serde(skip_serializing_if = "Option::is_none")]
461+
pub large_blob_key: Option<ByteBuf>,
462+
#[serde(default)]
463+
#[serde(skip_serializing_if = "Option::is_none")]
464+
pub unsigned_extension_outputs: Option<BTreeMap<Value, Value>>,
465+
#[serde(default)]
466+
#[serde(skip_serializing_if = "Option::is_none")]
467+
pub enterprise_attestation: Option<bool>,
468+
#[serde(default)]
469+
#[serde(skip_serializing_if = "Option::is_none")]
470+
pub attestation_statement: Option<Ctap2AttestationStatement>,
438471
}
439472

440473
pub trait Ctap2UserVerifiableRequest {

0 commit comments

Comments
 (0)