yubikey_test.go

  1package pgp
  2
  3import (
  4	"errors"
  5	"strconv"
  6	"strings"
  7	"testing"
  8)
  9
 10// TestParseASN1Signature_TruncatedDoesNotPanic covers the bounds-check path
 11// added for #613. Each input would have panicked in the original parser
 12// with "index out of range"; here we expect a typed error instead.
 13func TestParseASN1Signature_TruncatedDoesNotPanic(t *testing.T) {
 14	cases := []struct {
 15		name    string
 16		der     []byte
 17		wantErr string
 18	}{
 19		{
 20			// Length byte declares 0x10 bytes of R but only 1 byte follows.
 21			name:    "R length overruns buffer",
 22			der:     []byte{0x30, 0x06, 0x02, 0x10, 0xAA, 0x00},
 23			wantErr: "R length overflow",
 24		},
 25		{
 26			// Length byte declares 0x10 bytes of S but only 1 byte follows.
 27			name:    "S length overruns buffer",
 28			der:     []byte{0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x10, 0xAA},
 29			wantErr: "S length overflow",
 30		},
 31		{
 32			// Valid R, then no S block at all.
 33			name:    "missing S after R",
 34			der:     []byte{0x30, 0x06, 0x02, 0x01, 0x01, 0x00},
 35			wantErr: "expected INTEGER tag for S",
 36		},
 37	}
 38
 39	for _, tc := range cases {
 40		tc := tc
 41		t.Run(tc.name, func(t *testing.T) {
 42			// The test must not panic: the fix replaces panics with errors.
 43			defer func() {
 44				if r := recover(); r != nil {
 45					t.Fatalf("parseASN1Signature panicked: %v", r)
 46				}
 47			}()
 48			_, _, err := parseASN1Signature(tc.der)
 49			if err == nil {
 50				t.Fatalf("want error, got nil")
 51			}
 52			if !strings.Contains(err.Error(), tc.wantErr) {
 53				t.Fatalf("error = %q, want it to mention %q", err.Error(), tc.wantErr)
 54			}
 55		})
 56	}
 57}
 58
 59// TestParseASN1Signature_WellFormed guards against regressions in the
 60// happy path: a minimal SEQUENCE { INTEGER, INTEGER } must still decode
 61// to the original r and s bytes.
 62func TestParseASN1Signature_WellFormed(t *testing.T) {
 63	// SEQUENCE (6 bytes) { INTEGER 0x01, INTEGER 0x02 }
 64	der := []byte{0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}
 65
 66	r, s, err := parseASN1Signature(der)
 67	if err != nil {
 68		t.Fatalf("unexpected error: %v", err)
 69	}
 70	if len(r) != 1 || r[0] != 0x01 {
 71		t.Errorf("r = %x, want 01", r)
 72	}
 73	if len(s) != 1 || s[0] != 0x02 {
 74		t.Errorf("s = %x, want 02", s)
 75	}
 76}
 77
 78func TestGenerateMIMEBoundaryUsesCryptoRandomBytes(t *testing.T) {
 79	oldRandRead := randRead
 80	defer func() { randRead = oldRandRead }()
 81
 82	randRead = func(p []byte) (int, error) {
 83		for i := range p {
 84			p[i] = byte(i)
 85		}
 86		return len(p), nil
 87	}
 88
 89	got := generateMIMEBoundary()
 90	want := "----=_Part_000102030405060708090a0b0c0d0e0f"
 91	if got != want {
 92		t.Fatalf("boundary = %q, want %q", got, want)
 93	}
 94}
 95
 96func TestGenerateMIMEBoundaryFallsBackToUnixNano(t *testing.T) {
 97	oldRandRead := randRead
 98	defer func() { randRead = oldRandRead }()
 99
100	randRead = func(_ []byte) (int, error) {
101		return 0, errors.New("random source unavailable")
102	}
103
104	const prefix = "----=_Part_"
105	got := generateMIMEBoundary()
106	if !strings.HasPrefix(got, prefix) {
107		t.Fatalf("boundary = %q, want prefix %q", got, prefix)
108	}
109	if _, err := strconv.ParseInt(strings.TrimPrefix(got, prefix), 10, 64); err != nil {
110		t.Fatalf("fallback boundary suffix is not a UnixNano timestamp: %v", err)
111	}
112}