Skip to content
This repository was archived by the owner on Dec 15, 2020. It is now read-only.

Commit 256ef14

Browse files
authored
Merge pull request #29 from github/sep
Optionally store keys in SEP
2 parents ed0ab09 + 5babfb7 commit 256ef14

10 files changed

Lines changed: 251 additions & 200 deletions

File tree

SoftU2F.xcodeproj/project.pbxproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@
8585
F738F5871E4A3C09005680A2 /* DataReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5851E4A3C09005680A2 /* DataReaderTests.swift */; };
8686
F738F5881E4A3C09005680A2 /* DataWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5861E4A3C09005680A2 /* DataWriterTests.swift */; };
8787
F738F58A1E4A3C21005680A2 /* TestUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5891E4A3C21005680A2 /* TestUtil.swift */; };
88-
F78BEF5F1F3E654A0005B3D5 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78BEF5E1F3E654A0005B3D5 /* Settings.swift */; };
89-
F78BEF611F3E65510005B3D5 /* CLI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78BEF601F3E65510005B3D5 /* CLI.swift */; };
88+
F7713A5F1F477BA90036A0D5 /* CLI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7713A5D1F477BA90036A0D5 /* CLI.swift */; };
89+
F7713A601F477BA90036A0D5 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7713A5E1F477BA90036A0D5 /* Settings.swift */; };
9090
F7ABD9BE1E80603D00768FEC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7ABD9BD1E80603D00768FEC /* Assets.xcassets */; };
9191
F7B5DBAD1E4A5CED00E5ABD4 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAC1E4A5CED00E5ABD4 /* Command.swift */; };
9292
F7B5DBAF1E4A815700E5ABD4 /* RawConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAE1E4A815700E5ABD4 /* RawConvertible.swift */; };
@@ -243,8 +243,8 @@
243243
F738F5851E4A3C09005680A2 /* DataReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataReaderTests.swift; path = DataTests/DataReaderTests.swift; sourceTree = "<group>"; };
244244
F738F5861E4A3C09005680A2 /* DataWriterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataWriterTests.swift; path = DataTests/DataWriterTests.swift; sourceTree = "<group>"; };
245245
F738F5891E4A3C21005680A2 /* TestUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestUtil.swift; sourceTree = "<group>"; };
246-
F78BEF5E1F3E654A0005B3D5 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
247-
F78BEF601F3E65510005B3D5 /* CLI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CLI.swift; sourceTree = "<group>"; };
246+
F7713A5D1F477BA90036A0D5 /* CLI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CLI.swift; sourceTree = "<group>"; };
247+
F7713A5E1F477BA90036A0D5 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
248248
F7ABD9BD1E80603D00768FEC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
249249
F7B5DBAC1E4A5CED00E5ABD4 /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
250250
F7B5DBAE1E4A815700E5ABD4 /* RawConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawConvertible.swift; sourceTree = "<group>"; };
@@ -378,8 +378,8 @@
378378
51213EC51E3916EB005454E0 /* U2FHID.swift */,
379379
51B289E41E39903F00AD90CC /* U2FAuthenticator.swift */,
380380
51FE30EC1E403DA000BAE824 /* U2FRegistration.swift */,
381-
F78BEF601F3E65510005B3D5 /* CLI.swift */,
382-
F78BEF5E1F3E654A0005B3D5 /* Settings.swift */,
381+
F7713A5D1F477BA90036A0D5 /* CLI.swift */,
382+
F7713A5E1F477BA90036A0D5 /* Settings.swift */,
383383
518537BF1E380E4600600911 /* SoftU2F-Bridging-Header.h */,
384384
51F090181E37E8C600F03AD3 /* Info.plist */,
385385
);
@@ -847,16 +847,16 @@
847847
isa = PBXSourcesBuildPhase;
848848
buildActionMask = 2147483647;
849849
files = (
850-
F78BEF611F3E65510005B3D5 /* CLI.swift in Sources */,
851850
5190B6121E3BFE3D00E6FE06 /* UserPresence.swift in Sources */,
852851
51FE30F11E410B3D00BAE824 /* Utils.swift in Sources */,
852+
F7713A601F477BA90036A0D5 /* Settings.swift in Sources */,
853853
5119862E1E3C1519006A3BBB /* KnownFacets.swift in Sources */,
854854
51F090101E37E8C600F03AD3 /* AppDelegate.swift in Sources */,
855855
51213EC61E3916EB005454E0 /* U2FHID.swift in Sources */,
856856
51E214601E3823E7005B2864 /* SHA256.swift in Sources */,
857-
F78BEF5F1F3E654A0005B3D5 /* Settings.swift in Sources */,
858857
51B289E51E39903F00AD90CC /* U2FAuthenticator.swift in Sources */,
859858
514F3D821E43C833008FA513 /* Keychain.swift in Sources */,
859+
F7713A5F1F477BA90036A0D5 /* CLI.swift in Sources */,
860860
51FE30ED1E403DA000BAE824 /* U2FRegistration.swift in Sources */,
861861
51E214621E382521005B2864 /* WebSafeBase64.swift in Sources */,
862862
514F3D871E43E828008FA513 /* KeyPair.swift in Sources */,

SoftU2FTool/AppDelegate.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import Cocoa
1010
@NSApplicationMain
1111
class AppDelegate: NSObject, NSApplicationDelegate {
1212
func applicationDidFinishLaunching(_ aNotification: Notification) {
13+
// Fix up legacy keychain items.
14+
U2FRegistration.repair()
15+
1316
if CLI(CommandLine.arguments).run() {
1417
quit()
1518
} else if !U2FAuthenticator.start(){
@@ -25,7 +28,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
2528
}
2629

2730
func applicationDidBecomeActive(_ notification: Notification) {
28-
// Chrome gives ignores our U2F responses if it isn't active when we send them.
31+
// Chrome ignores our U2F responses if it isn't active when we send them.
2932
// This hack should give focus back to Chrome immediately after the user interacts
3033
// with our notification.
3134
NSApplication.shared().hide(nil)

SoftU2FTool/CLI.swift

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import Foundation
1010
// Command line flags
1111
fileprivate let listFlag = "--list"
1212
fileprivate let deleteAllFlag = "--delete-all"
13-
fileprivate let showTouchidFlag = "--show-touchid"
14-
fileprivate let enableTouchidFlag = "--enable-touchid"
15-
fileprivate let disableTouchidFlag = "--disable-touchid"
13+
fileprivate let showSEPFlag = "--show-sep"
14+
fileprivate let enableSEPFlag = "--enable-sep"
15+
fileprivate let disableSEPFlag = "--disable-sep"
1616

1717
class CLI {
1818
private let args: [String]
@@ -28,29 +28,36 @@ class CLI {
2828
} else if args.contains(deleteAllFlag) {
2929
deleteAll()
3030
return true
31-
} else if args.contains(showTouchidFlag) {
32-
showTouchid()
31+
} else if args.contains(showSEPFlag) {
32+
showSEP()
3333
return true
34-
} else if args.contains(enableTouchidFlag) {
35-
enableTouchid()
34+
} else if args.contains(enableSEPFlag) {
35+
enableSEP()
3636
return true
37-
} else if args.contains(disableTouchidFlag) {
38-
disableTouchid()
37+
} else if args.contains(disableSEPFlag) {
38+
disableSEP()
3939
return true
4040
}
4141

4242
return false
4343
}
4444

4545
private func listRegistrations() {
46+
let registrations = U2FRegistration.all
47+
if registrations.count == 0 {
48+
print("No registrations to list")
49+
return
50+
}
51+
4652
print("The following is a list of U2F registrations stored in your keychain. Each key contains several fields:")
4753
print(" - Key handle: This is the key handle that we registered with a website. For Soft U2F, the key handle is simply a hash of the public key.")
4854
print(" - Application parameter: This is the sha256 of the app-id of the site.")
4955
print(" - Known facet: For some sites we know the application parameter → site name mapping.")
5056
print(" - Counter: How many times this registration has been used.")
57+
print(" — In SEP: Whether this registration's private key is stored in the SEP.")
5158
print("")
5259

53-
U2FRegistration.all.forEach { reg in
60+
registrations.forEach { reg in
5461
print("Key handle: ", reg.keyHandle.base64EncodedString())
5562
print("Application parameter: ", reg.applicationParameter.base64EncodedString())
5663

@@ -61,6 +68,7 @@ class CLI {
6168
}
6269

6370
print("Counter: ", reg.counter)
71+
print("In SEP: ", reg.inSEP)
6472
print("")
6573
}
6674
}
@@ -79,24 +87,24 @@ class CLI {
7987
print("Deleted ", initialCount, " registrations")
8088
}
8189

82-
private func showTouchid() {
83-
if Settings.touchidDisabled {
84-
print("TouchID is disabled")
90+
private func showSEP() {
91+
if Settings.sepEnabled {
92+
print("SEP storage is enabled for new keys")
8593
} else {
86-
print("TouchID is enabled")
94+
print("SEP storage is disabled for new keys")
8795
}
8896
}
8997

90-
private func enableTouchid() {
91-
if Settings.enableTouchid() {
92-
print("TouchID is now enabled")
98+
private func enableSEP() {
99+
if Settings.enableSEP() {
100+
print("SEP storage is now enabled for new keys")
93101
} else {
94-
print("Error enabling TouchID. Does your system support it?")
102+
print("Error enabling SEP storage for new keys. Does your system support it?")
95103
}
96104
}
97105

98-
private func disableTouchid() {
99-
Settings.disableTouchid()
100-
print("TouchID is now disabled")
106+
private func disableSEP() {
107+
Settings.disableSEP()
108+
print("SEP storage is now disabled for new keys")
101109
}
102110
}

SoftU2FTool/KeyPair.swift

Lines changed: 30 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,73 +7,32 @@
77

88
import Foundation
99

10-
fileprivate class tmpKeyPair {
11-
var label: String
12-
var applicationLabel: Data
13-
var publicKey: SecKey?
14-
var privateKey: SecKey?
15-
16-
var keyPair: KeyPair? {
17-
guard let pub = publicKey else { return nil }
18-
guard let priv = privateKey else { return nil }
19-
20-
return KeyPair(label: label, appLabel: applicationLabel, publicKey: pub, privateKey: priv)
21-
}
22-
23-
init(label l: String, applicationLabel al: Data) {
24-
label = l
25-
applicationLabel = al
26-
}
27-
}
28-
2910
class KeyPair {
11+
// Fix up legacy keychain items.
12+
static func repair(label: String) {
13+
Keychain.repair(attrLabel: label as CFString)
14+
}
15+
3016
/// Get all KeyPairs with the given label.
3117
static func all(label: String) -> [KeyPair] {
32-
let secKeys = Keychain.getSecKeys(attrLabel: label as CFString)
33-
var tmpKeyPairs: [tmpKeyPair] = []
18+
let secKeys = Keychain.getPrivateSecKeys(attrLabel: label as CFString)
3419
var keyPairs: [KeyPair] = []
3520

36-
secKeys.forEach { secKey in
37-
guard let appLabel: CFData = Keychain.getSecKeyAttr(key: secKey, attr: kSecAttrApplicationLabel) else { return }
38-
39-
let applicationLabel = appLabel as Data
40-
var tmpKP: tmpKeyPair
41-
42-
if let kp = tmpKeyPairs.first(where: { $0.applicationLabel == applicationLabel }) {
43-
tmpKP = kp
44-
} else {
45-
tmpKP = tmpKeyPair.init(label: label, applicationLabel: applicationLabel)
46-
tmpKeyPairs.append(tmpKP)
47-
}
48-
49-
guard let klass: CFString = Keychain.getSecKeyAttr(key: secKey, attr: kSecAttrKeyClass) else { return }
50-
51-
if klass == kSecAttrKeyClassPublic {
52-
tmpKP.publicKey = secKey
53-
} else {
54-
tmpKP.privateKey = secKey
55-
}
56-
}
57-
58-
tmpKeyPairs.forEach { tmpKP in
59-
guard let kp = tmpKP.keyPair else { return }
60-
21+
secKeys.forEach { priv in
22+
guard let pub = SecKeyCopyPublicKey(priv) else { return }
23+
guard let appLabel: CFData = Keychain.getSecKeyAttr(key: priv, attr: kSecAttrApplicationLabel) else { return }
24+
25+
let kp = KeyPair(label: label, appLabel: appLabel as Data, publicKey: pub, privateKey: priv)
26+
6127
keyPairs.append(kp)
6228
}
6329

6430
return keyPairs
6531
}
6632

67-
// The number of key pairs (keys/2) in the keychain.
33+
// The number of private keys in the keychain.
6834
static func count(label: String) -> Int? {
69-
guard let c = Keychain.count(attrLabel: label as CFString) else { return nil }
70-
71-
if c % 2 != 0 {
72-
print("Uneven number of keys in the keychain.")
73-
return nil
74-
}
75-
76-
return c / 2
35+
return Keychain.count(attrLabel: label as CFString)
7736
}
7837

7938
// Delete all keys with the given label from the keychain.
@@ -97,19 +56,25 @@ class KeyPair {
9756

9857
set {
9958
let value = (newValue ?? Data())
100-
Keychain.setSecItemAttr(attrAppLabel: applicationLabel as CFData, name: kSecAttrApplicationTag, value: value as CFData)
59+
_ = Keychain.setSecItemAttr(attrAppLabel: applicationLabel as CFData, name: kSecAttrApplicationTag, value: value as CFData)
10160
}
10261
}
10362

10463
var publicKeyData: Data? {
105-
return Keychain.getSecItemData(attrAppLabel: applicationLabel)
64+
return Keychain.exportSecKey(publicKey)
65+
}
66+
67+
var inSEP: Bool {
68+
let tokenID: String = Keychain.getSecItemAttr(attrAppLabel: applicationLabel as CFData, name: kSecAttrTokenID) ?? "nope"
69+
70+
return tokenID == kSecAttrTokenIDSecureEnclave as String
10671
}
10772

10873
// Generate a new key pair.
109-
init?(label l: String) {
74+
init?(label l: String, inSEP sep: Bool) {
11075
label = l
11176

112-
guard let (pub, priv) = Keychain.generateKeyPair(attrLabel: label as CFString) else { return nil }
77+
guard let (pub, priv) = Keychain.generateKeyPair(attrLabel: label as CFString, inSEP: sep) else { return nil }
11378
publicKey = pub
11479
privateKey = priv
11580

@@ -118,17 +83,17 @@ class KeyPair {
11883
}
11984

12085
// Find a key pair with the given label and application label.
121-
init?(label l: String, appLabel al: Data) {
86+
init?(label l: String, appLabel al: Data, signPrompt sp: String) {
12287
label = l
12388
applicationLabel = al
12489

125-
// Lookup public key.
126-
guard let pub = Keychain.getSecKey(attrAppLabel: applicationLabel as CFData, keyClass: kSecAttrKeyClassPublic) else { return nil }
127-
publicKey = pub
128-
12990
// Lookup private key.
130-
guard let priv = Keychain.getSecKey(attrAppLabel: applicationLabel as CFData, keyClass: kSecAttrKeyClassPrivate) else { return nil }
91+
guard let priv = Keychain.getPrivateSecKey(attrAppLabel: applicationLabel as CFData, signPrompt: sp as CFString) else { return nil }
13192
privateKey = priv
93+
94+
// Generate public key from private key
95+
guard let pub = SecKeyCopyPublicKey(priv) else { return nil }
96+
publicKey = pub
13297
}
13398

13499
// Initialize a key pair with all the necessary data.

0 commit comments

Comments
 (0)