Skip to content

Commit 2dc6ddf

Browse files
committed
Improves Keys handling
1 parent 977c8fd commit 2dc6ddf

File tree

8 files changed

+61
-51
lines changed

8 files changed

+61
-51
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ let publicKey = privateKey.publicKey /* Recommended */
186186
// Store public key. Not recommended, as you can generate it
187187
try keychain.store(
188188
publicKey,
189-
query: .key(for: "Alice", descriptor: .ecsecPrimeRandom(.public))
189+
query: .key(for: "Alice", descriptor: .ecPublicKey)
190190
)
191191
```
192192

Sources/SwiftSecurity/CryptoKit/SecCertificateConvertible.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ extension X509.Certificate: SecCertificateConvertible {
4848
// MARK: - SecCertificate
4949

5050
public protocol SecCertificateRepresentable {
51-
/// Creates a certificate from a raw representation.
51+
/// Creates a certificate from a reference.
5252
init(certificate secCertificate: SecCertificate)
5353

5454
/// A certificate reference.

Sources/SwiftSecurity/CryptoKit/SecKeyConvertible.swift

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,59 +20,61 @@ public protocol SecKeyConvertible: SecKeyRepresentable {
2020

2121
// MARK: - CryptoKit
2222

23+
/// NIST P-256 (also known as `secp256r1` / `prime256r1` / `prime256v1`).
24+
2325
extension P256.Signing.PrivateKey: SecKeyConvertible {
24-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.private) }
26+
public var secKeyDescriptor: SecKeyDescriptor { .ecPrivateKey }
2527
}
26-
2728
extension P256.Signing.PublicKey: SecKeyConvertible {
28-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.public) }
29+
public var secKeyDescriptor: SecKeyDescriptor { .ecPublicKey }
2930
}
3031

3132
extension P256.KeyAgreement.PrivateKey: SecKeyConvertible {
32-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.private) }
33+
public var secKeyDescriptor: SecKeyDescriptor { .ecPrivateKey }
3334
}
34-
3535
extension P256.KeyAgreement.PublicKey: SecKeyConvertible {
36-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.public) }
36+
public var secKeyDescriptor: SecKeyDescriptor { .ecPublicKey }
3737
}
3838

39+
/// NIST P-384 (also known as `secp384r1` ).
40+
3941
extension P384.Signing.PrivateKey: SecKeyConvertible {
40-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.private) }
42+
public var secKeyDescriptor: SecKeyDescriptor { .ecPrivateKey }
4143
}
4244

4345
extension P384.Signing.PublicKey: SecKeyConvertible {
44-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.public) }
46+
public var secKeyDescriptor: SecKeyDescriptor { .ecPublicKey }
4547
}
4648

4749
extension P384.KeyAgreement.PrivateKey: SecKeyConvertible {
48-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.private) }
50+
public var secKeyDescriptor: SecKeyDescriptor { .ecPrivateKey }
4951
}
5052

5153
extension P384.KeyAgreement.PublicKey: SecKeyConvertible {
52-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.public) }
54+
public var secKeyDescriptor: SecKeyDescriptor { .ecPublicKey }
5355
}
5456

57+
/// NIST P-521 (also known as `secp521r1` ).
58+
5559
extension P521.Signing.PrivateKey: SecKeyConvertible {
56-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.private) }
60+
public var secKeyDescriptor: SecKeyDescriptor { .ecPrivateKey }
5761
}
58-
5962
extension P521.Signing.PublicKey: SecKeyConvertible {
60-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.public) }
63+
public var secKeyDescriptor: SecKeyDescriptor { .ecPublicKey }
6164
}
6265

6366
extension P521.KeyAgreement.PrivateKey: SecKeyConvertible {
64-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.private) }
67+
public var secKeyDescriptor: SecKeyDescriptor { .ecPrivateKey }
6568
}
66-
6769
extension P521.KeyAgreement.PublicKey: SecKeyConvertible {
68-
public var descriptor: SecKeyDescriptor { .ecsecPrimeRandom(.public) }
70+
public var secKeyDescriptor: SecKeyDescriptor { .ecPublicKey }
6971
}
7072

7173
// MARK: - SecKey
7274

7375
public protocol SecKeyRepresentable {
7476
/// A key descriptor for storage.
75-
var descriptor: SecKeyDescriptor { get }
77+
var secKeyDescriptor: SecKeyDescriptor { get }
7678

7779
/// A key reference.
7880
var secKey: SecKey { get throws }
@@ -81,14 +83,19 @@ public protocol SecKeyRepresentable {
8183
extension SecKeyConvertible {
8284
public var secKey: SecKey {
8385
get throws {
84-
guard descriptor.keyType == .ecsecPrimeRandom else {
85-
// RSA use is discouraged. If necessary, override and use ASN.1 format as external representation
86+
let keyData: Data
87+
switch secKeyDescriptor.keyType {
88+
case .ecsecPrimeRandom:
89+
keyData = x963Representation
90+
case .rsa:
91+
// override and use data in PKCS #1 format
8692
throw SwiftSecurityError.unimplemented
8793
}
94+
8895
var error: Unmanaged<CFError>?
89-
guard let secKey: SecKey = SecKeyCreateWithData(x963Representation as CFData, [
90-
kSecAttrKeyType: descriptor.keyType.rawValue,
91-
kSecAttrKeyClass: descriptor.keyClass.rawValue
96+
guard let secKey: SecKey = SecKeyCreateWithData(keyData as CFData, [
97+
kSecAttrKeyType: secKeyDescriptor.keyType.rawValue,
98+
kSecAttrKeyClass: secKeyDescriptor.keyClass.rawValue
9299
] as CFDictionary, &error) else {
93100
if let error = error?.takeRetainedValue() {
94101
throw SwiftSecurityError(error: error)
@@ -104,21 +111,19 @@ public struct SecKeyDescriptor {
104111
public var keyType: KeyType
105112
public var keyClass: KeyClass
106113

107-
public static func rsa(_ keyClass: KeyClass) -> SecKeyDescriptor {
108-
switch keyClass {
109-
case .public:
110-
SecKeyDescriptor(keyType: .rsa, keyClass: .public)
111-
case .private:
112-
SecKeyDescriptor(keyType: .rsa, keyClass: .private)
113-
}
114-
}
114+
/// A private key for elliptic curve cryptography. Suitable for `P256`/`P384`/`P521` keys from `CryptoKit`.
115+
public static let ecPrivateKey = SecKeyDescriptor(keyType: .ecsecPrimeRandom, keyClass: .private)
116+
/// A public key for elliptic curve cryptography. Suitable for `P256`/`P384`/`P521` keys from `CryptoKit`.
117+
public static let ecPublicKey = SecKeyDescriptor(keyType: .ecsecPrimeRandom, keyClass: .public)
115118

116-
public static func ecsecPrimeRandom(_ keyClass: KeyClass) -> SecKeyDescriptor {
117-
switch keyClass {
118-
case .public:
119-
SecKeyDescriptor(keyType: .ecsecPrimeRandom, keyClass: .public)
120-
case .private:
121-
SecKeyDescriptor(keyType: .ecsecPrimeRandom, keyClass: .private)
122-
}
119+
/// A private key for `RSA` cryptography.
120+
public static let rsaPrivateKey = SecKeyDescriptor(keyType: .rsa, keyClass: .private)
121+
/// A public key for `RSA` cryptography.
122+
public static let rsaPublicKey = SecKeyDescriptor(keyType: .rsa, keyClass: .public)
123+
124+
/// A descriptor that defines the properties of the key.
125+
public init(keyType: KeyType, keyClass: KeyClass) {
126+
self.keyType = keyType
127+
self.keyClass = keyClass
123128
}
124129
}

Sources/SwiftSecurity/Keychain/Keychain.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,14 @@ extension Keychain: SecKeyStore {
298298
query: SecItemQuery<SecKey>,
299299
accessPolicy: AccessPolicy = .default
300300
) throws -> SecValue<SecKey>? {
301-
if let specifiedKeyType = query.keyType {
302-
precondition(specifiedKeyType == key.descriptor.keyType)
303-
}
304-
if let specifiedKeyClass = query.keyClass {
305-
precondition(specifiedKeyClass == key.descriptor.keyClass)
301+
guard
302+
/// If key type specified in query, it should match with type from key's descriptor. Refer to `.key(for:descriptor:)`
303+
query.keyType == nil || query.keyType == key.secKeyDescriptor.keyType,
304+
/// If key class specified in query, it should match with class from key's descriptor.
305+
query.keyClass == nil || query.keyClass == key.secKeyDescriptor.keyClass
306+
else {
307+
/// You most likely tried to store a public key as a private key. While it might be accepted by the keychain, it could lead to confusion.
308+
throw SwiftSecurityError.invalidParameter
306309
}
307310
return try store(.reference(key.secKey), returning: returnType, query: query, accessPolicy: accessPolicy)
308311
}

Sources/SwiftSecurity/Keychain/SecItemAttr/KeyClass.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// KeyType.swift
2+
// KeyClass.swift
33
//
44
//
55
// Created by Dmitriy Zharov on 17.01.2024.
@@ -9,7 +9,9 @@ import Foundation
99
import Security
1010

1111
public enum KeyClass {
12+
/// A public key of a public-private pair.
1213
case `public`
14+
/// A private key of a public-private pair.
1315
case `private`
1416
}
1517

Sources/SwiftSecurity/Keychain/SecItemAttr/KeyType.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public typealias KeyCipher = KeyType
1414
public enum KeyType {
1515
/// RSA.
1616
case rsa
17-
/// Elliptic curve. Suitable for `P256`, `P384`, `P521` from `CryptoKit`.
17+
/// Elliptic curve. Suitable for `P256`, `P384`, `P521` keys from `CryptoKit`.
1818
case ecsecPrimeRandom
1919
}
2020

Sources/SwiftSecurity/Keychain/SecItemStore/SecItemQuery.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ public extension SecItemQuery {
7878
/// - Note: Suitable for `P256`/`P384`/`P521` keys from `CryptoKit` or custom `RSA` keys.
7979
/// - Parameters:
8080
/// - applicationTag: An application tag that you can use to identify the key within store.
81-
/// - descriptor: A key descriptor. Default value is `.ecsecPrimeRandom(.private)`.
81+
/// - descriptor: A key descriptor. Defaults to `.ecPrivateKey`.
8282
/// - synchronizable: A boolean value indicating whether the item synchronizes through iCloud.
8383
/// See [Developer Documentation](https://developer.apple.com/documentation/security/ksecattrsynchronizable).
8484
/// - Returns: ``SecItemQuery<SecKey>``.
85-
static func key(for applicationTag: String? = nil, descriptor: SecKeyDescriptor = .ecsecPrimeRandom(.private), synchronizable: Bool? = nil) -> SecItemQuery<SecKey> {
85+
static func key(for applicationTag: String? = nil, descriptor: SecKeyDescriptor = .ecPrivateKey, synchronizable: Bool? = nil) -> SecItemQuery<SecKey> {
8686
var query = SecItemQuery<SecKey>()
8787
query.keyClass = descriptor.keyClass
8888
query.keyType = descriptor.keyType
@@ -537,6 +537,6 @@ public extension SecItemQuery {
537537
/// - Returns: ``SecItemQuery<SecKey>``.
538538
@available(*, deprecated, renamed: "key(for:synchronizable:)")
539539
static func privateKey(for applicationTag: String? = nil, synchronizable: Bool? = nil) -> SecItemQuery<SecKey> {
540-
return key(for: applicationTag, descriptor: .ecsecPrimeRandom(.private), synchronizable: synchronizable)
540+
return key(for: applicationTag, descriptor: .ecPrivateKey, synchronizable: synchronizable)
541541
}
542542
}

Sources/SwiftSecurity/Security/DigitalIdentity.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ import Security
1212
public typealias Identity = DigitalIdentity
1313

1414
public struct DigitalIdentity {
15-
/// Creates an identity from a raw representation.
15+
/// Creates an identity from a reference.
1616
public init(identity secIdentity: SecIdentity) {
1717
self.secIdentity = secIdentity
1818
}
1919

20-
/// Creates an identity from a raw representation.
20+
/// An identity reference.
2121
public let secIdentity: SecIdentity
2222
}
2323

0 commit comments

Comments
 (0)