public CtrStream(Stream input, byte[] key, byte[] ctr, bool littleEndianCtr) { if (key.Length / 4 < 4 || key.Length / 4 > 8 || key.Length % 4 > 0) { throw new InvalidOperationException("Key has invalid length."); } if (ctr.Length != BlockSize) { throw new InvalidOperationException("Counter has invalid length."); } if (input.Length % BlockSize != 0) { throw new InvalidOperationException("Stream needs to have a length dividable by 16."); } _baseStream = input; _initialCtr = new byte[ctr.Length]; Array.Copy(ctr, _initialCtr, ctr.Length); _currentCtr = new byte[ctr.Length]; Array.Copy(ctr, _currentCtr, ctr.Length); _internalLength = input.Length; _lastBlockBuffer = new byte[BlockSize]; _littleEndianCtr = littleEndianCtr; var aes = AesCtr.Create(littleEndianCtr); _decryptor = (AesCtrCryptoTransform)aes.CreateDecryptor(key, ctr); _encryptor = (AesCtrCryptoTransform)aes.CreateEncryptor(key, ctr); }
public static byte[] CTR128(byte[] block, byte[] key, byte[] ctr) { var aes = new AesCtr(ctr); var output = new byte[block.Length]; aes.CreateDecryptor(key).TransformBlock(block, 0, block.Length, output, 0); return(output); }
public void TestEncrypt() { foreach (var example in LoadExamples()) { var key = Hex.Decode(example.Key); var iv = Hex.Decode(example.Iv); var plaintext = Hex.Decode(example.Plaintext); using (var ctr = new AesCtr(key, iv)) { ctr.Encrypt(plaintext, 0, plaintext.Length, plaintext, 0); Assert.Equal(example.Ciphertext, Hex.Encode(plaintext)); } } }
public void TestChallenge18() { var encrypted = Convert.FromBase64String(Set3Data.Challenge18Input); var keyStr = "YELLOW SUBMARINE"; var key = System.Text.Encoding.ASCII.GetBytes(keyStr); ulong nonce = 0; var decrypted = AesCtr.Decrypt(key, nonce, encrypted); var actual = System.Text.Encoding.ASCII.GetString(decrypted); Assert.AreEqual(Set3Data.Challenge18Solution, actual); // Encrypt and verify against input data var enc = AesCtr.Encrypt(key, nonce, decrypted); CollectionAssert.AreEqual(encrypted, enc); }
public void Aes128CtrDecrypt(string plainString, string encryptedHexString) { // Obtain the key. byte[] key = "7b6dcbffad4bbbcd25e2a80201739233".HexToBytes(); // Obtain the data as bytes. byte[] encryptedData = encryptedHexString.HexToBytes(); // Decrypt the provided data. byte[] resultData = AesCtr.Decrypt(key, encryptedData, null); string resultString = Encoding.UTF8.GetString(resultData); // Assert the data is the same length Assert.Equal(plainString.Length, resultString.Length); // Verify the string equals the original string. Assert.Equal(plainString, resultString); }
public void Aes256CtrEncrypt(string plainString, string encryptedHexString) { // Obtain the key. byte[] key = "7b6dcbffad4bbbcd25e2a802017392337b6dcbffad4bbbcd25e2a80201739233".HexToBytes(); // Obtain the data as bytes. byte[] plainData = Encoding.UTF8.GetBytes(plainString); // Encrypt the provided data. byte[] resultData = AesCtr.Encrypt(key, plainData, null); string resultHexString = resultData.ToHexString(false); // Assert the data is the same length Assert.Equal(plainData.Length, resultData.Length); // Verify the string equals the original string. Assert.Equal(encryptedHexString, resultHexString, true); }
public void Aes192CtrEncryptDecrypt(string testString) { // Generate a random key. byte[] key = new byte[24]; // 192 bit RandomNumberGenerator random = RandomNumberGenerator.Create(); random.GetBytes(key); // Obtain the data as bytes. byte[] testData = Encoding.UTF8.GetBytes(testString); // Encrypt then decrypt the data. byte[] encrypted = AesCtr.Encrypt(key, testData); byte[] decrypted = AesCtr.Decrypt(key, encrypted); // Get the decrypted result as a string string result = Encoding.UTF8.GetString(decrypted); // Verify the string equals the original string. Assert.Equal(testString, result); }
[InlineData("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", "601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c52b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6")] // AES-256-CTR public void AesCtrTestWithNISTVectors(string keyHexString, string counterHexString, string plainTextHexString, string cipherTextHexString) { // Obtain the components as bytes. byte[] key = keyHexString.HexToBytes(); byte[] counter = counterHexString.HexToBytes(); byte[] plainTextData = plainTextHexString.HexToBytes(); // Encrypt the provided data. byte[] encryptedData = AesCtr.Encrypt(key, plainTextData, counter); string encryptedDataHexString = encryptedData.ToHexString(false); // Assert this equals our ciphered text Assert.Equal(cipherTextHexString, encryptedDataHexString, true); // Decrypt the data back to it's original format byte[] decryptedData = AesCtr.Decrypt(key, encryptedData, counter); string decryptedDataHexString = decryptedData.ToHexString(false); // Assert this equals our plain text Assert.Equal(plainTextHexString, decryptedDataHexString, true); }
public void TestLargeMessage() { var key = new byte[16]; var iv = new byte[16]; var message = new byte[10000]; var last = new byte[16]; var expected = "3b9a44f22bb1522f10c00ff8ca5195ea"; // Test encrypting the whole message using (var ctr = new AesCtr(key, iv)) { ctr.Encrypt(message, 0, message.Length, message, 0); Array.Copy(message, message.Length - last.Length, last, 0, last.Length); Assert.Equal(expected, Hex.Encode(last)); } // Test encrypting the message in chunks using (var ctr = new AesCtr(key, iv)) { Array.Clear(message, 0, message.Length); var seg = new ArraySegment <byte>(message); var chunkSize = 137; while (seg.Count > 0) { ctr.Encrypt(seg.Array, seg.Offset, seg.Count, message, 0); seg = seg.Slice(Math.Min(seg.Count, chunkSize)); } Array.Copy(message, message.Length - last.Length, last, 0, last.Length); Assert.Equal(expected, Hex.Encode(last)); } }
public static byte[] Encrypt(EthereumEcdsa remotePublicKey, byte[] data, byte[] sharedMacData = null) { // If we have no shared mac data, we set it as a blank array sharedMacData = sharedMacData ?? Array.Empty <byte>(); // Generate a random private key EthereumEcdsa senderPrivateKey = EthereumEcdsa.Generate(new SystemRandomAccountDerivation()); // Generate the elliptic curve diffie hellman ("ECDH") shared key byte[] ecdhKey = senderPrivateKey.ComputeECDHKey(remotePublicKey); // Perform NIST SP 800-56 Concatenation Key Derivation Function ("KDF") Memory <byte> keyData = DeriveKeyKDF(ecdhKey, 32); // Split the AES encryption key and MAC from the derived key data. var aesKey = keyData.Slice(0, 16).ToArray(); byte[] hmacSha256Key = keyData.Slice(16, 16).ToArray(); hmacSha256Key = _sha256.ComputeHash(hmacSha256Key); // We generate a counter for our aes-128-ctr operation. byte[] counter = new byte[AesCtr.BLOCK_SIZE]; _randomNumberGenerator.GetBytes(counter); // Encrypt the data accordingly. byte[] encryptedData = AesCtr.Encrypt(aesKey, data, counter); // Obtain the sender's public key to compile our message. byte[] localPublicKey = senderPrivateKey.ToPublicKeyArray(false, true); // We'll want to put this data into the message in the following order (where || is concatenation): // ECIES_HEADER_BYTE (1 byte) || sender's public key (64 bytes) || counter (16 bytes) || encrypted data (arbitrary length) || tag (32 bytes) // This gives us a total size of 113 + data.Length byte[] result = new byte[ECIES_ADDITIONAL_OVERHEAD + encryptedData.Length]; // Define a pointer and copy in our data as suggested. int offset = 0; result[offset++] = ECIES_HEADER_BYTE; Array.Copy(localPublicKey, 0, result, offset, localPublicKey.Length); offset += localPublicKey.Length; Array.Copy(counter, 0, result, offset, counter.Length); offset += counter.Length; Array.Copy(encryptedData, 0, result, offset, encryptedData.Length); offset += encryptedData.Length; // We still have to copy the tag, which is a HMACSHA256 of our counter + encrypted data + shared mac. // We copy the data into a buffer for this hash computation since counter + encrypted data are already aligned. byte[] tagPreimage = new byte[counter.Length + encryptedData.Length + sharedMacData.Length]; Array.Copy(result, 65, tagPreimage, 0, counter.Length + encryptedData.Length); Array.Copy(sharedMacData, 0, tagPreimage, counter.Length + encryptedData.Length, sharedMacData.Length); // Obtain a HMACSHA256 provider HMACSHA256 hmacSha256 = new HMACSHA256(hmacSha256Key); // Compute a hash of our counter + encrypted data + shared mac data. byte[] tag = hmacSha256.ComputeHash(tagPreimage); // Copy the tag into our result buffer. Array.Copy(tag, 0, result, offset, tag.Length); offset += tag.Length; // Return the resulting data. return(result); }
public static byte[] Decrypt(EthereumEcdsa localPrivateKey, Memory <byte> message, Memory <byte> sharedMacData) { // Verify our provided key type if (localPrivateKey.KeyType != EthereumEcdsaKeyType.Private) { throw new ArgumentException("ECIES could not decrypt data because the provided key was not a private key."); } // Verify the size of our message (layout specified in Encrypt() describes this value) if (message.Length <= ECIES_ADDITIONAL_OVERHEAD) { throw new ArgumentException("ECIES could not decrypt data because the provided data did not contain enough information."); } // Verify the first byte of our data int offset = 0; if (message.Span[offset++] != ECIES_HEADER_BYTE) { throw new ArgumentException("ECIES could not decrypt data because the provided data had an invalid header."); } // Extract the sender's public key from the data. Memory <byte> remotePublicKeyData = message.Slice(offset, 64); EthereumEcdsa remotePublicKey = EthereumEcdsa.Create(remotePublicKeyData, EthereumEcdsaKeyType.Public); offset += remotePublicKeyData.Length; Memory <byte> counter = message.Slice(offset, AesCtr.BLOCK_SIZE); offset += counter.Length; Memory <byte> encryptedData = message.Slice(offset, message.Length - offset - 32); offset += encryptedData.Length; byte[] tag = message.Slice(offset, message.Length - offset).ToArray(); offset += tag.Length; // Generate the elliptic curve diffie hellman ("ECDH") shared key byte[] ecdhKey = localPrivateKey.ComputeECDHKey(remotePublicKey); // Perform NIST SP 800-56 Concatenation Key Derivation Function ("KDF") Memory <byte> keyData = DeriveKeyKDF(ecdhKey, 32); // Split the AES encryption key and MAC from the derived key data. var aesKey = keyData.Slice(0, 16).ToArray(); byte[] hmacSha256Key = keyData.Slice(16, 16).ToArray(); hmacSha256Key = _sha256.ComputeHash(hmacSha256Key); // Next we'll want to verify our tag (HMACSHA256 hash of counter + encrypted data + shared mac data). // We copy the data into a buffer for this hash computation since counter + encrypted data are already aligned. byte[] tagPreimage = new byte[counter.Length + encryptedData.Length + sharedMacData.Length]; Memory <byte> tagPreimageMemory = tagPreimage; counter.CopyTo(tagPreimageMemory); encryptedData.CopyTo(tagPreimageMemory.Slice(counter.Length, encryptedData.Length)); sharedMacData.CopyTo(tagPreimageMemory.Slice(counter.Length + encryptedData.Length)); // Obtain a HMACSHA256 provider HMACSHA256 hmacSha256 = new HMACSHA256(hmacSha256Key); // Compute a hash of our counter + encrypted data + shared mac data. byte[] validTag = hmacSha256.ComputeHash(tagPreimage); // Verify our tag is valid if (!tag.SequenceEqual(validTag)) { throw new ArgumentException("ECIES could not decrypt data because the hash/tag on the counter/encrypted data/shared mac data was invalid."); } // Decrypt the data accordingly. byte[] decryptedData = AesCtr.Decrypt(aesKey, encryptedData.ToArray(), counter.ToArray()); // Return the decrypted data. return(decryptedData); }
public void DeriveEncryptionParameters() { // Verify the session state is correct. if (SessionState != RLPxSessionState.AcknowledgementCompleted) { throw new Exception("RLPx encryption parameter deriviation should only occur after authentication and acknowledgement was processed."); } // Verify we have all required information if (AuthData == null || AuthAckData == null || RemoteEphermalPublicKey == null || InitiatorNonce == null || ResponderNonce == null) { throw new Exception("RLPx deriving encryption information failed: Insufficient data collected from handshake."); } // Generate the ecdh key with both ephemeral keys byte[] ecdhEphemeralKey = LocalEphemeralPrivateKey.ComputeECDHKey(RemoteEphermalPublicKey); // Generate a shared secret: Keccak256(ecdhEphemeralKey || Keccak256(ResponderNonce || InitiatorNonce)) byte[] combinedNonceHash = KeccakHash.ComputeHashBytes(ResponderNonce.Concat(InitiatorNonce)); byte[] sharedSecret = KeccakHash.ComputeHashBytes(ecdhEphemeralKey.Concat(combinedNonceHash)); // Derive the token as a hash of the shared secret. Token = KeccakHash.ComputeHashBytes(sharedSecret); // Derive AES secret: Keccak256(ecdhEphemeralKey || sharedSecret) AesSecret = KeccakHash.ComputeHashBytes(ecdhEphemeralKey.Concat(sharedSecret)); // Derive Mac secret: Keccak256(ecdhEphemeralKey || AesSecret) MacSecret = KeccakHash.ComputeHashBytes(ecdhEphemeralKey.Concat(AesSecret)); // Create our AES providers for incoming and outgoing traffic/frames. // Counter is 0, so it doesn't need to be provided, default value will handle this. IngressAes = new AesCtr(AesSecret); EgressAes = new AesCtr(AesSecret); // Next we'll want to derive our incoming (ingress) and outgoing (egress) traffic message authentication code ("MAC") // The initial state is based off of keccak((MacSecret ^ nonce) || auth/auth-ack). Later states update data from packet frames. // We begin by calculating the xor'd nonces byte[] initiatorTranformedNonce = (byte[])InitiatorNonce.Clone(); byte[] responderTransformedNonce = (byte[])ResponderNonce.Clone(); int loopSize = Math.Min(initiatorTranformedNonce.Length, MacSecret.Length); for (int i = 0; i < loopSize; i++) { initiatorTranformedNonce[i] ^= MacSecret[i]; responderTransformedNonce[i] ^= MacSecret[i]; } // Next we'll want to hash the data with our hash providers. KeccakHash initiatorOutgoing = KeccakHash.Create(); initiatorOutgoing.Update(responderTransformedNonce, 0, responderTransformedNonce.Length); initiatorOutgoing.Update(AuthData, 0, AuthData.Length); KeccakHash responderOutgoing = KeccakHash.Create(); responderOutgoing.Update(initiatorTranformedNonce, 0, initiatorTranformedNonce.Length); responderOutgoing.Update(AuthAckData, 0, AuthAckData.Length); // Assign the correct hash providers based off of role if (Role == RLPxSessionRole.Initiator) { EgressMac = initiatorOutgoing; IngressMac = responderOutgoing; } else { EgressMac = responderOutgoing; IngressMac = initiatorOutgoing; } }
public static void Test() { // Taken from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf // F.5.1 CTR-AES128.Encrypt and // F.5.2 CTR-AES128.Decrypt string[] keys = new[] { "2b7e151628aed2a6abf7158809cf4f3c", "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", }; string[] plains = new[] { "6bc1bee22e409f96e93d7e117393172a", "ae2d8a571e03ac9c9eb76fac45af8e51", "30c81c46a35ce411e5fbc1191a0a52ef", "f69f2445df4f9b17ad2b417be66c3710", }; string[][] encrypteds = new[] { new[] { "874d6191b620e3261bef6864990db6ce", "9806f66b7970fdff8617187bb9fffdff", "5ae4df3edbd5d35e5b4f09020db03eab", "1e031dda2fbe03d1792170a0f3009cee", }, new[] { "1abc932417521ca24f2b0459fe7e6e0b", "090339ec0aa6faefd5ccc2c6f4ce8e94", "1e36b26bd1ebc670d1bd1d665620abf7", "4f78a7f6d29809585a97daec58c6b050", }, new[] { "601ec313775789a5b7a7f504bbf3d228", "f443e3ca4d62b59aca84e990cacaf5c5", "2b0930daa23de94ce87017ba2d84988d", "dfc9c58db67aada613c2dd08457941a6", }, }; for (int i = 0; i < keys.Length; i++) { var aes = new AesCtr(); aes.Key = GetBytes(keys[i]); aes.IV = GetBytes("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"); Console.WriteLine("{0} bits", aes.KeySize); { Console.WriteLine("Encrypt"); ICryptoTransform encryptor = aes.CreateEncryptor(); var cipher = new byte[16]; for (int j = 0; j < plains.Length; j++) { byte[] plain = GetBytes(plains[j]); encryptor.TransformBlock(plain, 0, plain.Length, cipher, 0); string cipherHex = BitConverter.ToString(cipher).Replace("-", string.Empty).ToLowerInvariant(); if (cipherHex != encrypteds[i][j]) { throw new Exception("Error encrypting " + j); } Console.WriteLine(cipherHex); } } Console.WriteLine(); { Console.WriteLine("Decrypt"); ICryptoTransform decryptor = aes.CreateDecryptor(); var plain = new byte[16]; for (int j = 0; j < encrypteds[i].Length; j++) { byte[] encrypted = GetBytes(encrypteds[i][j]); decryptor.TransformBlock(encrypted, 0, encrypted.Length, plain, 0); string plainHex = BitConverter.ToString(plain).Replace("-", string.Empty).ToLowerInvariant(); if (plainHex != plains[j]) { throw new Exception("Error decrypting " + j); } Console.WriteLine(plainHex); } } Console.WriteLine(); } }