RCAN Cryptographic Profiles¶
Status: Draft — see rcan.dev/compatibility for the target version Issue: #188
Overview¶
A cryptographic profile names a complete signing algorithm suite used throughout RCAN for:
- RURI
?sig=query parameter (§1.6) firmware_hashenvelope field, type 13 (§3)attestation_refenvelope field, type 14 (§3)- M2M tokens (§11)
The profile is advertised in /.well-known/rcan-node.json via the crypto_profile field (see §17).
Profile: pqc-hybrid-v1 (Recommended — now through 2027)¶
Purpose¶
Provides quantum-resistant signing while remaining verifiable by pre-v2.2 receivers that only check Ed25519. Both signature halves are REQUIRED; a message carrying only one half MUST be rejected by v2.3+ receivers.
Algorithm Suite¶
| Component | Algorithm | Standard |
|---|---|---|
| Classical | Ed25519 | RFC 8032 |
| Post-Quantum | ML-DSA-65 / Dilithium3 | NIST FIPS 204 |
Key Sizes¶
| Key type | Raw bytes | Base64url length |
|---|---|---|
| Ed25519 public key | 32 B | 43 chars |
| Ed25519 signature | 64 B | 86 chars |
| ML-DSA-65 public key | 1952 B | 2603 chars |
| ML-DSA-65 signature | 3309 B | 4412 chars |
Signature Encoding¶
The combined signature is a single string of the form:
<ed25519_base64url>— base64url (no padding) of the 64-byte Ed25519 signature<ml_dsa_base64url>— base64url (no padding) of the 3309-byte ML-DSA-65 signature- The two components are separated by a literal
. - The prefix
pqc-hybrid-v1distinguishes this encoding from the legacy bare base64url format
Example (truncated for readability):
Signing Procedure¶
# message_bytes = canonical payload to sign (e.g. RURI path bytes)
ed25519_sig = ed25519_sign(ed25519_private_key, message_bytes)
ml_dsa_sig = ml_dsa_65_sign(ml_dsa_private_key, message_bytes)
combined_sig = (
"pqc-hybrid-v1."
+ base64url_no_pad(ed25519_sig)
+ "."
+ base64url_no_pad(ml_dsa_sig)
)
Verification Procedure¶
# sig_str = value from ?sig= or envelope field
parts = sig_str.split(".")
# parts[0] == "pqc-hybrid-v1", parts[1] == ed25519 half, parts[2] == ml_dsa half
if len(parts) != 3 or parts[0] != "pqc-hybrid-v1":
raise SignatureFormatError("Not a pqc-hybrid-v1 signature")
ed25519_bytes = base64url_decode(parts[1])
ml_dsa_bytes = base64url_decode(parts[2])
# BOTH halves must verify; either failure MUST reject the message
assert ed25519_verify(ed25519_public_key, message_bytes, ed25519_bytes), "Ed25519 half invalid"
assert ml_dsa_65_verify(ml_dsa_public_key, message_bytes, ml_dsa_bytes), "ML-DSA-65 half invalid"
Conformance Requirements¶
- A v2.3+ node advertising
crypto_profile: "pqc-hybrid-v1"MUST produce signatures in this combined format. - Receivers MUST reject any message where only one half is present.
- Receivers MUST reject any message where either half fails verification.
- The
ed25519_public_keyandpqc_public_keyfields MUST both be set in/.well-known/rcan-node.jsonwhen using this profile.
Profile: pqc-v1 (Future — post-2028)¶
Purpose¶
ML-DSA-65 only. For use after Ed25519 sunset (2027). Receivers that support pqc-v1 no longer need to maintain Ed25519 verification code.
Algorithm Suite¶
| Component | Algorithm | Standard |
|---|---|---|
| Post-Quantum | ML-DSA-65 / Dilithium3 | NIST FIPS 204 |
Signature Encoding¶
<ml_dsa_base64url>— base64url (no padding) of the 3309-byte ML-DSA-65 signature
Conformance Requirements¶
- General rule: Prefer
pqc-hybrid-v1during 2026–2027 for maximum interoperability with receivers that may not yet support ML-DSA verification. - Exception — operator-owned deployments: A robot operator who controls both the issuing node and all verifiers in the deployment MAY use
pqc-v1at any time, including before 2028. This is appropriate for closed fleets where the operator owns both sides of the trust chain. - A node advertising
crypto_profile: "pqc-v1"MUST reject legacy Ed25519-only messages. - The
ed25519_public_keyfield is absent or null when usingpqc-v1.
Migration Timeline¶
| Period | Profile | Notes |
|---|---|---|
| 2026 – 2027 | pqc-hybrid-v1 |
Default for interop. Both halves required. Old receivers still verify Ed25519. |
| 2026+ | pqc-v1 |
Operator-owned/closed deployments only. ML-DSA-65 only when operator controls all verifiers. |
| 2027 | Ed25519 sunset | SDKs stop signing with Ed25519; pqc-hybrid-v1 generators may set Ed25519 half to zeros per compat mode. |
| 2028+ | pqc-v1 |
General availability. ML-DSA-65 only. Ed25519 verification code removed. |
Reference Implementations¶
- Python:
dilithium-py(pure-software, no native deps) - TypeScript:
@noble/post-quantum(pure-software, no native deps)