Documentation
¶
Index ¶
- Constants
- Variables
- func DefaultEncryptPKCS7(_ context.Context, entity []byte, recipients []*x509.Certificate) (string, []byte, string, error)
- func DefaultSignPKCS7(_ context.Context, entity []byte, cert *x509.Certificate, key any, ...) (string, []byte, string, error)
- func NewAS2PostHandler(opts *PostHandlerOpts) http.HandlerFunc
- func SetPKCS7ContentEncryptionAlgorithm(algo PKCS7EncryptionAlgorithm)
- type CertificateValidator
- type Client
- type ClientOption
- func WithClientLogger(l *slog.Logger) ClientOption
- func WithHTTPClient(h *http.Client) ClientOption
- func WithLocalClientAS2ID(id string) ClientOption
- func WithMDNAddress(addr string) ClientOption
- func WithSigner(priv any, chain []*x509.Certificate) ClientOption
- func WithUserAgent(ua string) ClientOption
- type DefaultValidator
- type EncryptFunc
- type FileHandler
- type HTTPAuthFunc
- type InMemoryMessageStore
- type InMemoryPartnerStore
- func (s *InMemoryPartnerStore) Delete(_ context.Context, as2From string) error
- func (s *InMemoryPartnerStore) GetByAS2From(_ context.Context, as2From string) (*Partner, error)
- func (s *InMemoryPartnerStore) List(_ context.Context) ([]*Partner, error)
- func (s *InMemoryPartnerStore) Put(_ context.Context, p *Partner) error
- type MDN
- type MDNHandler
- type MDNMode
- type MICAlg
- type MessageStore
- type PKCS7EncryptionAlgorithm
- type Partner
- type PartnerStore
- type PostHandlerOpts
- type SMTPConfig
- type SendOption
- func WithAsyncReturnURL(url string) SendOption
- func WithCompression(v bool) SendOption
- func WithContentType(ct string) SendOption
- func WithEncryptFunc(f EncryptFunc) SendOption
- func WithExpectContinue(v bool) SendOption
- func WithFileName(name string) SendOption
- func WithMDNAddressOverride(addr string) SendOption
- func WithMICAlg(alg string) SendOption
- func WithMessageID(id string) SendOption
- func WithRequestMDN(v bool) SendOption
- func WithSign(f SignFunc, d MICAlg) SendOption
- func WithSignedMDN(v bool) SendOption
- func WithSubject(subj string) SendOption
- func WithTimeout(d time.Duration) SendOption
- type SendOptions
- type SendResult
- type Server
- type ServerOption
- func WithDecryptCertKey(cert *x509.Certificate, key any) ServerOption
- func WithFileHandler(fh FileHandler) ServerOption
- func WithHTTPAuthFunc(fn HTTPAuthFunc) ServerOption
- func WithInboundDecryptIdentity(cert *x509.Certificate, key any) ServerOption
- func WithInboundPath(p string) ServerOption
- func WithLocalAS2ID(id string) ServerOption
- func WithLogger(l *slog.Logger) ServerOption
- func WithMDNPath(p string) ServerOption
- func WithMDNSignDigest(d MICAlg) ServerOption
- func WithMDNSignFunc(f SignFunc) ServerOption
- func WithMDNSigner(cert *x509.Certificate, key any) ServerOption
- func WithMaxBodyBytes(limit int64) ServerOption
- func WithMessageStore(ms MessageStore) ServerOption
- func WithPartnerStore(st PartnerStore) ServerOption
- func WithPublicBaseURL(u string) ServerOption
- func WithSMTP(cfg *SMTPConfig) ServerOption
- func WithServerUserAgent(ua string) ServerOption
- func WithStrict(b bool) ServerOption
- func WithTimeouts(rh, r, w, i time.Duration) ServerOption
- type SignFunc
Constants ¶
const MaxBodyDefault = 100 << 20 // 100 MiB
Variables ¶
var ( ErrMissingHost = errors.New("missing Host header") ErrMissingContentType = errors.New("missing Content-Type header") ErrMissingAS2Identity = errors.New("missing AS2-From/AS2-To") ErrAS2IdentityTooLong = errors.New("AS2-From/AS2-To too long") ErrInvalidAS2IdentityFolding = errors.New("AS2-From/AS2-To invalid folding") ErrInvalidAS2IdentityFormat = errors.New("AS2-From/AS2-To must be printable ASCII") // ErrInvalidPartIndex is returned when a MIME part index is out of bounds. ErrInvalidPartIndex = errors.New("invalid part index") ErrAsyncMDNPostFailed = errors.New("async MDN POST failed") // ErrEmptyRDO is returned when Receipt-Delivery-Option is empty for async MDN. ErrEmptyRDO = errors.New("empty Receipt-Delivery-Option URL") ErrUnsupportedRDO = errors.New("unsupported Receipt-Delivery-Option scheme") ErrMDNSignerNotConfigured = errors.New("mdn signer not configured") ErrSMTPNotConfigured = errors.New("smtp not configured") ErrSignatureVerificationFailed = errors.New("signature verification failed") ErrSignerCertMismatch = errors.New("signer certificate mismatch") ErrDecryptIdentityMissing = errors.New("missing decryption identity") ErrDecryptFailed = errors.New("decrypt failed") ErrTrailingData = errors.New("trailing data after ContentInfo") ErrSignMissingKeyOrCert = errors.New("sign: missing signer key or cert chain") ErrEncryptPartnerCertMissing = errors.New("encrypt: partner certificate missing") ErrEmptyContentType = errors.New("empty content type") ErrServerStartFailed = errors.New("failed to start AS2 server") ErrServerShutdownFailed = errors.New("failed to shutdown AS2 server") ErrAsyncMDNRequestBuild = errors.New("failed to build async MDN request") ErrAsyncMDNRequestSend = errors.New("failed to send async MDN request") ErrNotMDNMultipartReport = errors.New("not an MDN multipart/report") ErrMDNMissingDisposition = errors.New("MDN missing Disposition") ErrUnsupportedMICAlg = errors.New("unsupported MIC algo") ErrEmptyCertChain = errors.New("empty certificate chain") ErrSignedMDNExpected = errors.New("signed MDN requested but unsigned MDN received") ErrMDNMessageIDMismatch = errors.New("MDN Original-Message-ID mismatch") ErrMDNAS2HeaderMismatch = errors.New("MDN AS2 headers mismatch") ErrMICMismatch = errors.New("MDN MIC mismatch") ErrMissingMIC = errors.New("MDN missing Received-content-MIC") ErrNotCompressedData = errors.New("not CompressedData") ErrUnsupportedCompression = errors.New("unsupported compression algorithm") ErrUnsupportedMDNMediaType = errors.New("unsupported MDN media type") ErrMissingBoundary = errors.New("multipart/signed missing boundary parameter") ErrUnsupportedPKCS7Payload = errors.New("application/pkcs7-mime payloads are not supported") ErrUnsupportedCTE = errors.New("unsupported Content-Transfer-Encoding") ErrEmptyPayloadMIC = errors.New("empty payload for MIC") ErrMissingReportPart = errors.New("signed MDN missing report part") ErrMissingSignaturePart = errors.New("signed MDN missing signature part") ErrInvalidDispositionAddr = errors.New("invalid Disposition-Notification-To address") ErrMissingDispositionAddr = errors.New("Disposition-Notification-To is required but no valid email address provided") ErrReceivedMICNoAlgo = errors.New("MDN Received-content-MIC has no algorithm") ErrSMTPFromEmpty = errors.New("smtp from address is empty") ErrSMTPHostEmpty = errors.New("smtp host is empty") ErrEmptyBoundary = errors.New("empty boundary") ErrNilPartner = errors.New("nil Partner") ErrMissingIDOrURL = errors.New("missing AS2ID or SendUrl") ErrMissingPayloadCContentType = errors.New("missing payload Content-Type") ErrBuildingMIME = errors.New("build MIME failed") ErrSigningPayload = errors.New("sign payload failed") ErrCompressingPayload = errors.New("compress payload failed") ErrEncryptingPayload = errors.New("encrypt payload failed") ErrCreatingRequest = errors.New("create request failed") ErrGeneratingHeader = errors.New("generate AS2 header failed") ErrSendingRequest = errors.New("send request failed") ErrParsingMDN = errors.New("parse MDN failed") ErrCanonicalizingMDN = errors.New("canonicalize MDN failed") ErrDecodingSignature = errors.New("decode signature failed") ErrSignedMDNMissingBoundary = errors.New("signed MDN missing boundary") ErrSignedMDNParsePart = errors.New("signed MDN parse part failed") ErrSignedMDNReadPart = errors.New("signed MDN read part failed") ErrBoundaryNotFound = errors.New("boundary not found") ErrMalformedBoundary = errors.New("malformed boundary") // ErrMessageNotFound is returned when a message is not found in the store. ErrMessageNotFound = errors.New("message not found") ErrPartNotFound = errors.New("part not found") ErrClosingBoundaryNotFound = errors.New("closing boundary not found") ErrCertNil = errors.New("certificate is nil") ErrCertNotYetValid = errors.New("certificate not yet valid") ErrCertExpired = errors.New("certificate expired") ErrCertRevoked = errors.New("certificate is revoked") )
var ( ErrIMPSAS2IDEmpty = errors.New("partner or AS2ID is empty") ErrIMPSPartnerNotFound = errors.New("partner not found") )
var ( // DefaultPKCS7EncryptionAlgorithm is the default algorithm used by // DefaultEncryptPKCS7. DefaultPKCS7EncryptionAlgorithm = PKCS7EncAES256CBC )
Global protection around pkcs7.ContentEncryptionAlgorithm.
Idea:
- RLock during Encrypt => multiple goroutines can encrypt simultaneously with the same algo.
- Lock only when modifying the global algo.
Functions ¶
func DefaultEncryptPKCS7 ¶
func DefaultSignPKCS7 ¶
func NewAS2PostHandler ¶
func NewAS2PostHandler(opts *PostHandlerOpts) http.HandlerFunc
func SetPKCS7ContentEncryptionAlgorithm ¶
func SetPKCS7ContentEncryptionAlgorithm(algo PKCS7EncryptionAlgorithm)
SetPKCS7ContentEncryptionAlgorithm change the global algorithm used by pkcs7.Encrypt in a thread-safe manner.
Types ¶
type CertificateValidator ¶
type CertificateValidator interface {
// Validate checks if the certificate is valid (not expired, not revoked).
// issuer is optional (can be nil if unknown).
Validate(cert, issuer *x509.Certificate) error
}
CertificateValidator defines the interface for validating certificates.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the AS2 client.
func (*Client) Send ¶
func (c *Client) Send(ctx context.Context, partner *Partner, payload []byte, opts *SendOptions) (*SendResult, error)
type ClientOption ¶
type ClientOption func(*Client)
ClientOption is a function that configures the client.
func WithClientLogger ¶
func WithClientLogger(l *slog.Logger) ClientOption
WithClientLogger sets the logger.
func WithHTTPClient ¶
func WithHTTPClient(h *http.Client) ClientOption
WithHTTPClient overrides the HTTP client.
func WithLocalClientAS2ID ¶
func WithLocalClientAS2ID(id string) ClientOption
WithLocalClientAS2ID sets the local AS2 ID.
func WithMDNAddress ¶
func WithMDNAddress(addr string) ClientOption
WithMDNAddress sets the default Disposition-Notification-To address (e.g. mailto:[email protected]).
func WithSigner ¶
func WithSigner(priv any, chain []*x509.Certificate) ClientOption
WithSigner configures the private key and certificate chain for signing.
func WithUserAgent ¶
func WithUserAgent(ua string) ClientOption
WithUserAgent sets the User-Agent for the client.
type DefaultValidator ¶
type DefaultValidator struct {
// CRLs is a map of Issuer DN -> CRL (parsed).
// In a real implementation, this would likely be a dynamic store or cache.
// For now, we provide a hook to check against a list of CRLs.
CRLs []*x509.RevocationList
}
DefaultValidator implements basic validation (expiration). It can be extended to check CRLs.
func NewDefaultValidator ¶
func NewDefaultValidator() *DefaultValidator
NewDefaultValidator creates a new validator.
func (*DefaultValidator) Validate ¶
func (v *DefaultValidator) Validate(cert, _ *x509.Certificate) error
Validate checks expiration and revocation (if CRLs are provided).
type EncryptFunc ¶
type EncryptFunc func( ctx context.Context, entity []byte, recipients []*x509.Certificate, ) (contentType string, encrypted []byte, algo string, err error)
EncryptFunc wraps the complete S/MIME entity and returns :
- contentType (application/pkcs7-mime; smime-type=enveloped-data)
- bytes of the enveloped-data entity
the entity param is the full MIME entity (signed or not) to encrypt (headers + body).
func NewPKCS7Encrypter ¶
func NewPKCS7Encrypter(algo PKCS7EncryptionAlgorithm) EncryptFunc
NewPKCS7Encrypter creates an EncryptFunc that uses the specified algorithm. It safely swaps the global algorithm during encryption to ensure the desired algorithm is used, even if other goroutines are encrypting with different algorithms.
type FileHandler ¶
FileHandler is a function that processes an incoming file.
type HTTPAuthFunc ¶
HTTPAuthFunc validates an incoming HTTP request before AS2 processing begins.
type InMemoryMessageStore ¶
type InMemoryMessageStore struct {
// contains filtered or unexported fields
}
func NewInMemoryMessageStore ¶
func NewInMemoryMessageStore() *InMemoryMessageStore
func (*InMemoryMessageStore) GetMDN ¶
type InMemoryPartnerStore ¶
type InMemoryPartnerStore struct {
// contains filtered or unexported fields
}
InMemoryPartnerStore is an in-memory implementation of PartnerStore. The data is stored in memory and will be lost when the application exits.
It is not suitable for production use, but can be useful for testing or prototyping.
func NewInMemoryPartnerStore ¶
func NewInMemoryPartnerStore() *InMemoryPartnerStore
NewInMemoryPartnerStore creates a new instance of InMemoryPartnerStore.
func (*InMemoryPartnerStore) Delete ¶
func (s *InMemoryPartnerStore) Delete(_ context.Context, as2From string) error
func (*InMemoryPartnerStore) GetByAS2From ¶
type MDN ¶
type MDN struct {
OriginalMessageID string
Disposition string
ReceivedMIC string // raw value from MDN (algo + base64 digest)
HumanText string
Signed bool // always false in this skeleton
Raw []byte // original body received
}
MDN captures the parsed disposition notification (unsigned in this skeleton).
func (*MDN) VerifyMIC ¶
VerifyMIC verifies that the MIC contained in the MDN matches the expected MIC calculated from the original message payload.
expectedMIC should be in the format "<digest-base64>, <algorithm>". Supported algorithms are sha1, md5, sha256, sha384, sha512.
type MDNHandler ¶
type MDNHandler struct {
// contains filtered or unexported fields
}
MDNHandler handles incoming asynchronous MDNs.
func NewMDNHandler ¶
func NewMDNHandler(logger *slog.Logger, store MessageStore, partners PartnerStore) *MDNHandler
NewMDNHandler creates a new handler for incoming Async MDNs.
func (*MDNHandler) ServeHTTP ¶
func (h *MDNHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements the http.Handler interface. It receives POST requests containing AS2 MDNs (Message Disposition Notifications), parses them, validates any signatures, and logs the receipt.
type MICAlg ¶
type MICAlg string
MICAlg represents the digest algorithm used for the MIC / micalg parameter.
const ( MICSHA1 MICAlg = "sha1" // default RFC token MICSHA1Cap MICAlg = "sha-1" // alternate spelling MICMD5 MICAlg = "md5" MICSHA256 MICAlg = "sha256" MICSHA256Cap MICAlg = "sha-256" // alternate spelling MICSHA384 MICAlg = "sha384" MICSHA384Cap MICAlg = "sha-384" MICSHA512 MICAlg = "sha512" MICSHA512Cap MICAlg = "sha-512" )
type MessageStore ¶
type MessageStore interface {
// Record attempts to record a new message ID.
// If the message ID already exists, it returns ErrDuplicateMessage.
// It should also store the generated MDN and its raw bytes (body + Content-Type)
// for future retrieval.
Record(ctx context.Context, msgID string, mdn *MDN, contentType string, rawBody []byte) error
// GetMDN retrieves the MDN associated with a previously processed message ID.
// Returns nil if not found.
GetMDN(ctx context.Context, msgID string) (*MDN, string, []byte, error)
}
MessageStore defines the interface for storing and retrieving message states to support duplicate detection (RFC 4130 Section 5.5).
type PKCS7EncryptionAlgorithm ¶
type PKCS7EncryptionAlgorithm int
PKCS7EncryptionAlgorithm is a thin wrapper over the pkcs7 package's encryption algorithm enum.
const ( PKCS7EncDESCBC PKCS7EncryptionAlgorithm = PKCS7EncryptionAlgorithm(pkcs7.EncryptionAlgorithmDESCBC) PKCS7EncAES128CBC PKCS7EncryptionAlgorithm = PKCS7EncryptionAlgorithm(pkcs7.EncryptionAlgorithmAES128CBC) PKCS7EncAES256CBC PKCS7EncryptionAlgorithm = PKCS7EncryptionAlgorithm(pkcs7.EncryptionAlgorithmAES256CBC) PKCS7EncAES128GCM PKCS7EncryptionAlgorithm = PKCS7EncryptionAlgorithm(pkcs7.EncryptionAlgorithmAES128GCM) PKCS7EncAES256GCM PKCS7EncryptionAlgorithm = PKCS7EncryptionAlgorithm(pkcs7.EncryptionAlgorithmAES256GCM) )
type Partner ¶
type Partner struct {
// Identity/mapping (exchanged out-of-band)
Name string // display
AS2ID string // AS2-From or AS2-To (depends on local/remote)
SendUrl string // sender's URL (for async MDN)
// security & preferences (negotiated out-of-band)
CertChain []*x509.Certificate // public key cert chain
RequireSignedMDN bool
MDNMode MDNMode
MICAlgs []string // ex: []string{"sha1"} (ordered by preference)
Validator CertificateValidator
// Housekeeping
CreatedAt time.Time
UpdatedAt time.Time
}
type PartnerStore ¶
type PostHandlerOpts ¶
type PostHandlerOpts struct {
Store PartnerStore
MessageStore MessageStore
LocalAS2ID string
Logger *slog.Logger
Strict bool
UserAgent string
SMTPConfig *SMTPConfig
FileHandler FileHandler
HTTPAuthFunc HTTPAuthFunc
DecryptCert *x509.Certificate
DecryptKey any
// contains filtered or unexported fields
}
type SMTPConfig ¶
SMTPConfig holds the minimal settings required to send an email via the Go standard library SMTP client.
type SendOption ¶
type SendOption func(*SendOptions)
SendOption is a functional option applied to SendOptions.
func WithAsyncReturnURL ¶
func WithAsyncReturnURL(url string) SendOption
WithAsyncReturnURL sets Receipt-Delivery-Option for async MDN.
func WithCompression ¶
func WithCompression(v bool) SendOption
WithCompression enables ZLIB compression of the payload.
func WithContentType ¶
func WithContentType(ct string) SendOption
WithContentType sets the payload Content-Type (e.g. application/edi-x12).
func WithEncryptFunc ¶
func WithEncryptFunc(f EncryptFunc) SendOption
WithEncryptFunc sets a custom encryption callback (S/MIME enveloped). If nil, the default encrypter (DefaultEncryptPKCS7) will be used when EncryptPayload is true.
func WithExpectContinue ¶
func WithExpectContinue(v bool) SendOption
WithExpectContinue toggles the Expect: 100-continue header.
func WithFileName ¶
func WithFileName(name string) SendOption
WithFileName sets the filename used in Content-Disposition of the payload part.
func WithMDNAddressOverride ¶
func WithMDNAddressOverride(addr string) SendOption
WithMDNAddressOverride overrides the Disposition-Notification-To address for this message.
func WithMICAlg ¶
func WithMICAlg(alg string) SendOption
WithMICAlg sets the MIC algorithm name used for the sender-side MIC string (normalized form like "sha-256", "sha1", "md5").
func WithMessageID ¶
func WithMessageID(id string) SendOption
WithMessageID sets a custom Message-ID (if empty, it will be generated).
func WithRequestMDN ¶
func WithRequestMDN(v bool) SendOption
WithRequestMDN toggles whether the sender requests an MDN.
func WithSign ¶
func WithSign(f SignFunc, d MICAlg) SendOption
func WithSignedMDN ¶
func WithSignedMDN(v bool) SendOption
WithSignedMDN toggles "want signed MDN" (Disposition-Notification-Options).
func WithSubject ¶
func WithSubject(subj string) SendOption
WithSubject sets the AS2 Subject header.
func WithTimeout ¶
func WithTimeout(d time.Duration) SendOption
WithTimeout overrides the per-request timeout for this send.
type SendOptions ¶
type SendOptions struct {
// contains filtered or unexported fields
}
SendOptions describe how we send a single AS2 message.
func NewSendOptions ¶
func NewSendOptions(opts ...SendOption) *SendOptions
NewSendOptions creates a SendOptions with sensible defaults, then applies any provided functional options.
type SendResult ¶
type SendResult struct {
StatusCode int
Headers http.Header
// Sync MDN only (when receiver answers inline). For async, this is nil.
MDN *MDN
}
SendResult summarizes the HTTP exchange + MDN (sync path).
type Server ¶
type Server struct {
Store PartnerStore
MessageStore MessageStore
// contains filtered or unexported fields
}
func NewServer ¶
func NewServer(opts ...ServerOption) *Server
func (*Server) ListenAndServeTLS ¶
func (*Server) ServeTLS ¶
type ServerOption ¶
type ServerOption func(*Server)
func WithDecryptCertKey ¶
func WithDecryptCertKey(cert *x509.Certificate, key any) ServerOption
WithDecryptCertKey configures the certificate/key used to decrypt inbound enveloped-data (application/pkcs7-mime; smime-type=enveloped-data) messages. If unset, encrypted inbound payloads are rejected.
func WithFileHandler ¶
func WithFileHandler(fh FileHandler) ServerOption
func WithHTTPAuthFunc ¶
func WithHTTPAuthFunc(fn HTTPAuthFunc) ServerOption
func WithInboundDecryptIdentity ¶
func WithInboundDecryptIdentity(cert *x509.Certificate, key any) ServerOption
func WithInboundPath ¶
func WithInboundPath(p string) ServerOption
func WithLocalAS2ID ¶
func WithLocalAS2ID(id string) ServerOption
func WithLogger ¶
func WithLogger(l *slog.Logger) ServerOption
func WithMDNPath ¶
func WithMDNPath(p string) ServerOption
func WithMDNSignDigest ¶
func WithMDNSignDigest(d MICAlg) ServerOption
func WithMDNSignFunc ¶
func WithMDNSignFunc(f SignFunc) ServerOption
func WithMDNSigner ¶
func WithMDNSigner(cert *x509.Certificate, key any) ServerOption
func WithMaxBodyBytes ¶
func WithMaxBodyBytes(limit int64) ServerOption
func WithMessageStore ¶
func WithMessageStore(ms MessageStore) ServerOption
func WithPartnerStore ¶
func WithPartnerStore(st PartnerStore) ServerOption
func WithPublicBaseURL ¶
func WithPublicBaseURL(u string) ServerOption
func WithSMTP ¶
func WithSMTP(cfg *SMTPConfig) ServerOption
func WithServerUserAgent ¶
func WithServerUserAgent(ua string) ServerOption
func WithStrict ¶
func WithStrict(b bool) ServerOption
func WithTimeouts ¶
func WithTimeouts(rh, r, w, i time.Duration) ServerOption
type SignFunc ¶
type SignFunc func( ctx context.Context, entity []byte, signerCert *x509.Certificate, signerKey any, digest MICAlg, ) (contentType string, signed []byte, micalg string, err error)
SignFunc create a multipart/signed (S/MIME detached signature) and returns:
- full contentType (with protocol, micalg)
- bytes of the multipart/signed entity
The entity param is the original MIME entity to sign (headers + body).
Source Files
¶
- client.go
- client_model.go
- compression.go
- crypto.go
- errors.go
- handler.go
- handler_as2.go
- handler_duplicate.go
- handler_flow.go
- handler_http.go
- handler_mdn.go
- handler_parse.go
- handler_parse_helpers.go
- header.go
- in_memory_message_store.go
- in_memory_parter_store.go
- log.go
- mdn.go
- message_store.go
- mime_helpers.go
- partner.go
- partner_store.go
- server.go
- smtp.go
- utils.go
- validator.go