dkim

package module
Version: v0.0.0-...-e1cd1a0 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2020 License: MIT Imports: 22 Imported by: 14

README

go-dkim

DKIM package for Golang

GoDoc

Getting started

Install
 	go get github.com/toorop/go-dkim

Warning: you need to use Go 1.4.2-master or 1.4.3 (when it will be available) see https://github.com/golang/go/issues/10482 fro more info.

Sign email
import (
	dkim "github.com/toorop/go-dkim"
)

func main(){
	// email is the email to sign (byte slice)
	// privateKey the private key (pem encoded, byte slice )	
	options := dkim.NewSigOptions()
	options.PrivateKey = privateKey
	options.Domain = "mydomain.tld"
	options.Selector = "myselector"
	options.SignatureExpireIn = 3600
	options.BodyLength = 50
	options.Headers = []string{"from", "date", "mime-version", "received", "received"}
	options.AddSignatureTimestamp = true
	options.Canonicalization = "relaxed/relaxed"
	err := dkim.Sign(&email, options)
	// handle err..

	// And... that's it, 'email' is signed ! Amazing© !!!
}
Verify
import (
	dkim "github.com/toorop/go-dkim"
)

func main(){
	// email is the email to verify (byte slice)
	status, err := Verify(&email)
	// handle status, err (see godoc for status)
}

Todo

  • handle z tag (copied header fields used for diagnostic use)

Documentation

Overview

Package dkim provides tools for signing and verify a email according to RFC 6376

Index

Constants

View Source
const (
	CRLF                = "\r\n"
	TAB                 = " "
	FWS                 = CRLF + TAB
	MaxHeaderLineLength = 70
)
View Source
const (
	SUCCESS verifyOutput = 1 + iota
	PERMFAIL
	TEMPFAIL
	NOTSIGNED
	TESTINGSUCCESS
	TESTINGPERMFAIL
	TESTINGTEMPFAIL
)

Variables

View Source
var (
	// ErrSignPrivateKeyRequired when there not private key in config
	ErrSignPrivateKeyRequired = errors.New("PrivateKey is required")

	// ErrSignDomainRequired when there is no domain defined in config
	ErrSignDomainRequired = errors.New("Domain is required")

	// ErrSignSelectorRequired when there is no Selcteir defined in config
	ErrSignSelectorRequired = errors.New("Selector is required")

	// ErrSignHeaderShouldContainsFrom If Headers is specified it should at least contain 'from'
	ErrSignHeaderShouldContainsFrom = errors.New("header must contains 'from' field")

	// ErrSignBadCanonicalization If bad Canonicalization parameter
	ErrSignBadCanonicalization = errors.New("bad Canonicalization parameter")

	// ErrCandNotParsePrivateKey when unable to parse private key
	ErrCandNotParsePrivateKey = errors.New("can not parse private key, check format (pem) and validity")

	// ErrSignBadAlgo Bad algorithm
	ErrSignBadAlgo = errors.New("bad algorithm. Only rsa-sha1 or rsa-sha256 are permitted")

	// ErrBadMailFormat unable to parse mail
	ErrBadMailFormat = errors.New("bad mail format")

	// ErrBadMailFormatHeaders bad headers format (not DKIM Header)
	ErrBadMailFormatHeaders = errors.New("bad mail format found in headers")

	// ErrBadDKimTagLBodyTooShort bad l tag
	ErrBadDKimTagLBodyTooShort = errors.New("bad tag l or bodyLength option. Body length < l value")

	// ErrDkimHeaderBadFormat when errors found in DKIM header
	ErrDkimHeaderBadFormat = errors.New("bad DKIM header format")

	// ErrDkimHeaderNotFound when there's no DKIM-Signature header in an email we have to verify
	ErrDkimHeaderNotFound = errors.New("no DKIM-Signature header field found ")

	// ErrDkimHeaderBTagNotFound when there's no b tag
	ErrDkimHeaderBTagNotFound = errors.New("no tag 'b' found in dkim header")

	// ErrDkimHeaderNoFromInHTag when from is missing in h tag
	ErrDkimHeaderNoFromInHTag = errors.New("'from' header is missing in h tag")

	// ErrDkimHeaderMissingRequiredTag when a required tag is missing
	ErrDkimHeaderMissingRequiredTag = errors.New("signature missing required tag")

	// ErrDkimHeaderDomainMismatch if i tag is not a sub domain of d tag
	ErrDkimHeaderDomainMismatch = errors.New("domain mismatch")

	// ErrDkimVersionNotsupported version not supported
	ErrDkimVersionNotsupported = errors.New("incompatible version")

	// ErrVerifyBodyHash when body hash doesn't verify
	ErrVerifyBodyHash = errors.New("body hash did not verify")

	// ErrVerifyNoKeyForSignature no key
	ErrVerifyNoKeyForSignature = errors.New("no key for verify")

	// ErrVerifyKeyUnavailable when service (dns) is anavailable
	ErrVerifyKeyUnavailable = errors.New("key unavailable")

	// ErrVerifyTagVMustBeTheFirst if present the v tag must be the firts in the record
	ErrVerifyTagVMustBeTheFirst = errors.New("pub key syntax error: v tag must be the first")

	// ErrVerifyVersionMusBeDkim1 if présent flag v (version) must be DKIM1
	ErrVerifyVersionMusBeDkim1 = errors.New("flag v must be set to DKIM1")

	// ErrVerifyBadKeyType bad type for pub key (only rsa is accepted)
	ErrVerifyBadKeyType = errors.New("bad type for key type")

	// ErrVerifyRevokedKey key(s) for this selector is revoked (p is empty)
	ErrVerifyRevokedKey = errors.New("revoked key")

	// ErrVerifyBadKey when we can't parse pubkey
	ErrVerifyBadKey = errors.New("unable to parse pub key")

	// ErrVerifyNoKey when no key is found on DNS record
	ErrVerifyNoKey = errors.New("no public key found in DNS TXT")

	// ErrVerifySignatureHasExpired when signature has expired
	ErrVerifySignatureHasExpired = errors.New("signature has expired")

	// ErrVerifyInappropriateHashAlgo when h tag in pub key doesn't contain hash algo from a tag of DKIM header
	ErrVerifyInappropriateHashAlgo = errors.New("inappropriate has algorithm")
)

Functions

func Sign

func Sign(email *[]byte, options SigOptions) error

Sign signs an email

func Verify

func Verify(email *[]byte, opts ...DNSOpt) (verifyOutput, error)

Verify verifies an email an return state: SUCCESS or PERMFAIL or TEMPFAIL, TESTINGSUCCESS, TESTINGPERMFAIL TESTINGTEMPFAIL or NOTSIGNED error: if an error occurs during verification

Types

type DKIMHeader

type DKIMHeader struct {
	// Version  This tag defines the version of DKIM
	// specification that applies to the signature record.
	// tag v
	Version string

	// The algorithm used to generate the signature..
	// Verifiers MUST support "rsa-sha1" and "rsa-sha256";
	// Signers SHOULD sign using "rsa-sha256".
	// tag a
	Algorithm string

	// The signature data (base64).
	// Whitespace is ignored in this value and MUST be
	// ignored when reassembling the original signature.
	// In particular, the signing process can safely insert
	// FWS in this value in arbitrary places to conform to line-length
	// limits.
	// tag b
	SignatureData string

	// The hash of the canonicalized body part of the message as
	// limited by the "l=" tag (base64; REQUIRED).
	// Whitespace is ignored in this value and MUST be ignored when reassembling the original
	// signature.  In particular, the signing process can safely insert
	// FWS in this value in arbitrary places to conform to line-length
	// limits.
	// tag bh
	BodyHash string

	// Message canonicalization (plain-text; OPTIONAL, default is
	//"simple/simple").  This tag informs the Verifier of the type of
	// canonicalization used to prepare the message for signing.  It
	// consists of two names separated by a "slash" (%d47) character,
	// corresponding to the header and body canonicalization algorithms,
	// respectively.  These algorithms are described in Section 3.4.  If
	// only one algorithm is named, that algorithm is used for the header
	// and "simple" is used for the body.  For example, "c=relaxed" is
	// treated the same as "c=relaxed/simple".
	// tag c
	MessageCanonicalization string

	// The SDID claiming responsibility for an introduction of a message
	//  into the mail stream (plain-text; REQUIRED).  Hence, the SDID
	//  value is used to form the query for the public key.  The SDID MUST
	// correspond to a valid DNS name under which the DKIM key record is
	// published.  The conventions and semantics used by a Signer to
	// create and use a specific SDID are outside the scope of this
	// specification, as is any use of those conventions and semantics.
	// When presented with a signature that does not meet these
	// requirements, Verifiers MUST consider the signature invalid.
	// Internationalized domain names MUST be encoded as A-labels, as
	// described in Section 2.3 of [RFC5890].
	// tag d
	Domain string

	// Signed header fields (plain-text, but see description; REQUIRED).
	// A colon-separated list of header field names that identify the
	// header fields presented to the signing algorithm.  The field MUST
	// contain the complete list of header fields in the order presented
	// to the signing algorithm.  The field MAY contain names of header
	// fields that do not exist when signed; nonexistent header fields do
	// not contribute to the signature computation (that is, they are
	// treated as the null input, including the header field name, the
	// separating colon, the header field value, and any CRLF
	// terminator).  The field MAY contain multiple instances of a header
	// field name, meaning multiple occurrences of the corresponding
	// header field are included in the header hash.  The field MUST NOT
	// include the DKIM-Signature header field that is being created or
	// verified but may include others.  Folding whitespace (FWS) MAY be
	// included on either side of the colon separator.  Header field
	// names MUST be compared against actual header field names in a
	// case-insensitive manner.  This list MUST NOT be empty.  See
	// Section 5.4 for a discussion of choosing header fields to sign and
	// Section 5.4.2 for requirements when signing multiple instances of
	// a single field.
	// tag h
	Headers []string

	// The Agent or User Identifier (AUID) on behalf of which the SDID is
	// taking responsibility (dkim-quoted-printable; OPTIONAL, default is
	// an empty local-part followed by an "@" followed by the domain from
	// the "d=" tag).
	// The syntax is a standard email address where the local-part MAY be
	// omitted.  The domain part of the address MUST be the same as, or a
	// subdomain of, the value of the "d=" tag.
	// Internationalized domain names MUST be encoded as A-labels, as
	// described in Section 2.3 of [RFC5890].
	// tag i
	Auid string

	// Body length count (plain-text unsigned decimal integer; OPTIONAL,
	// default is entire body).  This tag informs the Verifier of the
	// number of octets in the body of the email after canonicalization
	// included in the cryptographic hash, starting from 0 immediately
	// following the CRLF preceding the body.  This value MUST NOT be
	// larger than the actual number of octets in the canonicalized
	// message body.  See further discussion in Section 8.2.
	// tag l
	BodyLength uint

	// A colon-separated list of query methods used to retrieve the
	// public key (plain-text; OPTIONAL, default is "dns/txt").  Each
	// query method is of the form "type[/options]", where the syntax and
	// semantics of the options depend on the type and specified options.
	// If there are multiple query mechanisms listed, the choice of query
	// mechanism MUST NOT change the interpretation of the signature.
	// Implementations MUST use the recognized query mechanisms in the
	// order presented.  Unrecognized query mechanisms MUST be ignored.
	// Currently, the only valid value is "dns/txt", which defines the
	// DNS TXT resource record (RR) lookup algorithm described elsewhere
	// in this document.  The only option defined for the "dns" query
	// type is "txt", which MUST be included.  Verifiers and Signers MUST
	// support "dns/txt".
	// tag q
	QueryMethods []string

	// The selector subdividing the namespace for the "d=" (domain) tag
	// (plain-text; REQUIRED).
	// Internationalized selector names MUST be encoded as A-labels, as
	// described in Section 2.3 of [RFC5890].
	// tag s
	Selector string

	// Signature Timestamp (plain-text unsigned decimal integer;
	// RECOMMENDED, default is an unknown creation time).  The time that
	// this signature was created.  The format is the number of seconds
	// since 00:00:00 on January 1, 1970 in the UTC time zone.  The value
	// is expressed as an unsigned integer in decimal ASCII.  This value
	// is not constrained to fit into a 31- or 32-bit integer.
	// Implementations SHOULD be prepared to handle values up to at least
	// 10^12 (until approximately AD 200,000; this fits into 40 bits).
	// To avoid denial-of-service attacks, implementations MAY consider
	// any value longer than 12 digits to be infinite.  Leap seconds are
	// not counted.  Implementations MAY ignore signatures that have a
	// timestamp in the future.
	// tag t
	SignatureTimestamp time.Time

	// Signature Expiration (plain-text unsigned decimal integer;
	// RECOMMENDED, default is no expiration).  The format is the same as
	// in the "t=" tag, represented as an absolute date, not as a time
	// delta from the signing timestamp.  The value is expressed as an
	// unsigned integer in decimal ASCII, with the same constraints on
	// the value in the "t=" tag.  Signatures MAY be considered invalid
	// if the verification time at the Verifier is past the expiration
	// date.  The verification time should be the time that the message
	// was first received at the administrative domain of the Verifier if
	// that time is reliably available; otherwise, the current time
	// should be used.  The value of the "x=" tag MUST be greater than
	// the value of the "t=" tag if both are present.
	//tag x
	SignatureExpiration time.Time

	// Copied header fields (dkim-quoted-printable, but see description;
	// OPTIONAL, default is null).  A vertical-bar-separated list of
	// selected header fields present when the message was signed,
	// including both the field name and value.  It is not required to
	// include all header fields present at the time of signing.  This
	// field need not contain the same header fields listed in the "h="
	// tag.  The header field text itself must encode the vertical bar
	// ("|", %x7C) character (i.e., vertical bars in the "z=" text are
	// meta-characters, and any actual vertical bar characters in a
	// copied header field must be encoded).  Note that all whitespace
	// must be encoded, including whitespace between the colon and the
	// header field value.  After encoding, FWS MAY be added at arbitrary
	// locations in order to avoid excessively long lines; such
	// whitespace is NOT part of the value of the header field and MUST
	// be removed before decoding.
	// The header fields referenced by the "h=" tag refer to the fields
	// in the [RFC5322] header of the message, not to any copied fields
	// in the "z=" tag.  Copied header field values are for diagnostic
	// use.
	// tag z
	CopiedHeaderFields []string
	// contains filtered or unexported fields
}

func GetHeader

func GetHeader(email *[]byte) (*DKIMHeader, error)

GetHeader return a new DKIMHeader by parsing an email Note: according to RFC 6376 an email can have multiple DKIM Header in this case we return the last inserted or the last with d== mail from

type DNSOpt

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

DNSOpt represents an optional setting for looking up DNS records

func DNSOptLookupTXT

func DNSOptLookupTXT(netLookupTXT func(name string) ([]string, error)) DNSOpt

DNSOptLookupTXT sets the function to use to lookup TXT records.

This should probably only be used in tests.

type DNSOptions

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

DNSOptions holds settings for looking up DNS records

type PubKeyRep

type PubKeyRep struct {
	Version      string
	HashAlgo     []string
	KeyType      string
	Note         string
	PubKey       rsa.PublicKey
	ServiceType  []string
	FlagTesting  bool // flag y
	FlagIMustBeD bool // flag i
}

PubKeyRep represents a parsed version of public key record

func NewPubKeyResp

func NewPubKeyResp(dkimRecord string) (*PubKeyRep, verifyOutput, error)

NewPubKeyResp parses DKIM record (usually from DNS)

func NewPubKeyRespFromDNS

func NewPubKeyRespFromDNS(selector, domain string, opts ...DNSOpt) (*PubKeyRep, verifyOutput, error)

NewPubKeyRespFromDNS retrieves the TXT record from DNS based on the specified domain and selector and parses it.

type SigOptions

type SigOptions struct {

	// DKIM version (default 1)
	Version uint

	// Private key used for signing (required)
	PrivateKey []byte

	// Domain (required)
	Domain string

	// Selector (required)
	Selector string

	// The Agent of User IDentifier
	Auid string

	// Message canonicalization (plain-text; OPTIONAL, default is
	// "simple/simple").  This tag informs the Verifier of the type of
	// canonicalization used to prepare the message for signing.
	Canonicalization string

	// The algorithm used to generate the signature
	//"rsa-sha1" or "rsa-sha256"
	Algo string

	// Signed header fields
	Headers []string

	// Body length count( if set to 0 this tag is ommited in Dkim header)
	BodyLength uint

	// Query Methods used to retrieve the public key
	QueryMethods []string

	// Add a signature timestamp
	AddSignatureTimestamp bool

	// Time validity of the signature (0=never)
	SignatureExpireIn uint64

	// CopiedHeaderFileds
	CopiedHeaderFields []string
}

sigOptions represents signing options

func NewSigOptions

func NewSigOptions() SigOptions

NewSigOptions returns new sigoption with some defaults value

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
t or T : Toggle theme light dark auto