Skip to content

Add SPKI and PKCS8 inputs for ML-DSA - #186

Merged
cpu merged 2 commits into
C2SP:mainfrom
davidben:mldsa-spki-pkcs8
Oct 23, 2025
Merged

cpu merged 2 commits into
C2SP:mainfrom
davidben:mldsa-spki-pkcs8

Conversation

@davidben

Copy link
Copy Markdown
Contributor

Part of #184. I haven't done ML-KEM yet, but it should be fairly easy to extend the script.

Also fix a typo in mldsa_verify_schema.json. This went unnoticed because both missing and extra fields are allowed.

Generated with this script:
import json
import sys

OIDS = {
    "ML-DSA-44": b"\x60\x86\x48\x01\x65\x03\x04\x03\x11",
    "ML-DSA-65": b"\x60\x86\x48\x01\x65\x03\x04\x03\x12",
    "ML-DSA-87": b"\x60\x86\x48\x01\x65\x03\x04\x03\x13",
}

SEQUENCE = 0x30
INTEGER = 0x02
BIT_STRING = 0x03
OCTET_STRING = 0x04
OBJECT_IDENTIFIER = 0x06

CONTEXT_SPECIFIC = 0x80
CONSTRUCTED = 0x20

def encode_der(tag, body):
    ret = bytearray()
    ret.append(tag)
    if len(body) < 0x80:
        ret.append(len(body))
    elif len(body) <= 0xff:
        ret.append(0x81)
        ret.append(len(body))
    elif len(body) <= 0xffff:
        ret.append(0x82)
        ret.append(len(body) >> 8)
        ret.append(len(body) & 0xff)
    ret.extend(body)
    return bytes(ret)

def insert_after(d, pos, new_key, new_value):
    # This is silly. Maybe we can automatically order fields by the schema?
    # Or just alphabetize...
    if new_key in d:
        d[new_key] = new_value
        return
    assert pos in d
    items = list(d.items())
    d.clear()
    for k, v in items:
        d[k] = v
        if k == pos:
            d[new_key] = new_value

def algorithm_identifier_no_params(oid):
    return encode_der(
        SEQUENCE,
        encode_der(OBJECT_IDENTIFIER, oid)
    )

def fix_mldsa_verify(alg, g):
    key = bytes.fromhex(g["publicKey"])
    # This would just be adding a fixed prefix, except that some inputs
    # intentionally have the wrong length.
    spki = encode_der(
        SEQUENCE,
        algorithm_identifier_no_params(OIDS[alg]) +
        encode_der(
            BIT_STRING,
            b"\x00" + key,
        )
    )
    insert_after(g, "publicKey", "publicKeyDer", spki.hex())

def fix_mldsa_sign(alg, g):
    seed = bytes.fromhex(g["privateSeed"])
    pkcs8 = encode_der(
        SEQUENCE,
        encode_der(INTEGER, b"\x00") +
        algorithm_identifier_no_params(OIDS[alg]) +
        encode_der(
            OCTET_STRING,
            encode_der(CONTEXT_SPECIFIC | 0, seed)
        )
    )
    insert_after(g, "privateSeed", "privateKeyPkcs8", pkcs8.hex())

def fix(path):
    with open(path) as f:
        j = json.load(f)
    for g in j["testGroups"]:
        if j["schema"] == "mldsa_verify_schema.json" and g["type"] == "MlDsaVerify":
            fix_mldsa_verify(j["algorithm"], g)
        elif j["schema"] == "mldsa_sign_seed_schema.json" and g["type"] == "MlDsaSign":
            fix_mldsa_sign(j["algorithm"], g)
    with open(path, "w") as f:
        json.dump(j, f, indent=2, separators=(",", ": "), ensure_ascii=False)
        f.write("\n")

for arg in sys.argv[1:]:
    fix(arg)

Part of C2SP#184. I haven't done ML-KEM yet, but it should be fairly easy to
extend the script.

Also fix a typo in mldsa_verify_schema.json. This went unnoticed because
both missing and extra fields are allowed.
Comment thread schemas/mldsa_verify_schema.json Resolved
Disable additionalProperties, and add the missing generatorVersion
property (marked deprecated to match others). Also added a list of
required properties.

@botovq botovq left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I checked your python script and verified the output with that. I also manually spot-checked the DER prefixes using der2ascii.

@botovq botovq left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The schema changes also read fine.

@cpu
cpu merged commit a6e88a9 into C2SP:main Oct 23, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants