Skip to main content
Version: Next

SPAKE2+ and Matter PASE API

Headers: pake/noxtls_spake2p.h and pake/noxtls_matter_pase.h. Library: noxtls_pake. New in 0.3.0. All of it is off by default.

For the protocol overview, the differences between the two profiles, configuration knobs, message flow, and test vectors, see the guide SPAKE2+, Matter PASE and PBKDF2. This page lists the public declarations.

Enablement​

CMake knobDefaultRequires
NOXTLS_CFG_FEATURE_SPAKE2POFFPKC, ECC, SHA-256, HMAC, HKDF, DRBG; at least one profile
NOXTLS_CFG_FEATURE_SPAKE2P_RFC9383ON (effective with SPAKE2P)SPAKE2P
NOXTLS_CFG_FEATURE_SPAKE2P_MATTEROFFSPAKE2P
NOXTLS_CFG_FEATURE_MATTER_PASEOFFSPAKE2P, SPAKE2P_MATTER, PBKDF2

Constants​

MacroValue
NOXTLS_SPAKE2P_SCALAR_SIZE32 (big-endian scalars w0, w1)
NOXTLS_SPAKE2P_POINT_SIZE65 (uncompressed SEC 1 point)
NOXTLS_SPAKE2P_CONFIRMATION_SIZE32
NOXTLS_SPAKE2P_WS_SIZE80 (w0s ‖ w1s registration input)
NOXTLS_MATTER_PASE_VERIFIER_SIZE97 (w0 ‖ L)
NOXTLS_MATTER_PASE_CONTEXT_SIZE32
NOXTLS_MATTER_PASE_SESSION_KEY_SIZE / NOXTLS_MATTER_PASE_ATTESTATION_CHALLENGE_SIZE16
NOXTLS_MATTER_PASE_PBKDF_ITERATIONS_MIN / _MAX1000 / 100000
NOXTLS_MATTER_PASE_SALT_MIN_SIZE / _MAX_SIZE16 / 32

Types​

noxtls_spake2p_profile_t​

  • NOXTLS_SPAKE2P_PROFILE_RFC9383: the RFC 9383 key schedule (standard, default).
  • NOXTLS_SPAKE2P_PROFILE_MATTER: the draft-bar-cfrg-spake2plus-01 key schedule. Use it only for Matter interoperability.

The two profiles are not interchangeable.

noxtls_spake2p_params_t​

Transcript binding inputs: context/context_len, id_prover/id_prover_len, id_verifier/id_verifier_len. A pointer may be NULL when its length is 0.

noxtls_spake2p_ctx_t​

A caller-allocated exchange context of about 1 KB, which embeds a SHA-256 state. Its fields are private. Release it with noxtls_spake2p_free(), which erases every secret.

SPAKE2+ functions​

noxtls_return_t noxtls_spake2p_derive_w0_w1(const uint8_t *ws, uint32_t ws_len,
uint8_t w0[NOXTLS_SPAKE2P_SCALAR_SIZE],
uint8_t w1[NOXTLS_SPAKE2P_SCALAR_SIZE]);
noxtls_return_t noxtls_spake2p_compute_L(const uint8_t w1[NOXTLS_SPAKE2P_SCALAR_SIZE],
uint8_t L[NOXTLS_SPAKE2P_POINT_SIZE]);

noxtls_return_t noxtls_spake2p_prover_init(noxtls_spake2p_ctx_t *ctx,
noxtls_spake2p_profile_t profile,
const noxtls_spake2p_params_t *params,
const uint8_t w0[NOXTLS_SPAKE2P_SCALAR_SIZE],
const uint8_t w1[NOXTLS_SPAKE2P_SCALAR_SIZE]);
noxtls_return_t noxtls_spake2p_verifier_init(noxtls_spake2p_ctx_t *ctx,
noxtls_spake2p_profile_t profile,
const noxtls_spake2p_params_t *params,
const uint8_t w0[NOXTLS_SPAKE2P_SCALAR_SIZE],
const uint8_t L[NOXTLS_SPAKE2P_POINT_SIZE]);

noxtls_return_t noxtls_spake2p_generate_share(noxtls_spake2p_ctx_t *ctx,
uint8_t *share, uint32_t *share_len);
noxtls_return_t noxtls_spake2p_generate_share_with_scalar(noxtls_spake2p_ctx_t *ctx,
const uint8_t scalar[NOXTLS_SPAKE2P_SCALAR_SIZE],
uint8_t *share, uint32_t *share_len);
noxtls_return_t noxtls_spake2p_process_peer_share(noxtls_spake2p_ctx_t *ctx,
const uint8_t *peer_share,
uint32_t peer_share_len);
noxtls_return_t noxtls_spake2p_get_confirmation(const noxtls_spake2p_ctx_t *ctx,
uint8_t *confirmation,
uint32_t *confirmation_len);
noxtls_return_t noxtls_spake2p_verify_peer_confirmation(noxtls_spake2p_ctx_t *ctx,
const uint8_t *confirmation,
uint32_t confirmation_len);
noxtls_return_t noxtls_spake2p_get_shared_key(const noxtls_spake2p_ctx_t *ctx,
uint8_t *key, uint32_t *key_len);
noxtls_return_t noxtls_spake2p_free(noxtls_spake2p_ctx_t *ctx);

const uint8_t *noxtls_spake2p_p256_M(void);
const uint8_t *noxtls_spake2p_p256_N(void);
FunctionNotesTypical errors
derive_w0_w1Reduces the 80-byte registration output (w0s ‖ w1s) mod n in constant time.INVALID_PARAM (length), FAILED (reduced value is zero)
compute_LL = w1·G for the verifier record.INVALID_PARAM
prover_init / verifier_initErase the context, then absorb Context, the identities, M, and N into the transcript.NOT_SUPPORTED (profile not compiled in), INVALID_PARAM (w0/w1 out of range), BAD_DATA (L invalid)
generate_shareOwn share from a DRBG ephemeral scalar.NOT_INITIALIZED (wrong state), INVALID_PARAM (buffer too small)
generate_share_with_scalarCaller-supplied scalar, for known-answer tests or an external TRNG.as above
process_peer_shareValidates the peer point, computes Z and V, and derives keys.BAD_DATA (invalid share, or identity Z/V). The context is aborted.
get_confirmationOwn 32-byte confirmation MAC.INVALID_PARAM (buffer too small)
verify_peer_confirmationConstant-time compare. On mismatch the context is aborted, with no retry.FAILED
get_shared_keyOnly after the peer has been confirmed.NOT_INITIALIZED
freeErases all secrets and returns the context to EMPTY.NULL
p256_M / p256_NRFC 9383 section 4 constants in static read-only storage.n/a

Matter PASE functions​

int noxtls_matter_pase_passcode_is_valid(uint32_t passcode);

noxtls_return_t noxtls_matter_pase_compute_w0_w1(uint32_t passcode,
const uint8_t *salt, uint32_t salt_len,
uint32_t iterations,
uint8_t w0[NOXTLS_SPAKE2P_SCALAR_SIZE],
uint8_t w1[NOXTLS_SPAKE2P_SCALAR_SIZE]);
noxtls_return_t noxtls_matter_pase_compute_verifier(uint32_t passcode,
const uint8_t *salt, uint32_t salt_len,
uint32_t iterations,
uint8_t verifier[NOXTLS_MATTER_PASE_VERIFIER_SIZE]);
noxtls_return_t noxtls_matter_pase_parse_verifier(const uint8_t *verifier, uint32_t verifier_len,
uint8_t w0[NOXTLS_SPAKE2P_SCALAR_SIZE],
uint8_t L[NOXTLS_SPAKE2P_POINT_SIZE]);
noxtls_return_t noxtls_matter_pase_compute_context(const uint8_t *pbkdf_param_request, uint32_t request_len,
const uint8_t *pbkdf_param_response, uint32_t response_len,
uint8_t context[NOXTLS_MATTER_PASE_CONTEXT_SIZE]);
noxtls_return_t noxtls_matter_pase_initiator_init(noxtls_spake2p_ctx_t *ctx,
const uint8_t context[NOXTLS_MATTER_PASE_CONTEXT_SIZE],
const uint8_t w0[NOXTLS_SPAKE2P_SCALAR_SIZE],
const uint8_t w1[NOXTLS_SPAKE2P_SCALAR_SIZE]);
noxtls_return_t noxtls_matter_pase_responder_init(noxtls_spake2p_ctx_t *ctx,
const uint8_t context[NOXTLS_MATTER_PASE_CONTEXT_SIZE],
const uint8_t w0[NOXTLS_SPAKE2P_SCALAR_SIZE],
const uint8_t L[NOXTLS_SPAKE2P_POINT_SIZE]);
noxtls_return_t noxtls_matter_pase_derive_session_keys(const noxtls_spake2p_ctx_t *ctx,
uint8_t i2r_key[NOXTLS_MATTER_PASE_SESSION_KEY_SIZE],
uint8_t r2i_key[NOXTLS_MATTER_PASE_SESSION_KEY_SIZE],
uint8_t attestation_challenge[NOXTLS_MATTER_PASE_ATTESTATION_CHALLENGE_SIZE]);
  • passcode_is_valid() returns 1 for passcodes allowed by Matter Core section 5.1.7.1, otherwise 0.
  • compute_w0_w1() runs on the commissioner. It computes PBKDF2-HMAC-SHA256 over the passcode encoded as 4 little-endian bytes, with a 16 to 32 byte salt and 1000 to 100000 iterations. Outputs are erased on failure.
  • compute_verifier() runs at manufacturing and produces w0 ‖ L. parse_verifier() runs on the device: it splits a stored verifier and validates it.
  • compute_context() computes SHA-256("CHIP PAKE V1 Commissioning" ‖ PBKDFParamRequest ‖ PBKDFParamResponse). Pass the exact TLV payload bytes.
  • initiator_init() / responder_init() start a Matter-profile SPAKE2+ exchange with empty identities. After that, use the generic SPAKE2+ calls above.
  • derive_session_keys() requires a Matter-profile context in the CONFIRMED state. It produces I2RKey, R2IKey, and AttestationChallenge, each 16 bytes, and erases the outputs on failure.

Matter TLV encoding, session IDs, MRP, and the session table belong to the Matter stack, not NoxTLS.