Skip to main content

crypto_kmac/
algorithm.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: Apache-2.0
4
5//! KMAC256 implementation and typed secret-material owners.
6
7use crypto_core::{CryptoError, KdfAlgorithm, KdfFailureKind, KdfProfile};
8#[cfg(any(feature = "native", feature = "wasm"))]
9use sha3_kmac::Kmac256;
10use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
11
12/// Minimum KMAC256 key length required by its 256-bit security-strength
13/// instantiation.
14pub const KMAC256_MIN_KEY_LENGTH: usize = 32;
15
16/// Maximum KMAC256 key length accepted by the public primitive.
17///
18/// Key-derivation keys are compact cryptographic material. The cap prevents
19/// attacker-controlled allocation when a boundary copies the key into its
20/// zeroizing owner.
21pub const KMAC256_MAX_KEY_LENGTH: usize = 4_096;
22
23/// Maximum protocol context length accepted by the public primitive.
24pub const KMAC256_MAX_CONTEXT_LENGTH: usize = 65_536;
25
26/// Maximum KMAC customization-string length accepted by the public primitive.
27pub const KMAC256_MAX_CUSTOMIZATION_LENGTH: usize = 4_096;
28
29/// Maximum output accepted by the public primitive.
30///
31/// Protocols normally derive compact traffic or wrapping keys. Bounding the
32/// output prevents attacker-controlled allocation when this API is used behind
33/// an FFI or protobuf adapter.
34pub const KMAC256_MAX_OUTPUT_LENGTH: usize = 65_536;
35
36/// Secret KMAC256 key-derivation key.
37#[derive(Zeroize, ZeroizeOnDrop)]
38pub struct Kmac256Key {
39    bytes: Zeroizing<Vec<u8>>,
40}
41
42impl Kmac256Key {
43    /// Constructs a KMAC256 key after enforcing the minimum security-strength
44    /// length.
45    ///
46    /// # Errors
47    ///
48    /// Returns a typed KDF error when `input` is outside the accepted key
49    /// length range.
50    pub fn from_slice(input: &[u8]) -> Result<Self, CryptoError> {
51        if !(KMAC256_MIN_KEY_LENGTH..=KMAC256_MAX_KEY_LENGTH).contains(&input.len()) {
52            return Err(kmac_error(KdfFailureKind::InvalidSecretLength));
53        }
54
55        Ok(Self {
56            bytes: Zeroizing::new(input.to_vec()),
57        })
58    }
59
60    /// Borrows the secret key bytes.
61    pub fn as_bytes(&self) -> &[u8] {
62        &self.bytes
63    }
64}
65
66/// Output from KMAC256 key derivation.
67#[derive(Zeroize, ZeroizeOnDrop)]
68pub struct Kmac256Output {
69    bytes: Zeroizing<Vec<u8>>,
70}
71
72impl Kmac256Output {
73    /// Borrows the derived key bytes.
74    pub fn as_bytes(&self) -> &[u8] {
75        &self.bytes
76    }
77
78    /// Returns the derived-key length in bytes.
79    pub fn len(&self) -> usize {
80        self.bytes.len()
81    }
82
83    /// Returns whether the output is empty.
84    pub fn is_empty(&self) -> bool {
85        self.bytes.is_empty()
86    }
87}
88
89/// Derives `output_length` bytes with KMAC256.
90///
91/// `context` is the SP 800-185 KMAC input string `X`; `customization` is the
92/// customization string `S`. Protocols must construct and serialize their
93/// context deterministically before calling this primitive. This API is not
94/// the separately specified SP 800-108 KMAC-based key-derivation construction.
95///
96/// # Errors
97///
98/// Returns a typed KDF error for an empty or oversized output request or if
99/// the backend rejects the key.
100#[cfg(any(feature = "native", feature = "wasm"))]
101pub fn derive_kmac256(
102    key: &Kmac256Key,
103    context: &[u8],
104    customization: &[u8],
105    output_length: usize,
106) -> Result<Kmac256Output, CryptoError> {
107    if output_length == 0 || output_length > KMAC256_MAX_OUTPUT_LENGTH {
108        return Err(kmac_error(KdfFailureKind::InvalidOutputLength));
109    }
110    if context.len() > KMAC256_MAX_CONTEXT_LENGTH
111        || customization.len() > KMAC256_MAX_CUSTOMIZATION_LENGTH
112    {
113        return Err(kmac_error(KdfFailureKind::InvalidParams));
114    }
115
116    let mut kmac = Kmac256::new(key.as_bytes(), customization)
117        .map_err(|_| kmac_error(KdfFailureKind::InvalidSecretLength))?;
118    kmac.update(context);
119    let mut output = Zeroizing::new(vec![0u8; output_length]);
120    kmac.finalize_into(&mut output);
121    Ok(Kmac256Output { bytes: output })
122}
123
124fn kmac_error(kind: KdfFailureKind) -> CryptoError {
125    CryptoError::Kdf {
126        algorithm: KdfAlgorithm::Kmac256,
127        profile: KdfProfile::Sp800185Kmac256,
128        kind,
129    }
130}