131 |
131 |
132 HandshakeHash getHandshakeHash() { |
132 HandshakeHash getHandshakeHash() { |
133 return handshakeHash; |
133 return handshakeHash; |
134 } |
134 } |
135 |
135 |
136 /* |
136 void decrypt(MAC signer, CipherBox box) throws BadPaddingException { |
137 * Verify and remove the MAC ... used for all records. |
137 |
138 */ |
138 BadPaddingException reservedBPE = null; |
139 boolean checkMAC(MAC signer) { |
139 int tagLen = signer.MAClen(); |
140 int len = signer.MAClen(); |
140 int cipheredLength = count - headerSize; |
141 if (len == 0) { // no mac |
141 |
142 return true; |
142 if (!box.isNullCipher()) { |
143 } |
143 // sanity check length of the ciphertext |
144 |
144 if (!box.sanityCheck(tagLen, cipheredLength)) { |
145 int offset = count - len; |
145 throw new BadPaddingException( |
146 |
146 "ciphertext sanity check failed"); |
147 if (offset < headerSize) { |
147 } |
148 // data length would be negative, something is wrong |
148 |
149 return false; |
149 try { |
150 } |
150 // Note that the CipherBox.decrypt() does not change |
151 |
151 // the capacity of the buffer. |
152 byte[] mac = signer.compute(contentType(), buf, |
152 count = headerSize + |
153 headerSize, offset - headerSize); |
153 box.decrypt(buf, headerSize, cipheredLength, tagLen); |
154 |
154 } catch (BadPaddingException bpe) { |
155 if (len != mac.length) { |
155 // RFC 2246 states that decryption_failed should be used |
|
156 // for this purpose. However, that allows certain attacks, |
|
157 // so we just send bad record MAC. We also need to make |
|
158 // sure to always check the MAC to avoid a timing attack |
|
159 // for the same issue. See paper by Vaudenay et al and the |
|
160 // update in RFC 4346/5246. |
|
161 // |
|
162 // Failover to message authentication code checking. |
|
163 reservedBPE = bpe; |
|
164 } |
|
165 } |
|
166 |
|
167 if (tagLen != 0) { |
|
168 int macOffset = count - tagLen; |
|
169 int contentLen = macOffset - headerSize; |
|
170 |
|
171 // Note that although it is not necessary, we run the same MAC |
|
172 // computation and comparison on the payload for both stream |
|
173 // cipher and CBC block cipher. |
|
174 if (contentLen < 0) { |
|
175 // negative data length, something is wrong |
|
176 if (reservedBPE == null) { |
|
177 reservedBPE = new BadPaddingException("bad record"); |
|
178 } |
|
179 |
|
180 // set offset of the dummy MAC |
|
181 macOffset = headerSize + cipheredLength - tagLen; |
|
182 contentLen = macOffset - headerSize; |
|
183 } |
|
184 |
|
185 count -= tagLen; // Set the count before any MAC checking |
|
186 // exception occurs, so that the following |
|
187 // process can read the actual decrypted |
|
188 // content (minus the MAC) in the fragment |
|
189 // if necessary. |
|
190 |
|
191 // Run MAC computation and comparison on the payload. |
|
192 if (checkMacTags(contentType(), |
|
193 buf, headerSize, contentLen, signer, false)) { |
|
194 if (reservedBPE == null) { |
|
195 reservedBPE = new BadPaddingException("bad record MAC"); |
|
196 } |
|
197 } |
|
198 |
|
199 // Run MAC computation and comparison on the remainder. |
|
200 // |
|
201 // It is only necessary for CBC block cipher. It is used to get a |
|
202 // constant time of MAC computation and comparison on each record. |
|
203 if (box.isCBCMode()) { |
|
204 int remainingLen = calculateRemainingLen( |
|
205 signer, cipheredLength, contentLen); |
|
206 |
|
207 // NOTE: remainingLen may be bigger (less than 1 block of the |
|
208 // hash algorithm of the MAC) than the cipheredLength. However, |
|
209 // We won't need to worry about it because we always use a |
|
210 // maximum buffer for every record. We need a change here if |
|
211 // we use small buffer size in the future. |
|
212 if (remainingLen > buf.length) { |
|
213 // unlikely to happen, just a placehold |
|
214 throw new RuntimeException( |
|
215 "Internal buffer capacity error"); |
|
216 } |
|
217 |
|
218 // Won't need to worry about the result on the remainder. And |
|
219 // then we won't need to worry about what's actual data to |
|
220 // check MAC tag on. We start the check from the header of the |
|
221 // buffer so that we don't need to construct a new byte buffer. |
|
222 checkMacTags(contentType(), buf, 0, remainingLen, signer, true); |
|
223 } |
|
224 } |
|
225 |
|
226 // Is it a failover? |
|
227 if (reservedBPE != null) { |
|
228 throw reservedBPE; |
|
229 } |
|
230 } |
|
231 |
|
232 /* |
|
233 * Run MAC computation and comparison |
|
234 * |
|
235 * Please DON'T change the content of the byte buffer parameter! |
|
236 */ |
|
237 static boolean checkMacTags(byte contentType, byte[] buffer, |
|
238 int offset, int contentLen, MAC signer, boolean isSimulated) { |
|
239 |
|
240 int tagLen = signer.MAClen(); |
|
241 byte[] hash = signer.compute( |
|
242 contentType, buffer, offset, contentLen, isSimulated); |
|
243 if (hash == null || tagLen != hash.length) { |
|
244 // Something is wrong with MAC implementation. |
156 throw new RuntimeException("Internal MAC error"); |
245 throw new RuntimeException("Internal MAC error"); |
157 } |
246 } |
158 |
247 |
159 for (int i = 0; i < len; i++) { |
248 int[] results = compareMacTags(buffer, offset + contentLen, hash); |
160 if (buf[offset + i] != mac[i]) { |
249 return (results[0] != 0); |
161 return false; |
250 } |
162 } |
251 |
163 } |
252 /* |
164 count -= len; |
253 * A constant-time comparison of the MAC tags. |
165 return true; |
254 * |
166 } |
255 * Please DON'T change the content of the byte buffer parameter! |
167 |
256 */ |
168 void decrypt(CipherBox box) throws BadPaddingException { |
257 private static int[] compareMacTags( |
169 int len = count - headerSize; |
258 byte[] buffer, int offset, byte[] tag) { |
170 count = headerSize + box.decrypt(buf, headerSize, len); |
259 |
171 } |
260 // An array of hits is used to prevent Hotspot optimization for |
172 |
261 // the purpose of a constant-time check. |
|
262 int[] results = {0, 0}; // {missed #, matched #} |
|
263 |
|
264 // The caller ensures there are enough bytes available in the buffer. |
|
265 // So we won't need to check the length of the buffer. |
|
266 for (int i = 0; i < tag.length; i++) { |
|
267 if (buffer[offset + i] != tag[i]) { |
|
268 results[0]++; // mismatched bytes |
|
269 } else { |
|
270 results[1]++; // matched bytes |
|
271 } |
|
272 } |
|
273 |
|
274 return results; |
|
275 } |
|
276 |
|
277 /* |
|
278 * Calculate the length of a dummy buffer to run MAC computation |
|
279 * and comparison on the remainder. |
|
280 * |
|
281 * The caller MUST ensure that the fullLen is not less than usedLen. |
|
282 */ |
|
283 static int calculateRemainingLen( |
|
284 MAC signer, int fullLen, int usedLen) { |
|
285 |
|
286 int blockLen = signer.hashBlockLen(); |
|
287 int minimalPaddingLen = signer.minimalPaddingLen(); |
|
288 |
|
289 // (blockLen - minimalPaddingLen) is the maximum message size of |
|
290 // the last block of hash function operation. See FIPS 180-4, or |
|
291 // MD5 specification. |
|
292 fullLen += 13 - (blockLen - minimalPaddingLen); |
|
293 usedLen += 13 - (blockLen - minimalPaddingLen); |
|
294 |
|
295 // Note: fullLen is always not less than usedLen, and blockLen |
|
296 // is always bigger than minimalPaddingLen, so we don't worry |
|
297 // about negative values. 0x01 is added to the result to ensure |
|
298 // that the return value is positive. The extra one byte does |
|
299 // not impact the overall MAC compression function evaluations. |
|
300 return 0x01 + (int)(Math.ceil(fullLen/(1.0d * blockLen)) - |
|
301 Math.ceil(usedLen/(1.0d * blockLen))) * signer.hashBlockLen(); |
|
302 } |
173 |
303 |
174 /* |
304 /* |
175 * Well ... hello_request messages are _never_ hashed since we can't |
305 * Well ... hello_request messages are _never_ hashed since we can't |
176 * know when they'd appear in the sequence. |
306 * know when they'd appear in the sequence. |
177 */ |
307 */ |