public virtual byte[] EncodePlaintext(ContentType type, byte[] plaintext, int offset, int len) { int blocksize = encryptCipher.GetBlockSize(); // Add a random number of extra blocks worth of padding int minPaddingSize = blocksize - ((len + writeMac.Size + 1) % blocksize); int maxExtraPadBlocks = (255 - minPaddingSize) / blocksize; int actualExtraPadBlocks = ChooseExtraPadBlocks(context.SecureRandom, maxExtraPadBlocks); int paddingsize = minPaddingSize + (actualExtraPadBlocks * blocksize); int totalsize = len + writeMac.Size + paddingsize + 1; byte[] outbuf = new byte[totalsize]; Array.Copy(plaintext, offset, outbuf, 0, len); byte[] mac = writeMac.CalculateMac(type, plaintext, offset, len); Array.Copy(mac, 0, outbuf, len, mac.Length); int paddoffset = len + mac.Length; for (int i = 0; i <= paddingsize; i++) { outbuf[i + paddoffset] = (byte)paddingsize; } for (int i = 0; i < totalsize; i += blocksize) { encryptCipher.ProcessBlock(outbuf, i, outbuf, i); } return(outbuf); }
public virtual byte[] DecodeCiphertext(ContentType type, byte[] ciphertext, int offset, int len) { // TODO TLS 1.1 (RFC 4346) introduces an explicit IV int minLength = readMac.Size + 1; int blocksize = decryptCipher.GetBlockSize(); bool decrypterror = false; /* * ciphertext must be at least (macsize + 1) bytes long */ if (len < minLength) { throw new TlsFatalAlert(AlertDescription.decode_error); } /* * ciphertext must be a multiple of blocksize */ if (len % blocksize != 0) { throw new TlsFatalAlert(AlertDescription.decryption_failed); } /* * Decrypt all the ciphertext using the blockcipher */ for (int i = 0; i < len; i += blocksize) { decryptCipher.ProcessBlock(ciphertext, i + offset, ciphertext, i + offset); } /* * Check if padding is correct */ int lastByteOffset = offset + len - 1; byte paddingsizebyte = ciphertext[lastByteOffset]; int paddingsize = paddingsizebyte; int maxPaddingSize = len - minLength; if (paddingsize > maxPaddingSize) { decrypterror = true; paddingsize = 0; } else { /* * Now, check all the padding-bytes (constant-time comparison). */ byte diff = 0; for (int i = lastByteOffset - paddingsize; i < lastByteOffset; ++i) { diff |= (byte)(ciphertext[i] ^ paddingsizebyte); } if (diff != 0) { /* Wrong padding */ decrypterror = true; paddingsize = 0; } } /* * We now don't care if padding verification has failed or not, we will calculate * the mac to give an attacker no kind of timing profile he can use to find out if * mac verification failed or padding verification failed. */ int plaintextlength = len - minLength - paddingsize; byte[] calculatedMac = readMac.CalculateMac(type, ciphertext, offset, plaintextlength); /* * Check all bytes in the mac (constant-time comparison). */ byte[] decryptedMac = new byte[calculatedMac.Length]; Array.Copy(ciphertext, offset + plaintextlength, decryptedMac, 0, calculatedMac.Length); if (!Arrays.ConstantTimeAreEqual(calculatedMac, decryptedMac)) { decrypterror = true; } /* * Now, it is safe to fail. */ if (decrypterror) { throw new TlsFatalAlert(AlertDescription.bad_record_mac); } byte[] plaintext = new byte[plaintextlength]; Array.Copy(ciphertext, offset, plaintext, 0, plaintextlength); return(plaintext); }