pub fn encoded_len(
algorithm: Algorithm,
t_cost: u32,
m_cost: u32,
lanes: u32,
salt_len: u32,
hash_len: u32,
) -> usizeExpand description
argon2_encodedlen(...) from src/argon2.c.
strlen("$$v=$m=,t=,p=$$") + strlen(type) + numlen(t_cost) + numlen(m_cost)
+ numlen(parallelism) + b64len(saltlen) + b64len(hashlen)
+ numlen(ARGON2_VERSION_NUMBER) + 1The trailing + 1 is the C string’s NUL terminator. It is kept so the value
matches the C byte for byte; a Rust String is one byte shorter. It is
also exactly the buffer size encode_string wants, which reserves the
same byte (see there).
Note the C uses numlen(ARGON2_VERSION_NUMBER) and not the version actually
being encoded. Both 0x10 (16) and 0x13 (19) are two digits, so it makes
no difference; it is kept verbatim. (In the C it can differ, because
ctx->version is a raw uint32_t: with version = 0 the string is one
byte shorter than advertised, and a buffer of argon2_encodedlen() - 1
suffices. Measured on 1051 of 30000 fuzzed cases, all of them version = 0.
Version is a closed enum of two two-digit values, so the size is always
exact here — see encode_needs_encoded_len_bytes_exactly.)
§Argument order
t_cost comes before m_cost here, which is the reverse of
Params::new:
Params::new(m_cost, t_cost, lanes, output_len)
encoded_len(algorithm, t_cost, m_cost, lanes, salt_len, hash_len)
^^^^^^^^^^^^^^ reversed against Params::newThe order is the C’s, kept so a call can be transcribed position for
position: argon2_encodedlen(t_cost, m_cost, parallelism, saltlen, hashlen, type), declared at argon2.h:429 and defined at argon2.c:447. One
argument did move. type went from last to first and became algorithm.
The other five kept their order among themselves; parallelism is spelled
lanes here, the name Params uses for it.
algorithm comes first here and on the rest of the encode-and-construct
side; the verify family keeps the C’s trailing type and takes it last.
use argon2_rust::{Algorithm, Params, encoded_len};
// Params::new takes m_cost first.
let params = Params::new(65536, 3, 1, 32).unwrap();
assert_eq!((params.m_cost(), params.t_cost()), (65536, 3));
// encoded_len takes t_cost first: the same two costs, the other way round.
let n = encoded_len(
Algorithm::Argon2id,
params.t_cost(),
params.m_cost(),
params.lanes(),
16, // salt_len
32, // hash_len
);
assert_eq!(n, 98);§Against a string the crate really produced
The value is a C buffer size, so it counts the NUL terminator described
above and a Rust String is one byte shorter. That is the whole of the
relationship, and it is exact rather than an upper bound - see
encode_needs_encoded_len_bytes_exactly:
use argon2_rust::{Algorithm, Argon2, Params, Version, encoded_len};
let params = Params::new(64, 1, 1, 32)?;
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
let encoded = argon2.hash_encoded(b"password", b"somesalt")?;
// `salt_len` is the salt's own 8 bytes, not the 11 its base64 occupies.
let n = encoded_len(Algorithm::Argon2id, 1, 64, 1, 8, 32);
assert_eq!(n, 84);
assert_eq!(encoded.len(), n - 1);
assert_eq!(
encoded,
"$argon2id$v=19$m=64,t=1,p=1$c29tZXNhbHQ$cpx6VEQbwTVZvcpxNIxOVUWZ5xnAipUmAe1cg2GMG70",
);