cms

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 15, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

README

cms

codecov golangci-lint Go Report Card

A Go implementation of RFC 5652 Cryptographic Message Syntax (CMS).

go get github.com/mdean75/cms

Contents


Public Types at a Glance

Builders

All six types use the functional options pattern: pass options as variadic arguments to NewXxx, which validates configuration immediately and returns an error. The returned value is then safe for concurrent calls to the terminal method.

Type Terminal method Use when you want to…
Signer Sign(r) Sign content with an X.509 certificate and private key (RSA, ECDSA, or Ed25519)
CounterSigner CounterSign(r) Add a witnessed counter-signature to an existing SignedData without re-signing the content
Encryptor Encrypt(r) Encrypt content for one or more recipients using their public keys (RSA-OAEP or ECDH)
SymmetricEncryptor Encrypt(r) Encrypt content with a caller-supplied symmetric key when the key is already shared out of band
Digester Digest(r) Wrap content with a hash digest for integrity checking only (no signature, no encryption)
Authenticator Authenticate(r) Compute an HMAC over content and deliver the MAC key to recipients via RSA-OAEP or ECDH
Parsed Results

These types are returned by the corresponding ParseXxx function. Each exposes a Verify or Decrypt method to check the cryptographic result, and a Content method to retrieve the payload.

Type Parse function Verify / Decrypt
ParsedSignedData ParseSignedData(r) .Verify(opts...) / .VerifyDetached(r, opts...)
ParsedEnvelopedData ParseEnvelopedData(r) .Decrypt(key, cert)
ParsedEncryptedData ParseEncryptedData(r) .Decrypt(key)
ParsedDigestedData ParseDigestedData(r) .Verify() / .VerifyDetached(r)
ParsedAuthenticatedData ParseAuthenticatedData(r) .VerifyMAC(key, cert)
SignerInfo (field of ParsedSignedData) Per-signer details: certificate, algorithm, signature bytes, and attributes
Options and Enums
Type Used with Purpose
SigningOption NewSigner, NewCounterSigner WithHash, WithRSAPKCS1, WithSignerIdentifier, AddCertificate
SignerOption NewSigner only WithDetachedContent, WithContentType, WithTimestamp, WithAdditionalSigner, AddCRL, and all SigningOption values
EncryptorOption NewEncryptor WithRecipient, WithContentEncryption, WithMaxContentSize
SymmetricEncryptorOption NewSymmetricEncryptor WithKey, WithContentEncryption, WithContentType, WithMaxContentSize
DigesterOption NewDigester WithHash, WithDetachedContent, WithContentType, WithMaxContentSize
AuthenticatorOption NewAuthenticator WithRecipient, WithMACAlgorithm, WithContentType, WithMaxContentSize
VerifyOption ParsedSignedData.Verify Tune chain validation: WithTrustRoots, WithSystemTrustStore, WithNoChainValidation, WithVerifyTime
SignerIdentifierType WithSignerIdentifier(...) IssuerAndSerialNumber (default) or SubjectKeyIdentifier
ContentEncryptionAlgorithm WithContentEncryption(...) AES256GCM (default), AES128GCM, AES128CBC, AES256CBC
MACAlgorithm WithMACAlgorithm(...) HMACSHA256 (default), HMACSHA384, HMACSHA512
Errors
Type Purpose
Error The library's error type. Carries a Code, a human-readable Message, and an optional Cause.
ErrorCode Enum of all error categories. Use errors.Is(err, cms.ErrXxx) to check for specific kinds — see Error Handling for the full sentinel list.

Signing and Verifying

Sign a message

NewSigner takes the signing certificate, private key, and any options. Pass the returned *Signer to Sign with an io.Reader over the content.

import (
    "crypto/x509"
    "github.com/mdean75/cms"
)

func signMessage(cert *x509.Certificate, key crypto.Signer, content []byte) ([]byte, error) {
    s, err := cms.NewSigner(cert, key)
    if err != nil {
        return nil, err
    }
    return s.Sign(cms.FromBytes(content))
}

Sign returns a DER-encoded ContentInfo wrapping a SignedData structure. The signed content is embedded (attached) by default.

Common options (passed as variadic arguments to NewSigner):

Option Effect
WithHash(crypto.SHA384) Use SHA-384 instead of the default SHA-256
WithRSAPKCS1() Use RSA PKCS#1 v1.5 instead of the default RSA-PSS
WithDetachedContent() Omit the content from the output (see detached signatures)
WithSignerIdentifier(cms.SubjectKeyIdentifier) Identify the signer by SKI instead of issuer/serial
AddCertificate(cert) Embed additional certificates (e.g. intermediates)
WithTimestamp(tsaURL) Request an RFC 3161 timestamp from a TSA
WithMaxAttachedContentSize(n) Override the default 64 MiB content size limit
Verify a signature

Parse the DER bytes with ParseSignedData, then call Verify.

func verifyMessage(der []byte, roots *x509.CertPool) ([]byte, error) {
    psd, err := cms.ParseSignedData(cms.FromBytes(der))
    if err != nil {
        return nil, err
    }

    if err := psd.Verify(cms.WithTrustRoots(roots)); err != nil {
        return nil, err
    }

    r, err := psd.Content()
    if err != nil {
        return nil, err
    }
    return io.ReadAll(r)
}

Verification options:

Option Effect
WithTrustRoots(pool) Validate the certificate chain against the provided CA pool
WithSystemTrustStore() Validate against the OS system trust store
WithVerifyOptions(x509.VerifyOptions{...}) Full control over chain validation
WithNoChainValidation() Skip certificate chain checking entirely
WithVerifyTime(t) Use a fixed reference time for certificate validity
Detached signatures

When signing, call WithDetachedContent(). The returned DER contains no embedded content. Pass the original content to VerifyDetached.

// Sign — content is not embedded in the output
s, err := cms.NewSigner(cert, key, cms.WithDetachedContent())
if err != nil { ... }

der, err := s.Sign(cms.FromBytes(content))

// Verify — supply the original content separately
psd, err := cms.ParseSignedData(cms.FromBytes(der))
if err != nil { ... }

err = psd.VerifyDetached(cms.FromBytes(content), cms.WithTrustRoots(roots))
Multiple signers

Add additional signers with WithAdditionalSigner. Each signer is configured independently with its own certificate, key, and algorithm options.

signer2, err := cms.NewSigner(cert2, key2, cms.WithHash(crypto.SHA384))
if err != nil { ... }

signer1, err := cms.NewSigner(cert1, key1, cms.WithAdditionalSigner(signer2))
if err != nil { ... }

der, err := signer1.Sign(cms.FromBytes(content))

Verify and VerifyDetached check every SignerInfo in the structure. All must pass for the call to succeed.

Counter-signatures

A counter-signature signs an existing SignerInfo signature value, providing a witnessed-at timestamp without a TSA.

// counter is applied to every SignerInfo in the parsed ContentInfo
cs, err := cms.NewCounterSigner(counterCert, counterKey)
if err != nil { ... }

updated, err := cs.CounterSign(cms.FromBytes(der))

CounterSign returns a new DER-encoded ContentInfo with the counter-signature embedded as an unsigned attribute in each SignerInfo.


Encrypting and Decrypting (EnvelopedData)

EnvelopedData encrypts content for one or more recipients. The content encryption key is wrapped using the recipient's public key (RSA-OAEP for RSA keys, ECDH ephemeral-static for EC keys).

// Encrypt for a recipient
enc, err := cms.NewEncryptor(cms.WithRecipient(recipientCert))
if err != nil { ... }

der, err := enc.Encrypt(cms.FromBytes(content))

// Decrypt as the recipient
ped, err := cms.ParseEnvelopedData(cms.FromBytes(der))
if err != nil { ... }

plaintext, err := ped.Decrypt(privateKey, recipientCert)

The default content encryption algorithm is AES-256-GCM. Use WithContentEncryption to select a different cipher:

enc, err := cms.NewEncryptor(cms.WithRecipient(cert), cms.WithContentEncryption(cms.AES128GCM))
if err != nil { ... }

der, err := enc.Encrypt(r)

Available algorithms: AES256GCM (default), AES128GCM, AES128CBC, AES256CBC.


Symmetric Encryption (EncryptedData)

EncryptedData encrypts content with a caller-supplied symmetric key. Unlike EnvelopedData there are no recipients — key distribution is handled out of band.

key := make([]byte, 32) // 32-byte key for AES-256
if _, err := io.ReadFull(rand.Reader, key); err != nil { ... }

// Encrypt
se, err := cms.NewSymmetricEncryptor(cms.WithKey(key))
if err != nil { ... }

der, err := se.Encrypt(cms.FromBytes(content))

// Decrypt
ped, err := cms.ParseEncryptedData(cms.FromBytes(der))
if err != nil { ... }

plaintext, err := ped.Decrypt(key)

Digest-Only Messages (DigestedData)

DigestedData wraps content with a hash but provides no confidentiality or authentication. It is useful for integrity checking when the verifier already trusts the channel.

// Create
d, err := cms.NewDigester()
if err != nil { ... }

der, err := d.Digest(cms.FromBytes(content))

// Verify (attached — content is embedded)
pdd, err := cms.ParseDigestedData(cms.FromBytes(der))
if err != nil { ... }

if err := pdd.Verify(); err != nil { ... }

r, err := pdd.Content()

// Verify (detached)
d, err = cms.NewDigester(cms.WithDetachedContent())
if err != nil { ... }

der, err = d.Digest(cms.FromBytes(content))
pdd, err = cms.ParseDigestedData(cms.FromBytes(der))
err = pdd.VerifyDetached(cms.FromBytes(content))

MAC Authentication (AuthenticatedData)

AuthenticatedData computes an HMAC over the content and encrypts the MAC key for one or more recipients (RSA-OAEP or ECDH, same as EnvelopedData).

// Authenticate
a, err := cms.NewAuthenticator(cms.WithRecipient(recipientCert))
if err != nil { ... }

der, err := a.Authenticate(cms.FromBytes(content))

// Verify
pad, err := cms.ParseAuthenticatedData(cms.FromBytes(der))
if err != nil { ... }

if err := pad.VerifyMAC(privateKey, recipientCert); err != nil { ... }

r, err := pad.Content()

Default MAC algorithm is HMAC-SHA256. Use WithMACAlgorithm to select cms.HMACSHA384 or cms.HMACSHA512.


Error Handling

All errors are *cms.Error values carrying a Code, a human-readable Message, and an optional Cause. Use errors.Is to match against sentinel errors:

if errors.Is(err, cms.ErrInvalidSignature) {
    // signature check failed
}
if errors.Is(err, cms.ErrMissingCertificate) {
    // no matching certificate found in the message
}

Constructors validate configuration immediately. All validation failures are reported together by the constructor, before the terminal method is called:

_, err := cms.NewSigner(nil, nil)  // nil cert and nil key
// err contains both failures via errors.Join
// errors.Is(err, cms.ErrInvalidConfiguration) → true

Sentinel errors:

Sentinel Meaning
ErrParse Malformed ASN.1 input
ErrBERConversion BER→DER normalisation failure
ErrUnsupportedAlgorithm Algorithm OID not supported
ErrInvalidSignature Cryptographic verification failed
ErrCertificateChain Certificate chain validation failed
ErrMissingCertificate Required certificate not found
ErrTimestamp RFC 3161 timestamp error
ErrCounterSignature Counter-signature error
ErrVersionMismatch CMS version field mismatch
ErrAttributeInvalid Signed or authenticated attribute invalid
ErrContentTypeMismatch ContentInfo OID does not match structure
ErrPKCS7Format PKCS#7 construct not supported
ErrDetachedContentMismatch Detached content does not match digest
ErrPayloadTooLarge Content exceeds configured size limit
ErrInvalidConfiguration Builder configured incorrectly

License

This project is licensed under the Apache License 2.0. See COPYRIGHT.md for copyright information.

Contributing

Contributions are welcome. Please read CONTRIBUTING.md for guidelines on reporting issues, submitting pull requests, and code style expectations.

Changelog

See CHANGELOG.md for a history of notable changes.


Development

See DEVELOPMENT.md for instructions on regenerating interop test fixtures (OpenSSL and Bouncy Castle) and for running the live interop tools in cmd/, which verify that this library's output is accepted by OpenSSL, Bouncy Castle, go.mozilla.org/pkcs7, and github.com/smimesign/ietf-cms. Note that cmd/ is a separate Go module and is not included in go test ./... from the repository root.

Documentation

Overview

Package cms implements the Cryptographic Message Syntax as defined in RFC 5652.

It supports all five CMS content types:

  • [SignedData]: sign and verify content using RSA (PKCS#1 v1.5 and PSS), ECDSA, or Ed25519; supports attached and detached signatures, multiple signers, counter-signatures, CRL embedding, and RFC 3161 timestamps.
  • [EnvelopedData]: encrypt content for one or more recipients using RSA-OAEP or ECDH ephemeral-static key agreement; content is encrypted with AES-GCM or AES-CBC.
  • [EncryptedData]: encrypt content with a caller-supplied symmetric key when key distribution is handled out of band.
  • [DigestedData]: wrap content with a cryptographic hash for integrity checking without a signature or encryption.
  • [AuthenticatedData]: compute an HMAC over content and deliver the MAC key to recipients via RSA-OAEP or ECDH.

All six content-type constructors use the functional options pattern: pass options as variadic arguments to NewXxx, which validates configuration immediately and returns an error. The returned value is then safe for concurrent calls to the terminal method (Sign, CounterSign, Encrypt, Digest, or Authenticate), which produces a DER-encoded ContentInfo.

The corresponding ParseXxx function returns a parsed result whose Verify or Decrypt method checks the cryptographic output.

The package accepts both DER and BER input. BER is transparently converted to DER before parsing, so messages produced by Windows CryptoAPI, Java Bouncy Castle, and other implementations that emit BER are handled correctly.

The ber sub-package exposes the BER-to-DER converter directly for callers that need it outside of a CMS context.

Index

Constants

View Source
const DefaultMaxAttachedSize int64 = 64 * 1024 * 1024

DefaultMaxAttachedSize is the default maximum content size in bytes for attached signatures (64 MiB). Sign returns ErrPayloadTooLarge if this limit is exceeded. Use WithMaxAttachedContentSize to override.

View Source
const UnlimitedAttachedSize int64 = -1

UnlimitedAttachedSize disables the attached content size limit when passed to WithMaxAttachedContentSize. Use with caution: attached content is fully buffered in memory during Sign.

Variables

View Source
var (
	// ErrParse is returned when a CMS or ASN.1 structure cannot be parsed.
	ErrParse = &Error{Code: CodeParse}

	// ErrBERConversion is returned when BER to DER normalization fails.
	ErrBERConversion = &Error{Code: CodeBERConversion}

	// ErrUnsupportedAlgorithm is returned when a message uses an algorithm not
	// in the allow-list.
	ErrUnsupportedAlgorithm = &Error{Code: CodeUnsupportedAlgorithm}

	// ErrInvalidSignature is returned when cryptographic signature verification fails.
	ErrInvalidSignature = &Error{Code: CodeInvalidSignature}

	// ErrCertificateChain is returned when X.509 chain validation fails.
	ErrCertificateChain = &Error{Code: CodeCertificateChain}

	// ErrMissingCertificate is returned when the signer certificate is not present
	// in the SignedData certificates field.
	ErrMissingCertificate = &Error{Code: CodeMissingCertificate}

	// ErrTimestamp is returned when RFC 3161 timestamp operations fail.
	ErrTimestamp = &Error{Code: CodeTimestamp}

	// ErrCounterSignature is returned for counter-signature specific failures.
	ErrCounterSignature = &Error{Code: CodeCounterSignature}

	// ErrVersionMismatch is returned when a SignedData or SignerInfo version field
	// indicates an unsupported capability.
	ErrVersionMismatch = &Error{Code: CodeVersionMismatch}

	// ErrAttributeInvalid is returned when a mandatory signed attribute is missing
	// or its value fails validation.
	ErrAttributeInvalid = &Error{Code: CodeAttributeInvalid}

	// ErrContentTypeMismatch is returned when the content-type signed attribute
	// does not match the eContentType in EncapsulatedContentInfo.
	ErrContentTypeMismatch = &Error{Code: CodeContentTypeMismatch}

	// ErrPKCS7Format is returned when the parser detects PKCS #7 format rather than
	// CMS format. The two formats differ in how non-id-data content is encapsulated.
	ErrPKCS7Format = &Error{Code: CodePKCS7Format}

	// ErrDetachedContentMismatch is returned when Verify is called on a detached
	// signature or VerifyDetached is called on an attached signature.
	ErrDetachedContentMismatch = &Error{Code: CodeDetachedContentMismatch}

	// ErrPayloadTooLarge is returned when attached content exceeds the configured
	// size limit. Use WithDetachedContent or increase the limit with
	// WithMaxAttachedContentSize.
	ErrPayloadTooLarge = &Error{Code: CodePayloadTooLarge}

	// ErrInvalidConfiguration is returned when the builder configuration is invalid.
	// When multiple configuration errors exist, they are joined using errors.Join so
	// that each failure is individually inspectable.
	ErrInvalidConfiguration = &Error{Code: CodeInvalidConfiguration}
)

Sentinel errors for use with errors.Is. Each sentinel represents an error category. Errors returned by this package carry descriptive messages and causes; sentinels are used only for category matching.

Functions

func FromBytes

func FromBytes(b []byte) io.Reader

FromBytes wraps a byte slice as an io.Reader for use with Sign, ParseSignedData, and other functions that accept io.Reader.

Types

type Authenticator

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

Authenticator builds a CMS AuthenticatedData message. The MAC key is generated internally and distributed to recipients using the same RSA-OAEP / ECDH mechanisms as Encryptor. Configure with functional options passed to NewAuthenticator. Authenticate is safe for concurrent use once constructed.

func NewAuthenticator

func NewAuthenticator(opts ...AuthenticatorOption) (*Authenticator, error)

NewAuthenticator returns a new Authenticator configured with opts and validates the configuration immediately. Returns an error if any option is invalid or if no recipient is provided.

func (*Authenticator) Authenticate

func (a *Authenticator) Authenticate(r io.Reader) ([]byte, error)

Authenticate reads content from r, generates a random MAC key, distributes it to all configured recipients, computes the HMAC, and returns the DER-encoded ContentInfo wrapping AuthenticatedData.

type AuthenticatorOption

type AuthenticatorOption interface {
	// contains filtered or unexported methods
}

AuthenticatorOption configures an Authenticator. Pass to NewAuthenticator.

func WithMACAlgorithm

func WithMACAlgorithm(alg MACAlgorithm) AuthenticatorOption

WithMACAlgorithm sets the HMAC algorithm for NewAuthenticator. Defaults to HMACSHA256.

type ContentEncryptionAlgorithm

type ContentEncryptionAlgorithm int

ContentEncryptionAlgorithm identifies the symmetric cipher used to encrypt EnvelopedData content.

const (
	// AES256GCM selects AES-256 in GCM mode. This is the default.
	AES256GCM ContentEncryptionAlgorithm = iota
	// AES128GCM selects AES-128 in GCM mode.
	AES128GCM
	// AES128CBC selects AES-128 in CBC mode with PKCS#7 padding.
	AES128CBC
	// AES256CBC selects AES-256 in CBC mode with PKCS#7 padding.
	AES256CBC
)

type ContentEncryptionOption

type ContentEncryptionOption interface {
	EncryptorOption
	SymmetricEncryptorOption
}

ContentEncryptionOption applies to NewEncryptor and NewSymmetricEncryptor.

func WithContentEncryption

func WithContentEncryption(alg ContentEncryptionAlgorithm) ContentEncryptionOption

WithContentEncryption sets the symmetric cipher for content encryption. Applies to NewEncryptor and NewSymmetricEncryptor. Defaults to AES256GCM.

type ContentSizeOption

ContentSizeOption applies to NewEncryptor, NewSymmetricEncryptor, NewDigester, and NewAuthenticator.

func WithMaxContentSize

func WithMaxContentSize(maxBytes int64) ContentSizeOption

WithMaxContentSize sets the maximum content size in bytes for NewEncryptor, NewSymmetricEncryptor, NewDigester, and NewAuthenticator. Defaults to DefaultMaxAttachedSize (64 MiB). Pass UnlimitedAttachedSize to disable.

type ContentTypeOption

ContentTypeOption applies to NewSigner, NewSymmetricEncryptor, NewDigester, and NewAuthenticator.

func WithContentType

func WithContentType(oid asn1.ObjectIdentifier) ContentTypeOption

WithContentType sets a custom eContentType OID. Default is id-data. A non-id-data type forces SignedData version 3 per RFC 5652 §5.1. Also accepted by NewSymmetricEncryptor, NewDigester, and NewAuthenticator.

type CounterSigner

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

CounterSigner appends a counter-signature to every SignerInfo in an existing CMS SignedData. A counter-signature signs the Signature bytes of the target SignerInfo (not the original content), as defined in RFC 5652, section 11.4. Construct it with NewCounterSigner; CounterSign is safe for concurrent use once constructed.

func NewCounterSigner

func NewCounterSigner(cert *x509.Certificate, key crypto.Signer, opts ...SigningOption) (*CounterSigner, error)

NewCounterSigner constructs a CounterSigner with the given certificate and private key. Defaults: SHA-256 digest, IssuerAndSerialNumber signer identifier. All configuration errors (nil cert, nil key, invalid options) are reported together. CounterSign is safe for concurrent use once NewCounterSigner returns successfully.

func (*CounterSigner) CounterSign

func (cs *CounterSigner) CounterSign(r io.Reader) ([]byte, error)

CounterSign reads a DER-encoded CMS ContentInfo from r, appends a counter- signature as an unsigned attribute (id-countersignature, OID 1.2.840.113549.1.9.6) on every SignerInfo, and returns the updated DER-encoded ContentInfo.

type DetachedOption

type DetachedOption interface {
	SignerOption
	DigesterOption
}

DetachedOption applies to NewSigner and NewDigester.

func WithDetachedContent

func WithDetachedContent() DetachedOption

WithDetachedContent produces a detached signature or digest (eContent absent in output). Also accepted by NewDigester.

type Digester

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

Digester builds a CMS DigestedData message. Configure it with functional options passed to NewDigester. Digest is safe for concurrent use once constructed.

func NewDigester

func NewDigester(opts ...DigesterOption) (*Digester, error)

NewDigester returns a new Digester configured with opts and validates the configuration immediately. Returns an error if any option is invalid or if the hash algorithm is not supported.

func (*Digester) Digest

func (d *Digester) Digest(r io.Reader) ([]byte, error)

Digest reads content from r, computes the CMS DigestedData, and returns the DER-encoded ContentInfo.

type DigesterOption

type DigesterOption interface {
	// contains filtered or unexported methods
}

DigesterOption configures a Digester. Pass to NewDigester.

type Encryptor

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

Encryptor builds a CMS EnvelopedData message. Configure it with functional options passed to NewEncryptor. Encrypt is safe for concurrent use once constructed.

func NewEncryptor

func NewEncryptor(opts ...EncryptorOption) (*Encryptor, error)

NewEncryptor returns a new Encryptor configured with opts and validates the configuration immediately. Returns an error if any option is invalid or if no recipient is provided.

func (*Encryptor) Encrypt

func (e *Encryptor) Encrypt(r io.Reader) ([]byte, error)

Encrypt reads plaintext from r, encrypts it for all configured recipients, and returns the DER-encoded CMS ContentInfo wrapping EnvelopedData.

type EncryptorOption

type EncryptorOption interface {
	// contains filtered or unexported methods
}

EncryptorOption configures an Encryptor. Pass to NewEncryptor.

type Error

type Error struct {
	// Code identifies the category of this error.
	Code ErrorCode
	// Message is a human-readable description of the error.
	Message string
	// Cause is the underlying error that triggered this error, if any.
	Cause error
}

Error is the error type returned by all cms operations. It implements the error interface and supports error chain inspection via errors.Is and errors.As.

func (*Error) Error

func (e *Error) Error() string

Error returns a string representation of the error, including the cause if present.

func (*Error) Is

func (e *Error) Is(target error) bool

Is reports whether the target matches this error by comparing error codes. This enables errors.Is(err, cms.ErrInvalidSignature) to match any *Error with the same code, regardless of message or cause.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the underlying cause of the error, enabling errors.Is and errors.As to traverse the error chain.

type ErrorCode

type ErrorCode int

ErrorCode identifies the category of a CMS error.

const (
	// CodeParse indicates a malformed ASN.1 structure or invalid CMS content.
	CodeParse ErrorCode = iota
	// CodeBERConversion indicates a failure during BER to DER normalization.
	CodeBERConversion
	// CodeUnsupportedAlgorithm indicates an algorithm that is not in the allow-list.
	CodeUnsupportedAlgorithm
	// CodeInvalidSignature indicates the cryptographic signature verification failed.
	CodeInvalidSignature
	// CodeCertificateChain indicates an X.509 certificate chain validation failure.
	CodeCertificateChain
	// CodeMissingCertificate indicates the signer certificate was not found in the message.
	CodeMissingCertificate
	// CodeTimestamp indicates an RFC 3161 timestamp authority error.
	CodeTimestamp
	// CodeCounterSignature indicates a counter-signature specific failure.
	CodeCounterSignature
	// CodeVersionMismatch indicates the SignedData or SignerInfo version field
	// represents an unsupported capability.
	CodeVersionMismatch
	// CodeAttributeInvalid indicates a mandatory signed attribute is missing or
	// its value fails validation.
	CodeAttributeInvalid
	// CodeContentTypeMismatch indicates the content-type signed attribute does not
	// match the eContentType in EncapsulatedContentInfo.
	CodeContentTypeMismatch
	// CodePKCS7Format indicates the parser detected PKCS #7 format instead of CMS.
	// These formats differ in how non-id-data content types are encapsulated.
	CodePKCS7Format
	// CodeDetachedContentMismatch indicates Verify was called on a detached signature
	// or VerifyDetached was called on an attached signature.
	CodeDetachedContentMismatch
	// CodePayloadTooLarge indicates the attached content exceeds the configured size limit.
	CodePayloadTooLarge
	// CodeInvalidConfiguration indicates the builder configuration is invalid, such as
	// a nil certificate or private key. Multiple configuration errors are joined
	// using errors.Join.
	CodeInvalidConfiguration
)

type HashOption

type HashOption interface {
	SigningOption
	DigesterOption
}

HashOption applies to NewSigner, NewCounterSigner, and NewDigester.

func WithHash

func WithHash(h crypto.Hash) HashOption

WithHash sets the digest algorithm. For Ed25519, the library always uses SHA-512 per RFC 8419 regardless of this setting. Defaults to SHA-256. Also accepted by NewDigester.

type MACAlgorithm

type MACAlgorithm int

MACAlgorithm identifies the HMAC algorithm used in AuthenticatedData.

const (
	// HMACSHA256 selects HMAC with SHA-256. This is the default.
	HMACSHA256 MACAlgorithm = iota
	// HMACSHA384 selects HMAC with SHA-384.
	HMACSHA384
	// HMACSHA512 selects HMAC with SHA-512.
	HMACSHA512
)

type ParsedAuthenticatedData

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

ParsedAuthenticatedData wraps a parsed AuthenticatedData for verification.

func ParseAuthenticatedData

func ParseAuthenticatedData(r io.Reader) (*ParsedAuthenticatedData, error)

ParseAuthenticatedData parses a DER-encoded CMS ContentInfo wrapping AuthenticatedData.

func (*ParsedAuthenticatedData) Content

func (p *ParsedAuthenticatedData) Content() (io.Reader, error)

Content returns an io.Reader over the plaintext encapsulated content.

func (*ParsedAuthenticatedData) VerifyMAC

VerifyMAC decrypts the MAC key using the provided private key and certificate, then verifies the HMAC against the encapsulated content. Returns ErrMissingCertificate if no matching RecipientInfo is found.

type ParsedDigestedData

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

ParsedDigestedData wraps a parsed DigestedData for verification.

func ParseDigestedData

func ParseDigestedData(r io.Reader) (*ParsedDigestedData, error)

ParseDigestedData parses a DER-encoded CMS ContentInfo wrapping DigestedData.

func (*ParsedDigestedData) Content

func (p *ParsedDigestedData) Content() (io.Reader, error)

Content returns an io.Reader over the encapsulated content OCTET STRING value. Returns ErrDetachedContentMismatch if the DigestedData is detached.

func (*ParsedDigestedData) IsDetached

func (p *ParsedDigestedData) IsDetached() bool

IsDetached reports whether eContent is absent from the EncapsulatedContentInfo.

func (*ParsedDigestedData) Verify

func (p *ParsedDigestedData) Verify() error

Verify recomputes the hash of the embedded content and compares it to the stored Digest. Returns ErrDetachedContentMismatch if called on a detached DigestedData; use VerifyDetached instead.

func (*ParsedDigestedData) VerifyDetached

func (p *ParsedDigestedData) VerifyDetached(content io.Reader) error

VerifyDetached recomputes the hash of externally provided content and compares it to the stored Digest. Returns ErrDetachedContentMismatch if the DigestedData is not detached.

type ParsedEncryptedData

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

ParsedEncryptedData wraps a parsed EncryptedData for decryption.

func ParseEncryptedData

func ParseEncryptedData(r io.Reader) (*ParsedEncryptedData, error)

ParseEncryptedData parses a DER-encoded CMS ContentInfo wrapping EncryptedData.

func (*ParsedEncryptedData) Decrypt

func (p *ParsedEncryptedData) Decrypt(key []byte) ([]byte, error)

Decrypt decrypts the content using the supplied symmetric key and returns the plaintext. The key must match the algorithm used during encryption.

type ParsedEnvelopedData

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

ParsedEnvelopedData wraps a parsed EnvelopedData structure for decryption.

func ParseEnvelopedData

func ParseEnvelopedData(r io.Reader) (*ParsedEnvelopedData, error)

ParseEnvelopedData parses a BER- or DER-encoded CMS ContentInfo wrapping EnvelopedData. RFC 5652 permits BER encoding; the input is normalized to DER before parsing.

func (*ParsedEnvelopedData) Decrypt

func (p *ParsedEnvelopedData) Decrypt(key crypto.PrivateKey, cert *x509.Certificate) ([]byte, error)

Decrypt finds the RecipientInfo matching cert, decrypts the content encryption key using key, then decrypts and returns the plaintext content. Returns ErrMissingCertificate if no matching RecipientInfo is found.

type ParsedSignedData

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

ParsedSignedData is the result of parsing a CMS SignedData message.

func ParseSignedData

func ParseSignedData(r io.Reader) (*ParsedSignedData, error)

ParseSignedData parses a BER- or DER-encoded CMS ContentInfo wrapping SignedData. BER input is normalized to DER before parsing. If PKCS #7 encoding is detected, ErrPKCS7Format is returned with a descriptive message.

func (*ParsedSignedData) CRLs

func (p *ParsedSignedData) CRLs() []*x509.RevocationList

CRLs returns the Certificate Revocation Lists embedded in the SignedData.

func (*ParsedSignedData) Certificates

func (p *ParsedSignedData) Certificates() []*x509.Certificate

Certificates returns the certificates embedded in the SignedData.

func (*ParsedSignedData) Content

func (p *ParsedSignedData) Content() (io.Reader, error)

Content returns an io.Reader over the encapsulated content OCTET STRING value. For a signed 0-byte payload this returns a reader over zero bytes. Returns ErrDetachedContentMismatch if the signature is detached.

func (*ParsedSignedData) IsDetached

func (p *ParsedSignedData) IsDetached() bool

IsDetached reports whether eContent is absent from EncapsulatedContentInfo, meaning the signature is over external content (detached signature). A signed 0-byte payload has IsDetached() == false with an empty Content reader.

func (*ParsedSignedData) Signers

func (p *ParsedSignedData) Signers() []SignerInfo

Signers returns a summary of each SignerInfo in the parsed SignedData. Certificates are matched from the embedded certificates field. If no matching certificate is embedded, SignerInfo.Certificate is nil — this is valid; callers may hold the certificate out of band and pass it to Verify or VerifyDetached via WithTrustRoots.

func (*ParsedSignedData) Verify

func (p *ParsedSignedData) Verify(opts ...VerifyOption) error

Verify verifies all SignerInfos in an attached-content SignedData. Returns ErrDetachedContentMismatch if the SignedData is detached.

func (*ParsedSignedData) VerifyDetached

func (p *ParsedSignedData) VerifyDetached(content io.Reader, opts ...VerifyOption) error

VerifyDetached verifies all SignerInfos using the externally provided content. Returns ErrDetachedContentMismatch if the SignedData is not detached.

type RecipientOption

type RecipientOption interface {
	EncryptorOption
	AuthenticatorOption
}

RecipientOption applies to NewEncryptor and NewAuthenticator.

func WithRecipient

func WithRecipient(cert *x509.Certificate) RecipientOption

WithRecipient adds a recipient certificate for key delivery. Auto-selects RSA-OAEP (RSA key) or ECDH ephemeral-static (EC key). At least one recipient is required for NewEncryptor and NewAuthenticator.

type Signer

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

Signer produces a CMS SignedData message. Construct it with NewSigner; Sign is safe for concurrent use once constructed.

func NewSigner

func NewSigner(cert *x509.Certificate, key crypto.Signer, opts ...SignerOption) (*Signer, error)

NewSigner constructs a Signer with the given certificate and private key. Defaults: SHA-256 digest, attached content, IssuerAndSerialNumber signer identifier, id-data content type, 64 MiB attached content size limit. All configuration errors (nil cert, nil key, invalid options) are reported together. Sign is safe for concurrent use once NewSigner returns successfully.

func (*Signer) Sign

func (s *Signer) Sign(r io.Reader) ([]byte, error)

Sign reads content from r, constructs a CMS SignedData, and returns the DER-encoded ContentInfo.

type SignerIdentifierType

type SignerIdentifierType int

SignerIdentifierType controls how the signer's certificate is identified in the SignerInfo structure of a CMS SignedData message.

const (
	// IssuerAndSerialNumber identifies the signer by issuer distinguished name and
	// certificate serial number. This produces SignerInfo version 1 and is the most
	// widely compatible form. This is the default.
	IssuerAndSerialNumber SignerIdentifierType = iota

	// SubjectKeyIdentifier identifies the signer by the value of the certificate's
	// subjectKeyIdentifier extension. This produces SignerInfo version 3.
	SubjectKeyIdentifier
)

type SignerInfo

type SignerInfo struct {
	// Version is the SignerInfo syntax version: 1 for IssuerAndSerialNumber,
	// 3 for SubjectKeyIdentifier.
	Version int

	// Certificate is the signing certificate matched from the certificates
	// embedded in SignedData. Nil if the certificate is not embedded in the
	// message, which is valid; callers may hold it out of band.
	Certificate *x509.Certificate

	// DigestAlgorithm is the AlgorithmIdentifier for the message digest used
	// by this signer.
	DigestAlgorithm pkix.AlgorithmIdentifier

	// SignatureAlgorithm is the AlgorithmIdentifier for the signature algorithm,
	// including any algorithm-specific parameters (e.g., RSASSA-PSS-params for
	// RSA-PSS).
	SignatureAlgorithm pkix.AlgorithmIdentifier

	// Signature is the raw signature bytes. For ECDSA this is a DER-encoded
	// Ecdsa-Sig-Value; for RSA it is the raw modular exponentiation result.
	Signature []byte
}

SignerInfo describes a single signer extracted from a parsed CMS SignedData. It exposes the resolved certificate and algorithm identifiers without leaking raw ASN.1 types from the internal package.

type SignerOption

type SignerOption interface {
	// contains filtered or unexported methods
}

SignerOption configures a Signer. Options that apply only to Signer (not CounterSigner) implement this interface. Pass them to NewSigner.

func AddAuthenticatedAttribute

func AddAuthenticatedAttribute(oid asn1.ObjectIdentifier, val any) SignerOption

AddAuthenticatedAttribute adds a custom signed attribute. The content-type, message-digest, and signing-time attributes are managed by the library; callers must not add those manually. For signing-time, use WithSigningTimeFunc instead. NewSigner returns ErrAttributeInvalid if any reserved attribute is provided.

func AddCRL

func AddCRL(derCRL []byte) SignerOption

AddCRL embeds a DER-encoded Certificate Revocation List in the SignedData revocationInfoChoices field.

func AddUnauthenticatedAttribute

func AddUnauthenticatedAttribute(oid asn1.ObjectIdentifier, val any) SignerOption

AddUnauthenticatedAttribute adds a custom unsigned attribute.

func WithAdditionalSigner

func WithAdditionalSigner(other *Signer) SignerOption

WithAdditionalSigner adds a second (or subsequent) signer to the SignedData. The additional signer must have been successfully constructed via NewSigner. All signers share the primary signer's content, content type, and detached/attached setting.

func WithMaxAttachedContentSize

func WithMaxAttachedContentSize(maxBytes int64) SignerOption

WithMaxAttachedContentSize sets the maximum content size for attached signatures. Defaults to DefaultMaxAttachedSize (64 MiB). Pass UnlimitedAttachedSize to disable. Has no effect in detached mode.

func WithSigningTimeFunc

func WithSigningTimeFunc(clock func() time.Time) SignerOption

WithSigningTimeFunc sets a clock function called at each Sign() invocation to embed an id-signingTime authenticated attribute (RFC 5652 §11.3). Pass time.Now to use the system clock:

cms.NewSigner(cert, key, cms.WithSigningTimeFunc(time.Now))

The clock is called inside Sign(), so each call captures the time at the moment of signing rather than at signer construction time. If not configured, no signing-time attribute is included.

Note: id-signingTime carries the time claimed by the signer and is not cryptographically bound to a trusted source. For a verifiable trusted timestamp, use WithTimestamp instead.

func WithTimestamp

func WithTimestamp(tsaURL string) SignerOption

WithTimestamp requests an RFC 3161 timestamp from tsaURL after signing and embeds it as an unsigned attribute (id-aa-signatureTimeStampToken) on each SignerInfo. The timestamp covers the SignerInfo's Signature bytes.

func WithoutCertificates

func WithoutCertificates() SignerOption

WithoutCertificates omits all certificates from the SignedData output. Use this when certificates are delivered out of band. The Certificates field in RFC 5652 SignedData is OPTIONAL, so the output is a valid CMS message. Any certificates added via AddCertificate are silently ignored.

type SigningOption

type SigningOption interface {
	SignerOption
	// contains filtered or unexported methods
}

SigningOption configures both Signer and CounterSigner. Because SigningOption embeds SignerOption, a SigningOption value satisfies SignerOption and can be passed to NewSigner. Pass SigningOption values to either NewSigner or NewCounterSigner.

func AddCertificate

func AddCertificate(cert *x509.Certificate) SigningOption

AddCertificate adds an extra certificate to the CertificateSet in the output (for example, intermediate CA certificates needed for chain building).

func AddCertificateChain

func AddCertificateChain(certs ...*x509.Certificate) SigningOption

AddCertificateChain adds multiple certificates to the CertificateSet in the output. This is a convenience for embedding an entire certificate chain (typically intermediates and/or root CA) in a single call. The certificates are purely transport for the verifier's chain-building benefit — they have no effect on the signing operation itself.

func WithRSAPKCS1

func WithRSAPKCS1() SigningOption

WithRSAPKCS1 selects RSA PKCS1v15 as the signature algorithm. By default, RSA keys use RSA-PSS. This option has no effect for non-RSA keys.

func WithSignerIdentifier

func WithSignerIdentifier(t SignerIdentifierType) SigningOption

WithSignerIdentifier controls how the signer's certificate is identified in SignerInfo. Default is IssuerAndSerialNumber (SignerInfo version 1).

type SymmetricEncryptor

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

SymmetricEncryptor builds a CMS EncryptedData message. The caller supplies the symmetric key directly via WithKey. Configure with functional options passed to NewSymmetricEncryptor. Encrypt is safe for concurrent use once constructed.

func NewSymmetricEncryptor

func NewSymmetricEncryptor(opts ...SymmetricEncryptorOption) (*SymmetricEncryptor, error)

NewSymmetricEncryptor returns a new SymmetricEncryptor configured with opts and validates the configuration immediately. Returns an error if any option is invalid, if no key is provided, or if the key length does not match the chosen algorithm.

func (*SymmetricEncryptor) Encrypt

func (se *SymmetricEncryptor) Encrypt(r io.Reader) ([]byte, error)

Encrypt reads plaintext from r, encrypts it with the configured key and algorithm, and returns the DER-encoded ContentInfo wrapping EncryptedData.

type SymmetricEncryptorOption

type SymmetricEncryptorOption interface {
	// contains filtered or unexported methods
}

SymmetricEncryptorOption configures a SymmetricEncryptor. Pass to NewSymmetricEncryptor.

func WithKey

func WithKey(key []byte) SymmetricEncryptorOption

WithKey sets the symmetric content encryption key for NewSymmetricEncryptor. Must be 16 bytes (AES-128) or 32 bytes (AES-256). Required.

type VerifyOption

type VerifyOption func(*verifyConfig)

VerifyOption configures verification behavior.

func WithExternalCertificates

func WithExternalCertificates(certs ...*x509.Certificate) VerifyOption

WithExternalCertificates supplies additional certificates for signer identification and chain building. These supplement any certificates embedded in the SignedData. Use this when the signer's certificate was delivered out of band (e.g., the message was produced with WithoutCertificates).

Multiple certificates may be provided. Each message's SignerInfo contains a SignerIdentifier (IssuerAndSerialNumber or SubjectKeyIdentifier) that the library uses to select the correct certificate automatically. This means callers can safely pass several certificates for the same peer simultaneously — for example, during a certificate rotation where messages signed by both the old and new certificate may be in flight at the same time. Retain the old certificate in your store until its NotAfter has passed and you are confident no further messages signed with it will arrive; then prune it.

func WithNoChainValidation

func WithNoChainValidation() VerifyOption

WithNoChainValidation disables certificate chain validation. Only the cryptographic signature is verified. Use with caution.

func WithSystemTrustStore

func WithSystemTrustStore() VerifyOption

WithSystemTrustStore uses the system root certificate store for chain validation.

func WithTrustRoots

func WithTrustRoots(pool *x509.CertPool) VerifyOption

WithTrustRoots uses the given certificate pool as the set of trust anchors.

func WithVerifyOptions

func WithVerifyOptions(opts x509.VerifyOptions) VerifyOption

WithVerifyOptions provides full control over x509 verification parameters. This overrides any roots or time set by other options.

func WithVerifyTime

func WithVerifyTime(t time.Time) VerifyOption

WithVerifyTime sets the reference time for certificate validity checks. Defaults to time.Now() at the point Verify or VerifyDetached is called.

Directories

Path Synopsis
Package ber provides a BER to DER normalizer for ASN.1 encoded data.
Package ber provides a BER to DER normalizer for ASN.1 encoded data.
internal
asn1
Package pkiasn1 defines the ASN.1 wire-format types and OID constants for the Cryptographic Message Syntax as specified in RFC 5652.
Package pkiasn1 defines the ASN.1 wire-format types and OID constants for the Cryptographic Message Syntax as specified in RFC 5652.
timestamp
Package timestamp implements the RFC 3161 Time-Stamp Protocol client and token parsing utilities used by the cms package.
Package timestamp implements the RFC 3161 Time-Stamp Protocol client and token parsing utilities used by the cms package.

Jump to

Keyboard shortcuts

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