Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

RFC Support Reference

This page documents every RFC that is relevant to Akāmu, explaining what each one specifies, which parts are implemented, and — for RFCs that are intentionally not implemented — why.

Summary

SpecificationTitleStatus
dns-persist-01Let’s Encrypt Persistent DNS ChallengeFull
draft-aaron-acme-profiles-01ACME Certificate ProfilesFull
draft-ietf-cose-dilithium-11ML-DSA (Dilithium) for JOSE (JWK + JWS)Full
draft-ietf-lamps-pq-composite-sigsML-DSA Composite TLS Signature SchemesPartial (provisional code points)
RFC 8555Automatic Certificate Management Environment (ACME)Full
RFC 8659DNS Certification Authority Authorization (CAA)Full
RFC 8657CAA Extensions: accounturi and validationmethodsFull
RFC 8737ACME TLS-ALPN-01 Challenge ExtensionFull
RFC 8738ACME IP Identifier ValidationFull
RFC 8739ACME Short-Term, Automatically Renewed (STAR) CertificatesFull
RFC 9444ACME for SubdomainsFull
RFC 9773ACME Renewal Information (ARI)Full
RFC 9799ACME Extensions for .onion Special-Use Domain NamesFull
RFC 5280X.509 Certificate and CRL ProfileFull
RFC 8823ACME Extensions for S/MIME CertificatesNot implemented
RFC 9115ACME Profile for Delegated CertificatesNot implemented
RFC 9345Delegated Credentials for TLSNot implemented
RFC 9447ACME Challenges Using an Authority TokenNot implemented
RFC 9448ACME TNAuthList Authority TokenNot implemented
RFC 9538ACME Delegation Metadata for CDNINot implemented
RFC 9891ACME DTN Node ID Validation (Experimental)Not implemented

RFC 8555 — Core ACME

RFC 8555 is the foundation. It defines the full ACME protocol: the HTTP API, the JSON object model, the JWS (JSON Web Signature) authentication scheme, and the challenge validation framework.

What it covers

SectionFeatureStatus
§7.1Directory (GET /acme/directory)Yes
§7.2Nonces (HEAD /acme/new-nonce, GET /acme/new-nonce)Yes
§7.3Account creation and management (/acme/new-account, /acme/account/{id})Yes
§7.3.4externalAccountRequired enforcementYes
§7.4Order management (/acme/new-order, /acme/order/{id})Yes
§7.4.1Pre-authorization (POST /acme/new-authz)Yes
§7.1.3Honour order notBefore / notAfter in issued certificatesYes
§7.5Authorizations (/acme/authz/{id})Yes
§7.5.1Challenge response (/acme/chall/{authz}/{type})Yes
§7.4 finalizeCertificate issuance (/acme/order/{id}/finalize)Yes
§7.4.2Certificate download (/acme/cert/{id})Yes
§7.6Certificate revocation (/acme/revoke-cert)Yes
§7.3.5Account key rollover (/acme/key-change)Yes
§8.3http-01 challenge validationYes
§8.4dns-01 challenge validationYes

Pre-authorization (newAuthz)

Pre-authorization lets a client prove domain control ahead of any specific order. Once pre-authorized, the client can request multiple certificates for that domain (or its subdomains, if subdomainAuthAllowed is set) without repeating the challenge for each order.

POST /acme/new-authz
Content-Type: application/jose+json

payload: {
  "identifier": { "type": "dns", "value": "example.com" }
}

The response is identical to a reactive authorization created by newOrder.

External Account Binding

When server.external_account_required = true, every newAccount request must include an externalAccountBinding field. Requests without it are rejected with urn:ietf:params:acme:error:externalAccountRequired (HTTP 403).

EAB keys are provisioned in the TOML configuration under [server]:

[server]
external_account_required = true

[server.eab_keys]
"kid-1" = "c2VjcmV0LWhtYWMta2V5LWJ1ZmZlcg"   # base64url-encoded raw key bytes
"kid-2" = "YW5vdGhlci1rZXktaGVyZQ"

Keys are seeded into the eab_keys database table at startup using INSERT OR IGNORE, so a key consumed or modified at runtime is never overwritten by a server restart. The server performs full HMAC verification per RFC 8555 §7.3.4:

  1. Extracts the kid from the EAB protected header.
  2. Looks up the key in the database; rejects unknown or already-consumed kids.
  3. Validates the algorithm (HS256, HS384, or HS512), the url (must match the new-account endpoint), and the payload (must be the account public key).
  4. Verifies the HMAC signature using OpenSSL constant-time comparison.
  5. Inserts the new account and marks the EAB key as consumed in a single SQLite transaction.

The design is forward-compatible with an admin API endpoint: insert, delete, and get_by_kid are already implemented in the database layer.

Certificate validity window

If the newOrder request includes notBefore and/or notAfter fields, the issued certificate’s validity period will honour them, subject to the CA’s configured validity_days limit and a 5-minute clock-skew grace on notBefore.


RFC 8659 — CAA DNS Resource Record

RFC 8659 requires a CA to look up DNS Certification Authority Authorization (CAA) records before issuing a certificate. A domain owner can publish CAA records to restrict which CAs are allowed to issue certificates for that domain.

How Akāmu implements it

Before issuing any certificate, Akāmu queries CAA records for each DNS identifier in the order:

  1. It starts at the requested domain (e.g., sub.example.com) and walks up the DNS tree (example.com, com) until it finds a CAA record set or exhausts the tree.
  2. If no CAA records are found anywhere, issuance proceeds (unconstrained domain).
  3. If a CAA record set is found, Akāmu checks whether any issue record (or issuewild record for wildcard certs) contains one of the CA’s configured domain names (server.caa_identities).
  4. If none match, issuance is denied with urn:ietf:params:acme:error:caa (HTTP 403).

Configuration

[server]
caa_identities = ["acme.example.com"]

When caa_identities is empty (the default), CAA checking is disabled entirely.

Example CAA record

A domain owner who trusts only this Akāmu instance would publish:

example.com. IN CAA 0 issue "acme.example.com"

To also allow wildcard certificates:

example.com. IN CAA 0 issuewild "acme.example.com"

IP identifiers are not subject to CAA checking (CAA is a DNS mechanism).


RFC 8657 — CAA accounturi and validationmethods

RFC 8657 extends CAA with two optional parameters that give domain owners finer-grained control:

  • accounturi — Restricts issuance to a specific ACME account URI.
  • validationmethods — Restricts issuance to specific challenge types (e.g., only dns-01).

validationmethods

When Akāmu finds a matching issue or issuewild CAA record that contains a validationmethods parameter, it checks whether the challenge type used to validate the order appears in the list. If not, issuance is denied.

Example:

; Only allow dns-01 for this CA
example.com. IN CAA 0 issue "acme.example.com; validationmethods=dns-01"

With this record, an http-01-validated order for example.com would be denied at finalization time.

accounturi

When a matching issue or issuewild CAA record contains an accounturi parameter, Akāmu enforces it: the full ACME account URL of the requesting client (e.g. https://acme.example.com/acme/account/42) must match the parameter value exactly. If it does not match, the record is treated as non-authorizing and issuance is denied unless another record in the set authorizes it without an accounturi constraint.

Example:

; Only the named account may obtain a certificate from this CA
example.com. IN CAA 0 issue "acme.example.com; accounturi=https://acme.example.com/acme/account/42"

RFC 8737 — TLS-ALPN-01 Challenge

RFC 8737 defines the tls-alpn-01 challenge, which proves domain control by serving a specially crafted TLS certificate on port 443 using the ALPN protocol identifier acme-tls/1.

How it works

  1. Akāmu computes the SHA-256 of the key authorization.
  2. It opens a TLS connection to port 443 of the domain, advertising acme-tls/1 as the ALPN protocol.
  3. It verifies that the server presents a certificate with:
    • The domain as a dNSName SAN (exactly one SAN entry).
    • A critical id-pe-acmeIdentifier extension (OID 1.3.6.1.5.5.7.1.31) containing the SHA-256 hash of the key authorization as a DER OCTET STRING.
  4. For IP identifiers, the server connects directly to the IP address; the reverse-DNS name is used as the TLS SNI value.

Constraints

  • Port 443 must be reachable from the Akāmu server.
  • Wildcard identifiers cannot be validated with tls-alpn-01.
  • Both TLS 1.2 and TLS 1.3 are accepted.
  • RFC 8737 §3 requires exactly one SAN entry in the validation certificate. Certificates with multiple SANs are rejected.

RFC 8738 — IP Identifier Validation

RFC 8738 extends ACME to issue certificates for IP addresses (IPv4 and IPv6), not just domain names.

Supported identifier type

{ "type": "ip", "value": "192.0.2.1" }
{ "type": "ip", "value": "2001:db8::1" }

IPv4 values use dotted-decimal notation. IPv6 values use the compressed text representation defined in RFC 5952.

Supported challenge types for IP identifiers

ChallengeSupported
http-01Yes — connects directly to the IP; Host header is the IP address literal
tls-alpn-01Yes — connects to the IP; SNI uses the reverse-DNS name (e.g., 1.2.0.192.in-addr.arpa)
dns-01No — MUST NOT be used for IP identifiers per RFC 8738 §7
dns-persist-01No — DNS-based, not applicable to IP identifiers

RFC 8739 — ACME STAR

RFC 8739 (Short-Term, Automatically Renewed) allows a client to place a single order and receive a continuous stream of short-lived certificates without repeating domain validation. The CA automatically reissues each certificate before the previous one expires.

Use case

STAR is designed for scenarios where certificate revocation is unreliable. Instead of revoking a compromised certificate, the operator simply cancels the STAR order; the attacker’s window is limited to the remaining validity of the current short-lived certificate.

Another key use case is CDN delegation (see RFC 9115): the domain owner holds the STAR order and can revoke the CDN’s access at any time by canceling it.

Creating a STAR order

Include an auto-renewal object in the newOrder payload:

{
  "identifiers": [{ "type": "dns", "value": "example.com" }],
  "auto-renewal": {
    "start-date": "2025-01-01T00:00:00Z",
    "end-date":   "2025-12-31T00:00:00Z",
    "lifetime":   86400
  }
}
FieldRequiredDescription
end-dateYesThe latest date of validity of the last certificate issued (RFC 3339).
lifetimeYesValidity period of each certificate, in seconds.
start-dateNoThe earliest notBefore of the first certificate. Defaults to when the order becomes ready.
lifetime-adjustNoPre-dates each certificate’s notBefore by this many seconds (for clock-skew tolerance). Default: 0.
allow-certificate-getNoIf true, the rolling certificate URL can be fetched with an unauthenticated GET.

notBefore and notAfter must NOT be present in a STAR order.

Rolling certificate URL

After finalization, the order response includes a star-certificate URL instead of certificate:

{
  "status": "valid",
  "star-certificate": "https://acme.example.com/acme/cert/star/<order-id>"
}

GET /acme/cert/star/<order-id> always returns the currently active PEM certificate, along with Cert-Not-Before and Cert-Not-After HTTP headers matching the certificate’s validity window.

Canceling a STAR order

POST to the order URL with {"status": "canceled"} to stop automatic renewal:

POST /acme/order/<id>
{ "status": "canceled" }

Once canceled, the star-certificate endpoint returns HTTP 403 (autoRenewalCanceled). The currently active short-lived certificate continues to be usable until it expires naturally.

Server configuration

Advertise STAR capability in the directory by configuring minimum lifetime and maximum duration:

[server]
star_min_lifetime_secs = 86400     # 1 day minimum cert lifetime
star_max_duration_secs = 31536000  # 1 year maximum renewal period

When either field is set, the directory meta object includes the auto-renewal advertisement.


RFC 9444 — ACME for Subdomains

RFC 9444 allows a client to prove control of an ancestor domain (e.g., example.com) and then obtain certificates for any subdomain (e.g., api.example.com, www.example.com) without repeating the challenge for each one.

ancestorDomain in new orders

When placing an order for a subdomain, the client can declare which ancestor domain it controls:

{
  "identifiers": [
    {
      "type": "dns",
      "value": "api.example.com",
      "ancestorDomain": "example.com"
    }
  ]
}

Akāmu validates that ancestorDomain is a genuine ancestor (label-aligned DNS suffix) of the requested identifier. If accepted, the authorization challenge is issued against example.com rather than api.example.com.

subdomainAuthAllowed in pre-authorization

When pre-authorizing an ancestor domain, include subdomainAuthAllowed: true:

POST /acme/new-authz
{
  "identifier": { "type": "dns", "value": "example.com" },
  "subdomainAuthAllowed": true
}

The returned authorization object includes the same flag:

{
  "identifier": { "type": "dns", "value": "example.com" },
  "status": "valid",
  "subdomainAuthAllowed": true,
  ...
}

A client can reuse this authorization for any subsequent order that specifies ancestorDomain: "example.com".

Server advertisement

To advertise subdomain authorization support in the directory:

[server]
allow_subdomain_auth = true

This adds "subdomainAuthAllowed": true to the directory meta object.


RFC 9773 — ACME Renewal Information (ARI)

RFC 9773 defines the Renewal Information extension, which lets the server tell ACME clients when to renew their certificates — even before the certificate expires. This is useful when a CA needs to revoke and reissue certificates en masse (e.g., due to a key compromise or mis-issuance event).

Endpoints

GET /acme/renewal-info/<cert-id>

<cert-id> is the RFC 9773 certificate identifier: base64url(AKI keyIdentifier) "." base64url(DER-encoded serial number bytes).

The response includes a suggested renewal window:

{
  "suggestedWindow": {
    "start": "2025-03-15T00:00:00Z",
    "end":   "2025-03-20T00:00:00Z"
  }
}

The server includes a Retry-After header indicating how often to poll.

Renewal replacement

When placing a renewal order for a certificate that is being replaced, include the predecessor’s cert-id in the order:

{
  "identifiers": [...],
  "replaces": "<cert-id-of-predecessor>"
}

Akāmu validates that the predecessor cert belongs to the same account, marks it as replaced in the database at finalization, and returns an HTTP 409 (alreadyReplaced) if a replacement order has already been finalized.

Configuration

[server]
ari_retry_after_secs = 21600  # 6 hours between renewal-info polls (default)

RFC 9799 — ACME for .onion Domains

RFC 9799 defines how ACME can issue certificates for Tor Hidden Services (.onion Special-Use Domain Names). These are not DNS names — the second-level label encodes the hidden service’s Ed25519 public key.

Supported challenges for .onion identifiers

ChallengeSupportedNotes
onion-csr-01YesKey validation via CSR; no Tor network access needed server-side
http-01ConditionalOnly offered when server.tor_connectivity_enabled = true
tls-alpn-01ConditionalOnly offered when server.tor_connectivity_enabled = true
dns-01NoMUST NOT be used for .onion identifiers

Tor connectivity configuration

RFC 9799 §4 prohibits offering http-01 or tls-alpn-01 for .onion identifiers unless the CA can actually reach the Tor network. By default, Akāmu offers only onion-csr-01. To enable the additional challenge types, set:

[server]
tor_connectivity_enabled = true

Only set this when the Akāmu server process can make outbound Tor connections to hidden services (e.g. via torsocks or a SOCKS5 proxy configured at the OS level).

onion-csr-01 challenge

onion-csr-01 is the recommended challenge type for .onion domains because it does not require the ACME server to connect to the Tor network. Proof of control comes from a cryptographic signature by the hidden service’s private key (the same key embedded in the .onion address).

Protocol:

  1. Akāmu returns a challenge object with type: "onion-csr-01", a token, and an authKey (the JWK thumbprint of the ACME account key).
  2. The client builds a CSR that:
    • Contains the .onion SAN.
    • Includes a cabf-onion-csr-nonce extension (OID 2.23.140.41) containing the key authorization (token.thumbprint).
    • Is signed with both the CSR subject key and the hidden service’s Ed25519 private key.
  3. The client POSTs {"csr": "<base64url-CSR-DER>"} to the challenge URL.
  4. Akāmu:
    • Extracts the 32-byte Ed25519 public key from the .onion address.
    • Verifies the cabf-onion-csr-nonce extension contains the correct key authorization.
    • Verifies the CSR signature using the extracted hidden-service public key.
    • If all checks pass, marks the authorization as valid.

Identifier format

Only v3 (Ed25519) .onion addresses are accepted. A v3 address has a 56-character base32 second-level label:

bbcweb3hytmzhn5d532owbu6oqadra5z3ar726vq5kgwwn6aucdccrad.onion

Version 2 addresses (16-character label) are rejected per RFC 9799 §2.


RFC 5280 — X.509 Certificate Profile

RFC 5280 defines the structure of X.509 v3 certificates and Certificate Revocation Lists (CRLs). Akāmu issues certificates that conform to the RFC 5280 PKIX profile via the synta-certificate library.

Conformance includes:

  • Correct BasicConstraints (CA: false on end-entity certs).
  • SubjectKeyIdentifier and AuthorityKeyIdentifier extensions.
  • KeyUsage and ExtendedKeyUsage extensions.
  • SubjectAlternativeName extensions carrying dNSName (including .onion domains) or iPAddress.
  • CRL Distribution Points and OCSP Access Information when crl_url / ocsp_url are configured.

RFC 7807 — Problem Details for HTTP APIs

RFC 7807 defines a JSON format for HTTP error responses. All Akāmu error responses use this format with Content-Type: application/problem+json:

{
  "type":   "urn:ietf:params:acme:error:malformed",
  "detail": "JWS url mismatch: got '...', expected '...'",
  "status": 400
}

All ACME-specific error URNs are defined in RFC 8555 §6.7 and its extensions.


Let’s Encrypt dns-persist-01

The dns-persist-01 specification is a non-standard ACME challenge type published by Let’s Encrypt. Unlike the standard dns-01 challenge, which requires a fresh DNS TXT record for every renewal, dns-persist-01 uses a single long-lived TXT record that remains in place across renewals. This eliminates the need to modify DNS on every certificate renewal cycle.

How it differs from dns-01

Propertydns-01dns-persist-01
TXT record name_acme-challenge.<domain>_validation-persist.<domain>
Record changes per renewalRequiredNot required
Token in recordYes (changes each time)No
Record format<key-auth>"<issuer-domain>; accounturi=<uri>[; policy=wildcard][; persistUntil=<ISO8601Z>]"
Wildcard supportRequires explicit policy=wildcard parameter

Configuration

[server]
dns_persist_issuer_domain = "acme.example.com"

When dns_persist_issuer_domain is set, the server offers dns-persist-01 as an additional challenge type alongside http-01, dns-01, and tls-alpn-01. Without it, the challenge type is not advertised.

TXT record format

The domain owner publishes (and keeps permanently):

_validation-persist.example.com. IN TXT "acme.example.com; accounturi=https://acme.example.com/acme/account/abc123"

Optional extensions:

  • policy=wildcard — authorizes wildcard certificate issuance.
  • persistUntil=2026-12-31T00:00:00Z — caps the record’s validity. After this date, the record must be renewed.

Validation

Akāmu queries the _validation-persist.<domain> TXT record, verifies the issuer domain matches dns_persist_issuer_domain, and checks that the accounturi matches the requesting ACME account URL. If both match, the authorization is marked valid.


draft-aaron-acme-profiles-01

draft-aaron-acme-profiles-01 defines a mechanism for an ACME server to advertise named certificate profiles and for clients to request a specific profile when placing an order. This moves policy selection from CSR extensions and post-issuance inspection into the order object itself, making the server’s issuance policy explicit and machine-readable.

What it adds

FeatureLocationStatus
meta.profiles in directoryGET /acme/directoryYes
profile field in newOrder payloadPOST /acme/new-orderYes
profile field in order responseGET/POST /acme/order/{id}Yes
invalidProfile error typeAll order and finalize endpointsYes
Finalize-time profile re-validationPOST /acme/order/{id}/finalizeYes

Directory advertisement

When server.profiles is configured, the directory meta includes a profiles object:

"meta": {
  "profiles": {
    "tls-server-auth": "https://acme.example.com/docs/profiles/tls-server-auth",
    "client-auth":     "https://acme.example.com/docs/profiles/client-auth"
  }
}

Requesting a profile in newOrder

Clients include the profile field in the newOrder payload:

{
  "identifiers": [{ "type": "dns", "value": "example.com" }],
  "profile": "tls-server-auth"
}

The server validates that the requested profile is in the configured map. If not, it returns:

{
  "type": "urn:ietf:params:acme:error:invalidProfile",
  "status": 400,
  "detail": "profile 'unknown-profile' is not advertised by this server"
}

The profile field is echoed back in every subsequent order response so that clients can confirm which profile applies.

Finalize-time re-validation

If a profile is removed from the server’s configuration after an order has been placed but before it is finalized, the finalize endpoint rejects the request with invalidProfile. This prevents silent issuance under a policy the server no longer supports.

Configuration

[server.profiles]
"tls-server-auth" = "https://acme.example.com/docs/profiles/tls-server-auth"
"client-auth"     = "https://acme.example.com/docs/profiles/client-auth"

When profiles is empty (the default), profile selection is not advertised. A profile field in newOrder is accepted but ignored — the server issues under its default policy.


draft-ietf-cose-dilithium-11

draft-ietf-cose-dilithium-11 defines how ML-DSA (Module-Lattice-Based Digital Signature Algorithm, formerly CRYSTALS-Dilithium, standardized in FIPS 204) keys and signatures are represented in JOSE (JSON Object Signing and Encryption). This draft has been submitted for RFC publication and its wire format is frozen. Akāmu implements it for ACME account key authentication, meaning ACME clients can register an ML-DSA key pair and sign every subsequent ACME request with it.

JWK key type: AKP

ML-DSA keys use the key type "AKP" (Algorithm Key Pair). Unlike classical key types, the algorithm is encoded inside the JWK itself (not only in the JWS protected header), so the alg field is required in the JWK:

{
  "kty": "AKP",
  "alg": "ML-DSA-65",
  "pub": "<base64url-encoded raw public key bytes>"
}
JWK fieldRequiredDescription
ktyYesAlways "AKP" for ML-DSA keys
algYes"ML-DSA-44", "ML-DSA-65", or "ML-DSA-87"
pubYesBase64url-encoded raw public key bytes (no padding)
privNo32-byte seed (private key); never sent to the server and ignored if present

Supported variants

AlgorithmFIPS 204 parameter setPublic key sizeSignature sizeOID (SPKI)
ML-DSA-44Parameter set 2 (k=4, l=4)1312 bytes2420 bytes2.16.840.1.101.3.4.3.17
ML-DSA-65Parameter set 3 (k=6, l=5)1952 bytes3309 bytes2.16.840.1.101.3.4.3.18
ML-DSA-87Parameter set 5 (k=8, l=7)2592 bytes4627 bytes2.16.840.1.101.3.4.3.19

JWK thumbprint

Per draft-ietf-cose-dilithium-11 §6, the JWK thumbprint for an AKP key is the SHA-256 hash of the following canonical JSON object with members in lexicographic order:

{"alg":"ML-DSA-65","kty":"AKP","pub":"<base64url-key>"}

This is the same SHA-256 / base64url procedure as RFC 7638, applied to the three required members alg, kty, and pub (in that order).

Signature format

ML-DSA signatures in JOSE are raw bytes as defined by FIPS 204 §7.2. They are not DER-encoded. The server validates the signature length before attempting verification and returns HTTP 400 if the length does not match the declared algorithm.

The signing context MUST be an empty byte string (b"") per draft-ietf-cose-dilithium-11 §4. Verification uses BackendPublicKey::verify_ml_dsa_with_context(signing_input, signature, b""). Signature failures return HTTP 401 Unauthorized.

SPKI DER construction

When an AKP JWK arrives in a new-account request (or any request using jwk instead of kid), Akāmu converts it to a SubjectPublicKeyInfo (SPKI) DER structure for storage and subsequent signature verification. The SPKI follows the X.509 / PKIX encoding for ML-DSA keys:

SEQUENCE {                          -- outer SEQUENCE
  SEQUENCE {                        -- AlgorithmIdentifier
    OID <variant OID>               -- no parameters (absent, not NULL)
  }
  BIT STRING {
    0x00                            -- unused-bits octet
    <raw public key bytes>          -- from the "pub" JWK field
  }
}

The alg field in the JWK determines which OID is embedded. The length octets in the DER use the minimum-length encoding; for ML-DSA key sizes, the outer SEQUENCE and BIT STRING both use the two-byte (0x82) length form.

ACME client integration notes

An ACME client registering with an ML-DSA key must:

  1. Generate an ML-DSA key pair (any of the three variants).
  2. Construct the AKP JWK from the raw public key bytes (base64url-encode them into pub).
  3. Include the JWK in the new-account protected header (the jwk field).
  4. Sign all ACME requests with the ML-DSA private key using an empty context string.
  5. Set alg in the JWS protected header to match the JWK’s alg field.

Existing ACME clients designed for classical algorithms require ML-DSA support in their underlying JOSE library. There is no server-side configuration to enable or disable ML-DSA; the feature is always available.


draft-ietf-lamps-pq-composite-sigs

draft-ietf-lamps-pq-composite-sigs is an active IETF draft that defines hybrid post-quantum signature algorithms combining a classical algorithm (ECDSA or RSA) with a post-quantum algorithm (ML-DSA, formerly CRYSTALS-Dilithium). Akāmu implements the TLS 1.3 signature scheme code points from the provisional IANA allocations in this draft.

What this affects

These code points are used only for mutual TLS client authentication — they appear in the TLS CertificateVerify message when a client presents a certificate signed with a composite ML-DSA scheme. Server-side certificate issuance (for ACME clients) is not affected.

The 12 composite scheme code points implemented are:

Code pointScheme
0x0901id-MLDSA44-RSA2048-PSS-SHA256
0x0902id-MLDSA44-RSA2048-PKCS15-SHA256
0x0903id-MLDSA44-Ed25519-SHA512
0x0904id-MLDSA44-ECDSA-P256-SHA256
0x0905id-MLDSA65-RSA3072-PSS-SHA512
0x0906id-MLDSA65-RSA3072-PKCS15-SHA512
0x0907id-MLDSA65-ECDSA-P384-SHA512
0x0908id-MLDSA65-ECDSA-brainpoolP256r1-SHA512
0x0909id-MLDSA87-ECDSA-P384-SHA512
0x090Aid-MLDSA87-ECDSA-brainpoolP384r1-SHA512
0x090Bid-MLDSA87-Ed448-SHA512
0x090Cid-MLDSA65-ECDSA-P256-SHA512

Stability warning

These code points come from the provisional IANA registry for an in-progress draft. They may change as the draft advances toward RFC publication. Before deploying to production, verify the current draft version against the code points in src/tls/schemes.rs. If the code points change, only that file needs to be updated.


Not implemented

RFC 8823 — S/MIME Certificates (Informational)

Defines an email identifier type and email-reply-00 challenge, where proof of control is a DKIM-signed reply email.

Not implemented. Issuing S/MIME certificates requires an SMTP/IMAP email delivery stack that is outside the scope of an embedded CA.

RFC 9115 — ACME Profile for Delegated Certificates

Enables a three-party delegation model: a domain owner (IdO) authorizes a third party (e.g., a CDN) to obtain certificates for the IdO’s domain, where the certificate’s public key belongs to the third party rather than the domain owner. The CA acts as a proxy between the two parties and enforces a JSON CSR template that restricts what the delegate may request.

Not implemented. This requires a dedicated API surface for IdOs to manage delegation policies, plus proxy routing between the NDC and IdO accounts. It is primarily useful for large-scale CDN deployments.

RFC 9345 — Delegated Credentials for TLS

Defines a TLS certificate extension (delegated_credential) that allows a TLS server to present short-lived sub-credentials derived from an issued certificate, without requiring a new CA-signed certificate for each sub-credential. The ACME interaction is limited to requesting certs with this extension set.

Not implemented. Requires X.509 extension support in synta-certificate for the Delegated Credentials extension (OID TBD / draft status at time of implementation).

RFC 9447 — ACME Challenges Using an Authority Token

Defines a generic tkauth-01 challenge type where proof of control comes from a JWT issued by an external authority rather than from DNS or HTTP. Designed for identifier types that cannot be validated by the classic ACME challenges (e.g., telephone numbers in STIR/SHAKEN).

Not implemented. Requires integration with an external token authority, which is deployment-specific.

RFC 9448 — ACME TNAuthList Authority Token

Extends RFC 9447 for telephone number (STIR/SHAKEN) use cases, where the authority token contains a TNAuthList claim.

Not implemented. Telecom-specific; requires connectivity to a Secure Telephone Identity (STI) Policy Administrator.

RFC 9538 — ACME Delegation Metadata for CDNI

Extends RFC 9115 for CDN Interconnection (CDNI) scenarios where multiple CDN tiers chain certificate delegation.

Not implemented. Layered on top of RFC 9115 and equally CDN-infrastructure-specific.

RFC 9891 — ACME DTN Node ID Validation (Experimental)

An experimental RFC that defines a bundleEID identifier type and a Bundle Protocol (BP) challenge for validating Delay-Tolerant Networking node identities.

Not implemented. Experimental status; targets space/satellite networks using the Bundle Protocol (RFC 9171).