1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftCrypto open source project
//
// Copyright (c) 2019-2020 Apple Inc. and the SwiftCrypto project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.md for the list of SwiftCrypto project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API
@_exported import CryptoKit
#else
import Foundation
/// A standards-based implementation of an HMAC-based Key Derivation Function
/// (HKDF).
///
/// The key derivation functions allow you to derive one or more secrets of the
/// size of your choice from a main key or passcode. The key derivation function
/// is compliant with IETF RFC 5869. Use one of the `deriveKey` functions, such
/// as ``deriveKey(inputKeyMaterial:outputByteCount:)`` or
/// ``deriveKey(inputKeyMaterial:salt:info:outputByteCount:)``, to derive a key
/// from a main secret or passcode in a single function.
///
/// To derive a key with more fine-grained control, use
/// ``extract(inputKeyMaterial:salt:)`` to create cryptographically strong key
/// material in the form of a hashed authentication code, then call
/// ``expand(pseudoRandomKey:info:outputByteCount:)`` using that key material to
/// generate a symmetric key of the length you specify.
public struct HKDF<H: HashFunction> {
/// Derives a symmetric encryption key from a main key or passcode using
/// HKDF key derivation with information and salt you specify.
///
/// - Parameters:
/// - inputKeyMaterial: The main key or passcode the derivation function
/// uses to derive a key.
/// - salt: The salt to use for key derivation.
/// - info: The shared information to use for key derivation.
/// - outputByteCount: The length in bytes of the resulting symmetric key.
///
/// - Returns: The derived symmetric key.
public static func deriveKey<Salt: DataProtocol, Info: DataProtocol>(inputKeyMaterial: SymmetricKey,
salt: Salt,
info: Info,
outputByteCount: Int) -> SymmetricKey {
return expand(pseudoRandomKey: extract(inputKeyMaterial: inputKeyMaterial, salt: salt), info: info, outputByteCount: outputByteCount)
}
/// Derives a symmetric encryption key from a main key or passcode using
/// HKDF key derivation with information you specify.
///
/// - Parameters:
/// - inputKeyMaterial: The main key or passcode the derivation function
/// uses to derive a key.
/// - info: The shared information to use for key derivation.
/// - outputByteCount: The length in bytes of the resulting symmetric key.
///
/// - Returns: The derived symmetric key.
public static func deriveKey<Info: DataProtocol>(inputKeyMaterial: SymmetricKey,
info: Info,
outputByteCount: Int) -> SymmetricKey {
return deriveKey(inputKeyMaterial: inputKeyMaterial, salt: [UInt8](), info: info, outputByteCount: outputByteCount)
}
/// Derives a symmetric encryption key from a main key or passcode using
/// HKDF key derivation with salt that you specify.
///
/// - Parameters:
/// - inputKeyMaterial: The main key or passcode the derivation function
/// uses to derive a key.
/// - salt: The salt to use for key derivation.
/// - outputByteCount: The length in bytes of the resulting symmetric key.
///
/// - Returns: The derived symmetric key.
public static func deriveKey<Salt: DataProtocol>(inputKeyMaterial: SymmetricKey,
salt: Salt,
outputByteCount: Int) -> SymmetricKey {
return deriveKey(inputKeyMaterial: inputKeyMaterial, salt: salt, info: [UInt8](), outputByteCount: outputByteCount)
}
/// Derives a symmetric encryption key from a main key or passcode using
/// HKDF key derivation.
///
/// - Parameters:
/// - inputKeyMaterial: The main key or passcode the derivation function
/// uses to derive a key.
/// - outputByteCount: The length in bytes of the resulting symmetric key.
///
/// - Returns: The derived symmetric key.
public static func deriveKey(inputKeyMaterial: SymmetricKey,
outputByteCount: Int) -> SymmetricKey {
return deriveKey(inputKeyMaterial: inputKeyMaterial, salt: [UInt8](), info: [UInt8](), outputByteCount: outputByteCount)
}
/// Creates cryptographically strong key material from a main key or
/// passcode that you specify.
///
/// Generate a derived symmetric key from the cryptographically strong key
/// material this function creates by calling
/// ``expand(pseudoRandomKey:info:outputByteCount:)``.
///
/// - Parameters:
/// - inputKeyMaterial: The main key or passcode the derivation function
/// uses to derive a key.
/// - salt: The salt to use for key derivation.
///
/// - Returns: A pseudorandom, cryptographically strong key in the form of a
/// hashed authentication code.
public static func extract<Salt: DataProtocol>(inputKeyMaterial: SymmetricKey, salt: Salt?) -> HashedAuthenticationCode<H> {
let key: SymmetricKey
if let salt = salt {
if salt.regions.count != 1 {
let contiguousBytes = Array(salt)
key = SymmetricKey(data: contiguousBytes)
} else {
key = SymmetricKey(data: salt.regions.first!)
}
} else {
key = SymmetricKey(data: [UInt8]())
}
return inputKeyMaterial.withUnsafeBytes { ikmBytes in
return HMAC<H>.authenticationCode(for: ikmBytes, using: key)
}
}
/// Expands cryptographically strong key material into a derived symmetric
/// key.
///
/// Generate cryptographically strong key material to use with this function
/// by calling ``extract(inputKeyMaterial:salt:)``.
///
/// - Parameters:
/// - prk: A pseudorandom, cryptographically strong key generated from the
/// ``extract(inputKeyMaterial:salt:)`` function.
/// - info: The shared information to use for key derivation.
/// - outputByteCount: The length in bytes of the resulting symmetric key.
///
/// - Returns: The derived symmetric key.
public static func expand<PRK: ContiguousBytes, Info: DataProtocol>(pseudoRandomKey prk: PRK, info: Info?, outputByteCount: Int) -> SymmetricKey {
let iterations: UInt8 = UInt8(ceil((Float(outputByteCount) / Float(H.Digest.byteCount))))
var output = SecureBytes()
let key = SymmetricKey(data: prk)
var TMinusOne = SecureBytes()
for i in 1...iterations {
var hmac = HMAC<H>(key: key)
hmac.update(data: TMinusOne)
if let info = info {
hmac.update(data: info)
}
withUnsafeBytes(of: i) { counter in
hmac.update(bufferPointer: counter)
}
TMinusOne = SecureBytes(hmac.finalize())
output.append(TMinusOne)
}
return SymmetricKey(data: output.prefix(outputByteCount))
}
}
#endif // Linux or !SwiftPM
|