Skip to main content

crypto_argon2id/
derive.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use argon2::{Algorithm, Argon2, Params, Version};
6use crypto_core::{CryptoError, KdfAlgorithm, KdfFailureKind};
7use zeroize::{Zeroize, Zeroizing};
8
9use crate::constants::ARGON2ID_DERIVED_KEY_LENGTH;
10use crate::material::{Argon2Salt, Argon2Secret, Argon2idDerivedKey};
11use crate::profile::{Argon2KdfVersion, Argon2Profile};
12
13/// Inputs for a single Argon2id key-derivation operation.
14pub struct DeriveKeyRequest<'a> {
15    /// Parameter profile selecting the cost tuple.
16    pub profile: Argon2Profile,
17    /// Secret input (e.g. password) to hash.
18    pub secret: &'a Argon2Secret,
19    /// Salt to bind the derivation to.
20    pub salt: &'a Argon2Salt,
21}
22
23/// Derives a 32-byte key with Argon2id using the request's profile, secret, and
24/// salt. Returns an error on invalid parameters or derivation failure. The
25/// intermediate output buffer is zeroized before returning.
26pub fn derive_key(request: &DeriveKeyRequest<'_>) -> Result<Argon2idDerivedKey, CryptoError> {
27    let profile = request.profile;
28    let kdf_profile = profile.to_kdf_profile();
29    let (m_cost, t_cost, p_cost) = profile.params_tuple();
30
31    let params =
32        Params::new(m_cost, t_cost, p_cost, Some(ARGON2ID_DERIVED_KEY_LENGTH)).map_err(|_| {
33            CryptoError::Kdf {
34                algorithm: KdfAlgorithm::Argon2id,
35                profile: kdf_profile,
36                kind: KdfFailureKind::InvalidParams,
37            }
38        })?;
39
40    let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
41
42    let mut output = Zeroizing::new(vec![0u8; ARGON2ID_DERIVED_KEY_LENGTH]);
43    argon2
44        .hash_password_into(
45            request.secret.as_bytes(),
46            request.salt.as_bytes(),
47            &mut output,
48        )
49        .map_err(|_| CryptoError::Kdf {
50            algorithm: KdfAlgorithm::Argon2id,
51            profile: kdf_profile,
52            kind: KdfFailureKind::DerivationFailed,
53        })?;
54
55    let mut bytes = [0u8; ARGON2ID_DERIVED_KEY_LENGTH];
56    bytes.copy_from_slice(&output);
57    output.zeroize();
58
59    Ok(Argon2idDerivedKey::from_array(bytes))
60}
61
62/// Derives a 32-byte key from raw secret and salt bytes, selecting the profile
63/// from the KDF version integer. Returns an error if the version is unrecognized,
64/// the secret or salt length is invalid, or derivation fails.
65pub fn derive_key_for_version(
66    kdf_version: u32,
67    secret: &[u8],
68    salt: &[u8],
69) -> Result<Argon2idDerivedKey, CryptoError> {
70    let version = Argon2KdfVersion::try_from(kdf_version)?;
71    let profile = Argon2Profile::from(version);
72
73    let secret = Argon2Secret::from_slice(secret, profile)?;
74    let salt = Argon2Salt::from_slice(salt, profile)?;
75
76    derive_key(&DeriveKeyRequest {
77        profile,
78        secret: &secret,
79        salt: &salt,
80    })
81}