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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions