Skip to content

MLDSA Context String #3077

@DarkaMaul

Description

@DarkaMaul

Problem:

The EVP_DigestSign/EVP_DigestVerify code path for ML-DSA hardcodes the FIPS 204 context string to (NULL, 0), making it impossible to sign or verify with a non-empty context string through the standard EVP interface.

// RAW sign mode
if (!sign_digest) {
if (!pqdsa->method->pqdsa_sign_message(key->private_key, sig, sig_len, message, message_len, NULL, 0)) {
OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
return 0;
}
}

And

if(!verify_digest) {
if (sig_len != pqdsa->signature_len ||
!pqdsa->method->pqdsa_verify_message(key->public_key, sig, sig_len, message, message_len, NULL, 0)) {
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_SIGNATURE);
return 0;
}
}

The underlying ml_dsa_{44,65,87}_sign and ml_dsa_{44,65,87}_verify functions already accept ctx_string and ctx_string_len parameters, and the PQDSA_METHOD function pointer table declares them.

In my efforts into adding support for MLDSA using AWS-LC in pyca/cryptography, I've resorted to use the lower level API functions (here - ⚠️ code not merged, in PR only).

Solution:

Add a control to EVP_PKEY_CTX that lets callers set the context string before signing or verifying.

  1. Extend PQDSA_PKEY_CTXto store a context string:

    typedef struct {
      const PQDSA *pqdsa;
      uint8_t ctx_string[MLDSA_MAX_CONTEXT_STRING_LEN]; // 255
      size_t ctx_string_len;
    } PQDSA_PKEY_CTX;
  2. Add an helper function to set the context

  3. Update pkey_pqdsa_sign_message and pkey_pqdsa_verify_message to pass dctx->ctx_string and dctx->ctx_string_len instead of NULL, 0.

  • Does this change any public APIs? Yes - adds one new exported function (to set the context) and one new constant (for the context max size). No existing APIs change.
  • Which algorithm(s) will this impact? ML-DSA-44, ML-DSA-65, ML-DSA-87.

Requirements / Acceptance Criteria:

  • EVP_DigestSign with a non-empty context string produces a valid signature
  • EVP_DigestVerify with a non-empty context string verifies correctly
  • Mismatched context strings cause verification to fail
  • Context string length > 255 is rejected with an error
  • Default behavior remains unchanged - empty context string, backward compatible
  • Wycheproof ML-DSA test vectors with non-empty context strings pass through the EVP path
  • Works for all three ML-DSA variants
  • RFC links:FIPS 204 §5.2–5.3
  • Related Issues: None
  • Will the Usage Guide or other documentation need to be updated?
  • Testing: Unit tests in p_pqdsa_test.cc; update existing Wycheproof harness to use EVP path for context-string cases
  • Will this change trigger AWS LibCrypto Formal Verification changes? Only plumbing an existing parameter through the EVP layer
  • Should this change be fuzz tested? The context string is bounded to 255 bytes but I think it could be fuzzed.

Out of scope:

None

/cc @alex

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions