compsig

package module
v4.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 20, 2026 License: MIT Imports: 24 Imported by: 0

README

compsig

Post-quantum composite signatures for github.com/lestrrat-go/jwx, tracking draft-ietf-jose-pq-composite-sigs. Each composite algorithm pairs ML-DSA (FIPS 204) with a traditional signature scheme; both component signatures must verify for the composite to be accepted.

Status

Experimental. This module tracks an active IETF draft. The underlying cryptographic construction is inherited from the more mature draft-ietf-lamps-pq-composite-sigs, but the JOSE-specific bindings (algorithm identifiers, JWK shape) may shift as the draft evolves. Do not rely on on-wire compatibility across releases until the draft reaches WG Last Call.

Installation

go get github.com/jwx-go/compsig/v4

Usage

Side-effect import registers all six composite algorithms with jwx. It transitively pulls in github.com/jwx-go/mldsa and github.com/jwx-go/ed448, so pure ML-DSA-44/65/87 and Ed448 are also registered.

import _ "github.com/jwx-go/compsig/v4"
Sign and verify
import (
    compsig "github.com/jwx-go/compsig/v4"
    "github.com/lestrrat-go/jwx/v4/jws"
)

sk, _ := compsig.GenerateKey(compsig.MLDSA65ES256())
pub := sk.Public()

signed, _ := jws.Sign(payload, jws.WithKey(compsig.MLDSA65ES256(), sk))
verified, _ := jws.Verify(signed, jws.WithKey(compsig.MLDSA65ES256(), pub))
JWK round-trip
import (
    compsig "github.com/jwx-go/compsig/v4"
    "github.com/lestrrat-go/jwx/v4/jwk"
)

sk, _ := compsig.GenerateKey(compsig.MLDSA65Ed25519())
jwkKey, _ := jwk.Import[jwk.Key](sk)

pubJWK, _ := jwkKey.PublicKey()

Algorithms

Each composite algorithm uses the AKP (Algorithm Key Pair) JWK key type, discriminated by the alg field. Public and private key material is a concatenation of the ML-DSA component followed by the traditional component.

Algorithm ML-DSA Traditional Pre-hash
ML-DSA-44-ES256 ML-DSA-44 ECDSA P-256 SHA-256
ML-DSA-65-ES256 ML-DSA-65 ECDSA P-256 SHA-512
ML-DSA-87-ES384 ML-DSA-87 ECDSA P-384 SHA-512
ML-DSA-44-Ed25519 ML-DSA-44 Ed25519 SHA-512
ML-DSA-65-Ed25519 ML-DSA-65 Ed25519 SHA-512
ML-DSA-87-Ed448 ML-DSA-87 Ed448 SHAKE256(64)

Note: the ECDSA component inside the composite is encoded as ASN.1 DER-encoded Ecdsa-Sig-Value (inherited from LAMPS), not the JOSE fixed-length r‖s encoding.

License

MIT

Documentation

Overview

Package compsig provides post-quantum composite signatures for the jwx library, tracking draft-ietf-jose-pq-composite-sigs.

Each composite algorithm pairs ML-DSA (FIPS 204) with a traditional signature scheme (ECDSA P-256/P-384, Ed25519, or Ed448). A composite signature verifies only if BOTH component signatures verify, providing defense-in-depth against failure in either the post-quantum or traditional component.

Supported algorithms

  • ML-DSA-44-ES256
  • ML-DSA-65-ES256
  • ML-DSA-87-ES384
  • ML-DSA-44-Ed25519
  • ML-DSA-65-Ed25519
  • ML-DSA-87-Ed448

Status

This module tracks an active IETF draft (draft-ietf-jose-pq-composite-sigs) that inherits its cryptographic construction from the more mature draft-ietf-lamps-pq-composite-sigs. JOSE-specific details (algorithm identifiers, JWK shape, pre-hash table) may shift as the draft evolves.

Usage

Import for side effects to register all six composite algorithms with jwx:

import _ "github.com/jwx-go/compsig/v4"

The side-effect import also transitively registers pure ML-DSA and Ed448 algorithms (via github.com/jwx-go/mldsa and github.com/jwx-go/ed448), which the composite signer reuses for the ML-DSA and Ed448 component signatures. ECDSA and Ed25519 components are handled directly via crypto/ecdsa and crypto/ed25519 — ECDSA because the composite format demands ASN.1 DER Ecdsa-Sig-Value (per LAMPS) rather than the JOSE r||s encoding.

Registration happens in init(). If any underlying jwx Register* call returns an error, init() panics — importing this package will crash the program at load time. This is the house style across all jwx-go extension modules.

Index

Constants

View Source
const Prefix = "CompositeAlgorithmSignatures2025"

Prefix is the fixed 32-byte ASCII domain separator defined in draft-ietf-jose-pq-composite-sigs §4.2.

Variables

This section is empty.

Functions

func MLDSA44ES256

func MLDSA44ES256() jwa.SignatureAlgorithm

MLDSA44ES256 returns the ML-DSA-44 + ECDSA P-256 composite signature algorithm identifier.

func MLDSA44Ed25519

func MLDSA44Ed25519() jwa.SignatureAlgorithm

MLDSA44Ed25519 returns the ML-DSA-44 + Ed25519 composite signature algorithm identifier.

func MLDSA65ES256

func MLDSA65ES256() jwa.SignatureAlgorithm

MLDSA65ES256 returns the ML-DSA-65 + ECDSA P-256 composite signature algorithm identifier.

func MLDSA65Ed25519

func MLDSA65Ed25519() jwa.SignatureAlgorithm

MLDSA65Ed25519 returns the ML-DSA-65 + Ed25519 composite signature algorithm identifier.

func MLDSA87ES384

func MLDSA87ES384() jwa.SignatureAlgorithm

MLDSA87ES384 returns the ML-DSA-87 + ECDSA P-384 composite signature algorithm identifier.

func MLDSA87Ed448

func MLDSA87Ed448() jwa.SignatureAlgorithm

MLDSA87Ed448 returns the ML-DSA-87 + Ed448 composite signature algorithm identifier.

Types

type PrivateKey

type PrivateKey struct {
	// contains filtered or unexported fields
}

PrivateKey is a composite private key pairing an ML-DSA private key with a traditional-algorithm private key. The raw form is mldsaSeed (32 bytes) || traditionalPriv (algorithm-specific), matching the "priv" field of the JWK representation.

func GenerateKey

func GenerateKey(alg jwa.SignatureAlgorithm) (*PrivateKey, error)

GenerateKey produces a fresh composite key pair for the given composite algorithm using crypto/rand.

func GenerateKeyWithRand

func GenerateKeyWithRand(alg jwa.SignatureAlgorithm, r io.Reader) (*PrivateKey, error)

GenerateKeyWithRand is like GenerateKey but draws randomness from r.

func NewPrivateKey

func NewPrivateKey(alg jwa.SignatureAlgorithm, raw []byte) (*PrivateKey, error)

NewPrivateKey reconstructs a composite private key from its raw byte form (mldsaSeed || traditionalPriv) for the given algorithm.

func (*PrivateKey) Algorithm

func (sk *PrivateKey) Algorithm() jwa.SignatureAlgorithm

Algorithm returns the composite algorithm identifier for this key.

func (*PrivateKey) Bytes

func (sk *PrivateKey) Bytes() ([]byte, error)

Bytes is an alias for MarshalBinary.

func (*PrivateKey) Equal

func (sk *PrivateKey) Equal(other *PrivateKey) bool

Equal reports whether two composite private keys represent the same underlying key material for the same algorithm.

func (*PrivateKey) MarshalBinary

func (sk *PrivateKey) MarshalBinary() ([]byte, error)

MarshalBinary returns the concatenated mldsaSeed || tradPriv encoding used by the JWK "priv" field.

func (*PrivateKey) Public

func (sk *PrivateKey) Public() *PublicKey

Public returns the public half of this private key.

type PublicKey

type PublicKey struct {
	// contains filtered or unexported fields
}

PublicKey is the public half of a composite key. The raw form is mldsaPub || traditionalPub, matching the "pub" field of the JWK representation.

func NewPublicKey

func NewPublicKey(alg jwa.SignatureAlgorithm, raw []byte) (*PublicKey, error)

NewPublicKey reconstructs a composite public key from its raw byte form (mldsaPub || traditionalPub) for the given algorithm.

func (*PublicKey) Algorithm

func (pk *PublicKey) Algorithm() jwa.SignatureAlgorithm

Algorithm returns the composite algorithm identifier for this key.

func (*PublicKey) Bytes

func (pk *PublicKey) Bytes() ([]byte, error)

Bytes is an alias for MarshalBinary.

func (*PublicKey) Equal

func (pk *PublicKey) Equal(other *PublicKey) bool

Equal reports whether two composite public keys represent the same underlying key material for the same algorithm.

func (*PublicKey) MarshalBinary

func (pk *PublicKey) MarshalBinary() ([]byte, error)

MarshalBinary returns the concatenated mldsaPub || tradPub encoding used by the JWK "pub" field.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL