maddy/modify/dkim/keys_test.go
fox.cpp beef9e2455
Implement DKIM signing support
This support is based on github.com/foxcpp/go-msgauth fork until
emerison/go-msgauth#13 gets merged.

Further extensions are required to make sure only messages we can
actually "take responsibility for" are signed.

RSA-2048 is used as a default algorithm when generating new keys.
RSA-4096 can cause trouble with UDP-only DNS due to responses being
bigger than 512 octets. RSA-1024 is too weak and explicitly
disallowed in maddy for new keys. It could be possible to use Ed25519
but support is not widely deployed yet (according to warning in rspamd
docs dated 2019-09). Users concerned about security of RSA-2048 can
switch to RSA-4096 or Ed25519, keeping relevant problems in mind.

Ed25519 key format uses PKCS#8, this seems to be different from other
implementations that just dump key material into a file without any
wrapping. Interoperability is not considered to encourage key
rotation when migration, which is a good thing to do anyway.

There is no option to use "body limit", since it is dangerous
and go-msgauth/dkim does not support it for signing.

The default set of signed header fields is the list used by rspamd.
Most "core" fields are oversigned to provide strict integrity.
"Conditional oversigning" similar to rspamd is not implemented, though
it may be useful, further research is required.

Multi-tentant configuration with DKIM and DMARC is much more verbose,
configuration example is added to config.d/multitentant-dkim.conf to
explain how to make it work.
2019-10-27 20:40:38 +03:00

88 lines
2.1 KiB
Go

package dkim
import (
"crypto/ed25519"
"encoding/base64"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/foxcpp/maddy/testutils"
)
func TestKeyLoad_new(t *testing.T) {
m := Modifier{}
m.log = testutils.Logger(t, m.Name())
dir, err := ioutil.TempDir("", "maddy-tests-dkim-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
signer, err := m.loadOrGenerateKey("example.org", "default", filepath.Join(dir, "testkey.key"), "ed25519")
if err != nil {
t.Fatal(err)
}
recordBlob, err := ioutil.ReadFile(filepath.Join(dir, "testkey.dns"))
if err != nil {
t.Fatal(err)
}
var keyBlob []byte
for _, part := range strings.Split(string(recordBlob), ";") {
part = strings.TrimSpace(part)
if strings.HasPrefix(part, "k=") {
if part != "k=ed25519" {
t.Fatalf("Wrong type of generated key, want ed25519, got %s", part)
}
}
if strings.HasPrefix(part, "p=") {
keyBlob, err = base64.StdEncoding.DecodeString(part[2:])
if err != nil {
t.Fatal(err)
}
}
}
blob := signer.Public().(ed25519.PublicKey)
if string(blob) != string(keyBlob) {
t.Fatal("wrong public key placed into record file")
}
}
const pkeyEd25519 = `-----BEGIN PRIVATE KEY-----
X-DKIM-Domain: example.org
X-DKIM-Selector: default
MC4CAQAwBQYDK2VwBCIEIJG9zs4vi2MYNkL9gUQwlmBLCzDODIJ5/1CwTAZFDm5U
-----END PRIVATE KEY-----`
const pubkeyEd25519 = `5TPcCxzVByMyRsMFs5Dx23pnxKilI+1UrGg0t+O2oZU=`
func TestKeyLoad_existing(t *testing.T) {
m := Modifier{}
m.log = testutils.Logger(t, m.Name())
dir, err := ioutil.TempDir("", "maddy-tests-dkim-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
if err := ioutil.WriteFile(filepath.Join(dir, "testkey.key"), []byte(pkeyEd25519), 0600); err != nil {
t.Fatal(err)
}
signer, err := m.loadOrGenerateKey("example.org", "default", filepath.Join(dir, "testkey.key"), "ed25519")
if err != nil {
t.Fatal(err)
}
blob := signer.Public().(ed25519.PublicKey)
if signerKey := base64.StdEncoding.EncodeToString(blob); signerKey != pubkeyEd25519 {
t.Fatalf("wrong public key returned by loadOrGenerateKey, \nwant %s\ngot %s", pubkeyEd25519, signerKey)
}
}