direct

package
v0.1.23 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 36 Imported by: 0

Documentation

Overview

Package direct is Direct secure messaging as an edge system: a message to a Direct address is signed with this address's certificate and encrypted to the recipient's, as S/MIME, and sent to a HISP by SMTP; a message received from the HISP (by SMTP or collected over IMAP) is decrypted and its signature checked against the trust anchors before anything reads it.

It follows the Direct Project Applicability Statement for Secure Health Transport 1.2 and the XDM specification (IHE ITI TF-1 Annex 15) for documents with metadata. It is not a HISP: the HISP's accreditation, its certificate policy and its directory stay with the HISP.

Index

Constants

This section is empty.

Variables

View Source
var ErrUntrusted = errors.New("direct: the message is not signed by a trusted sender")

ErrUntrusted is a message whose signature does not lead to a trust anchor.

Functions

func DNSCertificates

func DNSCertificates(ctx context.Context, address, server string) ([]*x509.Certificate, error)

DNSCertificates looks up the certificates for a Direct address: the address-bound ones, else the domain's. server is a resolver (host:port); empty uses the system's first nameserver.

func FindCertificates added in v0.1.19

func FindCertificates(ctx context.Context, address, dnsServer, ldapServer string) ([]*x509.Certificate, string, error)

FindCertificates looks a recipient's certificates up the ways the certificate discovery guide allows, DNS first, then LDAP; ldapServer "off" skips the directory. It says where they were found.

func LDAPCertificates added in v0.1.19

func LDAPCertificates(ctx context.Context, address, server string) ([]*x509.Certificate, error)

LDAPCertificates finds the certificates published for a Direct address in its domain's LDAP directory: the second discovery method of the Direct Project's certificate discovery guide, after DNS CERT records. The directory is server (host:port) when given, else the ones the domain's _ldap._tcp SRV records name, in their priority order. Each is asked anonymously for its naming contexts, then, under each, for an entry whose mail is the address and its userSMIMECertificate (or userCertificate); with none, for an entry whose mail is the domain (a domain-bound certificate). Only certificates are read, so the bind is anonymous and the connection may be plain: what is found is checked against the trust anchors afterwards, exactly like a certificate found in DNS.

func Protect

func Protect(m Message, sender cms.KeyPair, chain []*x509.Certificate, recipients []*x509.Certificate) ([]byte, error)

Protect builds the RFC 5322 message the HISP receives: the content signed (multipart/signed, SHA-256) by sender's certificate, and that whole entity encrypted (application/pkcs7-mime enveloped-data) to every recipient certificate and the sender's own, so the sender can read what it sent. Headers a HISP routes by (From, To, Message-ID, Date) stay outside.

func WriteXDM

func WriteXDM(metadata []byte, docs map[string][]byte, readme string) ([]byte, error)

WriteXDM packages documents as IHE XDM: IHE_XDM/SUBSET01/METADATA.XML (the XDS metadata), the documents beside it, and INDEX.HTM and README.TXT at the top, as ITI TF-1 Annex 15 lays out.

func XDMFor

func XDMFor(req *xds.SubmitObjectsRequest, name string, content []byte) ([]byte, error)

XDMFor packages one document with its XDS metadata (built by xds.Build or xds.MetaFromCDA): the DocumentEntry gets the URI, hash and size XDM requires.

Types

type Attachment

type Attachment struct {
	Name     string
	MimeType string
	Content  []byte
}

Attachment is one part of a Direct message.

type Checker added in v0.1.19

type Checker struct {
	Anchors *x509.CertPool
	// Now is the time certificates are checked at; time.Now when nil.
	Now func() time.Time
	// Client fetches issuers and revocation lists; nil means one that refuses the cloud metadata address (egress).
	Client *http.Client
	// contains filtered or unexported fields
}

Checker decides whether a certificate found for a recipient may be encrypted to: it must chain to a trust anchor, fetching intermediate issuers named by its Authority Information Access extension when the chain is not complete, and it must not be revoked on its issuers' certificate revocation lists. Both are what the Direct Applicability Statement (4.2.2) asks for and what ONC's certificate discovery tool tests (D17, D18).

Revocation fails open: a list that cannot be fetched or read does not stop delivery, because a CRL server that is down would otherwise stop all Direct mail to everyone the authority issued for. A certificate listed as revoked, on a list its issuer signed, is always refused.

func (*Checker) Check added in v0.1.19

func (k *Checker) Check(ctx context.Context, c *x509.Certificate) error

Check returns nil when c may be used, else why not.

type Disposition

type Disposition struct {
	Kind, OriginalMessageID string
}

Disposition is an MDN's report.

type Message

type Message struct {
	From, Subject string
	To            []string
	Text          string
	Attachments   []Attachment
	MessageID     string
	Date          time.Time
	// DispositionNotification asks the recipient for an MDN (the Direct Implementation Guide for Delivery Notification).
	DispositionNotification bool
}

Message is a Direct message before it is protected, or after it is opened.

func MDN

func MDN(received *Opened, ourAddress, kind string) Message

MDN builds a processed disposition notification for a message received (the Direct Implementation Guide for Delivery Notification): the content to protect and send back to the sender.

type Opened

type Opened struct {
	Message
	// Signer is the certificate that signed it, checked to a trust anchor.
	Signer *x509.Certificate
	// SignerAddress is the Direct address (or domain) the signer's certificate is for.
	SignerAddress string
	// XDM is the documents and metadata when an attachment is an XDM package.
	XDM []XDMDocument
	// MDN is the disposition notification when the message is one: its kind (processed, dispatched, failed) and the
	// Message-ID it is about.
	MDN *Disposition
}

Opened is a received message after decryption and signature checking.

func Open

func Open(raw []byte, keys []cms.KeyPair, anchors *x509.CertPool) (*Opened, error)

Open reads a message from the HISP: decrypts it with whichever of keys it was encrypted to, verifies the signature against anchors, and checks that the signing certificate belongs to the From address (or its domain), as the Applicability Statement requires. An unsigned, untrusted or misattributed message is an error: it is never passed on as if it were Direct.

type Receiver

type Receiver struct {
	Hostname string
	// Accept says whether mail for an address is taken.
	Accept func(rcpt string) bool
	// Deliver is given each message; an error refuses it.
	Deliver func(ctx context.Context, from string, to []string, raw []byte) error
	TLS     *tls.Config
	// RequireTLS refuses MAIL before STARTTLS.
	RequireTLS bool
	MaxBytes   int64
	Log        *slog.Logger
}

Receiver is the SMTP server a HISP delivers a Direct address's mail to: the Direct "edge" side of SMTP. It accepts mail only for the addresses it holds certificates for, requires STARTTLS before MAIL when it has a certificate (a Direct message is encrypted end to end, and the hop is protected as well), and hands each message to Deliver. A message Deliver refuses is refused at DATA with the reason, so the HISP keeps it and reports it.

func (*Receiver) Serve

func (r *Receiver) Serve(ln net.Listener) error

Serve accepts connections until the listener is closed.

type XDMDocument

type XDMDocument struct {
	Name, MimeType, Title, PatientID, UniqueID, TypeCode string
	Content                                              []byte
}

XDMDocument is one document from an XDM package, with what its metadata says.

func ReadXDM

func ReadXDM(zipped []byte) ([]XDMDocument, error)

ReadXDM reads an XDM package's documents and what its METADATA.XML says about each (by the URI slot that names the file).

Jump to

Keyboard shortcuts

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