Initial commit.
[BoarSSL] / SSLTLS / RecordDecryptCBC.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.Text;
27
28 using Crypto;
29
30 namespace SSLTLS {
31
32 internal class RecordDecryptCBC : RecordDecrypt {
33
34 IBlockCipher bc;
35 HMAC hm;
36 byte[] iv, ivTmp;
37 bool explicitIV;
38 ulong seq;
39 byte[] tmp1, tmp2;
40
41 internal RecordDecryptCBC(IBlockCipher bc, HMAC hm, byte[] iv)
42 {
43 this.bc = bc;
44 this.hm = hm;
45 this.iv = new byte[bc.BlockSize];
46 this.ivTmp = new byte[bc.BlockSize];
47 if (iv == null) {
48 explicitIV = true;
49 } else {
50 Array.Copy(iv, 0, this.iv, 0, iv.Length);
51 explicitIV = false;
52 }
53 seq = 0;
54 tmp1 = new byte[Math.Max(13, hm.MACSize)];
55 tmp2 = new byte[Math.Max(13, hm.MACSize)];
56 }
57
58 internal override bool CheckLength(int len)
59 {
60 /*
61 * Record length (not counting the header) must be
62 * a multiple of the block size, and have enough room
63 * for the MAC and the padding-length byte. With
64 * TLS 1.1+, there must also be an explicit IV.
65 */
66 int blen = bc.BlockSize;
67 int hlen = hm.MACSize;
68 if ((len & (blen - 1)) != 0) {
69 return false;
70 }
71 int minLen = hlen + 1;
72 int maxLen = (16384 + 256 + hlen) & ~(blen - 1);
73 if (explicitIV) {
74 minLen += blen;
75 maxLen += blen;
76 }
77 return len >= minLen && len <= maxLen;
78 }
79
80 internal override bool Decrypt(int recordType, int version,
81 byte[] data, ref int off, ref int len)
82 {
83 int blen = bc.BlockSize;
84 int hlen = hm.MACSize;
85
86 /*
87 * Grab a copy of the last encrypted block; this is
88 * the "saved IV" for the next record.
89 */
90 Array.Copy(data, off + len - blen, ivTmp, 0, blen);
91
92 /*
93 * Decrypt the data. The length has already been
94 * checked. If there is an explicit IV, it gets
95 * "decrypted" as well, which is not a problem.
96 */
97 bc.CBCDecrypt(iv, data, off, len);
98 Array.Copy(ivTmp, 0, iv, 0, blen);
99 if (explicitIV) {
100 off += blen;
101 len -= blen;
102 }
103
104 /*
105 * Compute minimum and maximum length of plaintext + MAC.
106 * These can be inferred from the observable record length,
107 * and thus are not secret.
108 */
109 int minLen = (hlen + 256 < len) ? len - 256 : hlen;
110 int maxLen = len - 1;
111
112 /*
113 * Get the actual padding length and check padding. The
114 * padding length must match the minLen/maxLen range.
115 */
116 int padLen = data[off + len - 1];
117 int good = ~(((maxLen - minLen) - padLen) >> 31);
118 int lenWithMAC = minLen ^ (good & (minLen ^ (maxLen - padLen)));
119 int dbb = 0;
120 for (int i = minLen; i < maxLen; i ++) {
121 dbb |= ~((i - lenWithMAC) >> 31)
122 & (data[off + i] ^ padLen);
123 }
124 good &= ~((dbb | -dbb) >> 31);
125
126 /*
127 * Extract the MAC value; this is done in one pass, but
128 * results in a "rotate" MAC value. The rotation count
129 * is kept in 'rotCount': this is the offset of the
130 * first MAC value byte in tmp1[].
131 */
132 int lenNoMAC = lenWithMAC - hlen;
133 minLen -= hlen;
134 int rotCount = 0;
135 for (int i = 0; i < hlen; i ++) {
136 tmp1[i] = 0;
137 }
138 int v = 0;
139 for (int i = minLen; i < maxLen; i ++) {
140 int m = ~((i - lenNoMAC) >> 31)
141 & ((i - lenWithMAC) >> 31);
142 tmp1[v] |= (byte)(m & data[off + i]);
143 m = i - lenNoMAC;
144 rotCount |= ~((m | -m) >> 31) & v;
145 if (++ v == hlen) {
146 v = 0;
147 }
148 }
149 maxLen -= hlen;
150
151 /*
152 * Rotate back the MAC value. We do it bit by bit, with
153 * 6 iterations; this is good for all MAC value up to
154 * and including 64 bytes.
155 */
156 for (int i = 5; i >= 0; i --) {
157 int rc = 1 << i;
158 if (rc >= hlen) {
159 continue;
160 }
161 int ctl = -((rotCount >> i) & 1);
162 for (int j = 0, k = rc; j < hlen; j ++) {
163 int b1 = tmp1[j];
164 int b2 = tmp1[k];
165 tmp2[j] = (byte)(b1 ^ (ctl & (b1 ^ b2)));
166 if (++ k == hlen) {
167 k = 0;
168 }
169 }
170 Array.Copy(tmp2, 0, tmp1, 0, hlen);
171 rotCount &= ~rc;
172 }
173
174 /*
175 * Recompute the HMAC value. At that point, minLen and
176 * maxLen have been adjusted to match the plaintext
177 * without the MAC.
178 */
179 IO.Enc64be(seq ++, tmp2, 0);
180 IO.WriteHeader(recordType, version, lenNoMAC, tmp2, 8);
181 hm.Update(tmp2, 0, 13);
182 hm.ComputeCT(data, off, lenNoMAC, minLen, maxLen, tmp2, 0);
183
184 /*
185 * Compare MAC values.
186 */
187 dbb = 0;
188 for (int i = 0; i < hlen; i ++) {
189 dbb |= tmp1[i] ^ tmp2[i];
190 }
191 good &= ~((dbb | -dbb) >> 31);
192
193 /*
194 * We must also check that the plaintext length fits in
195 * the maximum allowed by the standard (previous check
196 * was on the encrypted length).
197 */
198 good &= (lenNoMAC - 16385) >> 31;
199 len = lenNoMAC;
200 return good != 0;
201 }
202 }
203
204 }