Skip to content

Commit 60440ad

Browse files
author
Bram
committed
KDF implementation
* Implemented several key derivation functions
1 parent 26f2288 commit 60440ad

File tree

3 files changed

+202
-2
lines changed

3 files changed

+202
-2
lines changed

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
11
# CCSwift
22

3-
A description of this package.
3+
A CommonCrypto implementation in Swift, including some of the SPI implementations.
4+
5+
### Swift Package manager
6+
7+
In order to use the package using the Swift package manager, add the following as dependency to your Package.swift.
8+
9+
```swift
10+
.package(url: "https://github.com/Craz1k0ek/CCSwift.git", from: "1.0.0")
11+
```
12+
13+
Afterwards, add `CCSwift` as dependency to the target where you need the package. Lastly, update the package.
14+
```bash
15+
swift package update
16+
```
17+
18+
### Requirements
19+
* iOS 12.0 or higher
20+
* macOS 10.13 or higher

Sources/CCSwift/Digests.swift

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import CCWrapper
22
import Foundation
33

4-
public enum Digest {
4+
public struct Digest {
55
public enum Algorithm {
66
/// No digest.
77
case none
@@ -49,6 +49,42 @@ public enum Digest {
4949
}
5050
}
5151
}
52+
53+
internal var reference: CCDigestRef
54+
55+
internal init(algorithm: Algorithm) throws {
56+
reference = CCDigestCreate(algorithm: algorithm.rawValue)
57+
try CCDigestInit(algorithm: algorithm.rawValue, reference: reference)
58+
}
59+
60+
/// Continue to digest data.
61+
/// - Parameter data: The data to digest.
62+
internal func update(_ data: Data) throws {
63+
try CCDigestUpdate(reference, data: data)
64+
}
65+
66+
/// Conclude digest operations and produce the digest output.
67+
/// - Returns: The digest bytes.
68+
internal func finalize() throws -> Data {
69+
try CCDigestFinalize(reference)
70+
}
71+
72+
/// Clear and free the digest context.
73+
internal func destroy() {
74+
CCDigestDestroy(reference)
75+
}
76+
77+
/// Clear and re-initialize the digest context.
78+
internal func reset() {
79+
CCDigestReset(reference)
80+
}
81+
82+
/// Stateless, one-shot digest function.
83+
/// - Parameter data: The data to digest.
84+
/// - Returns: The digest bytes.
85+
internal static func hash(_ data: Data, algorithm: Algorithm) throws -> Data {
86+
try CCDigest(algorithm: algorithm.rawValue, data: data)
87+
}
5288
}
5389

5490
public protocol DigestFunction {

Sources/CCSwift/KDF.swift

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import CCWrapper
2+
import Foundation
3+
4+
internal protocol KDFunction {
5+
var parameters: CCKDFParametersRef? { get }
6+
}
7+
8+
@available(iOS 13.0, macOS 10.15, *)
9+
extension KDFunction {
10+
internal func derive(digest: Digest.Algorithm, key: Data, size: Int) throws -> Data {
11+
return try CCDeriveKey(parameters, digest: digest.rawValue, key: key, derivedSize: size)
12+
}
13+
}
14+
15+
public struct PBKDF2: KDFunction {
16+
var parameters: CCKDFParametersRef?
17+
18+
@available(iOS 13.0, macOS 10.15, *)
19+
internal init(rounds: UInt32, salt: Data) throws {
20+
try CCKDFParametersCreatePbkdf2(&parameters, rounds: rounds, salt: salt)
21+
}
22+
23+
public static func derive(_ password: String, salt: Data, digest: Digest.Algorithm, rounds: UInt32 = 10_000, size: Int = 32) throws -> Data {
24+
if #available(iOS 13.0, macOS 10.15, *) {
25+
return try Self(rounds: rounds, salt: salt).derive(digest: digest, key: Data(password.utf8), size: size)
26+
} else {
27+
return try CCKeyDerivationPBKDF(algorithm: .pbkdf2, password: password, salt: salt, prf: prf(for: digest), rounds: rounds, derivedSize: size)
28+
}
29+
}
30+
31+
fileprivate static func prf(for digest: Digest.Algorithm) throws -> CCPseudoRandomAlgorithm {
32+
switch digest {
33+
case .SHA1: return .prfHmacSHA1
34+
case .SHA224: return .prfHmacSHA224
35+
case .SHA256: return .prfHmacSHA256
36+
case .SHA384: return .prfHmacSHA384
37+
case .SHA512: return .prfHmacSHA512
38+
default: throw CryptoError.unimplemented
39+
}
40+
}
41+
}
42+
43+
@available(iOS 13.0, macOS 10.15, *)
44+
public struct CTRHMAC: KDFunction {
45+
var parameters: CCKDFParametersRef?
46+
47+
init(label: Data, context: Data) throws {
48+
try CCKDFParametersCreateCtrHmac(&parameters, label: label, context: context)
49+
}
50+
51+
init(context: Data) throws {
52+
try CCKDFParametersCreateCtrHmacFixed(&parameters, context: context)
53+
}
54+
55+
public static func derive(_ key: Data, label: Data, context: Data, digest: Digest.Algorithm, size: Int = 32) throws -> Data {
56+
return try Self(label: label, context: context).derive(digest: digest, key: key, size: size)
57+
}
58+
59+
public static func derive(_ key: Data, context: Data, digest: Digest.Algorithm, size: Int = 32) throws -> Data {
60+
try Self(context: context).derive(digest: digest, key: key, size: size)
61+
}
62+
}
63+
64+
public struct HKDF: KDFunction {
65+
var parameters: CCKDFParametersRef?
66+
67+
@available(iOS 13.0, macOS 10.15, *)
68+
init(salt: Data, context: Data) throws {
69+
try CCKDFParametersCreateHkdf(&parameters, salt: salt, context: context)
70+
}
71+
72+
public static func derive(_ key: Data, salt: Data?, info: Data, digest: Digest.Algorithm, size: Int = 32) throws -> Data {
73+
if #available(iOS 13.0, macOS 10.15, *) {
74+
return try Self(salt: salt ?? Data(repeating: 0, count: digest.digestSize), context: info).derive(digest: digest, key: key, size: size)
75+
} else {
76+
return try hkdf(key, salt: salt, info: info, digest: digest, size: size)
77+
}
78+
}
79+
80+
@available(iOS, deprecated: 13.0)
81+
@available(macOS, deprecated: 10.15)
82+
fileprivate static func hkdf(_ ikm: Data, salt: Data?, info: Data, digest: Digest.Algorithm, size: Int) throws -> Data {
83+
let salt = salt ?? Data(repeating: 0, count: digest.digestSize)
84+
85+
// Generate the PRK (extract)
86+
let prk = HMAC.mac(for: ikm, using: salt, digest: digest)
87+
88+
// Prepare the OKM
89+
var mixin = Data()
90+
var derived = Data()
91+
92+
// Perform the derivation (expand)
93+
for iteration in 0 ..< Int(ceil(Double(size) / Double(digest.digestSize))) {
94+
mixin.append(info)
95+
mixin.append(Data([1 + UInt8(iteration)]))
96+
mixin = HMAC.mac(for: mixin, using: prk, digest: digest)
97+
derived += mixin
98+
}
99+
100+
return derived.subdata(in: 0 ..< size)
101+
}
102+
}
103+
104+
public struct ANSIX963: KDFunction {
105+
var parameters: CCKDFParametersRef?
106+
107+
@available(iOS 13.0, macOS 10.15, *)
108+
init(info: Data) throws {
109+
try CCKDFParametersCreateAnsiX963(&parameters, sharedInfo: info)
110+
}
111+
112+
public static func derive(_ key: Data, info: Data, digest: Digest.Algorithm, size: Int = 32) throws -> Data {
113+
if #available(iOS 13.0, macOS 10.15, *) {
114+
print("Available")
115+
return try Self(info: info).derive(digest: digest, key: key, size: size)
116+
} else {
117+
return try ansix963(key, info: info, digest: digest, size: size)
118+
}
119+
}
120+
121+
@available(iOS, deprecated: 13.0)
122+
@available(macOS, deprecated: 10.15)
123+
fileprivate static func ansix963(_ ikm: Data, info: Data, digest: Digest.Algorithm, size: Int) throws -> Data {
124+
var counter: UInt32 = 1
125+
var derived = Data()
126+
127+
for _ in 0 ..< Int(ceil(Double(size) / Double(digest.digestSize))) + 1 {
128+
let hasher = try Digest(algorithm: digest)
129+
try hasher.update(ikm)
130+
try hasher.update(counter.bigEndian.bytes)
131+
try hasher.update(info)
132+
derived.append(try hasher.finalize())
133+
hasher.destroy()
134+
counter += 1
135+
}
136+
137+
return derived.subdata(in: 0 ..< size)
138+
}
139+
}
140+
141+
/// Extension used to extract the bigendian bytes of the counter.
142+
fileprivate extension UInt32 {
143+
var bytes: Data {
144+
var copy = self
145+
return Data(bytes: &copy, count: MemoryLayout<Self>.size)
146+
}
147+
}

0 commit comments

Comments
 (0)