public void ModifiedCiphertextFails() { var rnd = new Random(); var key = new byte[Snuffle.KEY_SIZE_IN_BYTES]; rnd.NextBytes(key); var aad = new byte[16]; rnd.NextBytes(aad); var message = new byte[32]; rnd.NextBytes(message); var aead = new ChaCha20Poly1305(key); var ciphertext = aead.Encrypt(message, aad); // Flipping bits for (var b = 0; b < ciphertext.Length; b++) { for (var bit = 0; bit < 8; bit++) { var modified = new byte[ciphertext.Length]; Array.Copy(ciphertext, modified, ciphertext.Length); modified[b] ^= (byte)(1 << bit); Assert.Throws <CryptographicException>(() => aead.Decrypt(modified, aad), SnufflePoly1305.AEAD_EXCEPTION_INVALID_TAG); } } // Truncate the message for (var length = 0; length < ciphertext.Length; length++) { var modified = new byte[length]; Array.Copy(ciphertext, modified, length); Assert.Throws <CryptographicException>(() => aead.Decrypt(modified, aad), SnufflePoly1305.AEAD_EXCEPTION_INVALID_TAG); } // Modify AAD for (var b = 0; b < aad.Length; b++) { for (var bit = 0; bit < 8; bit++) { var modified = new byte[aad.Length]; Array.Copy(aad, modified, aad.Length); modified[b] ^= (byte)(1 << bit); Assert.Throws <CryptographicException>(() => aead.Decrypt(modified, aad), SnufflePoly1305.AEAD_EXCEPTION_INVALID_TAG); } } }
public void EncryptDecryptWithNonceTest() { var rnd = new Random(); var key = new byte[Snuffle.KEY_SIZE_IN_BYTES]; rnd.NextBytes(key); var aead = new ChaCha20Poly1305(key); for (var i = 0; i < 100; i++) { var message = new byte[100]; rnd.NextBytes(message); var aad = new byte[16]; rnd.NextBytes(aad); var nonce = new byte[12]; rnd.NextBytes(nonce); var ciphertext = aead.Encrypt(message, aad, nonce); var decrypted = aead.Decrypt(ciphertext, aad, nonce); //Assert.AreEqual(message, decrypted); Assert.IsTrue(CryptoBytes.ConstantTimeEquals(message, decrypted)); } }
public void EncryptDecryptLongMessagesWithNonceTest() { var rnd = new Random(); var dataSize = 16; while (dataSize <= (1 << 24)) { var plaintext = new byte[dataSize]; rnd.NextBytes(plaintext); var aad = new byte[dataSize / 3]; rnd.NextBytes(aad); var nonce = new byte[12]; rnd.NextBytes(nonce); var key = new byte[Snuffle.KEY_SIZE_IN_BYTES]; rnd.NextBytes(key); var aead = new ChaCha20Poly1305(key); var ciphertext = aead.Encrypt(plaintext, aad, nonce); var decrypted = aead.Decrypt(ciphertext, aad, nonce); //Assert.AreEqual(plaintext, decrypted); Assert.IsTrue(CryptoBytes.ConstantTimeEquals(plaintext, decrypted)); dataSize += 5 * dataSize / 11; } }
public void Decrypt(Tests.Vectors.Rfc8439TestVector test) { var aead = new ChaCha20Poly1305(test.Key); var plaintext = new byte[test.CipherText.Length]; aead.Decrypt(test.Nonce, test.CipherText, test.Tag, plaintext, test.Aad); }
public void DecryptWhenNonceLengthIsInvalidFails() { // Arrange, Act & Assert var aead = new ChaCha20Poly1305(new byte[Snuffle.KEY_SIZE_IN_BYTES]); Assert.Throws <CryptographicException>(() => aead.Decrypt(new byte[50], new byte[0], new byte[12 + TestHelpers.ReturnRandomPositiveNegative()]), EXCEPTION_MESSAGE_NONCE_LENGTH); }
public void DecryptWhenNonceIsEmptyFails() { // Arrange, Act & Assert var aead = new ChaCha20Poly1305(new byte[Snuffle.KEY_SIZE_IN_BYTES]); Assert.Throws <CryptographicException>(() => aead.Decrypt(new byte[50], new byte[0], new byte[0]), EXCEPTION_MESSAGE_NONCE_LENGTH); }
public string Decode(string token, int?ttl = null) { var tokenBytes = token.FromBase62(); var headerBytes = new byte[HeaderBytes]; Buffer.BlockCopy(tokenBytes, 0, headerBytes, 0, HeaderBytes); var nonceBytes = new byte[NonceBytes]; Buffer.BlockCopy(headerBytes, VersionByte + TimestampBytes, nonceBytes, 0, NonceBytes); var cipherBytes = new byte[tokenBytes.Length - HeaderBytes]; Buffer.BlockCopy(tokenBytes, headerBytes.Length, cipherBytes, 0, cipherBytes.Length); var keyBytes = new byte[Snuffle.KEY_SIZE_IN_BYTES]; Buffer.BlockCopy(_key.ToCharArray(), 0, keyBytes, 0, _key.ToCharArray().Length); var aead = new ChaCha20Poly1305(keyBytes); var cipherText = aead.Decrypt(cipherBytes, null, nonceBytes); return(cipherText.GetString()); }
public void DecryptWithNonceWhenCiphertextIsTooShortFails() { // Arrange & Act var aead = new ChaCha20Poly1305(new byte[Snuffle.KEY_SIZE_IN_BYTES]); // Assert Assert.Throws <CryptographicException>(() => aead.Decrypt(new byte[27], new byte[1], new byte[1])); }
public static void TwoEncryptionsAndDecryptionsUsingOneInstance() { byte[] key = "fde37f01fe9ca260f432e0ed98b3e0bb23895ca1ca1ce2cfcaaca2ccc98889d7".HexToByteArray(); byte[] originalData1 = Enumerable.Range(1, 15).Select((x) => (byte)x).ToArray(); byte[] originalData2 = Enumerable.Range(14, 97).Select((x) => (byte)x).ToArray(); byte[] associatedData2 = Enumerable.Range(100, 109).Select((x) => (byte)x).ToArray(); byte[] nonce1 = "b41329dd64af2c3036661b46".HexToByteArray(); byte[] nonce2 = "8ba10892e8b87d031196bf99".HexToByteArray(); byte[] expectedCiphertext1 = "75f5aafbbabab80a3cfa2ecfd1bc58".HexToByteArray(); byte[] expectedTag1 = "1ed70acc454fba01f0354e93eba9b428".HexToByteArray(); byte[] expectedCiphertext2 = ( "f95cc19929463ba96a2cfc21fac5345ec308e2748995ba285af6b21ca3d665bc" + "00144604b38e9645fb2d5f5893fc78871bd8f5fc91caaa013eac5f80397fd65c" + "358c239f013f3c75da17ddbd14de01eb67f5204dfa787986fb27a098fe21b2c5" + "07").HexToByteArray(); byte[] expectedTag2 = "9877f87f29f68b5f9efb071c1351ccf6".HexToByteArray(); using (var chaChaPoly = new ChaCha20Poly1305(key)) { byte[] ciphertext1 = new byte[originalData1.Length]; byte[] tag1 = new byte[expectedTag1.Length]; chaChaPoly.Encrypt(nonce1, originalData1, ciphertext1, tag1); Assert.Equal(expectedCiphertext1, ciphertext1); Assert.Equal(expectedTag1, tag1); byte[] ciphertext2 = new byte[originalData2.Length]; byte[] tag2 = new byte[expectedTag2.Length]; chaChaPoly.Encrypt(nonce2, originalData2, ciphertext2, tag2, associatedData2); Assert.Equal(expectedCiphertext2, ciphertext2); Assert.Equal(expectedTag2, tag2); byte[] plaintext1 = new byte[originalData1.Length]; chaChaPoly.Decrypt(nonce1, ciphertext1, tag1, plaintext1); Assert.Equal(originalData1, plaintext1); byte[] plaintext2 = new byte[originalData2.Length]; chaChaPoly.Decrypt(nonce2, ciphertext2, tag2, plaintext2, associatedData2); Assert.Equal(originalData2, plaintext2); } }
public static void EncryptDecryptNullTag() { byte[] key = "fde37f01fe9ca260f432e0ed98b3e0bb23895ca1ca1ce2cfcaaca2ccc98889d7".HexToByteArray(); byte[] nonce = new byte[NonceSizeInBytes]; byte[] plaintext = new byte[0]; byte[] ciphertext = new byte[0]; using (var chaChaPoly = new ChaCha20Poly1305(key)) { Assert.Throws <ArgumentNullException>(() => chaChaPoly.Encrypt(nonce, plaintext, ciphertext, (byte[])null)); Assert.Throws <ArgumentNullException>(() => chaChaPoly.Decrypt(nonce, ciphertext, (byte[])null, plaintext)); } }
internal Deserializer(CryptoDtoChannelStore channelStore, ReadOnlySpan <byte> bytes, bool ignoreSequence) { sequenceValid = false; headerLength = Unsafe.ReadUnaligned <ushort>(ref MemoryMarshal.GetReference(bytes)); //.NET Standard 2.0 doesn't have BitConverter.ToUInt16(Span<T>) if (bytes.Length < (2 + headerLength)) { throw new CryptographicException("Not enough bytes to process packet."); } ReadOnlySpan <byte> headerDataBuffer = bytes.Slice(2, headerLength); header = MessagePackSerializer.Deserialize <CryptoDtoHeaderDto>(headerDataBuffer.ToArray()); ReadOnlySpan <byte> receiveKey = channelStore.GetReceiveKey(header.ChannelTag, header.Mode); //This will throw exception if channel tag isn't in the store switch (header.Mode) { case CryptoDtoMode.ChaCha20Poly1305: { int aeLength = bytes.Length - (2 + headerLength); ReadOnlySpan <byte> aePayloadBuffer = bytes.Slice(2 + headerLength, aeLength); ReadOnlySpan <byte> adBuffer = bytes.Slice(0, 2 + headerLength); Span <byte> nonceBuffer = stackalloc byte[Aead.NonceSize]; BinaryPrimitives.WriteUInt64LittleEndian(nonceBuffer.Slice(4), header.Sequence); var aead = new ChaCha20Poly1305(receiveKey.ToArray()); ReadOnlySpan <byte> decryptedPayload = aead.Decrypt(aePayloadBuffer.ToArray(), adBuffer.ToArray(), nonceBuffer); if (ignoreSequence) { sequenceValid = channelStore.IsReceivedSequenceAllowed(header.ChannelTag, header.Sequence); } else { channelStore.CheckReceivedSequence(header.ChannelTag, header.Sequence); //The packet has passed MAC, so now check if it's being duplicated or replayed sequenceValid = true; } dtoNameLength = Unsafe.ReadUnaligned <ushort>(ref MemoryMarshal.GetReference(decryptedPayload)); //.NET Standard 2.0 doesn't have BitConverter.ToUInt16(Span<T>) dtoNameBuffer = decryptedPayload.Slice(2, dtoNameLength); dataLength = Unsafe.ReadUnaligned <ushort>(ref MemoryMarshal.GetReference(decryptedPayload.Slice(2 + dtoNameLength, 2))); //.NET Standard 2.0 doesn't have BitConverter.ToUInt16(Span<T>) dataBuffer = decryptedPayload.Slice(2 + dtoNameLength + 2, dataLength); break; } default: throw new CryptographicException("Mode not recognised"); } }
public static void PlaintextAndCiphertextSizeDiffer(int ptLen, int ctLen) { byte[] key = new byte[KeySizeInBytes]; byte[] nonce = new byte[NonceSizeInBytes]; byte[] plaintext = new byte[ptLen]; byte[] ciphertext = new byte[ctLen]; byte[] tag = new byte[TagSizeInBytes]; using (var chaChaPoly = new ChaCha20Poly1305(key)) { Assert.Throws <ArgumentException>(() => chaChaPoly.Encrypt(nonce, plaintext, ciphertext, tag)); Assert.Throws <ArgumentException>(() => chaChaPoly.Decrypt(nonce, ciphertext, tag, plaintext)); } }
/// <summary>Method which verifies that a supplied plain-text password matches the encrypted password.</summary> public bool Verify(string plaintext) { if (EncryptedPasswordb64 == null) { throw new Exception("You must have an encrypted password to verify against"); } // Unpack the Encrypted Password var base10 = Convert.FromBase64String(EncryptedPasswordb64); var dec = new ChaCha20Poly1305(Encoding.ASCII.GetBytes(Key)); var decryptedBytes = dec.Decrypt(base10); var decryptedString = Encoding.ASCII.GetString(decryptedBytes); return(PasswordHash.ArgonHashStringVerify(decryptedString, plaintext)); }
public static void Rfc8439Tests(AEADTest testCase) { using (var chaChaPoly = new ChaCha20Poly1305(testCase.Key)) { byte[] ciphertext = new byte[testCase.Plaintext.Length]; byte[] tag = new byte[testCase.Tag.Length]; chaChaPoly.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); Assert.Equal(testCase.Ciphertext, ciphertext); Assert.Equal(testCase.Tag, tag); byte[] plaintext = new byte[testCase.Plaintext.Length]; chaChaPoly.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData); Assert.Equal(testCase.Plaintext, plaintext); } }
public static void InplaceEncryptDecrypt() { byte[] key = "fde37f01fe9ca260f432e0ed98b3e0bb23895ca1ca1ce2cfcaaca2ccc98889d7".HexToByteArray(); byte[] nonce = RandomNumberGenerator.GetBytes(NonceSizeInBytes); byte[] originalPlaintext = new byte[] { 1, 2, 8, 12, 16, 99, 0 }; byte[] data = (byte[])originalPlaintext.Clone(); byte[] tag = new byte[TagSizeInBytes]; using (var chaChaPoly = new ChaCha20Poly1305(key)) { chaChaPoly.Encrypt(nonce, data, data, tag); Assert.NotEqual(originalPlaintext, data); chaChaPoly.Decrypt(nonce, data, tag, data); Assert.Equal(originalPlaintext, data); } }
public static void Rfc8439TestsTamperTag(AEADTest testCase) { using (var chaChaPoly = new ChaCha20Poly1305(testCase.Key)) { byte[] ciphertext = new byte[testCase.Plaintext.Length]; byte[] tag = new byte[testCase.Tag.Length]; chaChaPoly.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); Assert.Equal(testCase.Ciphertext, ciphertext); Assert.Equal(testCase.Tag, tag); tag[0] ^= 1; byte[] plaintext = RandomNumberGenerator.GetBytes(testCase.Plaintext.Length); Assert.Throws <CryptographicException>( () => chaChaPoly.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); Assert.Equal(new byte[plaintext.Length], plaintext); } }
public void ChaCha20Poly1305TestVector2() { // https://tools.ietf.org/html/rfc8439 // Arrange foreach (var test in Rfc8439TestVector.Rfc7634AeadTestVectors) { // Act var aead = new ChaCha20Poly1305(test.Key); var ct = aead.Encrypt(test.PlainText, test.Aad, test.Nonce); Assert.That(ct, Is.EqualTo(CryptoBytes.Combine(test.CipherText, test.Tag))); var output = aead.Decrypt(ct, test.Aad, test.Nonce); // Assert //Assert.That(output, Is.EqualTo(test.PlainText)); Assert.IsTrue(CryptoBytes.ConstantTimeEquals(test.PlainText, output)); } }
public static void ValidNonceAndTagSize() { const int dataLength = 35; byte[] plaintext = Enumerable.Range(1, dataLength).Select((x) => (byte)x).ToArray(); byte[] ciphertext = new byte[dataLength]; byte[] key = RandomNumberGenerator.GetBytes(KeySizeInBytes); byte[] nonce = RandomNumberGenerator.GetBytes(NonceSizeInBytes); byte[] tag = new byte[TagSizeInBytes]; using (var chaChaPoly = new ChaCha20Poly1305(key)) { chaChaPoly.Encrypt(nonce, plaintext, ciphertext, tag); byte[] decrypted = new byte[dataLength]; chaChaPoly.Decrypt(nonce, ciphertext, tag, decrypted); Assert.Equal(plaintext, decrypted); } }
public static void Test(string[] testVector) { var plaintext = testVector[0]; var aad = testVector[1]; var key = testVector[2]; var nonce = testVector[3]; var ciphertext = testVector[4]; var tag = testVector[5]; var a = new ChaCha20Poly1305(); using (var k = Key.Import(a, key.DecodeHex(), KeyBlobFormat.RawSymmetricKey)) { var b = a.Encrypt(k, new Nonce(nonce.DecodeHex(), 0), aad.DecodeHex(), plaintext.DecodeHex()); Assert.Equal((ciphertext + tag).DecodeHex(), b); var r = a.Decrypt(k, new Nonce(nonce.DecodeHex(), 0), aad.DecodeHex(), b); Assert.Equal(plaintext.DecodeHex(), r); } }
public static void EncryptTamperAADDecrypt(int dataLength, int additionalDataLength) { byte[] additionalData = new byte[additionalDataLength]; RandomNumberGenerator.Fill(additionalData); byte[] plaintext = Enumerable.Range(1, dataLength).Select((x) => (byte)x).ToArray(); byte[] ciphertext = new byte[dataLength]; byte[] key = RandomNumberGenerator.GetBytes(KeySizeInBytes); byte[] nonce = RandomNumberGenerator.GetBytes(NonceSizeInBytes); byte[] tag = new byte[TagSizeInBytes]; using (var chaChaPoly = new ChaCha20Poly1305(key)) { chaChaPoly.Encrypt(nonce, plaintext, ciphertext, tag, additionalData); additionalData[0] ^= 1; byte[] decrypted = new byte[dataLength]; Assert.Throws <CryptographicException>( () => chaChaPoly.Decrypt(nonce, ciphertext, tag, decrypted, additionalData)); } }
public int DecryptWithAd(ReadOnlySpan <byte> ad, ReadOnlySpan <byte> ciphertext, Span <byte> plaintext) { Debug.Assert(_key.Length == Aead.KEY_SIZE); Debug.Assert(ciphertext.Length >= Aead.TAG_SIZE); Debug.Assert(plaintext.Length >= ciphertext.Length - Aead.TAG_SIZE); Span <byte> nonce = stackalloc byte[Aead.NONCE_SIZE]; BinaryPrimitives.WriteUInt64LittleEndian(nonce.Slice(4), _nonce); var cipher = new ChaCha20Poly1305(_key); var cipherTextWithoutTag = ciphertext.Slice(0, ciphertext.Length - Aead.TAG_SIZE); var tag = ciphertext.Slice(ciphertext.Length - Aead.TAG_SIZE); _logger.LogDebug($"Decrypting text length - {plaintext.Length} with nonce - {_nonce}"); cipher.Decrypt(nonce, cipherTextWithoutTag, tag, plaintext, ad); _nonce++; return(cipherTextWithoutTag.Length); }
public void ModifiedAssociatedDataFails() { var rnd = new Random(); var key = new byte[Snuffle.KEY_SIZE_IN_BYTES]; rnd.NextBytes(key); var aead = new ChaCha20Poly1305(key); var aad = new byte[0]; for (var msgSize = 0; msgSize < 75; msgSize++) { var message = new byte[msgSize]; rnd.NextBytes(message); // encrypting with aad as a 0-length array var ciphertext = aead.Encrypt(message, aad); var decrypted = aead.Decrypt(ciphertext, aad); //Assert.AreEqual(message, decrypted); Assert.IsTrue(CryptoBytes.ConstantTimeEquals(message, decrypted)); var decrypted2 = aead.Decrypt(ciphertext, null); //Assert.AreEqual(message, decrypted2); Assert.IsTrue(CryptoBytes.ConstantTimeEquals(message, decrypted2)); var badAad = new byte[] { 1, 2, 3 }; Assert.Throws <CryptographicException>(() => aead.Decrypt(ciphertext, badAad), SnufflePoly1305.AEAD_EXCEPTION_INVALID_TAG); // encrypting with aad equal to null ciphertext = aead.Encrypt(message, null); decrypted = aead.Decrypt(ciphertext, aad); //Assert.AreEqual(message, decrypted); Assert.IsTrue(CryptoBytes.ConstantTimeEquals(message, decrypted)); decrypted2 = aead.Decrypt(ciphertext, null); //Assert.AreEqual(message, decrypted2); Assert.IsTrue(CryptoBytes.ConstantTimeEquals(message, decrypted2)); Assert.Throws <CryptographicException>(() => aead.Decrypt(ciphertext, badAad), SnufflePoly1305.AEAD_EXCEPTION_INVALID_TAG); } }
public byte[] DecryptWithNonce(Tests.Vectors.Rfc8439TestVector test) { var aead = new ChaCha20Poly1305(test.Key); return(aead.Decrypt(CryptoBytes.Combine(test.CipherText, test.Tag), test.Aad, test.Nonce)); }
public void WycheproofTestVectors() { var json = GetWycheproofTestVector(); var vector = JsonConvert.DeserializeObject <WycheproofVector>(json); //Utf8Json.JsonSerializer.Deserialize<WycheproofVector>(json); var errors = 0; foreach (var group in vector.TestGroups) { foreach (var test in group.Tests) { var id = $"TestCase {test.TcId}"; if (!string.IsNullOrEmpty(test.Comment)) { id += $" ({test.Comment})"; } var iv = CryptoBytes.FromHexString(test.Iv); var key = CryptoBytes.FromHexString(test.Key); var msg = CryptoBytes.FromHexString(test.Msg); var aad = CryptoBytes.FromHexString(test.Aad); var ct = CryptoBytes.FromHexString(test.Ct); var tag = CryptoBytes.FromHexString(test.Tag); var ciphertext = iv.Concat(ct).Concat(tag).ToArray(); // Result is one of "valid", "invalid", "acceptable". // "valid" are test vectors with matching plaintext, ciphertext and tag. // "invalid" are test vectors with invalid parameters or invalid ciphertext and tag. // "acceptable" are test vectors with weak parameters or legacy formats. var result = test.Result; try { var aead = new ChaCha20Poly1305(key); var decrypted = aead.Decrypt(ciphertext, aad); if (test.Result == "invalid") { TestContext.WriteLine($"FAIL {id}: accepting invalid ciphertext, cleartext: {test.Msg}, decrypted: {CryptoBytes.ToHexStringLower(decrypted)}"); errors++; continue; } if (!CryptoBytes.ConstantTimeEquals(msg, decrypted)) { TestContext.WriteLine($"FAIL {id}: incorrect decryption, result: {CryptoBytes.ToHexStringLower(decrypted)}, expected: {test.Msg}"); errors++; } } catch (Exception ex) { if (test.Result == "valid") { TestContext.WriteLine($"FAIL {id}: cannot decrypt, exception: {ex}"); errors++; } } } } Assert.AreEqual(0, errors); }