-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added an AAGUID type to represent authenticator attestations
- Loading branch information
1 parent
996e101
commit ecfe814
Showing
7 changed files
with
151 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
Sources/WebAuthn/Ceremonies/Shared/AuthenticatorAttestationGloballyUniqueID.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the WebAuthn Swift open source project | ||
// | ||
// Copyright (c) 2024 the WebAuthn Swift project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of WebAuthn Swift project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Foundation | ||
|
||
/// A globally unique ID identifying an authenticator. | ||
/// | ||
/// Each authenticator has an Authenticator Attestation Globally Unique Identifier or **AAGUID**, which is a 128-bit identifier indicating the type (e.g. make and model) of the authenticator. The AAGUID MUST be chosen by its maker to be identical across all substantially identical authenticators made by that maker, and different (with high probability) from the AAGUIDs of all other types of authenticators. The AAGUID for a given type of authenticator SHOULD be randomly generated to ensure this. | ||
/// | ||
/// The Relying Party MAY use the AAGUID to infer certain properties of the authenticator, such as certification level and strength of key protection, using information from other sources. The Relying Party MAY use the AAGUID to attempt to identify the maker of the authenticator without requesting and verifying attestation, but the AAGUID is not provably authentic without attestation. | ||
/// - SeeAlso: [WebAuthn Leven 3 Editor's Draft §6. WebAuthn Authenticator Model](https://w3c.github.io/webauthn/#aaguid) | ||
public struct AuthenticatorAttestationGloballyUniqueID: Hashable, Sendable { | ||
/// The underlying UUID for the authenticator. | ||
public let id: UUID | ||
|
||
/// Initialize an AAGUID with a UUID. | ||
@inlinable | ||
public init(uuid: UUID) { | ||
self.id = uuid | ||
} | ||
|
||
/// Initialize an AAGUID with a byte sequence. | ||
/// | ||
/// This sequence must be of length ``AuthenticatorAttestationGloballyUniqueID/size``. | ||
@inlinable | ||
public init?(bytes: some BidirectionalCollection<UInt8>) { | ||
let uuidSize = MemoryLayout<uuid_t>.size | ||
assert(uuidSize == Self.size, "Size of uuid_t (\(uuidSize)) does not match Self.size (\(Self.size))!") | ||
guard bytes.count == uuidSize else { return nil } | ||
self.init(uuid: UUID(uuid: bytes.casting())) | ||
} | ||
|
||
/// Initialize an AAGUID with a string-based UUID. | ||
@inlinable | ||
public init?(uuidString: String) { | ||
guard let uuid = UUID(uuidString: uuidString) | ||
else { return nil } | ||
|
||
self.init(uuid: uuid) | ||
} | ||
|
||
/// Access the AAGUID as an encoded byte sequence. | ||
@inlinable | ||
public var bytes: [UInt8] { withUnsafeBytes(of: id) { Array($0) } } | ||
|
||
/// The identifier of an anonymized authenticator, set to a byte sequence of 16 zeros. | ||
public static let anonymous = AuthenticatorAttestationGloballyUniqueID(bytes: Array(repeating: 0, count: 16))! | ||
|
||
/// The byte length of an encoded identifer. | ||
public static let size: Int = 16 | ||
} | ||
|
||
/// A shorthand for an ``AuthenticatorAttestationGloballyUniqueID`` | ||
public typealias AAGUID = AuthenticatorAttestationGloballyUniqueID | ||
|
||
extension AuthenticatorAttestationGloballyUniqueID: Codable { | ||
public init(from decoder: Decoder) throws { | ||
let container = try decoder.singleValueContainer() | ||
id = try container.decode(UUID.self) | ||
} | ||
|
||
public func encode(to encoder: Encoder) throws { | ||
var container = encoder.singleValueContainer() | ||
try container.encode(id) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the WebAuthn Swift open source project | ||
// | ||
// Copyright (c) 2024 the WebAuthn Swift project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of WebAuthn Swift project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Foundation | ||
|
||
extension BidirectionalCollection where Element == UInt8 { | ||
/// Cast a byte sequence into a trivial type like a primitive or a tuple of primitives. | ||
/// | ||
/// - Note: It is up to the caller to verify the receiver's size before casting it. | ||
@inlinable | ||
func casting<R>() -> R { | ||
precondition(self.count == MemoryLayout<R>.size, "self.count (\(self.count)) does not match MemoryLayout<R>.size (\(MemoryLayout<R>.size))") | ||
|
||
let result = self.withContiguousStorageIfAvailable({ | ||
$0.withUnsafeBytes { $0.loadUnaligned(as: R.self) } | ||
}) ?? Array(self).withUnsafeBytes { | ||
$0.loadUnaligned(as: R.self) | ||
} | ||
|
||
return result | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
Tests/WebAuthnTests/AuthenticatorAttestationGloballyUniqueIDTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the WebAuthn Swift open source project | ||
// | ||
// Copyright (c) 2024 the WebAuthn Swift project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of WebAuthn Swift project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import XCTest | ||
@testable import WebAuthn | ||
|
||
final class AuthenticatorAttestationGloballyUniqueIDTests: XCTestCase { | ||
func testByteCoding() throws { | ||
let aaguid = AuthenticatorAttestationGloballyUniqueID(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) | ||
XCTAssertNotNil(aaguid) | ||
XCTAssertEqual(aaguid?.bytes, [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f]) | ||
XCTAssertEqual(aaguid?.id, UUID(uuid: (0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f))) | ||
XCTAssertEqual(aaguid, AuthenticatorAttestationGloballyUniqueID(uuid: UUID(uuid: (0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f)))) | ||
XCTAssertEqual(aaguid, AuthenticatorAttestationGloballyUniqueID(uuidString: "00010203-0405-0607-0809-0A0B0C0D0E0F" )) | ||
} | ||
|
||
func testInvalidByteDecoding() throws { | ||
XCTAssertNil(AuthenticatorAttestationGloballyUniqueID(bytes: [])) | ||
XCTAssertNil(AuthenticatorAttestationGloballyUniqueID(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])) | ||
XCTAssertNil(AuthenticatorAttestationGloballyUniqueID(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters