Skip to content

Commit ef0d83c

Browse files
committed
reimplement authentication methods
1 parent 28fcd0d commit ef0d83c

16 files changed

+409
-192
lines changed

Sources/WebAuthn/Authenticator/AttestationObject/AuthenticatorData.swift

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
import Foundation
1516
import Crypto
1617

1718
/// AuthenticatorData From §6.1 of the spec.
@@ -35,3 +36,82 @@ struct AuthenticatorData {
3536
let attestedData: AttestedCredentialData?
3637
let extData: [UInt8]?
3738
}
39+
40+
extension AuthenticatorData {
41+
init(bytes: Data) throws {
42+
let minAuthDataLength = 37
43+
guard bytes.count >= minAuthDataLength else {
44+
throw WebAuthnError.authDataTooShort
45+
}
46+
47+
let relyingPartyIDHash = Array(bytes[..<32])
48+
let flags = AuthenticatorFlags(bytes[32])
49+
let counter: UInt32 = Data(bytes[33..<37]).toInteger(endian: .big)
50+
51+
var remainingCount = bytes.count - minAuthDataLength
52+
53+
var attestedCredentialData: AttestedCredentialData?
54+
// For attestation signatures, the authenticator MUST set the AT flag and include the attestedCredentialData.
55+
if flags.attestedCredentialData {
56+
let minAttestedAuthLength = 55
57+
guard bytes.count > minAttestedAuthLength else {
58+
throw WebAuthnError.attestedCredentialDataMissing
59+
}
60+
let (data, length) = try Self.parseAttestedData(bytes)
61+
attestedCredentialData = data
62+
remainingCount -= length
63+
// For assertion signatures, the AT flag MUST NOT be set and the attestedCredentialData MUST NOT be included.
64+
} else {
65+
if !flags.extensionDataIncluded && bytes.count != minAuthDataLength {
66+
throw WebAuthnError.attestedCredentialFlagNotSet
67+
}
68+
}
69+
70+
var extensionData: [UInt8]?
71+
if flags.extensionDataIncluded {
72+
guard remainingCount != 0 else {
73+
throw WebAuthnError.extensionDataMissing
74+
}
75+
extensionData = Array(bytes[(bytes.count - remainingCount)...])
76+
remainingCount -= extensionData!.count
77+
}
78+
79+
guard remainingCount == 0 else {
80+
throw WebAuthnError.leftOverBytes
81+
}
82+
83+
self.relyingPartyIDHash = relyingPartyIDHash
84+
self.flags = flags
85+
self.counter = counter
86+
self.attestedData = attestedCredentialData
87+
self.extData = extensionData
88+
89+
}
90+
91+
/// Returns: Attested credentials data and the length
92+
private static func parseAttestedData(_ data: Data) throws -> (AttestedCredentialData, Int) {
93+
// We've parsed the first 37 bytes so far, the next bytes now should be the attested credential data
94+
// See https://w3c.github.io/webauthn/#sctn-attested-credential-data
95+
let aaguidLength = 16
96+
let aaguid = data[37..<(37 + aaguidLength)] // To byte at index 52
97+
98+
let idLengthBytes = data[53..<55] // Length is 2 bytes
99+
let idLengthData = Data(idLengthBytes)
100+
let idLength: UInt16 = idLengthData.toInteger(endian: .big)
101+
let credentialIDEndIndex = Int(idLength) + 55
102+
103+
let credentialID = data[55..<credentialIDEndIndex]
104+
let publicKeyBytes = data[credentialIDEndIndex...]
105+
106+
let data = AttestedCredentialData(
107+
aaguid: Array(aaguid),
108+
credentialID: Array(credentialID),
109+
publicKey: Array(publicKeyBytes)
110+
)
111+
112+
// 2 is the bytes storing the size of the credential ID
113+
let length = data.aaguid.count + 2 + data.credentialID.count + data.publicKey.count
114+
115+
return (data, length)
116+
}
117+
}

Sources/WebAuthn/Authenticator/AuthenticatorAttestationResponse.swift

Lines changed: 0 additions & 150 deletions
This file was deleted.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the WebAuthn Swift open source project
4+
//
5+
// Copyright (c) 2022 the WebAuthn Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of WebAuthn Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
/// From §5.2.2
16+
/// The AuthenticatorAssertionResponse interface represents an authenticator's response to a client’s request for
17+
/// generation of a new authentication assertion given the WebAuthn Relying Party's challenge and OPTIONAL list of
18+
/// credentials it is aware of. This response contains a cryptographic signature proving possession of the credential
19+
/// private key, and optionally evidence of user consent to a specific transaction.
20+
public struct AuthenticatorAssertionResponse: AuthenticatorResponse, Codable {
21+
public let clientDataJSON: URLEncodedBase64
22+
/// Contains the authenticator data returned by the authenticator.
23+
public let authenticatorData: URLEncodedBase64
24+
/// Contains the raw signature returned from the authenticator
25+
public let signature: String
26+
/// Contains the user handle returned from the authenticator, or null if the authenticator did not return
27+
/// a user handle.
28+
public let userHandle: String?
29+
/// Contains an attestation object, if the authenticator supports attestation in assertions.
30+
/// The attestation object, if present, includes an attestation statement. Unlike the attestationObject
31+
/// in an AuthenticatorAttestationResponse, it does not contain an authData key because the authenticator
32+
/// data is provided directly in an AuthenticatorAssertionResponse structure.
33+
public let attestationObject: String?
34+
}
35+
36+
public struct ParsedAuthenticatorAssertionResponse {
37+
38+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the WebAuthn Swift open source project
4+
//
5+
// Copyright (c) 2022 the WebAuthn Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of WebAuthn Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Foundation
16+
import SwiftCBOR
17+
18+
/// From $ 5.2.1 (https://w3c.github.io/webauthn/#authenticatorattestationresponse)
19+
/// The unprocessed response from the authenticator for the creation of a new public key credential.
20+
/// It contains information about the new credential that can be used to identify it for later use, and
21+
/// metadata that can be used by the WebAuthn Relying Party to assess the characteristics of the credential during
22+
/// registration.
23+
public struct AuthenticatorAttestationResponse: AuthenticatorResponse, Codable {
24+
public let clientDataJSON: URLEncodedBase64
25+
public let attestationObject: String
26+
}
27+
28+
struct ParsedAuthenticatorAttestationResponse {
29+
let clientData: CollectedClientData
30+
let attestationObject: AttestationObject
31+
32+
init(from rawResponse: AuthenticatorAttestationResponse) throws {
33+
// assembling clientData
34+
guard let clientDataJSONData = rawResponse.clientDataJSON.base64URLDecodedData else {
35+
throw WebAuthnError.invalidClientDataJSON
36+
}
37+
let clientData = try JSONDecoder().decode(CollectedClientData.self, from: clientDataJSONData)
38+
self.clientData = clientData
39+
40+
// Step 11. (assembling attestationObject)
41+
guard let attestationData = rawResponse.attestationObject.base64URLDecodedData,
42+
let decodedAttestationObject = try CBOR.decode([UInt8](attestationData)) else {
43+
throw WebAuthnError.cborDecodingAttestationDataFailed
44+
}
45+
46+
guard let authData = decodedAttestationObject["authData"], case let .byteString(authDataBytes) = authData else {
47+
throw WebAuthnError.authDataInvalidOrMissing
48+
}
49+
guard let formatCBOR = decodedAttestationObject["fmt"], case let .utf8String(format) = formatCBOR else {
50+
throw WebAuthnError.formatError
51+
}
52+
53+
guard let attestationStatement = decodedAttestationObject["attStmt"] else {
54+
throw WebAuthnError.missingAttestationFormat
55+
}
56+
57+
guard let attestationFormat = AttestationFormat(rawValue: format) else {
58+
throw WebAuthnError.unsupportedAttestationFormat
59+
}
60+
61+
attestationObject = AttestationObject(
62+
authenticatorData: try AuthenticatorData(bytes: Data(authDataBytes)),
63+
rawAuthenticatorData: authDataBytes,
64+
format: attestationFormat,
65+
attestationStatement: attestationStatement
66+
)
67+
}
68+
69+
private static func parseAttestationStatement(format: AttestationFormat, statement: CBOR) throws {
70+
71+
}
72+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the WebAuthn Swift open source project
4+
//
5+
// Copyright (c) 2022 the WebAuthn Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of WebAuthn Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
/// From §5.2 (https://w3c.github.io/webauthn/#iface-authenticatorresponse)
16+
/// Authenticators respond to Relying Party requests by returning an object derived from the
17+
/// AuthenticatorResponse interface
18+
public protocol AuthenticatorResponse {
19+
var clientDataJSON: URLEncodedBase64 { get }
20+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the WebAuthn Swift open source project
4+
//
5+
// Copyright (c) 2022 the WebAuthn Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of WebAuthn Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
public enum AuthenticatorTransport: String, Codable {
16+
case usb
17+
case nfc
18+
case ble
19+
case hybrid
20+
case `internal`
21+
}

0 commit comments

Comments
 (0)