package encapsulation import ( "bytes" "io" "math/rand" "testing" ) // Return a byte slice with non-trivial contents. func pseudorandomBuffer(n int) []byte { source := rand.NewSource(0) p := make([]byte, n) for i := 0; i < len(p); i++ { p[i] = byte(source.Int63() & 0xff) } return p } func mustWriteData(w io.Writer, p []byte) int { n, err := WriteData(w, p) if err != nil { panic(err) } return n } func mustWritePadding(w io.Writer, n int) int { n, err := WritePadding(w, n) if err != nil { panic(err) } return n } // Test that ReadData(WriteData()) recovers the original data. func TestRoundtrip(t *testing.T) { // Test above and below interesting thresholds. for _, i := range []int{ 0x00, 0x01, 0x3e, 0x3f, 0x40, 0x41, 0xfe, 0xff, 0x100, 0x101, 0x1ffe, 0x1fff, 0x2000, 0x2001, 0xfffe, 0xffff, 0x10000, 0x10001, 0xffffe, 0xfffff, } { original := pseudorandomBuffer(i) var enc bytes.Buffer n, err := WriteData(&enc, original) if err != nil { t.Fatalf("size %d, WriteData returned error %v", i, err) } if enc.Len() != n { t.Fatalf("size %d, returned length was %d, written length was %d", i, n, enc.Len()) } inverse := make([]byte, i) n, err = ReadData(&enc, inverse) if err != nil { t.Fatalf("size %d, ReadData returned error %v", i, err) } if !bytes.Equal(inverse[:n], original) { t.Fatalf("size %d, got <%x>, expected <%x>", i, inverse[:n], original) } } } // Test that WritePadding writes exactly as much as requested. func TestPaddingLength(t *testing.T) { // Test above and below interesting thresholds. WritePadding also gets // values above 0xfffff, the maximum value of a single length prefix. for _, i := range []int{ 0x00, 0x01, 0x3f, 0x40, 0x41, 0x42, 0xff, 0x100, 0x101, 0x102, 0x2000, 0x2001, 0x2002, 0x2003, 0x10000, 0x10001, 0x10002, 0x10003, 0x100001, 0x100002, 0x100003, 0x100004, } { var enc bytes.Buffer n, err := WritePadding(&enc, i) if err != nil { t.Fatalf("size %d, WritePadding returned error %v", i, err) } if n != i { t.Fatalf("requested %d bytes, returned %d", i, n) } if enc.Len() != n { t.Fatalf("requested %d bytes, wrote %d bytes", i, enc.Len()) } } } // Test that ReadData skips over padding. func TestSkipPadding(t *testing.T) { var data = [][]byte{{}, {}, []byte("hello"), {}, []byte("world")} var enc bytes.Buffer mustWritePadding(&enc, 10) mustWritePadding(&enc, 100) mustWriteData(&enc, data[0]) mustWriteData(&enc, data[1]) mustWritePadding(&enc, 10) mustWriteData(&enc, data[2]) mustWriteData(&enc, data[3]) mustWritePadding(&enc, 10) mustWriteData(&enc, data[4]) mustWritePadding(&enc, 10) mustWritePadding(&enc, 10) for i, expected := range data { var actual [10]byte n, err := ReadData(&enc, actual[:]) if err != nil { t.Fatalf("slice %d, got error %v, expected %v", i, err, nil) } if !bytes.Equal(actual[:n], expected) { t.Fatalf("slice %d, got <%x>, expected <%x>", i, actual[:n], expected) } } n, err := ReadData(&enc, nil) if n != 0 || err != io.EOF { t.Fatalf("got (%v, %v), expected (%v, %v)", n, err, 0, io.EOF) } } // Test that EOF before a length prefix returns io.EOF. func TestEOF(t *testing.T) { n, err := ReadData(bytes.NewReader(nil), nil) if n != 0 || err != io.EOF { t.Fatalf("got (%v, %v), expected (%v, %v)", n, err, 0, io.EOF) } } // Test that an EOF while reading a length prefix, or while reading the // subsequent data/padding, returns io.ErrUnexpectedEOF. func TestUnexpectedEOF(t *testing.T) { for _, test := range [][]byte{ {0x40}, // expecting a second length byte {0xc0}, // expecting a second length byte {0x41, 0x80}, // expecting a third length byte {0xc1, 0x80}, // expecting a third length byte {0x02}, // expecting 2 bytes of padding {0x82}, // expecting 2 bytes of data {0x02, 'X'}, // expecting 1 byte of padding {0x82, 'X'}, // expecting 1 byte of data {0x41, 0x00}, // expecting 128 bytes of padding {0xc1, 0x00}, // expecting 128 bytes of data {0x41, 0x00, 'X'}, // expecting 127 bytes of padding {0xc1, 0x00, 'X'}, // expecting 127 bytes of data {0x41, 0x80, 0x00}, // expecting 32768 bytes of padding {0xc1, 0x80, 0x00}, // expecting 32768 bytes of data {0x41, 0x80, 0x00, 'X'}, // expecting 32767 bytes of padding {0xc1, 0x80, 0x00, 'X'}, // expecting 32767 bytes of data } { n, err := ReadData(bytes.NewReader(test), nil) if n != 0 || err != io.ErrUnexpectedEOF { t.Fatalf("<%x> got (%v, %v), expected (%v, %v)", test, n, err, 0, io.ErrUnexpectedEOF) } } } // Test that length encodings that are longer than they could be are still // interpreted. func TestNonMinimalLengthEncoding(t *testing.T) { for _, test := range []struct { enc []byte expected []byte }{ {[]byte{0x81, 'X'}, []byte("X")}, {[]byte{0xc0, 0x01, 'X'}, []byte("X")}, {[]byte{0xc0, 0x80, 0x01, 'X'}, []byte("X")}, } { var p [10]byte n, err := ReadData(bytes.NewReader(test.enc), p[:]) if err != nil { t.Fatalf("<%x> got error %v, expected %v", test.enc, err, nil) } if !bytes.Equal(p[:n], test.expected) { t.Fatalf("<%x> got <%x>, expected <%x>", test.enc, p[:n], test.expected) } } } // Test that ReadData only reads up to 3 bytes of length prefix. func TestReadLimits(t *testing.T) { // Test the maximum length that's possible with 3 bytes of length // prefix. maxLength := (0x3f << 14) | (0x7f << 7) | 0x7f data := bytes.Repeat([]byte{'X'}, maxLength) prefix := []byte{0xff, 0xff, 0x7f} // encodes 0xfffff var p [0xfffff]byte n, err := ReadData(bytes.NewReader(append(prefix, data...)), p[:]) if err != nil { t.Fatalf("got error %v, expected %v", err, nil) } if !bytes.Equal(p[:n], data) { t.Fatalf("got %d bytes unequal to %d bytes", len(p), len(data)) } // Test a 4-byte prefix. prefix = []byte{0xc0, 0xc0, 0x80, 0x80} // encodes 0x100000 data = bytes.Repeat([]byte{'X'}, maxLength+1) n, err = ReadData(bytes.NewReader(append(prefix, data...)), nil) if n != 0 || err != ErrTooLong { t.Fatalf("got (%v, %v), expected (%v, %v)", n, err, 0, ErrTooLong) } // Test that 4 bytes don't work, even when they encode an integer that // would fix in 3 bytes. prefix = []byte{0xc0, 0x80, 0x80, 0x80} // encodes 0x0 data = []byte{} n, err = ReadData(bytes.NewReader(append(prefix, data...)), nil) if n != 0 || err != ErrTooLong { t.Fatalf("got (%v, %v), expected (%v, %v)", n, err, 0, ErrTooLong) } // Do the same tests with padding lengths. data = []byte("hello") prefix = []byte{0x7f, 0xff, 0x7f} // encodes 0xfffff padding := bytes.Repeat([]byte{'X'}, maxLength) enc := bytes.NewBuffer(append(prefix, padding...)) mustWriteData(enc, data) n, err = ReadData(enc, p[:]) if err != nil { t.Fatalf("got error %v, expected %v", err, nil) } if !bytes.Equal(p[:n], data) { t.Fatalf("got <%x>, expected <%x>", p[:n], data) } prefix = []byte{0x40, 0xc0, 0x80, 0x80} // encodes 0x100000 padding = bytes.Repeat([]byte{'X'}, maxLength+1) enc = bytes.NewBuffer(append(prefix, padding...)) mustWriteData(enc, data) n, err = ReadData(enc, nil) if n != 0 || err != ErrTooLong { t.Fatalf("got (%v, %v), expected (%v, %v)", n, err, 0, ErrTooLong) } prefix = []byte{0x40, 0x80, 0x80, 0x80} // encodes 0x0 padding = []byte{} enc = bytes.NewBuffer(append(prefix, padding...)) mustWriteData(enc, data) n, err = ReadData(enc, nil) if n != 0 || err != ErrTooLong { t.Fatalf("got (%v, %v), expected (%v, %v)", n, err, 0, ErrTooLong) } } // Test that WriteData and WritePadding only accept lengths that can be encoded // in up to 3 bytes of length prefix. func TestWriteLimits(t *testing.T) { maxLength := (0x3f << 14) | (0x7f << 7) | 0x7f var enc bytes.Buffer n, err := WriteData(&enc, bytes.Repeat([]byte{'X'}, maxLength)) if n != maxLength+3 || err != nil { t.Fatalf("got (%d, %v), expected (%d, %v)", n, err, maxLength, nil) } enc.Reset() n, err = WriteData(&enc, bytes.Repeat([]byte{'X'}, maxLength+1)) if n != 0 || err != ErrTooLong { t.Fatalf("got (%d, %v), expected (%d, %v)", n, err, 0, ErrTooLong) } // Padding gets an extra 3 bytes because the prefix is counted as part // of the length. enc.Reset() n, err = WritePadding(&enc, maxLength+3) if n != maxLength+3 || err != nil { t.Fatalf("got (%d, %v), expected (%d, %v)", n, err, maxLength+3, nil) } // Writing a too-long padding is okay because WritePadding will break it // into smaller chunks. enc.Reset() n, err = WritePadding(&enc, maxLength+4) if n != maxLength+4 || err != nil { t.Fatalf("got (%d, %v), expected (%d, %v)", n, err, maxLength+4, nil) } } // Test that WritePadding panics when given a negative length. func TestNegativeLength(t *testing.T) { for _, n := range []int{-1, ^0} { var enc bytes.Buffer panicked, nn, err := testNegativeLengthSub(t, &enc, n) if !panicked { t.Fatalf("WritePadding(%d) returned (%d, %v) instead of panicking", n, nn, err) } } } // Calls WritePadding(w, n) and augments the return value with a flag indicating // whether the call panicked. func testNegativeLengthSub(t *testing.T, w io.Writer, n int) (panicked bool, nn int, err error) { defer func() { if r := recover(); r != nil { panicked = true } }() t.Helper() nn, err = WritePadding(w, n) return false, n, err } // Test that MaxDataForSize panics when given a 0 length. func TestMaxDataForSizeZero(t *testing.T) { defer func() { if r := recover(); r == nil { t.Fatal("didn't panic") } }() MaxDataForSize(0) } // Test thresholds of available sizes for MaxDataForSize. func TestMaxDataForSize(t *testing.T) { for _, test := range []struct { size int expected int }{ {0x01, 0x00}, {0x02, 0x01}, {0x3f, 0x3e}, {0x40, 0x3e}, {0x41, 0x3f}, {0x1fff, 0x1ffd}, {0x2000, 0x1ffd}, {0x2001, 0x1ffe}, {0xfffff, 0xffffc}, {0x100000, 0xffffc}, {0x100001, 0xffffc}, {0x7fffffff, 0xffffc}, } { max := MaxDataForSize(test.size) if max != test.expected { t.Fatalf("size %d, got %d, expected %d", test.size, max, test.expected) } } } // Test that ReadData truncates the data when the destination slice is too // short. func TestReadDataTruncate(t *testing.T) { var enc bytes.Buffer mustWriteData(&enc, []byte("12345678")) mustWriteData(&enc, []byte("abcdefgh")) var p [4]byte // First ReadData should return truncated "1234". n, err := ReadData(&enc, p[:]) if err != io.ErrShortBuffer { t.Fatalf("got error %v, expected %v", err, io.ErrShortBuffer) } if !bytes.Equal(p[:n], []byte("1234")) { t.Fatalf("got <%x>, expected <%x>", p[:n], []byte("1234")) } // Second ReadData should return truncated "abcd", not the rest of // "12345678". n, err = ReadData(&enc, p[:]) if err != io.ErrShortBuffer { t.Fatalf("got error %v, expected %v", err, io.ErrShortBuffer) } if !bytes.Equal(p[:n], []byte("abcd")) { t.Fatalf("got <%x>, expected <%x>", p[:n], []byte("abcd")) } // Last ReadData should give io.EOF. n, err = ReadData(&enc, p[:]) if err != io.EOF { t.Fatalf("got error %v, expected %v", err, io.EOF) } } // Test that even when the result is truncated, ReadData fills the provided // buffer as much as possible (and not stop at the boundary of an internal Read, // say). func TestReadDataTruncateFull(t *testing.T) { pr, pw := io.Pipe() go func() { // Send one data chunk that will be delivered across two Read // calls. pw.Write([]byte{0x8a, 'h', 'e', 'l', 'l', 'o'}) pw.Write([]byte{'w', 'o', 'r', 'l', 'd'}) }() var p [8]byte n, err := ReadData(pr, p[:]) if err != io.ErrShortBuffer { t.Fatalf("got error %v, expected %v", err, io.ErrShortBuffer) } // Should not stop after "hello". if !bytes.Equal(p[:n], []byte("hellowor")) { t.Fatalf("got <%x>, expected <%x>", p[:n], []byte("hellowor")) } } // Benchmark the ReadData function when reading from a stream of data packets of // different sizes. func BenchmarkReadData(b *testing.B) { pr, pw := io.Pipe() go func() { for { for length := 0; length < 128; length++ { WriteData(pw, paddingBuffer[:length]) } } }() var p [128]byte for i := 0; i < b.N; i++ { _, err := ReadData(pr, p[:]) if err != nil { b.Fatal(err) } } }