1// Copyright (c) 2019, Google Inc. 2// 3// Permission to use, copy, modify, and/or distribute this software for any 4// purpose with or without fee is hereby granted, provided that the above 5// copyright notice and this permission notice appear in all copies. 6// 7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15package runner 16 17import ( 18 "bytes" 19 "encoding/binary" 20 "fmt" 21 "io" 22 "net" 23) 24 25type encryptionLevel byte 26 27const ( 28 encryptionInitial encryptionLevel = 0 29 encryptionEarlyData encryptionLevel = 1 30 encryptionHandshake encryptionLevel = 2 31 encryptionApplication encryptionLevel = 3 32) 33 34// mockQUICTransport provides a record layer for sending/receiving messages 35// when testing TLS over QUIC. It is only intended for testing, as it runs over 36// an in-order reliable transport, looks nothing like the QUIC wire image, and 37// provides no confidentiality guarantees. (In fact, it leaks keys in the 38// clear.) 39// 40// Messages from TLS that are sent over a mockQUICTransport are a series of 41// records in the following format: 42// 43// enum { 44// initial(0), early_data(1), handshake(2), application(3), (255) 45// } EncryptionLevel; 46// 47// struct { 48// ContentType record_type; 49// EncryptionLevel level; 50// CipherSuite cipher_suite; 51// opaque encrypted_record<0..2^32-1>; 52// } MockQUICRecord; 53// 54// The "encrypted" record is the concatenation of the encryption key and 55// plaintext. It and the cipher suite exist only to check both sides agree on 56// encryption parameters. The key is included in the length prefix so records 57// may be skipped without knowing the key length. 58type mockQUICTransport struct { 59 net.Conn 60 readLevel, writeLevel encryptionLevel 61 readSecret, writeSecret []byte 62 readCipherSuite, writeCipherSuite uint16 63 skipEarlyData bool 64} 65 66func newMockQUICTransport(conn net.Conn) *mockQUICTransport { 67 return &mockQUICTransport{Conn: conn} 68} 69 70func (m *mockQUICTransport) read() (recordType, []byte, error) { 71 for { 72 header := make([]byte, 8) 73 if _, err := io.ReadFull(m.Conn, header); err != nil { 74 return 0, nil, err 75 } 76 typ := recordType(header[0]) 77 level := header[1] 78 cipherSuite := binary.BigEndian.Uint16(header[2:4]) 79 length := binary.BigEndian.Uint32(header[4:]) 80 value := make([]byte, length) 81 if _, err := io.ReadFull(m.Conn, value); err != nil { 82 return 0, nil, fmt.Errorf("error reading record") 83 } 84 if level != byte(m.readLevel) { 85 if m.skipEarlyData && level == byte(encryptionEarlyData) { 86 continue 87 } 88 return 0, nil, fmt.Errorf("received level %d does not match expected %d", level, m.readLevel) 89 } 90 if cipherSuite != m.readCipherSuite { 91 return 0, nil, fmt.Errorf("received cipher suite %d does not match expected %d", cipherSuite, m.readCipherSuite) 92 } 93 if len(m.readSecret) > len(value) { 94 return 0, nil, fmt.Errorf("input length too short") 95 } 96 secret := value[:len(m.readSecret)] 97 out := value[len(m.readSecret):] 98 if !bytes.Equal(secret, m.readSecret) { 99 return 0, nil, fmt.Errorf("secrets don't match: got %x but expected %x", secret, m.readSecret) 100 } 101 // Although not true for QUIC in general, our transport is ordered, so 102 // we expect to stop skipping early data after a valid record. 103 m.skipEarlyData = false 104 return typ, out, nil 105 } 106} 107 108func (m *mockQUICTransport) readRecord(want recordType) (recordType, *block, error) { 109 typ, contents, err := m.read() 110 if err != nil { 111 return 0, nil, err 112 } 113 return typ, &block{contents, 0, nil}, nil 114} 115 116func (m *mockQUICTransport) writeRecord(typ recordType, data []byte) (int, error) { 117 if typ != recordTypeApplicationData && typ != recordTypeHandshake { 118 return 0, fmt.Errorf("unsupported record type %d\n", typ) 119 } 120 length := len(m.writeSecret) + len(data) 121 payload := make([]byte, 1+1+2+4+length) 122 payload[0] = byte(typ) 123 payload[1] = byte(m.writeLevel) 124 binary.BigEndian.PutUint16(payload[2:4], m.writeCipherSuite) 125 binary.BigEndian.PutUint32(payload[4:8], uint32(length)) 126 copy(payload[8:], m.writeSecret) 127 copy(payload[8+len(m.writeSecret):], data) 128 if _, err := m.Conn.Write(payload); err != nil { 129 return 0, err 130 } 131 return len(data), nil 132} 133 134func (m *mockQUICTransport) Write(b []byte) (int, error) { 135 panic("unexpected call to Write") 136} 137 138func (m *mockQUICTransport) Read(b []byte) (int, error) { 139 panic("unexpected call to Read") 140} 141