HKDF.swift 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. //
  2. // CryptoSwift
  3. //
  4. // Copyright (C) 2014-2021 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. #elseif canImport(Glibc)
  20. import Glibc
  21. #elseif canImport(ucrt)
  22. import ucrt
  23. #endif
  24. /// A key derivation function.
  25. ///
  26. /// HKDF - HMAC-based Extract-and-Expand Key Derivation Function.
  27. public struct HKDF {
  28. public enum Error: Swift.Error {
  29. case invalidInput
  30. case derivedKeyTooLong
  31. }
  32. private let numBlocks: Int // l
  33. private let dkLen: Int
  34. private let info: Array<UInt8>
  35. private let prk: Array<UInt8>
  36. private let variant: HMAC.Variant
  37. /// - parameters:
  38. /// - variant: hash variant
  39. /// - salt: optional salt (if not provided, it is set to a sequence of variant.digestLength zeros)
  40. /// - info: optional context and application specific information
  41. /// - keyLength: intended length of derived key
  42. public init(password: Array<UInt8>, salt: Array<UInt8>? = nil, info: Array<UInt8>? = nil, keyLength: Int? = nil /* dkLen */, variant: HMAC.Variant = .sha2(.sha256)) throws {
  43. guard !password.isEmpty else {
  44. throw Error.invalidInput
  45. }
  46. let dkLen = keyLength ?? variant.digestLength
  47. let keyLengthFinal = Double(dkLen)
  48. let hLen = Double(variant.digestLength)
  49. let numBlocks = Int(ceil(keyLengthFinal / hLen)) // l = ceil(keyLength / hLen)
  50. guard numBlocks <= 255 else {
  51. throw Error.derivedKeyTooLong
  52. }
  53. /// HKDF-Extract(salt, password) -> PRK
  54. /// - PRK - a pseudo-random key; it is used by calculate()
  55. self.prk = try HMAC(key: salt ?? [], variant: variant).authenticate(password)
  56. self.info = info ?? []
  57. self.variant = variant
  58. self.dkLen = dkLen
  59. self.numBlocks = numBlocks
  60. }
  61. public func calculate() throws -> Array<UInt8> {
  62. let hmac = HMAC(key: prk, variant: variant)
  63. var ret = Array<UInt8>()
  64. ret.reserveCapacity(self.numBlocks * self.variant.digestLength)
  65. var value = Array<UInt8>()
  66. for i in 1...self.numBlocks {
  67. value.append(contentsOf: self.info)
  68. value.append(UInt8(i))
  69. let bytes = try hmac.authenticate(value)
  70. ret.append(contentsOf: bytes)
  71. /// update value to use it as input for next iteration
  72. value = bytes
  73. }
  74. return Array(ret.prefix(self.dkLen))
  75. }
  76. }