Skip to content

Commit 0e13155

Browse files
Updated throws to specify types when possible
1 parent 53f1469 commit 0e13155

7 files changed

+38
-34
lines changed

Diff for: Sources/WebAuthn/Ceremonies/Shared/AuthenticatorData.swift

+19-17
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ struct AuthenticatorData: Equatable, Sendable {
2727
}
2828

2929
extension AuthenticatorData {
30-
init(bytes: [UInt8]) throws {
30+
init(bytes: [UInt8]) throws(WebAuthnError) {
3131
let minAuthDataLength = 37
3232
guard bytes.count >= minAuthDataLength else {
33-
throw WebAuthnError.authDataTooShort
33+
throw .authDataTooShort
3434
}
3535

3636
let relyingPartyIDHash = Array(bytes[..<32])
@@ -44,29 +44,29 @@ extension AuthenticatorData {
4444
if flags.attestedCredentialData {
4545
let minAttestedAuthLength = 37 + AAGUID.size + 2
4646
guard bytes.count > minAttestedAuthLength else {
47-
throw WebAuthnError.attestedCredentialDataMissing
47+
throw .attestedCredentialDataMissing
4848
}
4949
let (data, length) = try Self.parseAttestedData(bytes)
5050
attestedCredentialData = data
5151
remainingCount -= length
5252
// For assertion signatures, the AT flag MUST NOT be set and the attestedCredentialData MUST NOT be included.
5353
} else {
5454
if !flags.extensionDataIncluded && bytes.count != minAuthDataLength {
55-
throw WebAuthnError.attestedCredentialFlagNotSet
55+
throw .attestedCredentialFlagNotSet
5656
}
5757
}
5858

5959
var extensionData: [UInt8]?
6060
if flags.extensionDataIncluded {
6161
guard remainingCount != 0 else {
62-
throw WebAuthnError.extensionDataMissing
62+
throw .extensionDataMissing
6363
}
6464
extensionData = Array(bytes[(bytes.count - remainingCount)...])
6565
remainingCount -= extensionData!.count
6666
}
6767

6868
guard remainingCount == 0 else {
69-
throw WebAuthnError.leftOverBytesInAuthenticatorData
69+
throw .leftOverBytesInAuthenticatorData
7070
}
7171

7272
self.relyingPartyIDHash = relyingPartyIDHash
@@ -81,31 +81,33 @@ extension AuthenticatorData {
8181
///
8282
/// This is assumed to take place after the first 37 bytes of `data`, which is always of fixed size.
8383
/// - SeeAlso: [WebAuthn Level 3 Editor's Draft §6.5.1. Attested Credential Data]( https://w3c.github.io/webauthn/#sctn-attested-credential-data)
84-
private static func parseAttestedData(_ data: [UInt8]) throws -> (AttestedCredentialData, Int) {
84+
private static func parseAttestedData(_ data: [UInt8]) throws(WebAuthnError) -> (AttestedCredentialData, Int) {
8585
/// **aaguid** (16): The AAGUID of the authenticator.
8686
guard let aaguid = AAGUID(bytes: data[37..<(37 + AAGUID.size)]) // Bytes [37-52]
87-
else { throw WebAuthnError.attestedCredentialDataMissing }
87+
else { throw .attestedCredentialDataMissing }
8888

8989
/// **credentialIdLength** (2): Byte length L of credentialId, 16-bit unsigned big-endian integer. Value MUST be ≤ 1023.
9090
let idLengthBytes = data[53..<55] // Length is 2 bytes
9191
let idLengthData = Data(idLengthBytes)
9292
let idLength = UInt16(bigEndianBytes: idLengthData)
9393

9494
guard idLength <= 1023
95-
else { throw WebAuthnError.credentialIDTooLong }
95+
else { throw .credentialIDTooLong }
9696

9797
let credentialIDEndIndex = Int(idLength) + 55
9898
guard data.count >= credentialIDEndIndex
99-
else { throw WebAuthnError.credentialIDTooShort }
99+
else { throw .credentialIDTooShort }
100100

101101
/// **credentialId** (L): Credential ID
102102
let credentialID = data[55..<credentialIDEndIndex]
103103

104104
/// **credentialPublicKey** (variable): The credential public key encoded in `COSE_Key` format, as defined in [Section 7](https://tools.ietf.org/html/rfc9052#section-7) of [RFC9052], using the CTAP2 canonical CBOR encoding form.
105-
/// Assuming valid CBOR, verify the public key's length by decoding the next CBOR item.
105+
/// Assuming valid CBOR, verify the public key's length by decoding the next CBOR item, and checking how much data is left on the stream.
106106
let inputStream = ByteInputStream(data[credentialIDEndIndex...])
107-
let decoder = CBORDecoder(stream: inputStream, options: CBOROptions(maximumDepth: 16))
108-
_ = try decoder.decodeItem()
107+
do {
108+
let decoder = CBORDecoder(stream: inputStream, options: CBOROptions(maximumDepth: 16))
109+
_ = try decoder.decodeItem()
110+
} catch { throw .invalidPublicKeyLength }
109111
let publicKeyBytes = data[credentialIDEndIndex..<(data.count - inputStream.remainingBytes)]
110112

111113
let data = AttestedCredentialData(
@@ -132,13 +134,13 @@ class ByteInputStream: CBORInputStream {
132134
/// The remaining bytes in the original data buffer.
133135
var remainingBytes: Int { slice.count }
134136

135-
func popByte() throws -> UInt8 {
136-
if slice.count < 1 { throw CBORError.unfinishedSequence }
137+
func popByte() throws(CBORError) -> UInt8 {
138+
if slice.count < 1 { throw .unfinishedSequence }
137139
return slice.removeFirst()
138140
}
139141

140-
func popBytes(_ n: Int) throws -> ArraySlice<UInt8> {
141-
if slice.count < n { throw CBORError.unfinishedSequence }
142+
func popBytes(_ n: Int) throws(CBORError) -> ArraySlice<UInt8> {
143+
if slice.count < n { throw .unfinishedSequence }
142144
let result = slice.prefix(n)
143145
slice = slice.dropFirst(n)
144146
return result

Diff for: Sources/WebAuthn/Ceremonies/Shared/CollectedClientData.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ public struct CollectedClientData: Codable, Hashable, Sendable {
3434
public let challenge: URLEncodedBase64
3535
public let origin: String
3636

37-
func verify(storedChallenge: [UInt8], ceremonyType: CeremonyType, relyingPartyOrigin: String) throws {
38-
guard type == ceremonyType else { throw CollectedClientDataVerifyError.ceremonyTypeDoesNotMatch }
37+
func verify(storedChallenge: [UInt8], ceremonyType: CeremonyType, relyingPartyOrigin: String) throws(CollectedClientDataVerifyError) {
38+
guard type == ceremonyType else { throw .ceremonyTypeDoesNotMatch }
3939
guard challenge == storedChallenge.base64URLEncodedString() else {
40-
throw CollectedClientDataVerifyError.challengeDoesNotMatch
40+
throw .challengeDoesNotMatch
4141
}
42-
guard origin == relyingPartyOrigin else { throw CollectedClientDataVerifyError.originDoesNotMatch }
42+
guard origin == relyingPartyOrigin else { throw .originDoesNotMatch }
4343
}
4444
}

Diff for: Sources/WebAuthn/Ceremonies/Shared/CredentialPublicKey.swift

+10-10
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ struct EC2PublicKey: PublicKey, Sendable {
108108
self.yCoordinate = yCoordinate
109109
}
110110

111-
init(publicKeyObject: CBOR, algorithm: COSEAlgorithmIdentifier) throws {
111+
init(publicKeyObject: CBOR, algorithm: COSEAlgorithmIdentifier) throws(WebAuthnError) {
112112
self.algorithm = algorithm
113113

114114
// Curve is key -1 - or -0 for SwiftCBOR
@@ -117,18 +117,18 @@ struct EC2PublicKey: PublicKey, Sendable {
117117
guard let curveRaw = publicKeyObject[COSEKey.crv.cbor],
118118
case let .unsignedInt(curve) = curveRaw,
119119
let coseCurve = COSECurve(rawValue: curve) else {
120-
throw WebAuthnError.invalidCurve
120+
throw .invalidCurve
121121
}
122122
self.curve = coseCurve
123123

124124
guard let xCoordRaw = publicKeyObject[COSEKey.x.cbor],
125125
case let .byteString(xCoordinateBytes) = xCoordRaw else {
126-
throw WebAuthnError.invalidXCoordinate
126+
throw .invalidXCoordinate
127127
}
128128
xCoordinate = xCoordinateBytes
129129
guard let yCoordRaw = publicKeyObject[COSEKey.y.cbor],
130130
case let .byteString(yCoordinateBytes) = yCoordRaw else {
131-
throw WebAuthnError.invalidYCoordinate
131+
throw .invalidYCoordinate
132132
}
133133
yCoordinate = yCoordinateBytes
134134
}
@@ -167,18 +167,18 @@ struct RSAPublicKeyData: PublicKey, Sendable {
167167

168168
var rawRepresentation: [UInt8] { n + e }
169169

170-
init(publicKeyObject: CBOR, algorithm: COSEAlgorithmIdentifier) throws {
170+
init(publicKeyObject: CBOR, algorithm: COSEAlgorithmIdentifier) throws(WebAuthnError) {
171171
self.algorithm = algorithm
172172

173173
guard let nRaw = publicKeyObject[COSEKey.n.cbor],
174174
case let .byteString(nBytes) = nRaw else {
175-
throw WebAuthnError.invalidModulus
175+
throw .invalidModulus
176176
}
177177
n = nBytes
178178

179179
guard let eRaw = publicKeyObject[COSEKey.e.cbor],
180180
case let .byteString(eBytes) = eRaw else {
181-
throw WebAuthnError.invalidExponent
181+
throw .invalidExponent
182182
}
183183
e = eBytes
184184
}
@@ -213,17 +213,17 @@ struct OKPPublicKey: PublicKey, Sendable {
213213
let curve: UInt64
214214
let xCoordinate: [UInt8]
215215

216-
init(publicKeyObject: CBOR, algorithm: COSEAlgorithmIdentifier) throws {
216+
init(publicKeyObject: CBOR, algorithm: COSEAlgorithmIdentifier) throws(WebAuthnError) {
217217
self.algorithm = algorithm
218218
// Curve is key -1, or NegativeInt 0 for SwiftCBOR
219219
guard let curveRaw = publicKeyObject[.negativeInt(0)], case let .unsignedInt(curve) = curveRaw else {
220-
throw WebAuthnError.invalidCurve
220+
throw .invalidCurve
221221
}
222222
self.curve = curve
223223
// X Coordinate is key -2, or NegativeInt 1 for SwiftCBOR
224224
guard let xCoordRaw = publicKeyObject[.negativeInt(1)],
225225
case let .byteString(xCoordinateBytes) = xCoordRaw else {
226-
throw WebAuthnError.invalidXCoordinate
226+
throw .invalidXCoordinate
227227
}
228228
xCoordinate = xCoordinateBytes
229229
}

Diff for: Sources/WebAuthn/WebAuthnError.swift

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public struct WebAuthnError: Error, Hashable, Sendable {
5151
case leftOverBytesInAuthenticatorData
5252
case credentialIDTooLong
5353
case credentialIDTooShort
54+
case invalidPublicKeyLength
5455

5556
// MARK: CredentialPublicKey
5657
case badPublicKeyBytes
@@ -110,6 +111,7 @@ public struct WebAuthnError: Error, Hashable, Sendable {
110111
public static let leftOverBytesInAuthenticatorData = Self(reason: .leftOverBytesInAuthenticatorData)
111112
public static let credentialIDTooLong = Self(reason: .credentialIDTooLong)
112113
public static let credentialIDTooShort = Self(reason: .credentialIDTooShort)
114+
public static let invalidPublicKeyLength = Self(reason: .invalidPublicKeyLength)
113115

114116
// MARK: CredentialPublicKey
115117
public static let badPublicKeyBytes = Self(reason: .badPublicKeyBytes)

Diff for: Sources/WebAuthn/WebAuthnManager.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public struct WebAuthnManager: Sendable {
139139
timeout: Duration? = .seconds(60),
140140
allowCredentials: [PublicKeyCredentialDescriptor]? = nil,
141141
userVerification: UserVerificationRequirement = .preferred
142-
) throws -> PublicKeyCredentialRequestOptions {
142+
) -> PublicKeyCredentialRequestOptions {
143143
let challenge = challengeGenerator.generate()
144144

145145
return PublicKeyCredentialRequestOptions(

Diff for: Tests/WebAuthnTests/WebAuthnManagerAuthenticationTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ final class WebAuthnManagerAuthenticationTests: XCTestCase {
3535

3636
func testBeginAuthentication() async throws {
3737
let allowCredentials: [PublicKeyCredentialDescriptor] = [.init(type: .publicKey, id: [1, 0, 2, 30])]
38-
let options = try webAuthnManager.beginAuthentication(
38+
let options = webAuthnManager.beginAuthentication(
3939
timeout: .seconds(1234),
4040
allowCredentials: allowCredentials,
4141
userVerification: .preferred

Diff for: Tests/WebAuthnTests/WebAuthnManagerIntegrationTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ final class WebAuthnManagerIntegrationTests: XCTestCase {
9999
id: [UInt8](URLEncodedBase64(credential.id).urlDecoded.decoded!)
100100
)]
101101

102-
let authenticationOptions = try webAuthnManager.beginAuthentication(
102+
let authenticationOptions = webAuthnManager.beginAuthentication(
103103
timeout: authenticationTimeout,
104104
allowCredentials: rememberedCredentials,
105105
userVerification: userVerification

0 commit comments

Comments
 (0)