Files
mesh-catalog/modules/brother/cmd/brother/pin_test.go
T
jschoubben 6312bc4219
mesh/merge-gate head 6312bc421964 merged into trunk c294f7bd16dc — pass: builds brother → shanks; no bus step; every machine composes with the change as it…
mesh/repo-check head 6312bc421964 merged into trunk c294f7bd16dc — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
Let printer-certificate pin several fingerprints, since the printer serves an ECDSA and an RSA certificate (issue 1503)
2026-10-20 22:43:02 +02:00

106 lines
3.9 KiB
Go

package main
import (
"context"
"encoding/pem"
"os"
"regexp"
"strings"
"testing"
)
// Captured from the printer 2026-10-20 (testdata/pin): it serves two self-signed certificates for one name, an
// ECDSA one to a client offering ECDSA and an RSA one to a client offering RSA only (issue 1503).
const (
printerECDSAPrint = "EA:B4:B7:0F:96:CD:A6:2B:61:53:2E:F0:A8:D6:9F:42:EA:9F:66:0A:82:5B:C2:43:0B:97:C5:9A:7A:B4:18:5B"
printerRSAPrint = "7A:3D:E2:A4:1E:AB:A9:7B:E6:61:0A:C4:18:DD:68:FF:9B:A5:77:E8:02:7F:7E:F8:97:D0:3B:82:A2:89:7D:F4"
)
func capturedDER(t *testing.T, name string) []byte {
t.Helper()
raw, err := os.ReadFile("testdata/pin/" + name)
if err != nil {
t.Fatal(err)
}
b, _ := pem.Decode(raw)
if b == nil {
t.Fatalf("%s is not PEM", name)
}
return b.Bytes
}
// 1503/AC1: the two captured certificates have the two fingerprints, and a pin listing both accepts each.
func TestThePrintersTwoCapturedCertificatesAreBothAcceptedByAPinListingBoth(t *testing.T) {
both := printerECDSAPrint + ", " + printerRSAPrint
for file, want := range map[string]string{"ecdsa.pem": printerECDSAPrint, "rsa.pem": printerRSAPrint} {
der := capturedDER(t, file)
if got := Fingerprint(der); got != want {
t.Fatalf("%s has fingerprint %s, want %s", file, got, want)
}
if err := pinned(both)([][]byte{der}, nil); err != nil {
t.Errorf("%s refused by a pin listing both: %v", file, err)
}
}
}
// 1503/AC1: a server presenting one certificate is accepted when the pin lists its fingerprint among two.
func TestAPinListingTwoFingerprintsAcceptsTheCertificateTheServerPresents(t *testing.T) {
f := newFakePrinter(t, "B1891", "hunter2")
a, err := NewAdmin(f.host(), printerECDSAPrint+","+f.print(), "hunter2")
if err != nil {
t.Fatal(err)
}
if err := a.Login(context.Background()); err != nil {
t.Fatalf("a pin listing the served certificate was refused: %v", err)
}
}
// 1503/AC2: a pin that lists only another certificate still refuses, and names every pinned fingerprint.
func TestAPinListingOnlyOtherCertificatesStillRefuses(t *testing.T) {
f := newFakePrinter(t, "B1891", "hunter2")
a, _ := NewAdmin(f.host(), printerECDSAPrint+" , "+printerRSAPrint, "hunter2")
err := a.Login(context.Background())
if err == nil {
t.Fatal("a certificate that is in no pinned fingerprint was accepted")
}
for _, want := range []string{f.print(), printerECDSAPrint, printerRSAPrint} {
if !strings.Contains(err.Error(), want) {
t.Errorf("error %q does not name %s", err, want)
}
}
if len(f.requests) != 0 {
t.Errorf("the server was sent %v", f.requests)
}
}
// 1503/AC1: a single fingerprint is still a valid pin, in either case.
func TestASingleFingerprintStillPins(t *testing.T) {
der := capturedDER(t, "rsa.pem")
for _, p := range []string{printerRSAPrint, strings.ToLower(printerRSAPrint), " " + printerRSAPrint + " "} {
if err := pinned(p)([][]byte{der}, nil); err != nil {
t.Errorf("pin %q refused: %v", p, err)
}
}
if err := pinned(printerECDSAPrint)([][]byte{der}, nil); err == nil {
t.Error("the other certificate's pin accepted the RSA certificate")
}
}
// 1503/AC3: the manifest's pattern takes one or more fingerprints, separated by commas and optional spaces, and
// nothing else.
func TestThePrinterCertificatePatternTakesACommaSeparatedList(t *testing.T) {
m := readManifest(t)
re := regexp.MustCompile(`^(?:` + m.Config["printer-certificate"]["pattern"].(string) + `)$`)
for _, ok := range []string{printerRSAPrint, printerECDSAPrint + "," + printerRSAPrint, printerECDSAPrint + ", " + printerRSAPrint} {
if !re.MatchString(ok) {
t.Errorf("pattern refuses %q", ok)
}
}
for _, bad := range []string{"", printerRSAPrint + ",", "," + printerRSAPrint, printerECDSAPrint + printerRSAPrint,
printerECDSAPrint + ",," + printerRSAPrint, printerECDSAPrint + ";" + printerRSAPrint, printerRSAPrint + ",EAB4", strings.ToLower(printerRSAPrint)} {
if re.MatchString(bad) {
t.Errorf("pattern admits %q", bad)
}
}
}