Skip to main content

crypto_hkdf/
material.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
6
7/// Length in bytes of an HKDF-SHA384 pseudorandom key.
8pub const HKDF_SHA384_PRK_LENGTH: usize = 48;
9
10/// HKDF input keying material, zeroized on drop.
11#[derive(Zeroize, ZeroizeOnDrop)]
12pub struct HkdfInputKeyMaterial {
13    bytes: Vec<u8>,
14}
15
16impl HkdfInputKeyMaterial {
17    /// Build input keying material by copying the given bytes.
18    pub fn from_slice(input: &[u8]) -> Self {
19        Self {
20            bytes: input.to_vec(),
21        }
22    }
23
24    /// Borrow the input keying material bytes.
25    pub fn as_bytes(&self) -> &[u8] {
26        &self.bytes
27    }
28}
29
30/// Optional HKDF salt value, zeroized on drop.
31///
32/// HKDF salts can carry protocol or user correlation material. The wrapper
33/// deliberately has no `Debug` or `Clone` implementation so accidental logs
34/// and unmanaged duplicate buffers are not part of the public API.
35#[derive(Zeroize, ZeroizeOnDrop)]
36pub struct HkdfSalt {
37    bytes: Vec<u8>,
38}
39
40impl HkdfSalt {
41    /// Build an HKDF salt by copying the given bytes.
42    pub fn from_slice(input: &[u8]) -> Self {
43        Self {
44            bytes: input.to_vec(),
45        }
46    }
47
48    /// Borrow the salt bytes.
49    pub fn as_bytes(&self) -> &[u8] {
50        &self.bytes
51    }
52}
53
54/// HKDF `info` context/application-binding value.
55///
56/// Context can contain stable protocol or user bindings. The owner clears its
57/// allocation on drop and redacts `Debug` output so those bindings are not
58/// retained in allocator memory or copied into logs.
59#[derive(Zeroize, ZeroizeOnDrop, PartialEq, Eq)]
60pub struct HkdfInfo {
61    bytes: Vec<u8>,
62}
63
64impl core::fmt::Debug for HkdfInfo {
65    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66        formatter.write_str("HkdfInfo(<redacted>)")
67    }
68}
69
70impl HkdfInfo {
71    /// Build an HKDF `info` value by copying the given bytes.
72    pub fn from_slice(input: &[u8]) -> Self {
73        Self {
74            bytes: input.to_vec(),
75        }
76    }
77
78    /// Borrow the `info` bytes.
79    pub fn as_bytes(&self) -> &[u8] {
80        &self.bytes
81    }
82
83    pub(crate) fn from_vec(bytes: Vec<u8>) -> Self {
84        Self { bytes }
85    }
86}
87
88/// Fixed-length HKDF output keying material, zeroized on drop.
89#[derive(Zeroize, ZeroizeOnDrop)]
90pub struct HkdfOutput<const N: usize> {
91    bytes: [u8; N],
92}
93
94/// Pseudorandom key produced by HKDF-SHA384 extract.
95///
96/// The PRK is secret key material. It has no `Debug`, `Display`, `Clone`, or
97/// serialization implementation and is zeroized when its owner is dropped.
98#[derive(Zeroize, ZeroizeOnDrop)]
99pub struct HkdfSha384Prk {
100    bytes: [u8; HKDF_SHA384_PRK_LENGTH],
101}
102
103impl HkdfSha384Prk {
104    /// Borrows the fixed-size PRK for use by HKDF expand.
105    pub fn as_bytes(&self) -> &[u8; HKDF_SHA384_PRK_LENGTH] {
106        &self.bytes
107    }
108
109    pub(crate) fn from_array(bytes: [u8; HKDF_SHA384_PRK_LENGTH]) -> Self {
110        Self { bytes }
111    }
112}
113
114impl<const N: usize> HkdfOutput<N> {
115    /// Borrow the `N`-byte derived output.
116    pub fn as_bytes(&self) -> &[u8; N] {
117        &self.bytes
118    }
119
120    /// Consumes the output and returns an owned zeroizing array.
121    pub fn into_zeroizing(self) -> Zeroizing<[u8; N]> {
122        Zeroizing::new(self.bytes)
123    }
124
125    pub(crate) fn from_array(bytes: [u8; N]) -> Self {
126        Self { bytes }
127    }
128}