crypto

package
v0.5.9 Latest Latest
Warning

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

Go to latest
Published: May 21, 2026 License: BSD-2-Clause Imports: 26 Imported by: 0

Documentation

Overview

Package crypto provides cryptographic primitives for DIDComm v2.1 messaging.

This package implements the cryptographic algorithms required by the DIDComm Messaging specification (https://identity.foundation/didcomm-messaging/spec/v2.1/).

Supported Algorithms

Key Agreement (for encryption):

  • ECDH-ES+A256KW: Ephemeral-Static ECDH with AES-256 Key Wrap (anoncrypt)
  • ECDH-1PU+A256KW: ECDH-1PU with AES-256 Key Wrap (authcrypt)

Content Encryption:

  • A256GCM: AES-256-GCM
  • A256CBC-HS512: AES-256-CBC with HMAC-SHA-512
  • XC20P: XChaCha20-Poly1305 (DIDComm anoncrypt default)

Signing:

  • EdDSA: Ed25519
  • ES256: ECDSA with P-256 and SHA-256
  • ES256K: ECDSA with secp256k1 and SHA-256

Interoperability

The implementation targets interoperability with the sicpa-dlab/didcomm-rust reference implementation. See the test/didcomm_interop directory for interoperability tests.

Package crypto provides cryptographic operations for DIDComm v2.1.

This package implements:

  • JWE encryption (anoncrypt with ECDH-ES, authcrypt with ECDH-1PU)
  • JWS signing for signed messages
  • Key derivation for ECDH key agreement

Encryption Modes

DIDComm supports two encryption modes:

Anoncrypt (Anonymous Encryption): Uses ECDH-ES for key agreement. The sender is anonymous and cannot be authenticated from the ciphertext.

encrypted, err := crypto.Anoncrypt(message, recipientKeys)

Authcrypt (Authenticated Encryption): Uses ECDH-1PU for key agreement. The sender is authenticated and the recipient can verify who sent the message.

encrypted, err := crypto.Authcrypt(message, senderKey, recipientKeys)

Content Encryption

Supported content encryption algorithms:

  • A256GCM (recommended for anoncrypt)
  • A256CBC-HS512 (required for authcrypt)

Key Agreement Curves

Supported curves for key agreement:

  • X25519 (required)
  • P-256 (required)
  • P-384 (required)

Signing

Supported signing algorithms:

  • EdDSA with Ed25519 (required)
  • ES256 with P-256 (required)
  • ES256K with secp256k1 (required)

Index

Constants

View Source
const (
	AlgECDHESA256KW  = "ECDH-ES+A256KW"  // ECDH-ES with AES-256 Key Wrap (anoncrypt)
	AlgECDHESA128KW  = "ECDH-ES+A128KW"  // ECDH-ES with AES-128 Key Wrap
	AlgECDH1PUA256KW = "ECDH-1PU+A256KW" // ECDH-1PU with AES-256 Key Wrap (authcrypt)
)

Key Agreement Algorithms

View Source
const (
	EncA256GCM      = "A256GCM"       // AES-256-GCM content encryption
	EncA256CBCHS512 = "A256CBC-HS512" // AES-256-CBC with HMAC-SHA-512
	EncXC20P        = "XC20P"         // XChaCha20-Poly1305 content encryption (DIDComm default for anoncrypt)
	EncA128GCM      = "A128GCM"       // AES-128-GCM content encryption
	EncA128CBCHS256 = "A128CBC-HS256" // AES-128-CBC with HMAC-SHA-256
)

Content Encryption Algorithms

View Source
const (
	SigEdDSA  = "EdDSA"  // EdDSA signing (Ed25519)
	SigES256  = "ES256"  // ECDSA with P-256 and SHA-256
	SigES256K = "ES256K" // ECDSA with secp256k1 and SHA-256
	SigES384  = "ES384"  // ECDSA with P-384 and SHA-384
	SigES512  = "ES512"  // ECDSA with P-521 and SHA-512
)

Signing Algorithms

View Source
const (
	CurveX25519    = "X25519"    // Curve25519 for key agreement
	CurveEd25519   = "Ed25519"   // Ed25519 for signing
	CurveP256      = "P-256"     // NIST P-256
	CurveP384      = "P-384"     // NIST P-384
	CurveP521      = "P-521"     // NIST P-521
	CurveSecp256k1 = "secp256k1" // secp256k1 (Bitcoin/Ethereum curve)
)

Curve names

View Source
const (
	// XC20PKeySize is the key size for XChaCha20-Poly1305 (256 bits)
	XC20PKeySize = chacha20poly1305.KeySize // 32 bytes

	// XC20PNonceSize is the nonce size for XChaCha20-Poly1305 (192 bits)
	XC20PNonceSize = chacha20poly1305.NonceSizeX // 24 bytes

	// XC20PTagSize is the authentication tag size (128 bits)
	XC20PTagSize = 16
)

Variables

View Source
var (
	ErrEncryptionFailed     = errors.New("didcomm/crypto: encryption failed")
	ErrDecryptionFailed     = errors.New("didcomm/crypto: decryption failed")
	ErrSigningFailed        = errors.New("didcomm/crypto: signing failed")
	ErrVerificationFailed   = errors.New("didcomm/crypto: verification failed")
	ErrUnsupportedAlgorithm = errors.New("didcomm/crypto: unsupported algorithm")
	ErrInvalidKey           = errors.New("didcomm/crypto: invalid key")
	ErrInvalidNonce         = errors.New("didcomm/crypto: invalid nonce")
	ErrNoRecipients         = errors.New("didcomm/crypto: no recipients specified")
	ErrRecipientNotFound    = errors.New("didcomm/crypto: recipient key not found")
	ErrInvalidJWE           = errors.New("didcomm/crypto: invalid JWE")
	ErrInvalidJWS           = errors.New("didcomm/crypto: invalid JWS")
)

Sentinel errors for cryptographic operations.

Functions

func DIDCommAnoncryptDefaults

func DIDCommAnoncryptDefaults() (keyAlg string, contentAlg string)

DIDCommAnoncryptDefaults returns the default algorithms for DIDComm anoncrypt.

func DIDCommAuthcryptDefaults

func DIDCommAuthcryptDefaults() (keyAlg string, contentAlg string)

DIDCommAuthcryptDefaults returns the default algorithms for DIDComm authcrypt.

func Decrypt

func Decrypt(ctx context.Context, encrypted []byte, privateKey jwk.Key) ([]byte, error)

Decrypt decrypts a JWE using the provided private key. Supports both standard JOSE algorithms via jwx and custom algorithms (XC20P, ECDH-1PU).

func DecryptECDH1PU

func DecryptECDH1PU(ctx context.Context, encrypted []byte, recipientPrivateKey, senderPublicKey jwk.Key) ([]byte, error)

DecryptECDH1PU decrypts a JWE that uses ECDH-1PU (authcrypt). Requires the recipient's private key and the sender's public key.

func DecryptWithKeyStore

func DecryptWithKeyStore(ctx context.Context, encrypted []byte, keyStore KeyStore) ([]byte, error)

DecryptWithKeyStore decrypts a JWE using a key store to find the appropriate key.

func Encrypt

func Encrypt(ctx context.Context, plaintext []byte, recipientKeys []jwk.Key, opts EncryptionOptions) ([]byte, error)

Encrypt encrypts a plaintext message for the given recipients. Returns a JWE compact serialization or JSON serialization depending on recipient count.

For algorithms not natively supported by jwx (XC20P, ECDH-1PU), this function delegates to custom implementations.

func GenerateContentEncryptionKey

func GenerateContentEncryptionKey(enc string) ([]byte, error)

GenerateContentEncryptionKey generates a random key for the specified content encryption algorithm.

func GenerateSecp256k1Key

func GenerateSecp256k1Key() (jwk.Key, error)

GenerateSecp256k1Key generates a new secp256k1 key pair.

func GenerateSecp256k1KeyWithID

func GenerateSecp256k1KeyWithID(keyID string) (jwk.Key, error)

GenerateSecp256k1KeyWithID generates a new secp256k1 key pair with a key ID.

func InteropSafeAnoncryptDefaults

func InteropSafeAnoncryptDefaults() (keyAlg string, contentAlg string)

InteropSafeAnoncryptDefaults returns anoncrypt algorithms that work with jwx natively. Use these for immediate interoperability before XC20P is implemented.

func Sign

func Sign(ctx context.Context, plaintext []byte, privateKey jwk.Key, opts SignOptions) ([]byte, error)

Sign creates a JWS signature over the plaintext using the private key.

func Verify

func Verify(ctx context.Context, signed []byte, publicKey jwk.Key) ([]byte, error)

Verify verifies a JWS signature using the public key. Returns the verified payload.

func VerifyWithResolver

func VerifyWithResolver(ctx context.Context, signed []byte, resolver KeyResolver) ([]byte, error)

VerifyWithResolver verifies a JWS using a resolver to fetch the public key.

Types

type AlgorithmSupport

type AlgorithmSupport int

AlgorithmSupport indicates the support level for an algorithm.

const (
	SupportNative         AlgorithmSupport = iota // Algorithm is natively supported by jwx
	SupportCustom                                 // We have a custom implementation
	SupportNotImplemented                         // Algorithm is recognized but not yet implemented
	SupportUnknown                                // Algorithm is not recognized
)

type ContentAlgorithmInfo

type ContentAlgorithmInfo struct {
	Name      string
	Support   AlgorithmSupport
	KeySize   int // Required key size in bytes
	NonceSize int // Required nonce size in bytes
	TagSize   int // Authentication tag size in bytes
}

ContentAlgorithmInfo contains information about a content encryption algorithm.

func GetContentAlgorithmInfo

func GetContentAlgorithmInfo(enc string) (ContentAlgorithmInfo, bool)

GetContentAlgorithmInfo returns information about a content encryption algorithm.

type ContentEncryptor

type ContentEncryptor interface {
	// Encrypt encrypts plaintext with AAD, generating a random nonce
	Encrypt(plaintext, aad []byte) ([]byte, error)

	// EncryptWithNonce encrypts plaintext with a specific nonce
	EncryptWithNonce(nonce, plaintext, aad []byte) ([]byte, error)

	// Decrypt decrypts ciphertext (with prepended nonce) using AAD
	Decrypt(ciphertext, aad []byte) ([]byte, error)

	// DecryptWithNonce decrypts ciphertext with a specific nonce
	DecryptWithNonce(nonce, ciphertext, aad []byte) ([]byte, error)

	// KeySize returns the required key size in bytes
	KeySize() int

	// NonceSize returns the required nonce size in bytes
	NonceSize() int

	// Overhead returns the authentication tag overhead
	Overhead() int
}

ContentEncryptor is an interface for content encryption algorithms.

func NewContentEncryptor

func NewContentEncryptor(algorithm string, key []byte) (ContentEncryptor, error)

NewContentEncryptor creates a ContentEncryptor for the specified algorithm.

type ECDH1PUKeyAgreement

type ECDH1PUKeyAgreement struct {
	// SenderPrivateKey is the sender's static private key
	SenderPrivateKey jwk.Key

	// RecipientPublicKey is the recipient's static public key
	RecipientPublicKey jwk.Key

	// EphemeralPrivateKey is the sender's ephemeral private key (generated per message)
	EphemeralPrivateKey jwk.Key

	// Algorithm is the key wrapping algorithm (e.g., "ECDH-1PU+A256KW")
	Algorithm string

	// ContentEncryption is the content encryption algorithm (e.g., "A256CBC-HS512")
	ContentEncryption string

	// APU is Agreement PartyU Info (sender identifier, typically base64url-encoded DID)
	APU []byte

	// APV is Agreement PartyV Info (recipient identifier)
	APV []byte

	// CCTag is the content ciphertext authentication tag (used in ECDH-1PU KDF per draft-madden-jose-ecdh-1pu)
	// This binds the key derivation to the specific ciphertext, providing key commitment.
	CCTag []byte
}

ECDH1PUKeyAgreement performs ECDH-1PU key agreement.

func NewECDH1PU

func NewECDH1PU(senderKey, recipientKey jwk.Key, algorithm, contentEnc string) (*ECDH1PUKeyAgreement, error)

NewECDH1PU creates a new ECDH-1PU key agreement instance. For encryption: senderKey must be the sender's private key, recipientKey is recipient's public key. For decryption: senderKey can be nil, recipientKey must be the recipient's private key.

func (*ECDH1PUKeyAgreement) DeriveKey

func (e *ECDH1PUKeyAgreement) DeriveKey() ([]byte, jwk.Key, error)

DeriveKey derives the content encryption key using ECDH-1PU. Returns the derived key and the ephemeral public key (for inclusion in JWE header). Note: This method does NOT include the cc_tag. Use DeriveKeyWithTag for ECDH-1PU key commitment.

func (*ECDH1PUKeyAgreement) DeriveKeyForDecryption

func (e *ECDH1PUKeyAgreement) DeriveKeyForDecryption(ephemeralPubKey jwk.Key, senderPubKey jwk.Key) ([]byte, error)

DeriveKeyForDecryption derives the key for decryption given the ephemeral public key.

func (*ECDH1PUKeyAgreement) DeriveKeyWithTag

func (e *ECDH1PUKeyAgreement) DeriveKeyWithTag(ccTag []byte) ([]byte, jwk.Key, error)

DeriveKeyWithTag derives the key using ECDH-1PU with optional key commitment (cc_tag). If ccTag is nil or empty, uses standard Concat KDF. If ccTag is provided, includes the content ciphertext authentication tag in the KDF per draft-madden-jose-ecdh-1pu for key commitment. Returns the derived key and the ephemeral public key.

func (*ECDH1PUKeyAgreement) GenerateEphemeralKey

func (e *ECDH1PUKeyAgreement) GenerateEphemeralKey() error

GenerateEphemeralKey generates a new ephemeral key pair for the key agreement. The curve is determined by the recipient's public key.

type EncryptedMessage

type EncryptedMessage struct {
	// Protected is the base64url-encoded protected header
	Protected string `json:"protected"`

	// Recipients contains per-recipient encrypted key material
	Recipients []Recipient `json:"recipients"`

	// IV is the initialization vector (base64url-encoded)
	IV string `json:"iv"`

	// Ciphertext is the encrypted content (base64url-encoded)
	Ciphertext string `json:"ciphertext"`

	// Tag is the authentication tag (base64url-encoded)
	Tag string `json:"tag"`

	// AAD is additional authenticated data (base64url-encoded), optional
	AAD string `json:"aad,omitempty"`
}

EncryptedMessage represents a DIDComm encrypted message (JWE).

type EncryptionOptions

type EncryptionOptions struct {
	// Algorithm is the key agreement algorithm. Default: ECDH-ES (anoncrypt)
	Algorithm string

	// Encryption is the content encryption algorithm. Default: A256GCM
	Encryption string

	// SenderKey is the sender's private key (required for ECDH-1PU authcrypt)
	SenderKey jwk.Key

	// SenderDID is the sender's DID (for APU)
	SenderDID string
}

EncryptionOptions configures encryption behavior.

func AuthcryptOptions

func AuthcryptOptions(senderKey jwk.Key, senderDID string) EncryptionOptions

AuthcryptOptions returns options for authenticated encryption (authcrypt).

func DefaultEncryptionOptions

func DefaultEncryptionOptions() EncryptionOptions

DefaultEncryptionOptions returns options for anonymous encryption (anoncrypt).

type JWEHeader

type JWEHeader struct {
	// Algorithm is the key agreement algorithm (e.g., ECDH-ES, ECDH-1PU)
	Algorithm string `json:"alg"`

	// Encryption is the content encryption algorithm (e.g., A256GCM, A256CBC-HS512)
	Encryption string `json:"enc"`

	// Type is the media type of the JWE (should be "didcomm-encrypted+json")
	Type string `json:"typ,omitempty"`

	// SenderKeyID is the sender's key ID (for ECDH-1PU authcrypt)
	SenderKeyID string `json:"skid,omitempty"`

	// AgreementPartyUInfo is the APU for ECDH (base64url-encoded sender DID)
	AgreementPartyUInfo string `json:"apu,omitempty"`

	// AgreementPartyVInfo is the APV for ECDH (base64url-encoded recipient DIDs hash)
	AgreementPartyVInfo string `json:"apv,omitempty"`

	// EphemeralPublicKey is the ephemeral public key used for ECDH key agreement
	EphemeralPublicKey json.RawMessage `json:"epk,omitempty"`
}

JWEHeader represents the protected header of a JWE.

type JWSHeader

type JWSHeader struct {
	// Algorithm is the signing algorithm (EdDSA, ES256, ES256K)
	Algorithm string `json:"alg"`

	// KeyID identifies the signing key
	KeyID string `json:"kid,omitempty"`

	// Type is the media type (should be "didcomm-signed+json")
	Type string `json:"typ,omitempty"`
}

JWSHeader represents the protected header of a JWS.

type KeyAlgorithmInfo

type KeyAlgorithmInfo struct {
	Name              string
	Support           AlgorithmSupport
	RequiresSenderKey bool // Indicates if the algorithm requires a sender key (authcrypt)
}

KeyAlgorithmInfo contains information about a key agreement algorithm.

func GetKeyAlgorithmInfo

func GetKeyAlgorithmInfo(alg string) (KeyAlgorithmInfo, bool)

GetKeyAlgorithmInfo returns information about a key agreement algorithm.

type KeyResolver

type KeyResolver interface {
	ResolveKey(ctx context.Context, kid string) (jwk.Key, error)
}

KeyResolver resolves public keys by key ID.

type KeyStore

type KeyStore interface {
	// GetPrivateKey retrieves a private key by key ID
	GetPrivateKey(ctx context.Context, kid string) (jwk.Key, error)

	// ListKeyIDs returns all available key IDs
	ListKeyIDs(ctx context.Context) ([]string, error)
}

KeyStore provides access to private keys for decryption.

type Recipient

type Recipient struct {
	// Header contains per-recipient unprotected header
	Header RecipientHeader `json:"header"`

	// EncryptedKey is the encrypted content encryption key (base64url-encoded)
	EncryptedKey string `json:"encrypted_key"`
}

Recipient represents a JWE recipient.

type RecipientHeader

type RecipientHeader struct {
	// KeyID identifies the recipient's key
	KeyID string `json:"kid"`

	// EphemeralPublicKey is the sender's ephemeral public key
	EphemeralPublicKey json.RawMessage `json:"epk,omitempty"`
}

RecipientHeader contains per-recipient JWE header parameters.

type Secp256k1Signer

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

Secp256k1Signer implements JWS signing using ES256K.

func NewSecp256k1Signer

func NewSecp256k1Signer(key jwk.Key) (*Secp256k1Signer, error)

NewSecp256k1Signer creates a new ES256K signer from a JWK.

func (*Secp256k1Signer) KeyID

func (s *Secp256k1Signer) KeyID() string

KeyID returns the key ID if available.

func (*Secp256k1Signer) PublicKey

func (s *Secp256k1Signer) PublicKey() (jwk.Key, error)

PublicKey returns the public key as a JWK.

func (*Secp256k1Signer) Sign

func (s *Secp256k1Signer) Sign(payload []byte) ([]byte, error)

Sign signs the payload and returns the raw signature (r || s, 64 bytes).

func (*Secp256k1Signer) SignJWS

func (s *Secp256k1Signer) SignJWS(payload []byte, typ string) ([]byte, error)

SignJWS creates a complete JWS with ES256K algorithm.

type Secp256k1Verifier

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

Secp256k1Verifier implements JWS verification using ES256K.

func NewSecp256k1Verifier

func NewSecp256k1Verifier(key jwk.Key) (*Secp256k1Verifier, error)

NewSecp256k1Verifier creates a new ES256K verifier from a JWK.

func (*Secp256k1Verifier) Verify

func (v *Secp256k1Verifier) Verify(payload, signature []byte) error

Verify verifies a raw signature against the payload.

func (*Secp256k1Verifier) VerifyJWS

func (v *Secp256k1Verifier) VerifyJWS(compactJWS []byte) ([]byte, error)

VerifyJWS verifies a compact JWS and returns the payload.

type SignOptions

type SignOptions struct {
	// Algorithm is the signing algorithm. If empty, inferred from key type.
	Algorithm string

	// Detached indicates whether to use detached payload
	Detached bool
}

SignOptions configures signing behavior.

type Signature

type Signature struct {
	// Protected is the base64url-encoded protected header
	Protected string `json:"protected"`

	// Signature is the base64url-encoded signature value
	Signature string `json:"signature"`
}

Signature represents a single JWS signature.

type SignedMessage

type SignedMessage struct {
	// Payload is the base64url-encoded message
	Payload string `json:"payload"`

	// Signatures contains one or more signatures
	Signatures []Signature `json:"signatures"`
}

SignedMessage represents a DIDComm signed message (JWS).

type XC20PAEAD

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

XC20PAEAD wraps the XChaCha20-Poly1305 AEAD cipher for DIDComm use.

func NewXC20P

func NewXC20P(key []byte) (*XC20PAEAD, error)

NewXC20P creates a new XChaCha20-Poly1305 AEAD instance. The key must be exactly 32 bytes (256 bits).

func (*XC20PAEAD) Decrypt

func (x *XC20PAEAD) Decrypt(ciphertext, aad []byte) ([]byte, error)

Decrypt decrypts ciphertext that was encrypted with Encrypt. The input should be: nonce (24 bytes) || ciphertext || tag (16 bytes)

func (*XC20PAEAD) DecryptSeparate

func (x *XC20PAEAD) DecryptSeparate(nonce, ciphertext, tag, aad []byte) ([]byte, error)

DecryptSeparate decrypts ciphertext with a separate tag. This is useful for JWE decryption where ciphertext and tag are separate fields.

func (*XC20PAEAD) DecryptWithNonce

func (x *XC20PAEAD) DecryptWithNonce(nonce, ciphertext, aad []byte) ([]byte, error)

DecryptWithNonce decrypts ciphertext using a specific nonce. The ciphertext should NOT include the nonce prefix. Input format: ciphertext || tag (16 bytes)

func (*XC20PAEAD) Encrypt

func (x *XC20PAEAD) Encrypt(plaintext, aad []byte) ([]byte, error)

Encrypt encrypts plaintext with additional authenticated data (AAD). Returns the ciphertext with the authentication tag appended. A random nonce is generated and prepended to the ciphertext.

Output format: nonce (24 bytes) || ciphertext || tag (16 bytes)

func (*XC20PAEAD) EncryptSeparate

func (x *XC20PAEAD) EncryptSeparate(nonce, plaintext, aad []byte) (ciphertext, tag []byte, err error)

EncryptSeparate encrypts plaintext and returns ciphertext and tag separately. This is useful for JWE construction where ciphertext and tag are separate fields.

func (*XC20PAEAD) EncryptWithNonce

func (x *XC20PAEAD) EncryptWithNonce(nonce, plaintext, aad []byte) ([]byte, error)

EncryptWithNonce encrypts plaintext using a specific nonce. This is useful for deterministic testing or when the nonce is derived from other data.

Output format: nonce (24 bytes) || ciphertext || tag (16 bytes)

func (*XC20PAEAD) KeySize

func (x *XC20PAEAD) KeySize() int

KeySize returns the required key size in bytes.

func (*XC20PAEAD) NonceSize

func (x *XC20PAEAD) NonceSize() int

NonceSize returns the required nonce size in bytes.

func (*XC20PAEAD) Overhead

func (x *XC20PAEAD) Overhead() int

Overhead returns the maximum difference between plaintext and ciphertext lengths.

Jump to

Keyboard shortcuts

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