Skip to content

Commit 2f2e8ed

Browse files
author
Dr. Brandon Wiley
committed
Refactored macOS-specific code into its own library
1 parent 8dbb40b commit 2f2e8ed

File tree

6 files changed

+16
-167
lines changed

6 files changed

+16
-167
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
.swiftpm
2+
.build
3+
14
# Xcode
25
#
36
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

-7
This file was deleted.

.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

-8
This file was deleted.

Package.resolved

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
{
22
"object": {
33
"pins": [
4+
{
5+
"package": "KeychainMacOS",
6+
"repositoryURL": "https://github.com/OperatorFoundation/KeychainMacOS.git",
7+
"state": {
8+
"branch": null,
9+
"revision": "e6d545e3b8ebb4b3b6b3f58c6936ab708a8d7373",
10+
"version": "1.0.1"
11+
}
12+
},
413
{
514
"package": "swift-crypto",
615
"repositoryURL": "https://github.com/apple/swift-crypto.git",

Package.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:5.3
1+
// swift-tools-version:5.5
22
// The swift-tools-version declares the minimum version of Swift required to build this package.
33

44
import PackageDescription
@@ -18,13 +18,14 @@ let package = Package(
1818
// .package(url: /* package url */, from: "1.0.0"),
1919
.package(url: "https://github.com/apple/swift-crypto.git",
2020
from: "2.0.0"),
21+
.package(url: "https://github.com/OperatorFoundation/KeychainMacOS.git", from: "1.0.1"),
2122
],
2223
targets: [
2324
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
2425
// Targets can depend on other targets in this package, and on products in packages this package depends on.
2526
.target(
2627
name: "Keychain",
27-
dependencies: [
28+
dependencies: ["KeychainMacOS",
2829
.product(name: "Crypto", package: "swift-crypto"),
2930
]),
3031
.testTarget(

Sources/Keychain/Keychain.swift

+1-150
Original file line numberDiff line numberDiff line change
@@ -3,158 +3,9 @@ import Crypto
33
import Foundation
44

55
#if os(macOS)
6-
public class Keychain
7-
{
8-
public init() {}
9-
10-
public func retrieveOrGeneratePrivateKey(label: String) -> P256.KeyAgreement.PrivateKey?
11-
{
12-
// Do we already have a key?
13-
if let key = retrievePrivateKey(label: label)
14-
{
15-
return key
16-
}
17-
18-
// We don't?
19-
// Let's create some and return those
20-
let privateKey = P256.KeyAgreement.PrivateKey()
21-
22-
// Save the key we stored
23-
let stored = storePrivateKey(privateKey, label: label)
24-
if !stored
25-
{
26-
print("😱 Failed to store our new server key.")
27-
return nil
28-
}
29-
return privateKey
30-
}
31-
32-
public func generateAndSavePrivateKey(label: String) -> P256.KeyAgreement.PrivateKey?
33-
{
34-
let privateKey = P256.KeyAgreement.PrivateKey()
35-
36-
// Save the key we stored
37-
let stored = storePrivateKey(privateKey, label: label)
38-
if !stored
39-
{
40-
print("😱 Failed to store our new server key.")
41-
return nil
42-
}
43-
44-
return privateKey
45-
}
46-
47-
public func storePrivateKey(_ key: P256.KeyAgreement.PrivateKey, label: String) -> Bool
48-
{
49-
let attributes = [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
50-
kSecAttrKeyClass: kSecAttrKeyClassPrivate] as [String: Any]
516

52-
// Get a SecKey representation.
53-
var error: Unmanaged<CFError>?
54-
let keyData = key.x963Representation as CFData
55-
guard let secKey = SecKeyCreateWithData(keyData,
56-
attributes as CFDictionary,
57-
&error)
58-
else
59-
{
60-
print("Unable to create SecKey representation.")
61-
if let secKeyError = error
62-
{
63-
print(secKeyError)
64-
}
65-
return false
66-
}
67-
68-
// Describe the add operation.
69-
let query = [kSecClass: kSecClassKey,
70-
kSecAttrApplicationLabel: label,
71-
kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
72-
kSecUseDataProtectionKeychain: true,
73-
kSecValueRef: secKey] as [String: Any]
7+
@_exported import KeychainMacOS
748

75-
// Add the key to the keychain.
76-
let status = SecItemAdd(query as CFDictionary, nil)
77-
78-
switch status {
79-
case errSecSuccess:
80-
return true
81-
default:
82-
if let statusString = SecCopyErrorMessageString(status, nil)
83-
{
84-
print("Unable to store item: \(statusString)")
85-
}
86-
87-
return false
88-
}
89-
}
90-
91-
public func retrievePrivateKey(label: String) -> P256.KeyAgreement.PrivateKey?
92-
{
93-
let query: CFDictionary = generateKeySearchQuery(label: label)
94-
95-
// Find and cast the result as a SecKey instance.
96-
var item: CFTypeRef?
97-
var secKey: SecKey
98-
switch SecItemCopyMatching(query as CFDictionary, &item) {
99-
case errSecSuccess: secKey = item as! SecKey
100-
case errSecItemNotFound: return nil
101-
case let status:
102-
print("Keychain read failed: \(status)")
103-
return nil
104-
}
105-
106-
// Convert the SecKey into a CryptoKit key.
107-
var error: Unmanaged<CFError>?
108-
guard let data = SecKeyCopyExternalRepresentation(secKey, &error) as Data?
109-
else
110-
{
111-
print(error.debugDescription)
112-
return nil
113-
}
114-
115-
do {
116-
let key = try P256.KeyAgreement.PrivateKey(x963Representation: data)
117-
return key
118-
}
119-
catch let keyError
120-
{
121-
print("Error decoding key: \(keyError)")
122-
return nil
123-
}
124-
}
125-
126-
public func generateKeySearchQuery(label: String) -> CFDictionary
127-
{
128-
let query: [String: Any] = [kSecClass as String: kSecClassKey,
129-
kSecAttrApplicationLabel as String: label,
130-
//kSecAttrApplicationTag as String: tag,
131-
kSecMatchLimit as String: kSecMatchLimitOne,
132-
kSecReturnRef as String: true,
133-
kSecReturnAttributes as String: false,
134-
kSecReturnData as String: false]
135-
136-
return query as CFDictionary
137-
}
138-
139-
public func deleteKey(label: String)
140-
{
141-
print("\nAttempted to delete key.")
142-
//Remove client keys from secure enclave
143-
//let query: [String: Any] = [kSecClass as String: kSecClassKey, kSecAttrApplicationTag as String: tag]
144-
let query = generateKeySearchQuery(label: label)
145-
let deleteStatus = SecItemDelete(query as CFDictionary)
146-
147-
switch deleteStatus
148-
{
149-
case errSecItemNotFound:
150-
print("Could not find a key to delete.\n")
151-
case noErr:
152-
print("Deleted a key.\n")
153-
default:
154-
print("Unexpected status: \(deleteStatus.description)\n")
155-
}
156-
}
157-
}
1589
#else
15910

16011
@_exported import KeychainLinux

0 commit comments

Comments
 (0)