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

Commit

Permalink
Merge pull request #29 from github/sep
Browse files Browse the repository at this point in the history
Optionally store keys in SEP
  • Loading branch information
mastahyeti authored Aug 22, 2017
2 parents ed0ab09 + 5babfb7 commit 256ef14
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 200 deletions.
16 changes: 8 additions & 8 deletions SoftU2F.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@
F738F5871E4A3C09005680A2 /* DataReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5851E4A3C09005680A2 /* DataReaderTests.swift */; };
F738F5881E4A3C09005680A2 /* DataWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5861E4A3C09005680A2 /* DataWriterTests.swift */; };
F738F58A1E4A3C21005680A2 /* TestUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5891E4A3C21005680A2 /* TestUtil.swift */; };
F78BEF5F1F3E654A0005B3D5 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78BEF5E1F3E654A0005B3D5 /* Settings.swift */; };
F78BEF611F3E65510005B3D5 /* CLI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78BEF601F3E65510005B3D5 /* CLI.swift */; };
F7713A5F1F477BA90036A0D5 /* CLI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7713A5D1F477BA90036A0D5 /* CLI.swift */; };
F7713A601F477BA90036A0D5 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7713A5E1F477BA90036A0D5 /* Settings.swift */; };
F7ABD9BE1E80603D00768FEC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7ABD9BD1E80603D00768FEC /* Assets.xcassets */; };
F7B5DBAD1E4A5CED00E5ABD4 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAC1E4A5CED00E5ABD4 /* Command.swift */; };
F7B5DBAF1E4A815700E5ABD4 /* RawConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAE1E4A815700E5ABD4 /* RawConvertible.swift */; };
Expand Down Expand Up @@ -243,8 +243,8 @@
F738F5851E4A3C09005680A2 /* DataReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataReaderTests.swift; path = DataTests/DataReaderTests.swift; sourceTree = "<group>"; };
F738F5861E4A3C09005680A2 /* DataWriterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataWriterTests.swift; path = DataTests/DataWriterTests.swift; sourceTree = "<group>"; };
F738F5891E4A3C21005680A2 /* TestUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestUtil.swift; sourceTree = "<group>"; };
F78BEF5E1F3E654A0005B3D5 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
F78BEF601F3E65510005B3D5 /* CLI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CLI.swift; sourceTree = "<group>"; };
F7713A5D1F477BA90036A0D5 /* CLI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CLI.swift; sourceTree = "<group>"; };
F7713A5E1F477BA90036A0D5 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
F7ABD9BD1E80603D00768FEC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
F7B5DBAC1E4A5CED00E5ABD4 /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
F7B5DBAE1E4A815700E5ABD4 /* RawConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawConvertible.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -378,8 +378,8 @@
51213EC51E3916EB005454E0 /* U2FHID.swift */,
51B289E41E39903F00AD90CC /* U2FAuthenticator.swift */,
51FE30EC1E403DA000BAE824 /* U2FRegistration.swift */,
F78BEF601F3E65510005B3D5 /* CLI.swift */,
F78BEF5E1F3E654A0005B3D5 /* Settings.swift */,
F7713A5D1F477BA90036A0D5 /* CLI.swift */,
F7713A5E1F477BA90036A0D5 /* Settings.swift */,
518537BF1E380E4600600911 /* SoftU2F-Bridging-Header.h */,
51F090181E37E8C600F03AD3 /* Info.plist */,
);
Expand Down Expand Up @@ -847,16 +847,16 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F78BEF611F3E65510005B3D5 /* CLI.swift in Sources */,
5190B6121E3BFE3D00E6FE06 /* UserPresence.swift in Sources */,
51FE30F11E410B3D00BAE824 /* Utils.swift in Sources */,
F7713A601F477BA90036A0D5 /* Settings.swift in Sources */,
5119862E1E3C1519006A3BBB /* KnownFacets.swift in Sources */,
51F090101E37E8C600F03AD3 /* AppDelegate.swift in Sources */,
51213EC61E3916EB005454E0 /* U2FHID.swift in Sources */,
51E214601E3823E7005B2864 /* SHA256.swift in Sources */,
F78BEF5F1F3E654A0005B3D5 /* Settings.swift in Sources */,
51B289E51E39903F00AD90CC /* U2FAuthenticator.swift in Sources */,
514F3D821E43C833008FA513 /* Keychain.swift in Sources */,
F7713A5F1F477BA90036A0D5 /* CLI.swift in Sources */,
51FE30ED1E403DA000BAE824 /* U2FRegistration.swift in Sources */,
51E214621E382521005B2864 /* WebSafeBase64.swift in Sources */,
514F3D871E43E828008FA513 /* KeyPair.swift in Sources */,
Expand Down
5 changes: 4 additions & 1 deletion SoftU2FTool/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Fix up legacy keychain items.
U2FRegistration.repair()

if CLI(CommandLine.arguments).run() {
quit()
} else if !U2FAuthenticator.start(){
Expand All @@ -25,7 +28,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

func applicationDidBecomeActive(_ notification: Notification) {
// Chrome gives ignores our U2F responses if it isn't active when we send them.
// Chrome ignores our U2F responses if it isn't active when we send them.
// This hack should give focus back to Chrome immediately after the user interacts
// with our notification.
NSApplication.shared().hide(nil)
Expand Down
50 changes: 29 additions & 21 deletions SoftU2FTool/CLI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import Foundation
// Command line flags
fileprivate let listFlag = "--list"
fileprivate let deleteAllFlag = "--delete-all"
fileprivate let showTouchidFlag = "--show-touchid"
fileprivate let enableTouchidFlag = "--enable-touchid"
fileprivate let disableTouchidFlag = "--disable-touchid"
fileprivate let showSEPFlag = "--show-sep"
fileprivate let enableSEPFlag = "--enable-sep"
fileprivate let disableSEPFlag = "--disable-sep"

class CLI {
private let args: [String]
Expand All @@ -28,29 +28,36 @@ class CLI {
} else if args.contains(deleteAllFlag) {
deleteAll()
return true
} else if args.contains(showTouchidFlag) {
showTouchid()
} else if args.contains(showSEPFlag) {
showSEP()
return true
} else if args.contains(enableTouchidFlag) {
enableTouchid()
} else if args.contains(enableSEPFlag) {
enableSEP()
return true
} else if args.contains(disableTouchidFlag) {
disableTouchid()
} else if args.contains(disableSEPFlag) {
disableSEP()
return true
}

return false
}

private func listRegistrations() {
let registrations = U2FRegistration.all
if registrations.count == 0 {
print("No registrations to list")
return
}

print("The following is a list of U2F registrations stored in your keychain. Each key contains several fields:")
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.")
print(" - Application parameter: This is the sha256 of the app-id of the site.")
print(" - Known facet: For some sites we know the application parameter → site name mapping.")
print(" - Counter: How many times this registration has been used.")
print(" — In SEP: Whether this registration's private key is stored in the SEP.")
print("")

U2FRegistration.all.forEach { reg in
registrations.forEach { reg in
print("Key handle: ", reg.keyHandle.base64EncodedString())
print("Application parameter: ", reg.applicationParameter.base64EncodedString())

Expand All @@ -61,6 +68,7 @@ class CLI {
}

print("Counter: ", reg.counter)
print("In SEP: ", reg.inSEP)
print("")
}
}
Expand All @@ -79,24 +87,24 @@ class CLI {
print("Deleted ", initialCount, " registrations")
}

private func showTouchid() {
if Settings.touchidDisabled {
print("TouchID is disabled")
private func showSEP() {
if Settings.sepEnabled {
print("SEP storage is enabled for new keys")
} else {
print("TouchID is enabled")
print("SEP storage is disabled for new keys")
}
}

private func enableTouchid() {
if Settings.enableTouchid() {
print("TouchID is now enabled")
private func enableSEP() {
if Settings.enableSEP() {
print("SEP storage is now enabled for new keys")
} else {
print("Error enabling TouchID. Does your system support it?")
print("Error enabling SEP storage for new keys. Does your system support it?")
}
}

private func disableTouchid() {
Settings.disableTouchid()
print("TouchID is now disabled")
private func disableSEP() {
Settings.disableSEP()
print("SEP storage is now disabled for new keys")
}
}
95 changes: 30 additions & 65 deletions SoftU2FTool/KeyPair.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,73 +7,32 @@

import Foundation

fileprivate class tmpKeyPair {
var label: String
var applicationLabel: Data
var publicKey: SecKey?
var privateKey: SecKey?

var keyPair: KeyPair? {
guard let pub = publicKey else { return nil }
guard let priv = privateKey else { return nil }

return KeyPair(label: label, appLabel: applicationLabel, publicKey: pub, privateKey: priv)
}

init(label l: String, applicationLabel al: Data) {
label = l
applicationLabel = al
}
}

class KeyPair {
// Fix up legacy keychain items.
static func repair(label: String) {
Keychain.repair(attrLabel: label as CFString)
}

/// Get all KeyPairs with the given label.
static func all(label: String) -> [KeyPair] {
let secKeys = Keychain.getSecKeys(attrLabel: label as CFString)
var tmpKeyPairs: [tmpKeyPair] = []
let secKeys = Keychain.getPrivateSecKeys(attrLabel: label as CFString)
var keyPairs: [KeyPair] = []

secKeys.forEach { secKey in
guard let appLabel: CFData = Keychain.getSecKeyAttr(key: secKey, attr: kSecAttrApplicationLabel) else { return }

let applicationLabel = appLabel as Data
var tmpKP: tmpKeyPair

if let kp = tmpKeyPairs.first(where: { $0.applicationLabel == applicationLabel }) {
tmpKP = kp
} else {
tmpKP = tmpKeyPair.init(label: label, applicationLabel: applicationLabel)
tmpKeyPairs.append(tmpKP)
}

guard let klass: CFString = Keychain.getSecKeyAttr(key: secKey, attr: kSecAttrKeyClass) else { return }

if klass == kSecAttrKeyClassPublic {
tmpKP.publicKey = secKey
} else {
tmpKP.privateKey = secKey
}
}

tmpKeyPairs.forEach { tmpKP in
guard let kp = tmpKP.keyPair else { return }

secKeys.forEach { priv in
guard let pub = SecKeyCopyPublicKey(priv) else { return }
guard let appLabel: CFData = Keychain.getSecKeyAttr(key: priv, attr: kSecAttrApplicationLabel) else { return }

let kp = KeyPair(label: label, appLabel: appLabel as Data, publicKey: pub, privateKey: priv)

keyPairs.append(kp)
}

return keyPairs
}

// The number of key pairs (keys/2) in the keychain.
// The number of private keys in the keychain.
static func count(label: String) -> Int? {
guard let c = Keychain.count(attrLabel: label as CFString) else { return nil }

if c % 2 != 0 {
print("Uneven number of keys in the keychain.")
return nil
}

return c / 2
return Keychain.count(attrLabel: label as CFString)
}

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

set {
let value = (newValue ?? Data())
Keychain.setSecItemAttr(attrAppLabel: applicationLabel as CFData, name: kSecAttrApplicationTag, value: value as CFData)
_ = Keychain.setSecItemAttr(attrAppLabel: applicationLabel as CFData, name: kSecAttrApplicationTag, value: value as CFData)
}
}

var publicKeyData: Data? {
return Keychain.getSecItemData(attrAppLabel: applicationLabel)
return Keychain.exportSecKey(publicKey)
}

var inSEP: Bool {
let tokenID: String = Keychain.getSecItemAttr(attrAppLabel: applicationLabel as CFData, name: kSecAttrTokenID) ?? "nope"

return tokenID == kSecAttrTokenIDSecureEnclave as String
}

// Generate a new key pair.
init?(label l: String) {
init?(label l: String, inSEP sep: Bool) {
label = l

guard let (pub, priv) = Keychain.generateKeyPair(attrLabel: label as CFString) else { return nil }
guard let (pub, priv) = Keychain.generateKeyPair(attrLabel: label as CFString, inSEP: sep) else { return nil }
publicKey = pub
privateKey = priv

Expand All @@ -118,17 +83,17 @@ class KeyPair {
}

// Find a key pair with the given label and application label.
init?(label l: String, appLabel al: Data) {
init?(label l: String, appLabel al: Data, signPrompt sp: String) {
label = l
applicationLabel = al

// Lookup public key.
guard let pub = Keychain.getSecKey(attrAppLabel: applicationLabel as CFData, keyClass: kSecAttrKeyClassPublic) else { return nil }
publicKey = pub

// Lookup private key.
guard let priv = Keychain.getSecKey(attrAppLabel: applicationLabel as CFData, keyClass: kSecAttrKeyClassPrivate) else { return nil }
guard let priv = Keychain.getPrivateSecKey(attrAppLabel: applicationLabel as CFData, signPrompt: sp as CFString) else { return nil }
privateKey = priv

// Generate public key from private key
guard let pub = SecKeyCopyPublicKey(priv) else { return nil }
publicKey = pub
}

// Initialize a key pair with all the necessary data.
Expand Down
Loading

0 comments on commit 256ef14

Please sign in to comment.