HKDF.swift 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. //
  2. // CryptoSwift
  3. //
  4. // Copyright (C) 2014-2017 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
  5. // This software is provided 'as-is', without any express or implied warranty.
  6. //
  7. // In no event will the authors be held liable for any damages arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
  10. //
  11. // - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
  12. // - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
  13. // - This notice may not be removed or altered from any source or binary distribution.
  14. //
  15. // https://www.ietf.org/rfc/rfc5869.txt
  16. //
  17. #if canImport(Darwin)
  18. import Darwin
  19. #else
  20. import Glibc
  21. #endif
  22. /// A key derivation function.
  23. ///
  24. /// HKDF - HMAC-based Extract-and-Expand Key Derivation Function.
  25. public struct HKDF {
  26. public enum Error: Swift.Error {
  27. case invalidInput
  28. case derivedKeyTooLong
  29. }
  30. private let numBlocks: Int // l
  31. private let dkLen: Int
  32. private let info: Array<UInt8>
  33. fileprivate let prk: Array<UInt8>
  34. fileprivate let variant: HMAC.Variant
  35. /// - parameters:
  36. /// - variant: hash variant
  37. /// - salt: optional salt (if not provided, it is set to a sequence of variant.digestLength zeros)
  38. /// - info: optional context and application specific information
  39. /// - keyLength: intended length of derived key
  40. public init(password: Array<UInt8>, salt: Array<UInt8>? = nil, info: Array<UInt8>? = nil, keyLength: Int? = nil /* dkLen */, variant: HMAC.Variant = .sha256) throws {
  41. guard !password.isEmpty else {
  42. throw Error.invalidInput
  43. }
  44. let dkLen = keyLength ?? variant.digestLength
  45. let keyLengthFinal = Double(dkLen)
  46. let hLen = Double(variant.digestLength)
  47. let numBlocks = Int(ceil(keyLengthFinal / hLen)) // l = ceil(keyLength / hLen)
  48. guard numBlocks <= 255 else {
  49. throw Error.derivedKeyTooLong
  50. }
  51. /// HKDF-Extract(salt, password) -> PRK
  52. /// - PRK - a pseudo-random key; it is used by calculate()
  53. prk = try HMAC(key: salt ?? [], variant: variant).authenticate(password)
  54. self.info = info ?? []
  55. self.variant = variant
  56. self.dkLen = dkLen
  57. self.numBlocks = numBlocks
  58. }
  59. public func calculate() throws -> Array<UInt8> {
  60. let hmac = HMAC(key: prk, variant: variant)
  61. var ret = Array<UInt8>()
  62. ret.reserveCapacity(numBlocks * variant.digestLength)
  63. var value = Array<UInt8>()
  64. for i in 1...numBlocks {
  65. value.append(contentsOf: info)
  66. value.append(UInt8(i))
  67. let bytes = try hmac.authenticate(value)
  68. ret.append(contentsOf: bytes)
  69. /// update value to use it as input for next iteration
  70. value = bytes
  71. }
  72. return Array(ret.prefix(dkLen))
  73. }
  74. }