Files
mesh-catalog/modules/brother/cmd/brother/pin.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

65 lines
2.3 KiB
Go

package main
import (
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"fmt"
"strings"
)
// Fingerprint is the SHA-256 of a certificate's DER bytes, as upper-case hex pairs joined by colons: the
// form the printer's own certificate page and the config value printer-certificate give (ADR 0467 clause 3).
func Fingerprint(der []byte) string {
sum := sha256.Sum256(der)
parts := make([]string, len(sum))
for i, b := range sum {
parts[i] = fmt.Sprintf("%02X", b)
}
return strings.Join(parts, ":")
}
// pinList splits the config value printer-certificate into its fingerprints: one or more, separated by commas
// with optional spaces, upper-cased. The printer serves two certificates for one name, an ECDSA one and an RSA
// one, and which it serves depends on the client, so the operator pins both (issue 1503).
func pinList(value string) []string {
var out []string
for _, p := range strings.Split(value, ",") {
if p = strings.ToUpper(strings.TrimSpace(p)); p != "" {
out = append(out, p)
}
}
return out
}
// pinned is the check on the certificate a printer serves: the leaf's fingerprint must be one of those pinned,
// and any other certificate is refused, with the served and every pinned fingerprint in the message. It runs in
// the handshake, so a refused certificate is refused before one byte of a request (a password) is sent.
func pinned(value string) func([][]byte, [][]*x509.Certificate) error {
want := pinList(value)
list := strings.Join(want, ", ")
return func(raw [][]byte, _ [][]*x509.Certificate) error {
if len(raw) == 0 {
return fmt.Errorf("the printer served no certificate; the pinned printer-certificate is %s", list)
}
got := Fingerprint(raw[0])
for _, w := range want {
if got == w {
return nil
}
}
return fmt.Errorf("the printer's certificate is %s, but the pinned printer-certificate is %s: refused", got, list)
}
}
// pinnedTLS is the TLS configuration for the printer's admin page: the printer's certificate is self-signed,
// so no chain is verified; the pinned fingerprint is the whole of the trust (ADR 0467 clause 3).
func pinnedTLS(fingerprint string) *tls.Config {
return &tls.Config{
MinVersion: tls.VersionTLS12,
// The chain is not verified because the pinned fingerprint is checked in its place, below.
InsecureSkipVerify: true,
VerifyPeerCertificate: pinned(fingerprint),
}
}