HKDF.swift 3.2 KB

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