This repository has been archived by the owner on Dec 15, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 135
/
Copy pathU2FRegistration.swift
135 lines (108 loc) · 3.95 KB
/
U2FRegistration.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//
// U2FRegistration.swift
// SoftU2F
//
// Created by Benjamin P Toews on 1/30/17.
//
import Foundation
class U2FRegistration {
// Allow using separate keychain namespace for tests.
static var namespace = "SoftU2F Security Key"
static var all: [U2FRegistration] {
let kps = KeyPair.all(label: namespace)
var regs: [U2FRegistration] = []
kps.forEach { kp in
guard let reg = U2FRegistration(keyPair: kp) else {
print("Error initializing U2FRegistration")
return
}
regs.append(reg)
}
return regs
}
// The number of key pairs (keys/2) in the keychain.
static var count: Int? {
return KeyPair.count(label: namespace)
}
// Fix up legacy keychain items.
static func repair() {
KeyPair.repair(label: namespace)
let legacyCounterSize = MemoryLayout<UInt32>.size
let appTagSize = Int(U2F_APPID_SIZE)
var maxCtr = Counter.current ?? 0
for kp in KeyPair.all(label: namespace) {
guard let appTag = kp.applicationTag else { continue }
switch appTag.count {
case appTagSize:
continue
case legacyCounterSize + appTagSize:
// Find the maximum legacy counter.
let ctr = appTag.withUnsafeBytes { (ptr:UnsafePointer<UInt32>) -> UInt32 in
return ptr.pointee.bigEndian
}
if ctr > maxCtr {
maxCtr = ctr
}
// remove legacy counter from the application tag.
kp.applicationTag = appTag.subdata(in: legacyCounterSize..<(legacyCounterSize + appTagSize))
default:
print("bad applicationTag size")
continue
}
}
// Use the highest per-registration counter value plus one as our global
// counter value.
if maxCtr > 0 {
Counter.current = maxCtr + 1
}
}
// Delete all SoftU2F keys from keychain.
static func deleteAll() -> Bool {
return KeyPair.delete(label: namespace)
}
let keyPair: KeyPair
let applicationParameter: Data
// Key handle is application label plus 50 bytes of padding. Conformance
// tests require key handle to be >64 bytes.
var keyHandle: Data {
return padKeyHandle(keyPair.applicationLabel)
}
var inSEP: Bool {
return keyPair.inSEP
}
// Generate a new registration.
init?(applicationParameter ap: Data, inSEP sep: Bool) {
// TODO Specify applicationTag during creation. Alternatively, detect if
// setting tag fails.
guard let kp = KeyPair(label: U2FRegistration.namespace, inSEP: sep) else { return nil }
kp.applicationTag = ap
applicationParameter = ap
keyPair = kp
}
// Find a registration with the given key handle.
init?(keyHandle kh: Data, applicationParameter ap: Data) {
let appLabel = unpadKeyHandle(kh)
let kf = KnownFacets[ap] ?? "site"
let prompt = "authenticate with \(kf)"
guard let kp = KeyPair(label: U2FRegistration.namespace, appLabel: appLabel, signPrompt: prompt) else { return nil }
keyPair = kp
// Read our application parameter from the keychain and make sure it
// matches.
guard let appTag = keyPair.applicationTag else { return nil }
applicationParameter = appTag
if applicationParameter != ap {
print("Bad applicationParameter")
return nil
}
}
// Initialize a registration with all the necessary data.
init?(keyPair kp: KeyPair) {
keyPair = kp
guard let appTag = keyPair.applicationTag else { return nil }
applicationParameter = appTag
}
// Sign some data with the private key and increment our counter.
func sign(_ data: Data) -> Data? {
return keyPair.sign(data)
}
}