private static void EncryptRecord(CipherSuite cipherSuite, Record record, ICryptoTransform cipher, byte[] nonceExplicit) { BulkCipherAlgorithmType cipherType = cipherSuite.BulkCipherAlgorithm.Type; int recordIVLength = cipherSuite.BulkCipherAlgorithm.RecordIVLength; // Add explicit IV if required by protocol version if (cipherType == BulkCipherAlgorithmType.Block && record.Version.HasExplicitIV) { byte[] explicitIV = new byte[recordIVLength]; RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider(); rngCsp.GetBytes(explicitIV); // Replace the fragment with a new fragment including explicit IV byte[] fragment = new byte[explicitIV.Length + record.Fragment.Length]; Buffer.BlockCopy(explicitIV, 0, fragment, 0, explicitIV.Length); Buffer.BlockCopy(record.Fragment, 0, fragment, explicitIV.Length, record.Fragment.Length); record.Fragment = fragment; } // Replace the unencrypted fragment with the encrypted fragment record.Fragment = TransformRecordBytes(cipherType, cipher, record.Fragment); // Add explicit part of the nonce if using AEAD if (cipherType == BulkCipherAlgorithmType.AEAD) { byte[] fragment = new byte[nonceExplicit.Length + record.Fragment.Length]; Buffer.BlockCopy(nonceExplicit, 0, fragment, 0, nonceExplicit.Length); Buffer.BlockCopy(record.Fragment, 0, fragment, nonceExplicit.Length, record.Fragment.Length); record.Fragment = fragment; } }
private static byte[] GenerateMAC(CipherSuite cipherSuite, Record record, UInt64 seqNum, KeyedHashAlgorithm hasher) { BulkCipherAlgorithmType cipherType = cipherSuite.BulkCipherAlgorithm.Type; if (cipherType == BulkCipherAlgorithmType.Stream || cipherType == BulkCipherAlgorithmType.Block) { byte[] additional = GetAdditionalBytes(seqNum, (byte)record.Type, record.Version, record.Fragment.Length); byte[] data = new byte[additional.Length + record.Fragment.Length]; Buffer.BlockCopy(additional, 0, data, 0, additional.Length); Buffer.BlockCopy(record.Fragment, 0, data, additional.Length, record.Fragment.Length); Log.Trace("MAC data bytes: " + BitConverter.ToString(data)); // Calculate the MAC of the packet hasher.Initialize(); var MAC = hasher.ComputeHash(data); /* Add MAC to the end of the fragment */ byte[] fragment = new byte[record.Fragment.Length + MAC.Length]; Buffer.BlockCopy(record.Fragment, 0, fragment, 0, record.Fragment.Length); Buffer.BlockCopy(MAC, 0, fragment, record.Fragment.Length, MAC.Length); record.Fragment = fragment; return(MAC); } return(new byte[0]); }
private static byte[] TransformRecordBytes(BulkCipherAlgorithmType cipherType, ICryptoTransform transform, byte[] input) { if (cipherType != BulkCipherAlgorithmType.AEAD) { // In case of non-AEAD cipher algorithm, check that data matches block size if (input.Length % transform.InputBlockSize != 0) { throw new Exception("Input data size doesn't match block size"); } } int blockCount = input.Length / transform.InputBlockSize; if (cipherType == BulkCipherAlgorithmType.AEAD) { // Make sure there is enough data at TransformFinalBlock, because // decryption requires that the authentication tag is present if (blockCount > 0) { blockCount--; } } byte[] output = new byte[blockCount * transform.OutputBlockSize]; if (transform.CanTransformMultipleBlocks) { transform.TransformBlock(input, 0, blockCount * transform.InputBlockSize, output, 0); } else { for (int i = 0; i < blockCount; i++) { transform.TransformBlock(input, i * transform.InputBlockSize, transform.InputBlockSize, output, i * transform.OutputBlockSize); } } if (cipherType == BulkCipherAlgorithmType.AEAD) { int currentPosition = blockCount * transform.InputBlockSize; // Transfer the last block when encrypting or authentication tag when decrypting byte[] finalBytes = transform.TransformFinalBlock(input, currentPosition, input.Length - currentPosition); if (finalBytes == null) { return(null); } else if (finalBytes.Length > 0) { byte[] finalOutput = new byte[output.Length + finalBytes.Length]; Buffer.BlockCopy(output, 0, finalOutput, 0, output.Length); Buffer.BlockCopy(finalBytes, 0, finalOutput, output.Length, finalBytes.Length); output = finalOutput; } } return(output); }
private static void GeneratePadding(CipherSuite cipherSuite, Record record) { BulkCipherAlgorithmType cipherType = cipherSuite.BulkCipherAlgorithm.Type; if (cipherType == BulkCipherAlgorithmType.Block) { int blockSize = cipherSuite.BulkCipherAlgorithm.BlockSize; // Add the required padding to the end of fragment if necessary, // minimum padding 1 bytes, the length byte of padding itself int paddingLength = blockSize - (record.Fragment.Length % blockSize); if (record.Version.HasVariablePadding) { // Add 0-1 additional blocks int extra = (new System.Random()).Next(2); if (paddingLength + extra * blockSize < 256) { paddingLength += extra * blockSize; } } // Add the actual padding bytes here byte[] padding = new byte[paddingLength]; if (record.Version == ProtocolVersion.SSL3_0) { RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider(); rngCsp.GetBytes(padding); padding[padding.Length - 1] = (byte)(padding.Length - 1); } else { for (int i = 1; i <= padding.Length; i++) { padding[padding.Length - i] = (byte)(padding.Length - 1); } } byte[] fragment = new byte[record.Fragment.Length + padding.Length]; Buffer.BlockCopy(record.Fragment, 0, fragment, 0, record.Fragment.Length); Buffer.BlockCopy(padding, 0, fragment, record.Fragment.Length, padding.Length); Log.Trace("Padded fragment: " + BitConverter.ToString(fragment)); record.Fragment = fragment; } }
private static bool RemovePadding(CipherSuite cipherSuite, Record record) { BulkCipherAlgorithmType cipherType = cipherSuite.BulkCipherAlgorithm.Type; bool verified = true; if (cipherType == BulkCipherAlgorithmType.Block) { // Get padding bytes from the end of the fragment int padding = record.Fragment[record.Fragment.Length - 1] + 1; // Verify the correctness of padding if (padding > record.Fragment.Length) { verified = false; } else { verified = true; if (record.Version.HasVerifiablePadding) { for (int i = 1; i <= padding; i++) { if (record.Fragment[record.Fragment.Length - i] != (padding - 1)) { verified = false; } } } // Remove padding from the fragment data if (verified) { byte[] fragment = new byte[record.Fragment.Length - padding]; Buffer.BlockCopy(record.Fragment, 0, fragment, 0, fragment.Length); record.Fragment = fragment; } } } return(verified); }
private static void GenerateMAC(CipherSuite cipherSuite, Record record, UInt64 seqNum, KeyedHashAlgorithm hasher) { BulkCipherAlgorithmType cipherType = cipherSuite.BulkCipherAlgorithm.Type; if (cipherType == BulkCipherAlgorithmType.Stream || cipherType == BulkCipherAlgorithmType.Block) { byte[] additional = GetAdditionalBytes(seqNum, record.Type, record.Version, record.Fragment.Length); // Calculate the MAC of the packet hasher.Initialize(); hasher.TransformBlock(additional, 0, additional.Length, additional, 0); hasher.TransformFinalBlock(record.Fragment, 0, record.Fragment.Length); byte[] MAC = hasher.Hash; /* Add MAC to the end of the fragment */ byte[] fragment = new byte[record.Fragment.Length + MAC.Length]; Buffer.BlockCopy(record.Fragment, 0, fragment, 0, record.Fragment.Length); Buffer.BlockCopy(MAC, 0, fragment, record.Fragment.Length, MAC.Length); record.Fragment = fragment; } }
private static bool RemoveMAC(CipherSuite cipherSuite, Record record, UInt64 seqNum, KeyedHashAlgorithm hasher) { BulkCipherAlgorithmType cipherType = cipherSuite.BulkCipherAlgorithm.Type; bool verified = true; if (cipherType == BulkCipherAlgorithmType.Stream || cipherType == BulkCipherAlgorithmType.Block) { int MACLength = cipherSuite.MACAlgorithm.HashSize; if (record.Fragment.Length < MACLength) { verified = false; } else { // Allocate a fragment without the MAC value byte[] newFragment = new byte[record.Fragment.Length - MACLength]; Buffer.BlockCopy(record.Fragment, 0, newFragment, 0, newFragment.Length); // Calculate the MAC again for new fragment byte[] oldFragment = record.Fragment; record.Fragment = newFragment; GenerateMAC(cipherSuite, record, seqNum, hasher); // Compare our MAC value with theirs verified = true; for (int i = 1; i <= MACLength; i++) { if (oldFragment[oldFragment.Length - i] != record.Fragment[record.Fragment.Length - i]) { verified = false; } } // Replace fragment with the one without MAC value record.Fragment = newFragment; } } return(verified); }
private static bool DecryptRecord(CipherSuite cipherSuite, Record record, ICryptoTransform cipher) { BulkCipherAlgorithmType cipherType = cipherSuite.BulkCipherAlgorithm.Type; int recordIVLength = cipherSuite.BulkCipherAlgorithm.RecordIVLength; if (cipherType == BulkCipherAlgorithmType.AEAD) { int authTagSize = cipherSuite.BulkCipherAlgorithm.AuthenticationTagSize; // Remove explicit nonce from the beginning of the fragment byte[] tmp = new byte[record.Fragment.Length - recordIVLength]; Buffer.BlockCopy(record.Fragment, recordIVLength, tmp, 0, tmp.Length); record.Fragment = tmp; // Make sure there is enough data for the authentication tag if (record.Fragment.Length < authTagSize) { return(false); } } // Replace the encrypted fragment with the decrypted fragment byte[] fragment = TransformRecordBytes(cipherType, cipher, record.Fragment); if (fragment == null) { return(false); } record.Fragment = fragment; // Remove explicit IV from the beginning of the fragment if necessary if (cipherType == BulkCipherAlgorithmType.Block && record.Version.HasExplicitIV) { fragment = new byte[record.Fragment.Length - recordIVLength]; Buffer.BlockCopy(record.Fragment, recordIVLength, fragment, 0, record.Fragment.Length - recordIVLength); record.Fragment = fragment; } return(true); }
private static byte[] TransformRecordBytes(BulkCipherAlgorithmType cipherType, ICryptoTransform transform, byte[] input) { if (cipherType != BulkCipherAlgorithmType.AEAD) { // In case of non-AEAD cipher algorithm, check that data matches block size if (input.Length % transform.InputBlockSize != 0) { throw new Exception("Input data size doesn't match block size"); } } int blockCount = input.Length / transform.InputBlockSize; if (cipherType == BulkCipherAlgorithmType.AEAD) { // Make sure there is enough data at TransformFinalBlock, because // decryption requires that the authentication tag is present if (blockCount > 0) { blockCount--; } } byte[] output = new byte[blockCount * transform.OutputBlockSize]; if (transform.CanTransformMultipleBlocks) { transform.TransformBlock(input, 0, blockCount*transform.InputBlockSize, output, 0); } else { for (int i=0; i<blockCount; i++) { transform.TransformBlock(input, i*transform.InputBlockSize, transform.InputBlockSize, output, i*transform.OutputBlockSize); } } if (cipherType == BulkCipherAlgorithmType.AEAD) { int currentPosition = blockCount*transform.InputBlockSize; // Transfer the last block when encrypting or authentication tag when decrypting byte[] finalBytes = transform.TransformFinalBlock(input, currentPosition, input.Length-currentPosition); if (finalBytes == null) { return null; } else if (finalBytes.Length > 0) { byte[] finalOutput = new byte[output.Length + finalBytes.Length]; Buffer.BlockCopy(output, 0, finalOutput, 0, output.Length); Buffer.BlockCopy(finalBytes, 0, finalOutput, output.Length, finalBytes.Length); output = finalOutput; } } return output; }