tls_client_property_test.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. package runtime
  2. import (
  3. "bytes"
  4. "crypto/sha256"
  5. "encoding/base64"
  6. "encoding/hex"
  7. "strings"
  8. "testing"
  9. "pgregory.net/rapid"
  10. )
  11. func insertColons(h string) string {
  12. var b strings.Builder
  13. for i := 0; i < len(h); i += 2 {
  14. if i > 0 {
  15. b.WriteByte(':')
  16. }
  17. b.WriteString(h[i : i+2])
  18. }
  19. return b.String()
  20. }
  21. // TestProp_DecodeCertPin_FormatAgnostic asserts that for ANY 32-byte pin, every
  22. // accepted encoding (hex lower/upper, openssl colon-hex, base64 std/raw/url) decodes
  23. // back to the same bytes. Generalizes the fixed-input TestDecodeCertPin so a mutant
  24. // that breaks one decoding path is caught across the whole input space.
  25. func TestProp_DecodeCertPin_FormatAgnostic(t *testing.T) {
  26. rapid.Check(t, func(t *rapid.T) {
  27. raw := rapid.SliceOfN(rapid.Byte(), sha256.Size, sha256.Size).Draw(t, "raw")
  28. hx := hex.EncodeToString(raw)
  29. forms := []string{
  30. hx,
  31. strings.ToUpper(hx),
  32. insertColons(hx),
  33. base64.StdEncoding.EncodeToString(raw),
  34. base64.RawStdEncoding.EncodeToString(raw),
  35. base64.URLEncoding.EncodeToString(raw),
  36. base64.RawURLEncoding.EncodeToString(raw),
  37. }
  38. for _, f := range forms {
  39. got, err := DecodeCertPin(f)
  40. if err != nil {
  41. t.Fatalf("DecodeCertPin(%q) errored: %v", f, err)
  42. }
  43. if !bytes.Equal(got, raw) {
  44. t.Fatalf("DecodeCertPin(%q) = %x, want %x", f, got, raw)
  45. }
  46. }
  47. })
  48. }
  49. // FuzzDecodeCertPin asserts the security-load-bearing decoder never panics, never
  50. // returns a non-32-byte slice with a nil error, and never returns bytes alongside an
  51. // error. Seeded from the known-good/known-bad cases.
  52. func FuzzDecodeCertPin(f *testing.F) {
  53. seed := sha256.Sum256([]byte("seed"))
  54. f.Add(hex.EncodeToString(seed[:]))
  55. f.Add(base64.StdEncoding.EncodeToString(seed[:]))
  56. f.Add(insertColons(hex.EncodeToString(seed[:])))
  57. f.Add("")
  58. f.Add("not-a-pin")
  59. f.Fuzz(func(t *testing.T, s string) {
  60. got, err := DecodeCertPin(s)
  61. if err == nil && len(got) != sha256.Size {
  62. t.Fatalf("DecodeCertPin(%q): nil error but %d bytes, want %d", s, len(got), sha256.Size)
  63. }
  64. if err != nil && got != nil {
  65. t.Fatalf("DecodeCertPin(%q): error %v but returned bytes %x", s, err, got)
  66. }
  67. })
  68. }