2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
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:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
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
32 internal class RecordDecryptCBC : RecordDecrypt {
41 internal RecordDecryptCBC(IBlockCipher bc, HMAC hm, byte[] iv)
45 this.iv = new byte[bc.BlockSize];
46 this.ivTmp = new byte[bc.BlockSize];
50 Array.Copy(iv, 0, this.iv, 0, iv.Length);
54 tmp1 = new byte[Math.Max(13, hm.MACSize)];
55 tmp2 = new byte[Math.Max(13, hm.MACSize)];
58 internal override bool CheckLength(int len)
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.
66 int blen = bc.BlockSize;
67 int hlen = hm.MACSize;
68 if ((len & (blen - 1)) != 0) {
71 int minLen = hlen + 1;
72 int maxLen = (16384 + 256 + hlen) & ~(blen - 1);
77 return len >= minLen && len <= maxLen;
80 internal override bool Decrypt(int recordType, int version,
81 byte[] data, ref int off, ref int len)
83 int blen = bc.BlockSize;
84 int hlen = hm.MACSize;
87 * Grab a copy of the last encrypted block; this is
88 * the "saved IV" for the next record.
90 Array.Copy(data, off + len - blen, ivTmp, 0, blen);
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.
97 bc.CBCDecrypt(iv, data, off, len);
98 Array.Copy(ivTmp, 0, iv, 0, blen);
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.
109 int minLen = (hlen + 256 < len) ? len - 256 : hlen;
110 int maxLen = len - 1;
113 * Get the actual padding length and check padding. The
114 * padding length must match the minLen/maxLen range.
116 int padLen = data[off + len - 1];
117 int good = ~(((maxLen - minLen) - padLen) >> 31);
118 int lenWithMAC = minLen ^ (good & (minLen ^ (maxLen - padLen)));
120 for (int i = minLen; i < maxLen; i ++) {
121 dbb |= ~((i - lenWithMAC) >> 31)
122 & (data[off + i] ^ padLen);
124 good &= ~((dbb | -dbb) >> 31);
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[].
132 int lenNoMAC = lenWithMAC - hlen;
135 for (int i = 0; i < hlen; i ++) {
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]);
144 rotCount |= ~((m | -m) >> 31) & v;
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.
156 for (int i = 5; i >= 0; i --) {
161 int ctl = -((rotCount >> i) & 1);
162 for (int j = 0, k = rc; j < hlen; j ++) {
165 tmp2[j] = (byte)(b1 ^ (ctl & (b1 ^ b2)));
170 Array.Copy(tmp2, 0, tmp1, 0, hlen);
175 * Recompute the HMAC value. At that point, minLen and
176 * maxLen have been adjusted to match the plaintext
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);
185 * Compare MAC values.
188 for (int i = 0; i < hlen; i ++) {
189 dbb |= tmp1[i] ^ tmp2[i];
191 good &= ~((dbb | -dbb) >> 31);
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).
198 good &= (lenNoMAC - 16385) >> 31;