Skip to main content

pakery_cpace/
generator.rs

1//! Generator calculation per draft-irtf-cfrg-cpace-18.
2//!
3//! ```text
4//! generator_string(DSI, PRS, CI, sid, s_in_bytes):
5//!     len_zpad = max(0, s_in_bytes - 1 - len(prepend_len(PRS)) - len(prepend_len(DSI)))
6//!     return lv_cat(DSI, PRS, zero_bytes(len_zpad), CI, sid)
7//!
8//! calculate_generator(PRS, CI, sid):
9//!     gen_str = generator_string(DSI, PRS, CI, sid, H.s_in_bytes)
10//!     gen_str_hash = H.hash(gen_str, 2 * field_size_bytes)
11//!     return element_derivation(gen_str_hash)
12//! ```
13
14use alloc::vec;
15use alloc::vec::Vec;
16use pakery_core::crypto::{CpaceGroup, Hash};
17use pakery_core::encoding::{lv_cat, prepend_len};
18
19use crate::ciphersuite::CpaceCiphersuite;
20
21/// Build the generator string per the CPace specification.
22///
23/// Returns the concatenation `lv_cat(DSI, PRS, zero_bytes(len_zpad), CI, sid)`.
24pub fn generator_string<C: CpaceCiphersuite>(password: &[u8], ci: &[u8], sid: &[u8]) -> Vec<u8> {
25    let s_in_bytes = C::HASH_BLOCK_SIZE;
26    let prepend_len_dsi_len = prepend_len(C::DSI).len();
27    let prepend_len_prs_len = prepend_len(password).len();
28
29    let len_zpad = s_in_bytes
30        .saturating_sub(1)
31        .saturating_sub(prepend_len_prs_len)
32        .saturating_sub(prepend_len_dsi_len);
33
34    let zpad = vec![0u8; len_zpad];
35    lv_cat(&[C::DSI, password, &zpad, ci, sid])
36}
37
38/// Calculate the CPace generator from password, channel identifier, and session ID.
39///
40/// Hashes the generator string and maps the result to a group element.
41/// Returns [`pakery_core::PakeError::IdentityPoint`] if the derived generator is the
42/// identity element (defense-in-depth; spec-compliant `from_uniform_bytes`
43/// implementations should never produce the identity).
44pub fn calculate_generator<C: CpaceCiphersuite>(
45    password: &[u8],
46    ci: &[u8],
47    sid: &[u8],
48) -> Result<C::Group, pakery_core::PakeError> {
49    const { assert!(<C::Hash as pakery_core::crypto::Hash>::OUTPUT_SIZE >= 2 * C::FIELD_SIZE_BYTES) };
50    let gen_str = generator_string::<C>(password, ci, sid);
51    let hash_output = C::Hash::digest(&gen_str);
52    let g = C::Group::from_uniform_bytes(&hash_output)?;
53    if g.is_identity() {
54        return Err(pakery_core::PakeError::IdentityPoint);
55    }
56    Ok(g)
57}