crypto

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 20 Imported by: 0

README

go-eudi-crypto

ECCG-pinned cryptographic policy and JOSE/COSE/X.509 façades for EUDI Wallet relying-party and issuer components.

  • Algorithm policy pinned to ECCG Agreed Cryptographic Mechanisms v2.0 (EW-PIO-01-003): ES256/ES384/ES512, ECDH-ES(+A128KW/+A256KW) × A128GCM/A256GCM, COSE ES256/ES384/ES512, curves P-256/P-384/P-521. Unknown algorithm = reject.
  • KeyProvider interface with file-based (dev) and in-memory implementations.
  • JWS/JWE façade over lestrrat-go/jwx/v3 (SignJWS — incl. x5c from []*x509.Certificate, VerifyJWS, EncryptJWE, DecryptJWE); COSE_Sign1 façade over veraison/go-cose; X.509 chain validation against explicit anchors only (no system pool).
  • Structural (pre-trust) header peek: ParseJWSHeader([]byte) (Header, error) and X5CFromHeader(Header) ([]*x509.Certificate, error) read the protected header / decode the x5c chain (RFC 7515 §4.1 / §4.1.6) WITHOUT verifying the signature or validating the chain — they exist so a caller can resolve a candidate issuer key (e.g. from x5c, against a trust anchor) BEFORE calling VerifyJWS. Never trust their output directly; verify first.
  • Hash-name policy for content formats: HashForName (SD-JWT _sd_alg, e.g. sha-256; empty name → baseline sha-256 per SD-JWT §4.1.1) and HashForMSODigestAlg (mdoc digestAlgorithm, e.g. SHA-256) over one ECCG allow-list.
  • EC JWK helpers: ParseECPublicKeyJWK([]byte) / ECPublicKeyToJWKmap[string]any (RFC 7518 §6.2; used for SD-JWT VC cnf.jwk holder keys, RFC 7800), strict + fuzzed.
  • JWKThumbprint(crypto.PublicKey) (string, error): RFC 7638 JWK Thumbprint (SHA-256, base64url, no padding) of an EC public key's REQUIRED-members-only JWK; used to bind an ephemeral EC key into an mdoc SessionTranscript (OpenID4VP 1.0 Annex B.2).

Implemented specs: RFC 7515/7516/7518, RFC 9052/9053, RFC 5280, RFC 7638, RFC 7800, HAIP 1.0 crypto profile; SD-JWT _sd_alg (IANA named-hash) and ISO/IEC 18013-5 MSO digestAlgorithm. See SPECREFS.md for pinned versions.

Status: pre-v1. API frozen no earlier than OIDF conformance pass.

Documentation

Overview

Package crypto is the single cryptographic policy and primitives layer for the gmb-eudi EUDI modules. All JOSE/COSE/X.509 operations go through this package; no other module names an algorithm (ECCG Agreed Cryptographic Mechanisms v2.0, EW-PIO-01-003).

Import with an alias to avoid clashing with the standard library:

eudicrypto "github.com/gmb-eudi/go-eudi-crypto"

Index

Constants

View Source
const ECCGVersion = "2.0"

ECCGVersion is the pinned version of the ECCG Agreed Cryptographic Mechanisms document this policy table implements (EW-PIO-01-003).

Variables

View Source
var (
	ErrAlgorithmNotAllowed = errors.New("crypto: algorithm not allowed by ECCG policy")
	ErrCurveNotAllowed     = errors.New("crypto: curve not allowed by ECCG policy")
	ErrKeyTypeNotAllowed   = errors.New("crypto: key type not allowed by ECCG policy")
	ErrKeyNotFound         = errors.New("crypto: key not found")
	ErrNoAnchors           = errors.New("crypto: no trust anchors provided")
	ErrCritUnsupported     = errors.New("crypto: unsupported critical header parameter")
	ErrAlgKeyMismatch      = errors.New("crypto: token algorithm does not match key")
	ErrProtectedHeader     = errors.New("crypto: forbidden protected header parameter")
	ErrMalformed           = errors.New("crypto: malformed input")
	ErrVerificationFailed  = errors.New("crypto: verification failed")
	ErrDecryptionFailed    = errors.New("crypto: decryption failed")
)

Sentinel errors. Callers map these to their own problem/reason codes; this library never carries HTTP semantics or framework dependencies.

Functions

func ECPublicKeyToJWK

func ECPublicKeyToJWK(pub stdcrypto.PublicKey) (map[string]any, error)

ECPublicKeyToJWK serializes an EC public key to an [RFC 7518 §6.2] JWK as a map (ready to embed as an SD-JWT VC `cnf.jwk` object; RFC 7800). Rejects non-EC keys and curves not on the ECCG allow-list.

func EncryptJWE

func EncryptJWE(recipient stdcrypto.PublicKey, protected map[string]any, payload []byte) ([]byte, error)

EncryptJWE encrypts payload to an EC recipient key using ECDH-ES key agreement with A256GCM content encryption (RFC 7516; ECCG Agreed Mechanisms / HAIP 1.0 encrypted-response profile). Compact serialization. The protected map may carry additional header params (e.g. OID4VP Annex B.2 apu = base64url(mdocGeneratedNonce)); alg/enc/epk are set by the library from policy and must not be supplied by the caller. The pair is never taken from the caller — algorithms are policy-driven, never chosen by callers or tokens.

func GenerateEphemeralKey

func GenerateEphemeralKey(crv string) (*ecdsa.PrivateKey, error)

GenerateEphemeralKey creates a fresh EC keypair for per-session response encryption (HAIP response encryption; [OID4VP §8.3] direct_post.jwt). The curve must be ECCG-allowed.

func JWKThumbprint added in v0.0.2

func JWKThumbprint(pub stdcrypto.PublicKey) (string, error)

JWKThumbprint computes the RFC 7638 JWK Thumbprint of an EC public key: the base64url-encoded (RawURLEncoding, no padding) SHA-256 digest of the UTF-8 JSON representation of the JWK's REQUIRED members only — for an EC key exactly {"kty","crv","x","y"} — serialized with no whitespace and member names in lexicographic order ([RFC 7638 §3, §3.1]). The hash is fixed to SHA-256 by RFC 7638 itself, not a caller-configurable ECCG policy choice.

Used to bind an ephemeral EC public key into an mdoc SessionTranscript (OpenID4VP 1.0 Annex B.2).

Delegates JWK construction to ECPublicKeyToJWK, which already returns exactly the four REQUIRED members for an EC key; this function does not reimplement JWK construction. Returns whatever error ECPublicKeyToJWK returns for a key that is not a supported EC public key (fail closed).

func ParseCertChain

func ParseCertChain(raw []byte) ([]*x509.Certificate, error)

ParseCertChain parses one or more certificates from PEM (CERTIFICATE blocks) or raw DER. Untrusted input — fuzzed (FuzzParseCertChain).

func ParseECPublicKeyJWK

func ParseECPublicKeyJWK(jwkJSON []byte) (stdcrypto.PublicKey, error)

ParseECPublicKeyJWK parses an RFC 7518 EC public-key JWK — as carried in an SD-JWT VC `cnf.jwk` (RFC 7800) — into a *ecdsa.PublicKey. Strict and fail-closed: only kty="EC" on an ECCG-allowed curve is accepted, x/y must be fixed-size big-endian coordinates, and the point is fully validated (on-curve, non-identity) by ecdsa.ParseUncompressedPublicKey. Malformed input is rejected and never panics (fuzzed: FuzzParseECPublicKeyJWK).

func SignCOSESign1

func SignCOSESign1(ctx context.Context, kp KeyProvider, keyID string, protected COSEHeader, payload []byte) ([]byte, error)

SignCOSESign1 signs payload as COSE_Sign1 ([RFC 9052 §4.2]). The alg label is derived from the key; callers must not set label 1 (algorithms are derived from keys, never chosen by callers or messages).

func SignJWS

func SignJWS(ctx context.Context, kp KeyProvider, keyID string, protected map[string]any, payload []byte) ([]byte, error)

SignJWS signs payload as a compact JWS (RFC 7515). The alg header is derived from the key; callers must not set it (algorithms are derived from keys, never chosen by callers or tokens).

func VerifyChain

func VerifyChain(leaf *x509.Certificate, intermediates []*x509.Certificate, opts ChainOptions) ([][]*x509.Certificate, error)

VerifyChain performs [RFC 5280 §6.1] path validation of leaf against the explicit anchor set. Fail closed: empty anchors is an error, never a system-pool fallback.

func X5CFromHeader added in v0.0.3

func X5CFromHeader(h Header) ([]*x509.Certificate, error)

X5CFromHeader decodes the [RFC 7515 §4.1.6] x5c header member (a JSON array of standard-base64-encoded DER certificates, leaf first) into parsed certificates. It is the symmetric decode counterpart of the certChain encode side and returns the certificates leaf-first, in header order. Returns (nil, nil) if the header carries no x5c member — absence is not an error; the caller decides whether a chain is required. The certificates are NOT validated against any trust anchor and their DER is NOT verified against any signature — this is a structural decode only; resolve/validate the chain through the trust layer before trusting it. Fail closed (ErrMalformed) if x5c is present but not an array, an entry is not a string, an entry is not valid standard base64, or a decoded entry is not a parseable certificate.

Types

type COSEHeader

type COSEHeader map[int64]any

COSEHeader carries integer-labeled COSE header parameters ([RFC 9052 §3]). EUDI profiles (ISO 18013-5 IssuerAuth/DeviceAuth) use integer labels only; string labels are not surfaced by this façade.

func VerifyCOSESign1

func VerifyCOSESign1(raw []byte, key stdcrypto.PublicKey) ([]byte, COSEHeader, error)

VerifyCOSESign1 verifies a COSE_Sign1 message ([RFC 9052 §4.2]) against an explicitly supplied key. The expected algorithm is derived from the key; a mismatching protected alg is rejected.

type ChainOptions

type ChainOptions struct {
	Anchors []*x509.Certificate
	EKUs    []x509.ExtKeyUsage // empty = any EKU accepted (callers pass profile EKUs)
	At      time.Time          // required; explicitly injected clock, no hidden time.Now
}

ChainOptions configures VerifyChain. Anchors MUST come from go-eudi-trust — never the system pool, never inline PEM in services.

type Decrypter

type Decrypter interface {
	PrivateKey() stdcrypto.PrivateKey
}

Decrypter exposes the private key material needed for JWE ECDH-ES key agreement ([RFC 7518 §4.6]). In-memory implementations return the key directly; HSM-backed implementations require a native-decrypt extension of this interface — revisited when an HSM-backed provider lands.

type Header map[string]any

Header is a decoded JOSE protected header.

func DecryptJWE

func DecryptJWE(ctx context.Context, kp KeyProvider, keyID string, token []byte) ([]byte, Header, error)

DecryptJWE decrypts a compact JWE (RFC 7516) addressed to keyID. The protected header's alg/enc pair is checked against the ECCG policy BEFORE any decryption is attempted (HAIP: ECDH-ES with AES-GCM only).

func ParseJWSHeader added in v0.0.3

func ParseJWSHeader(token []byte) (Header, error)

ParseJWSHeader reads and JSON-decodes the protected header of a 3-segment compact JWS WITHOUT verifying the signature ([RFC 7515 §4.1]). Structural only — NEVER use its output to make a trust decision. It exists so a caller can inspect header members (e.g. an x5c chain, via X5CFromHeader) to resolve a candidate key BEFORE calling VerifyJWS; verify with VerifyJWS for anything security-relevant. Fail closed (ErrMalformed) on a wrong segment count, an empty segment, an undecodable base64url header, or a header that is not a JSON object. Must not panic on adversarial input (fuzzed: FuzzParseJWSHeader).

func VerifyJWS

func VerifyJWS(token []byte, key stdcrypto.PublicKey) ([]byte, Header, error)

VerifyJWS verifies a compact JWS against an explicitly supplied key. The expected algorithm is derived from the key, never taken from the token ([RFC 8725 §3.1 / §3.2]); jku/x5u are never dereferenced — key resolution belongs to the trust layer, not this library.

type KeyProvider

type KeyProvider interface {
	Signer(ctx context.Context, keyID string) (stdcrypto.Signer, error)
	Decrypter(ctx context.Context, keyID string) (Decrypter, error)
	Public(ctx context.Context, keyID string) (stdcrypto.PublicKey, error)
}

KeyProvider abstracts access to the operator's private keys. Planned implementations: filekeys (PEM/PKCS#8, dev), k8ssecret, pkcs11 (build tag), kms — only filekeys and the in-memory StaticProvider ship today.

type Policy

type Policy interface {
	AllowedJWSAlg(alg string) bool
	AllowedJWEAlg(alg, enc string) bool
	AllowedCOSEAlg(alg int64) bool
	AllowedCurve(crv string) bool
	HashForAlg(alg string) (stdcrypto.Hash, error)
	AllowedHashName(name string) bool
	HashForName(name string) (stdcrypto.Hash, error)
	HashForMSODigestAlg(alg string) (stdcrypto.Hash, error)
}

Policy is the ECCG-pinned algorithm allow-list. Immutable; no env/config overrides for algorithms — they are derived from keys, never chosen by callers or tokens. Unknown = reject.

func ECCG

func ECCG() Policy

ECCG returns the immutable policy singleton pinned to ECCGVersion.

type StaticProvider

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

StaticProvider is an in-memory KeyProvider for per-session ephemeral keys (GenerateEphemeralKey) and tests. The map is copied at construction and immutable afterwards, so concurrent use needs no locking.

func NewStaticProvider

func NewStaticProvider(keys map[string]*ecdsa.PrivateKey) *StaticProvider

NewStaticProvider returns a StaticProvider serving the given keys. The map is copied, so callers may safely mutate their own copy afterwards.

func (*StaticProvider) Decrypter

func (p *StaticProvider) Decrypter(_ context.Context, keyID string) (Decrypter, error)

Decrypter returns a Decrypter wrapping the EC private key matching keyID.

func (*StaticProvider) Public

func (p *StaticProvider) Public(_ context.Context, keyID string) (stdcrypto.PublicKey, error)

Public returns the EC public key matching keyID.

func (*StaticProvider) Signer

func (p *StaticProvider) Signer(_ context.Context, keyID string) (stdcrypto.Signer, error)

Signer returns the EC private key matching keyID as a crypto.Signer.

Directories

Path Synopsis
Package filekeys is the file-based (PEM/PKCS#8) KeyProvider for development and tests.
Package filekeys is the file-based (PEM/PKCS#8) KeyProvider for development and tests.

Jump to

Keyboard shortcuts

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