Skip to content

Commit 3cec1dc

Browse files
authored
Merge pull request #15 from swift-server/refactor3
Add initial set of tests
2 parents d86bda1 + 23b366e commit 3cec1dc

12 files changed

+386
-123
lines changed

.gitignore

Lines changed: 4 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,6 @@
1-
# Xcode
2-
#
3-
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4-
5-
## User settings
6-
xcuserdata/
7-
8-
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9-
*.xcscmblueprint
10-
*.xccheckout
11-
12-
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13-
build/
14-
DerivedData/
15-
*.moved-aside
16-
*.pbxuser
17-
!default.pbxuser
18-
*.mode1v3
19-
!default.mode1v3
20-
*.mode2v3
21-
!default.mode2v3
22-
*.perspectivev3
23-
!default.perspectivev3
24-
25-
## Obj-C/Swift specific
26-
*.hmap
27-
28-
## App packaging
29-
*.ipa
30-
*.dSYM.zip
31-
*.dSYM
32-
33-
## Playgrounds
34-
timeline.xctimeline
35-
playground.xcworkspace
36-
37-
# Swift Package Manager
38-
#
39-
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40-
# Packages/
41-
# Package.pins
421
Package.resolved
43-
# *.xcodeproj
44-
#
45-
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46-
# hence it is not needed unless you have added a package configuration file to your project
47-
# .swiftpm
48-
492
.build/
50-
51-
# CocoaPods
52-
#
53-
# We recommend against adding the Pods directory to your .gitignore. However
54-
# you should judge for yourself, the pros and cons are mentioned at:
55-
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56-
#
57-
# Pods/
58-
#
59-
# Add this line if you want to avoid checking in source code from the Xcode workspace
60-
# *.xcworkspace
61-
62-
# Carthage
63-
#
64-
# Add this line if you want to avoid checking in source code from Carthage dependencies.
65-
# Carthage/Checkouts
66-
67-
Carthage/Build/
68-
69-
# Accio dependency management
70-
Dependencies/
71-
.accio/
72-
73-
# fastlane
74-
#
75-
# It is recommended to not store the screenshots in the git repo.
76-
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
77-
# For more information about the recommended setup visit:
78-
# https://docs.fastlane.tools/best-practices/source-control/#source-control
79-
80-
fastlane/report.xml
81-
fastlane/Preview.html
82-
fastlane/screenshots/**/*.png
83-
fastlane/test_output
84-
85-
# Code Injection
86-
#
87-
# After new code Injection tools there's a generated folder /iOSInjectionProject
88-
# https://github.com/johnno1962/injectionforxcode
89-
90-
iOSInjectionProject/
3+
*.xcodeproj
4+
DerivedData
5+
.DS_Store
6+
.swiftpm/

Sources/WebAuthn/Ceremonies/Registration/AttestationFormat.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
public enum AttestationFormat: String, RawRepresentable {
15+
public enum AttestationFormat: String, RawRepresentable, Equatable {
1616
case packed
1717
case tpm
1818
case androidKey = "android-key"

Sources/WebAuthn/Ceremonies/Registration/AttestationObject.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import Crypto
1616
import SwiftCBOR
1717

1818
/// Contains the cryptographic attestation that a new key pair was created by that authenticator.
19-
public struct AttestationObject {
19+
public struct AttestationObject: Equatable {
2020
let authenticatorData: AuthenticatorData
2121
let rawAuthenticatorData: [UInt8]
2222
let format: AttestationFormat
@@ -43,7 +43,7 @@ public struct AttestationObject {
4343
case .none:
4444
// if format is `none` statement must be empty
4545
guard attestationStatement == .map([:]) else {
46-
throw WebAuthnError.attestationStatementMissing
46+
throw WebAuthnError.attestationStatementMustBeEmpty
4747
}
4848
default:
4949
throw WebAuthnError.attestationVerificationNotSupported

Sources/WebAuthn/Ceremonies/Registration/AttestedCredentialData.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
// Contains the new public key created by the authenticator.
16-
struct AttestedCredentialData: Codable {
16+
struct AttestedCredentialData: Codable, Equatable {
1717
let aaguid: [UInt8]
1818
let credentialID: [UInt8]
1919
let publicKey: [UInt8]

Sources/WebAuthn/Ceremonies/Registration/AuthenticatorAttestationResponse.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import SwiftCBOR
1818
/// The response from the authenticator device for the creation of a new public key credential.
1919
public struct AuthenticatorAttestationResponse: Codable {
2020
public let clientDataJSON: URLEncodedBase64
21-
public let attestationObject: String
21+
public let attestationObject: URLEncodedBase64
2222
}
2323

2424
/// A parsed version of `AuthenticatorAttestationResponse`
@@ -35,9 +35,9 @@ struct ParsedAuthenticatorAttestationResponse {
3535
self.clientData = clientData
3636

3737
// Step 11. (assembling attestationObject)
38-
guard let attestationData = rawResponse.attestationObject.base64URLDecodedData,
39-
let decodedAttestationObject = try CBOR.decode([UInt8](attestationData)) else {
40-
throw WebAuthnError.invalidAttestationData
38+
guard let attestationObjectData = rawResponse.attestationObject.base64URLDecodedData,
39+
let decodedAttestationObject = try CBOR.decode([UInt8](attestationObjectData)) else {
40+
throw WebAuthnError.invalidAttestationObject
4141
}
4242

4343
guard let authData = decodedAttestationObject["authData"],
@@ -51,7 +51,7 @@ struct ParsedAuthenticatorAttestationResponse {
5151
}
5252

5353
guard let attestationStatement = decodedAttestationObject["attStmt"] else {
54-
throw WebAuthnError.invalidAttStmt
54+
throw WebAuthnError.missingAttStmt
5555
}
5656

5757
attestationObject = AttestationObject(

Sources/WebAuthn/Ceremonies/Registration/RegistrationCredential.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ struct ParsedCredentialCreationResponse {
8888

8989
// Step 23.
9090
guard rawID.count <= 1023 else {
91-
throw WebAuthnError.credentialRawIDTooBig
91+
throw WebAuthnError.credentialRawIDTooLong
9292
}
9393
}
9494
}

Sources/WebAuthn/Ceremonies/Shared/AuthenticatorData.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Crypto
1717

1818
/// Data created and/ or used by the authenticator during authentication/ registration.
1919
/// The data contains, for example, whether a user was present or verified.
20-
struct AuthenticatorData {
20+
struct AuthenticatorData: Equatable {
2121
let relyingPartyIDHash: [UInt8]
2222
let flags: AuthenticatorFlags
2323
let counter: UInt32
@@ -89,6 +89,9 @@ extension AuthenticatorData {
8989
let idLength: UInt16 = idLengthData.toInteger(endian: .big)
9090
let credentialIDEndIndex = Int(idLength) + 55
9191

92+
guard data.count >= credentialIDEndIndex else {
93+
throw WebAuthnError.credentialIDTooShort
94+
}
9295
let credentialID = data[55..<credentialIDEndIndex]
9396
let publicKeyBytes = data[credentialIDEndIndex...]
9497

Sources/WebAuthn/Ceremonies/Shared/AuthenticatorFlags.swift

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

15-
struct AuthenticatorFlags {
15+
struct AuthenticatorFlags: Equatable {
1616

1717
/**
1818
Taken from https://w3c.github.io/webauthn/#sctn-authenticator-data
@@ -40,6 +40,12 @@ struct AuthenticatorFlags {
4040
let attestedCredentialData: Bool
4141
let extensionDataIncluded: Bool
4242

43+
static func isFlagSet(on byte: UInt8, at position: Bit) -> Bool {
44+
(byte & (1 << position.rawValue)) != 0
45+
}
46+
}
47+
48+
extension AuthenticatorFlags {
4349
init(_ byte: UInt8) {
4450
userPresent = Self.isFlagSet(on: byte, at: .userPresent)
4551
userVerified = Self.isFlagSet(on: byte, at: .userVerified)
@@ -48,8 +54,4 @@ struct AuthenticatorFlags {
4854
attestedCredentialData = Self.isFlagSet(on: byte, at: .attestedCredentialDataIncluded)
4955
extensionDataIncluded = Self.isFlagSet(on: byte, at: .extensionDataIncluded)
5056
}
51-
52-
static func isFlagSet(on byte: UInt8, at position: Bit) -> Bool {
53-
(byte & (1 << position.rawValue)) != 0
54-
}
5557
}

Sources/WebAuthn/WebAuthnError.swift

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

15-
public enum WebAuthnError: Error {
15+
public enum WebAuthnError: Error, Equatable {
1616
// MARK: Shared
1717
case invalidClientDataJSON
1818
case attestedCredentialDataMissing
@@ -22,7 +22,7 @@ public enum WebAuthnError: Error {
2222

2323
// MARK: AttestationObject
2424
case userVerificationRequiredButFlagNotSet
25-
case attestationStatementMissing
25+
case attestationStatementMustBeEmpty
2626
case attestationVerificationNotSupported
2727

2828
// MARK: WebAuthnManager
@@ -36,22 +36,23 @@ public enum WebAuthnError: Error {
3636
case invalidAssertionCredentialType
3737

3838
// MARK: ParsedAuthenticatorAttestationResponse
39-
case invalidAttestationData
39+
case invalidAttestationObject
4040
case invalidAuthData
4141
case invalidFmt
42-
case invalidAttStmt
42+
case missingAttStmt
4343
case attestationFormatNotSupported
4444

4545
// MARK: ParsedCredentialCreationResponse
4646
case invalidRawID
4747
case invalidCredentialCreationType
48-
case credentialRawIDTooBig
48+
case credentialRawIDTooLong
4949

5050
// MARK: AuthenticatorData
5151
case authDataTooShort
5252
case attestedCredentialFlagNotSet
5353
case extensionDataMissing
5454
case leftOverBytesInAuthenticatorData
55+
case credentialIDTooShort
5556

5657
// MARK: CredentialPublicKey
5758
case badPublicKeyBytes
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import Foundation
2+
3+
extension String {
4+
/// Create `[UInt8]` from hexadecimal string representation
5+
var hexadecimal: [UInt8]? {
6+
let hex = self
7+
guard hex.count.isMultiple(of: 2) else {
8+
return nil
9+
}
10+
11+
let chars = hex.map { $0 }
12+
let bytes = stride(from: 0, to: chars.count, by: 2)
13+
.map { String(chars[$0]) + String(chars[$0 + 1]) }
14+
.compactMap { UInt8($0, radix: 16) }
15+
16+
guard hex.count / bytes.count == 2 else { return nil }
17+
18+
return bytes
19+
}
20+
}
21+
22+
extension Data {
23+
var hexadecimal: String {
24+
return map { String(format: "%02x", $0) }
25+
.joined()
26+
}
27+
}

0 commit comments

Comments
 (0)