Initial commit.
[BoarSSL] / SSLTLS / InputRecord.cs
1 /*
2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 using System;
26 using System.IO;
27
28 namespace SSLTLS {
29
30 internal class InputRecord {
31
32 Stream sub;
33 byte[] buffer;
34 int recordPtr, recordEnd;
35 int recordType;
36 int recordVersion;
37 int expectedVersion;
38 RecordDecrypt rdec;
39
40 /*
41 * Get the length (in bytes) of the data that remains to be
42 * read from the received buffer.
43 */
44 internal int BufferedLength {
45 get {
46 return recordEnd - recordPtr;
47 }
48 }
49
50 /*
51 * Get the current record type (-1 if no record was read yet).
52 */
53 internal int RecordType {
54 get {
55 return recordType;
56 }
57 }
58
59 /*
60 * Get the current record version (-1 if no record was read yet).
61 */
62 internal int RecordVersion {
63 get {
64 return recordVersion;
65 }
66 }
67
68 internal InputRecord(Stream sub)
69 {
70 this.sub = sub;
71 buffer = new byte[16384 + 500];
72 recordPtr = 0;
73 recordEnd = 0;
74 recordType = -1;
75 recordVersion = -1;
76 expectedVersion = -1;
77 rdec = new RecordDecryptPlain();
78 }
79
80 /*
81 * Set the expected version. If this value is nonnegative, then
82 * all subsequent records are expected to match this version; a
83 * version mismatch will trigger an exception.
84 *
85 * If not initially set explicitly, then this value is automatically
86 * set to the version of the first incoming record.
87 */
88 internal void SetExpectedVersion(int expectedVersion)
89 {
90 this.expectedVersion = expectedVersion;
91 }
92
93 /*
94 * Set the new decryption engine. This is possible only if the
95 * end of the current record was reached.
96 */
97 internal void SetDecryption(RecordDecrypt rdec)
98 {
99 if (recordPtr != recordEnd) {
100 throw new SSLException(
101 "Cannot switch encryption: buffered data");
102 }
103 this.rdec = rdec;
104 }
105
106 /*
107 * Get next record. Returned value is false if EOF was reached
108 * before obtaining the first record header byte.
109 */
110 internal bool NextRecord()
111 {
112 if (!IO.ReadAll(sub, buffer, 0, 5, true)) {
113 return false;
114 }
115 recordType = buffer[0];
116 recordVersion = IO.Dec16be(buffer, 1);
117 int len = IO.Dec16be(buffer, 3);
118 if (expectedVersion >= 0 && expectedVersion != recordVersion) {
119 throw new SSLException(string.Format(
120 "Wrong record version: 0x{0:X4}"
121 + " (expected: 0x{1:X4})",
122 recordVersion, expectedVersion));
123 } else {
124 if ((recordVersion >> 8) != 0x03) {
125 throw new SSLException(string.Format(
126 "Unsupported record version: 0x{0:X4}",
127 recordVersion));
128 }
129 if (expectedVersion < 0) {
130 expectedVersion = recordVersion;
131 }
132 }
133 if (!rdec.CheckLength(len)) {
134 throw new SSLException("Wrong record length: " + len);
135 }
136 IO.ReadAll(sub, buffer, 0, len, false);
137 int off = 0;
138 if (!rdec.Decrypt(recordType, recordVersion,
139 buffer, ref off, ref len))
140 {
141 throw new SSLException("Decryption failure");
142 }
143 recordPtr = off;
144 recordEnd = off + len;
145 return true;
146 }
147
148 /*
149 * Read the next byte from the current record. -1 is returned if
150 * the current record is finished.
151 */
152 internal int Read()
153 {
154 if (recordPtr == recordEnd) {
155 return -1;
156 } else {
157 return buffer[recordPtr ++];
158 }
159 }
160
161 /*
162 * Read some bytes from the current record. The number of
163 * obtained bytes is returned; a short count (including 0)
164 * is possible only if the end of the current record was
165 * reached.
166 */
167 internal int Read(byte[] buf)
168 {
169 return Read(buf, 0, buf.Length);
170 }
171
172 /*
173 * Read some bytes from the current record. The number of
174 * obtained bytes is returned; a short count (including 0)
175 * is possible only if the end of the current record was
176 * reached.
177 */
178 internal int Read(byte[] buf, int off, int len)
179 {
180 int clen = Math.Min(len, recordEnd - recordPtr);
181 Array.Copy(buffer, recordPtr, buf, off, clen);
182 recordPtr += clen;
183 return clen;
184 }
185 }
186
187 }