CI / go-test (push) Canceled after 0s
CI / postgres-durable-first (push) Canceled after 0s
CI / codegen (push) Canceled after 0s
CI / govulncheck (push) Canceled after 0s
CI / race (push) Canceled after 0s
CI / fuzz-smoke (push) Canceled after 0s
CI / golangci (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CodeQL Advanced / Analyze (go) (push) Canceled after 0s
CodeQL Advanced / Analyze (actions) (push) Canceled after 0s
CodeQL Advanced / Analyze (javascript-typescript) (push) Canceled after 0s
Docs CI / build (push) Canceled after 0s
Docs Deploy (GitHub Pages) / build (push) Canceled after 0s
Release 3X-UI / build (386) (push) Canceled after 0s
Release 3X-UI / build (amd64) (push) Canceled after 0s
Release 3X-UI / build (arm64) (push) Canceled after 0s
Release 3X-UI / build (armv5) (push) Canceled after 0s
Release 3X-UI / build (armv6) (push) Canceled after 0s
Release 3X-UI / build (armv7) (push) Canceled after 0s
Release 3X-UI / build (s390x) (push) Canceled after 0s
Release 3X-UI / Build for Windows (amd64) (push) Canceled after 0s
Deploy Smoke Tests / noninteractive-install (ubuntu-24.04-arm) (push) Canceled after 0s
Deploy Smoke Tests / noninteractive-install (ubuntu-latest) (push) Canceled after 0s
Deploy Smoke Tests / release-tag-install (ubuntu-24.04-arm) (push) Canceled after 0s
Deploy Smoke Tests / release-tag-install (ubuntu-latest) (push) Canceled after 0s
Docs Deploy (GitHub Pages) / deploy (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
74 lines
2.1 KiB
Go
74 lines
2.1 KiB
Go
package runtime
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"strings"
|
|
"testing"
|
|
|
|
"pgregory.net/rapid"
|
|
)
|
|
|
|
func insertColons(h string) string {
|
|
var b strings.Builder
|
|
for i := 0; i < len(h); i += 2 {
|
|
if i > 0 {
|
|
b.WriteByte(':')
|
|
}
|
|
b.WriteString(h[i : i+2])
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// TestProp_DecodeCertPin_FormatAgnostic asserts that for ANY 32-byte pin, every
|
|
// accepted encoding (hex lower/upper, openssl colon-hex, base64 std/raw/url) decodes
|
|
// back to the same bytes. Generalizes the fixed-input TestDecodeCertPin so a mutant
|
|
// that breaks one decoding path is caught across the whole input space.
|
|
func TestProp_DecodeCertPin_FormatAgnostic(t *testing.T) {
|
|
rapid.Check(t, func(t *rapid.T) {
|
|
raw := rapid.SliceOfN(rapid.Byte(), sha256.Size, sha256.Size).Draw(t, "raw")
|
|
hx := hex.EncodeToString(raw)
|
|
forms := []string{
|
|
hx,
|
|
strings.ToUpper(hx),
|
|
insertColons(hx),
|
|
base64.StdEncoding.EncodeToString(raw),
|
|
base64.RawStdEncoding.EncodeToString(raw),
|
|
base64.URLEncoding.EncodeToString(raw),
|
|
base64.RawURLEncoding.EncodeToString(raw),
|
|
}
|
|
for _, f := range forms {
|
|
got, err := DecodeCertPin(f)
|
|
if err != nil {
|
|
t.Fatalf("DecodeCertPin(%q) errored: %v", f, err)
|
|
}
|
|
if !bytes.Equal(got, raw) {
|
|
t.Fatalf("DecodeCertPin(%q) = %x, want %x", f, got, raw)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// FuzzDecodeCertPin asserts the security-load-bearing decoder never panics, never
|
|
// returns a non-32-byte slice with a nil error, and never returns bytes alongside an
|
|
// error. Seeded from the known-good/known-bad cases.
|
|
func FuzzDecodeCertPin(f *testing.F) {
|
|
seed := sha256.Sum256([]byte("seed"))
|
|
f.Add(hex.EncodeToString(seed[:]))
|
|
f.Add(base64.StdEncoding.EncodeToString(seed[:]))
|
|
f.Add(insertColons(hex.EncodeToString(seed[:])))
|
|
f.Add("")
|
|
f.Add("not-a-pin")
|
|
f.Fuzz(func(t *testing.T, s string) {
|
|
got, err := DecodeCertPin(s)
|
|
if err == nil && len(got) != sha256.Size {
|
|
t.Fatalf("DecodeCertPin(%q): nil error but %d bytes, want %d", s, len(got), sha256.Size)
|
|
}
|
|
if err != nil && got != nil {
|
|
t.Fatalf("DecodeCertPin(%q): error %v but returned bytes %x", s, err, got)
|
|
}
|
|
})
|
|
}
|