protected override int Decrypt(DisposeContext d, ContentType contentType, IBufferOffsetSize input, IBufferOffsetSize output) { if ((input.Size % BlockSize) != 0) { return(-1); } if (input.Size < MinExtraEncryptedBytes) { return(-1); } var plen = DecryptRecord(d, input, output); if (plen <= 0) { return(-1); } var padlen = output.Buffer [output.Offset + plen - 1]; #if DEBUG_FULL if (Cipher.EnableDebugging) { DebugHelper.WriteLine("DECRYPT: {0} {1} {2}", input.Size, plen, padlen); DebugHelper.WriteBuffer("DECRYPTED", output.Buffer, output.Offset, plen); } #endif /* * VERY IMPORTANT: * * The Compiler and JIT *** MUST NOT *** optimize the following block of code. * * It is essential that the dummy checks and dummy calls be kept in place. * Also do not put any debugging code into that region as it would mess up with * the timing. * */ #region The following block of code *** MUST NOT *** be optimized in any way if (MacSize + padlen + 1 > plen) { // Invalid padding: plaintext is not long enough. // First run a loop as if there were 256 bytes of padding, with a dummy check. int ok = -1; for (int i = 0; i < 256; i++) { if (output.Buffer [i % output.Size] != padlen) { ok--; } } // Now assume there's no padding, compute the MAC over the entire buffer. var first = new BufferOffsetSize(output.Buffer, output.Offset, plen - MacSize); var invalidMac = ComputeRecordMAC(contentType, first); // Constant-time compare - this will always fail, TlsBuffer.ConstantTimeCompare() will return a negative value on error. ok += TlsBuffer.ConstantTimeCompare(invalidMac, 0, invalidMac.Length, output.Buffer, output.Offset + plen - MacSize, MacSize); return(ok); } else { int ok = 0; var resultLength = plen - padlen - MacSize - 1; for (int i = 0; i < padlen; i++) { if (output.Buffer [output.Offset + resultLength + MacSize + i] != padlen) { ok--; } } var dummyOk = ok; var dummyLen = 256 - padlen - 1; for (int i = 0; i < dummyLen; i++) { if (output.Buffer [i % output.Size] != padlen) { dummyOk--; } } if (ok < 0) { // Now assume there's no padding, compute the MAC over the entire buffer. var first = new BufferOffsetSize(output.Buffer, output.Offset, plen - MacSize); var invalidMac = ComputeRecordMAC(contentType, first); // Constant-time compare - this will always fail, TlsBuffer.ConstantTimeCompare() will return a negative value on error. ok += TlsBuffer.ConstantTimeCompare(invalidMac, 0, invalidMac.Length, output.Buffer, output.Offset + plen - MacSize, MacSize); return(ok); } else { var first = new BufferOffsetSize(output.Buffer, output.Offset, resultLength); var checkMac = ComputeRecordMAC(contentType, first); var L1 = 13 + plen - MacSize; var L2 = 13 + plen - padlen - 1 - MacSize; var additional = ((L1 - 55) / 64) - ((L2 - 55) / 64); if (additional > 0) { var algorithm = HMac.CreateHash(Cipher.HashAlgorithmType); for (int i = 0; i < additional; i++) { algorithm.TransformBlock(input.Buffer, input.Offset, BlockSize, null, 0); } } ok += TlsBuffer.ConstantTimeCompare(checkMac, 0, checkMac.Length, output.Buffer, output.Offset + resultLength, MacSize); if (ok == 0) { ok = resultLength; } return(ok); } } #endregion }