|
| 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(¶meters, 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(¶meters, label: label, context: context) |
| 49 | + } |
| 50 | + |
| 51 | + init(context: Data) throws { |
| 52 | + try CCKDFParametersCreateCtrHmacFixed(¶meters, 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(¶meters, 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(¶meters, 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: ©, count: MemoryLayout<Self>.size) |
| 146 | + } |
| 147 | +} |
0 commit comments