hkdf.odin 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. /*
  2. package hkdf implements the HKDF HMAC-based Extract-and-Expand Key
  3. Derivation Function.
  4. See: https://www.rfc-editor.org/rfc/rfc5869
  5. */
  6. package hkdf
  7. import "core:crypto/hash"
  8. import "core:crypto/hmac"
  9. import "core:mem"
  10. // extract_and_expand derives output keying material (OKM) via the
  11. // HKDF-Extract and HKDF-Expand algorithms, with the specified has
  12. // function, salt, input keying material (IKM), and optional info.
  13. // The dst buffer must be less-than-or-equal to 255 HMAC tags.
  14. extract_and_expand :: proc(algorithm: hash.Algorithm, salt, ikm, info, dst: []byte) {
  15. h_len := hash.DIGEST_SIZES[algorithm]
  16. tmp: [hash.MAX_DIGEST_SIZE]byte
  17. prk := tmp[:h_len]
  18. defer mem.zero_explicit(raw_data(prk), h_len)
  19. extract(algorithm, salt, ikm, prk)
  20. expand(algorithm, prk, info, dst)
  21. }
  22. // extract derives a pseudorandom key (PRK) via the HKDF-Extract algorithm,
  23. // with the specified hash function, salt, and input keying material (IKM).
  24. // It requires that the dst buffer be the HMAC tag size for the specified
  25. // hash function.
  26. extract :: proc(algorithm: hash.Algorithm, salt, ikm, dst: []byte) {
  27. // PRK = HMAC-Hash(salt, IKM)
  28. hmac.sum(algorithm, dst, ikm, salt)
  29. }
  30. // expand derives output keying material (OKM) via the HKDF-Expand algorithm,
  31. // with the specified hash function, pseudorandom key (PRK), and optional
  32. // info. The dst buffer must be less-than-or-equal to 255 HMAC tags.
  33. expand :: proc(algorithm: hash.Algorithm, prk, info, dst: []byte) {
  34. h_len := hash.DIGEST_SIZES[algorithm]
  35. // (<= 255*HashLen)
  36. dk_len := len(dst)
  37. switch {
  38. case dk_len == 0:
  39. return
  40. case dk_len > h_len * 255:
  41. panic("crypto/hkdf: derived key too long")
  42. case:
  43. }
  44. // The output OKM is calculated as follows:
  45. //
  46. // N = ceil(L/HashLen)
  47. // T = T(1) | T(2) | T(3) | ... | T(N)
  48. // OKM = first L octets of T
  49. //
  50. // where:
  51. // T(0) = empty string (zero length)
  52. // T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
  53. // T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
  54. // T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
  55. // ...
  56. n := dk_len / h_len
  57. r := dk_len % h_len
  58. base: hmac.Context
  59. defer hmac.reset(&base)
  60. hmac.init(&base, algorithm, prk)
  61. dst_blk := dst
  62. prev: []byte
  63. for i in 1 ..= n {
  64. _F(&base, prev, info, i, dst_blk[:h_len])
  65. prev = dst_blk[:h_len]
  66. dst_blk = dst_blk[h_len:]
  67. }
  68. if r > 0 {
  69. tmp: [hash.MAX_DIGEST_SIZE]byte
  70. blk := tmp[:h_len]
  71. defer mem.zero_explicit(raw_data(blk), h_len)
  72. _F(&base, prev, info, n + 1, blk)
  73. copy(dst_blk, blk)
  74. }
  75. }
  76. @(private)
  77. _F :: proc(base: ^hmac.Context, prev, info: []byte, i: int, dst_blk: []byte) {
  78. prf: hmac.Context
  79. hmac.clone(&prf, base)
  80. hmac.update(&prf, prev)
  81. hmac.update(&prf, info)
  82. hmac.update(&prf, []byte{u8(i)})
  83. hmac.final(&prf, dst_blk)
  84. }