hkdf.rs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. //! HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
  19. //! https://tools.ietf.org/html/rfc5869
  20. use core::fmt;
  21. use digest::{
  22. crypto_common::BlockSizeUser, typenum::Unsigned, Digest, Output, OutputSizeUser, Update,
  23. };
  24. use super::hmac::Hmac;
  25. /// Structure for InvalidPrkLength, used for output error handling.
  26. #[derive(Copy, Clone, Debug)]
  27. pub struct InvalidPrkLength;
  28. impl fmt::Display for InvalidPrkLength {
  29. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  30. f.write_str("invalid pseudorandom key length, too short")
  31. }
  32. }
  33. /// Structure for InvalidLength, used for output error handling.
  34. #[derive(Copy, Clone, Debug)]
  35. pub struct InvalidLength;
  36. impl fmt::Display for InvalidLength {
  37. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  38. f.write_str("invalid number of blocks, too large output")
  39. }
  40. }
  41. /// HKDF-Extract for arbitrary hash functions implementing `Digest`
  42. /// and `BlockSizeUser` traits.
  43. #[derive(Clone)]
  44. pub struct HkdfExtract<H: Digest + BlockSizeUser + Clone> {
  45. hmac: Hmac<H>,
  46. }
  47. impl<H: Digest + BlockSizeUser + Clone> HkdfExtract<H> {
  48. /// Iniitialize a new `HkdfExtract` with the given salt.
  49. pub fn new(salt: &[u8]) -> Self {
  50. Self { hmac: Hmac::<H>::new_from_slice(salt) }
  51. }
  52. /// Feeds in additional input key material to the HKDF-Extract context.
  53. pub fn input_ikm(&mut self, ikm: &[u8]) {
  54. self.hmac.update(ikm);
  55. }
  56. /// Completes the HKDF-Extract operation, returning both the generated
  57. /// pseudorandom key and `Hkdf` struct for expanding.
  58. pub fn finalize(self) -> (Output<H>, Hkdf<H>) {
  59. let prk = self.hmac.finalize();
  60. let hkdf = Hkdf::from_prk(&prk).expect("PRK size is correct");
  61. (prk, hkdf)
  62. }
  63. }
  64. /// Structure representing the HKDF, capable of HKDF-Expand
  65. /// and HKDF-Extract operations.
  66. #[derive(Clone)]
  67. pub struct Hkdf<H: Digest + BlockSizeUser + Clone> {
  68. hmac: Hmac<H>,
  69. }
  70. impl<H: Digest + BlockSizeUser + Clone> Hkdf<H> {
  71. /// Convenience method for `extract` when the generated pseudorandom
  72. /// key can be ignored and only the HKDF-Expand operation is needed.
  73. pub fn new(salt: &[u8], ikm: &[u8]) -> Self {
  74. let (_, hkdf) = Self::extract(salt, ikm);
  75. hkdf
  76. }
  77. /// HKDF-Extract operation returning both the generated pseudorandom
  78. /// key and `Hkdf` struct for expanding.
  79. pub fn extract(salt: &[u8], ikm: &[u8]) -> (Output<H>, Self) {
  80. let mut extract_ctx = HkdfExtract::new(salt);
  81. extract_ctx.input_ikm(ikm);
  82. extract_ctx.finalize()
  83. }
  84. /// Create `Hkdf` from an already cryptographically strong pseudorandom key.
  85. pub fn from_prk(prk: &[u8]) -> Result<Self, InvalidPrkLength> {
  86. if prk.len() < <H as OutputSizeUser>::OutputSize::to_usize() {
  87. return Err(InvalidPrkLength)
  88. }
  89. Ok(Self { hmac: Hmac::<H>::new_from_slice(prk) })
  90. }
  91. /// HKDF-Expand operation. If you don't have any `info` to pass, use
  92. /// an empty slice.
  93. pub fn expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength> {
  94. self.expand_multi_info(&[info], okm)
  95. }
  96. pub fn expand_multi_info(&self, infos: &[&[u8]], okm: &mut [u8]) -> Result<(), InvalidLength> {
  97. let mut prev: Option<Output<H>> = None;
  98. let chunk_len = <H as OutputSizeUser>::OutputSize::USIZE;
  99. if okm.len() > chunk_len * 255 {
  100. return Err(InvalidLength)
  101. }
  102. for (block_n, block) in okm.chunks_mut(chunk_len).enumerate() {
  103. let mut hmac = self.hmac.clone();
  104. if let Some(ref prev) = prev {
  105. hmac.update(prev);
  106. }
  107. // Feed in the info components in sequence. This is equivalent
  108. // to feeding in the concatenation of all the info components.
  109. for info in infos {
  110. hmac.update(info);
  111. }
  112. hmac.update(&[block_n as u8 + 1]);
  113. let output = hmac.finalize();
  114. let block_len = block.len();
  115. block.copy_from_slice(&output[..block_len]);
  116. prev = Some(output);
  117. }
  118. Ok(())
  119. }
  120. }