private void TestExceptions() { ChaCha20Poly1305 c = new ChaCha20Poly1305(); try { c = new ChaCha20Poly1305(new SipHash()); Fail("incorrect mac size not picked up"); } catch (ArgumentException e) { // expected } try { c.Init(false, new KeyParameter(new byte[32])); Fail("illegal argument not picked up"); } catch (ArgumentException e) { // expected } AeadTestUtilities.TestTampering(this, c, new AeadParameters(new KeyParameter(new byte[32]), 128, new byte[12])); byte[] P = Strings.ToByteArray("Hello world!"); byte[] buf = new byte[100]; c = new ChaCha20Poly1305(); AeadParameters aeadParameters = new AeadParameters(new KeyParameter(new byte[32]), 128, new byte[12]); c.Init(true, aeadParameters); c.ProcessBytes(P, 0, P.Length, buf, 0); c.DoFinal(buf, 0); try { c.DoFinal(buf, 0); Fail("no exception on reuse"); } catch (InvalidOperationException e) { IsTrue("wrong message", e.Message.Equals("ChaCha20Poly1305 cannot be reused for encryption")); } try { c.Init(true, aeadParameters); Fail("no exception on reuse"); } catch (ArgumentException e) { IsTrue("wrong message", e.Message.Equals("cannot reuse nonce for ChaCha20Poly1305 encryption")); } }
private byte[] EncryptOrDecrypt(bool forEncryption, byte[] data, byte[] key, byte[] nonce) { if (ExpectedKeySize != key.Length) { throw new CryptoException("Invalid key size"); } if (ExpectedNonceSize != nonce.Length) { throw new CryptoException("Invalid nonce size"); } // We do the X-part of XChaCha20 and then use the ChaCha20 from bouncy castle byte[] subkey = new byte[KeySizeBytes]; ChaCha20Base.HChaCha20(subkey, key, nonce); byte[] chaChaNonce = CreateChaChaNonce(nonce); ICipherParameters aeadParams = new AeadParameters( new KeyParameter(subkey), MacSizeBytes * 8, chaChaNonce, null); IAeadCipher chaCha20Poly1305 = new ChaCha20Poly1305(); chaCha20Poly1305.Init(forEncryption, aeadParams); byte[] result = new byte[chaCha20Poly1305.GetOutputSize(data.Length)]; int len = chaCha20Poly1305.ProcessBytes(data, 0, data.Length, result, 0); chaCha20Poly1305.DoFinal(result, len); return(result); }
private void RunTestCase( string testName, byte[] K, byte[] N, byte[] A, byte[] SA, byte[] P, byte[] C, byte[] T) { AeadParameters parameters = new AeadParameters(new KeyParameter(K), T.Length * 8, N, A); ChaCha20Poly1305 encCipher = InitCipher(true, parameters); ChaCha20Poly1305 decCipher = InitCipher(false, parameters); CheckTestCase(encCipher, decCipher, testName, SA, P, C, T); encCipher = InitCipher(true, parameters); CheckTestCase(encCipher, decCipher, testName + " (reused)", SA, P, C, T); // Key reuse AeadParameters keyReuseParams = AeadTestUtilities.ReuseKey(parameters); try { encCipher.Init(true, keyReuseParams); Fail("no exception"); } catch (ArgumentException e) { IsTrue("wrong message", "cannot reuse nonce for ChaCha20Poly1305 encryption".Equals(e.Message)); } }
private ChaCha20Poly1305 InitCipher(bool forEncryption, AeadParameters parameters) { ChaCha20Poly1305 c = new ChaCha20Poly1305(); c.Init(forEncryption, parameters); return(c); }
private static byte[] XChaCha20Poly1305(bool isEncryption, byte[] key, byte[] nonce, byte[] message, byte[] aad = null) { if (key.Length != 32) { throw new ArgumentException("Key must be 32 bytes", nameof(key)); } if (nonce.Length != 24) { throw new ArgumentException("Nonce must be 24 bytes", nameof(nonce)); } // subkey (hchacha20(key, nonce[0:15])) var subkey = HChaCha20.CreateSubkey(key, nonce); // TODO: parse nonce bytes to pass through here instead // nonce (chacha20_nonce = "\x00\x00\x00\x00" + nonce[16:23]) var chaChaNonce = new byte[12]; Array.Copy(new byte[] { 0, 0, 0, 0 }, chaChaNonce, 4); Array.Copy(nonce, 16, chaChaNonce, 4, 8); // chacha20_encrypt(subkey, chacha20_nonce, plaintext, blk_ctr) var outputLength = message.Length; if (isEncryption) { outputLength += 16; } else { outputLength -= 16; } var output = new byte[outputLength]; var keyMaterial = new KeyParameter(subkey); var parameters = new ParametersWithIV(keyMaterial, chaChaNonce); var chaCha20Poly1305 = new ChaCha20Poly1305(); chaCha20Poly1305.Init(isEncryption, parameters); // if aditional data present if (aad != null) { chaCha20Poly1305.ProcessAadBytes(aad, 0, aad.Length); } var len = chaCha20Poly1305.ProcessBytes(message, 0, message.Length, output, 0); chaCha20Poly1305.DoFinal(output, len); return(output); }
public void ChaCha20_Poly1305_Decrypt(CBORObject alg, byte[] K) { ChaCha20Poly1305 cipher = new ChaCha20Poly1305(); KeyParameter ContentKey; // The requirements from JWA // IV is 96 bits // Authentication tag is 128 bits // key sizes are 128, 192 and 256 bits ContentKey = new KeyParameter(K); byte[] IV = new byte[96 / 8]; CBORObject cbor = FindAttribute(HeaderKeys.IV); if (cbor == null) { throw new Exception("Missing IV"); } if (cbor.Type != CBORType.ByteString) { throw new CoseException("IV is incorrectly formed."); } if (cbor.GetByteString().Length > IV.Length) { throw new CoseException("IV is too long."); } Array.Copy(cbor.GetByteString(), 0, IV, IV.Length - cbor.GetByteString().Length, cbor.GetByteString().Length); AeadParameters parameters = new AeadParameters(ContentKey, 128, IV, getAADBytes()); cipher.Init(false, parameters); byte[] C = new byte[cipher.GetOutputSize(RgbEncrypted.Length)]; int len = cipher.ProcessBytes(RgbEncrypted, 0, RgbEncrypted.Length, C, 0); len += cipher.DoFinal(C, len); rgbContent = C; }
private void OutputSizeTests() { byte[] K = new byte[32]; byte[] A = null; byte[] N = new byte[12]; AeadParameters parameters = new AeadParameters(new KeyParameter(K), 16 * 8, N, A); ChaCha20Poly1305 cipher = InitCipher(true, parameters); if (cipher.GetUpdateOutputSize(0) != 0) { Fail("incorrect getUpdateOutputSize for initial 0 bytes encryption"); } if (cipher.GetOutputSize(0) != 16) { Fail("incorrect getOutputSize for initial 0 bytes encryption"); } cipher.Init(false, parameters); if (cipher.GetUpdateOutputSize(0) != 0) { Fail("incorrect getUpdateOutputSize for initial 0 bytes decryption"); } // NOTE: 0 bytes would be truncated data, but we want it to fail in the doFinal, not here if (cipher.GetOutputSize(0) != 0) { Fail("fragile getOutputSize for initial 0 bytes decryption"); } if (cipher.GetOutputSize(16) != 0) { Fail("incorrect getOutputSize for initial MAC-size bytes decryption"); } }
private void RandomTest(SecureRandom random) { int kLength = 32; byte[] K = new byte[kLength]; random.NextBytes(K); int pHead = random.Next(256); int pLength = random.Next(65536); int pTail = random.Next(256); byte[] P = new byte[pHead + pLength + pTail]; random.NextBytes(P); int aLength = random.Next(256); byte[] A = new byte[aLength]; random.NextBytes(A); int saLength = random.Next(256); byte[] SA = new byte[saLength]; random.NextBytes(SA); int nonceLength = 12; byte[] nonce = new byte[nonceLength]; random.NextBytes(nonce); AeadParameters parameters = new AeadParameters(new KeyParameter(K), 16 * 8, nonce, A); ChaCha20Poly1305 cipher = InitCipher(true, parameters); int ctLength = cipher.GetOutputSize(pLength); byte[] C = new byte[saLength + ctLength]; Array.Copy(SA, 0, C, 0, saLength); int split = NextInt(random, saLength + 1); cipher.ProcessAadBytes(C, 0, split); cipher.ProcessAadBytes(C, split, saLength - split); int predicted = cipher.GetUpdateOutputSize(pLength); int len = cipher.ProcessBytes(P, pHead, pLength, C, saLength); if (predicted != len) { Fail("encryption reported incorrect update length in randomised test"); } len += cipher.DoFinal(C, saLength + len); if (ctLength != len) { Fail("encryption reported incorrect length in randomised test"); } byte[] encT = cipher.GetMac(); byte[] tail = new byte[ctLength - pLength]; Array.Copy(C, saLength + pLength, tail, 0, tail.Length); if (!AreEqual(encT, tail)) { Fail("stream contained wrong mac in randomised test"); } cipher.Init(false, parameters); int decPHead = random.Next(256); int decPLength = cipher.GetOutputSize(ctLength); int decPTail = random.Next(256); byte[] decP = new byte[decPHead + decPLength + decPTail]; split = NextInt(random, saLength + 1); cipher.ProcessAadBytes(C, 0, split); cipher.ProcessAadBytes(C, split, saLength - split); predicted = cipher.GetUpdateOutputSize(ctLength); len = cipher.ProcessBytes(C, saLength, ctLength, decP, decPHead); if (predicted != len) { Fail("decryption reported incorrect update length in randomised test"); } len += cipher.DoFinal(decP, decPHead + len); if (!AreEqual(P, pHead, pHead + pLength, decP, decPHead, decPHead + decPLength)) { Fail("incorrect decrypt in randomised test"); } byte[] decT = cipher.GetMac(); if (!AreEqual(encT, decT)) { Fail("decryption produced different mac from encryption"); } // // key reuse test // cipher.Init(false, AeadTestUtilities.ReuseKey(parameters)); decPHead = random.Next(256); decPLength = cipher.GetOutputSize(ctLength); decPTail = random.Next(256); decP = new byte[decPHead + decPLength + decPTail]; split = NextInt(random, saLength + 1); cipher.ProcessAadBytes(C, 0, split); cipher.ProcessAadBytes(C, split, saLength - split); len = cipher.ProcessBytes(C, saLength, ctLength, decP, decPHead); len += cipher.DoFinal(decP, decPHead + len); if (!AreEqual(P, pHead, pHead + pLength, decP, decPHead, decPHead + decPLength)) { Fail("incorrect decrypt in randomised test"); } decT = cipher.GetMac(); if (!AreEqual(encT, decT)) { Fail("decryption produced different mac from encryption"); } }
private byte[] ChaCha20_Poly1305(CBORObject alg, byte[] K) { ChaCha20Poly1305 cipher = new ChaCha20Poly1305(); KeyParameter ContentKey; int cbitTag = 128; // The requirements from JWA // IV is 96 bits // Authentication tag is 128 bits // key size is 256 bits byte[] IV = new byte[96 / 8]; CBORObject cbor = FindAttribute(HeaderKeys.IV); if (cbor != null) { if (cbor.Type != CBORType.ByteString) { throw new CoseException("IV is incorrectly formed."); } if (cbor.GetByteString().Length > IV.Length) { throw new CoseException("IV is too long."); } Array.Copy(cbor.GetByteString(), 0, IV, IV.Length - cbor.GetByteString().Length, cbor.GetByteString().Length); } else { s_PRNG.NextBytes(IV); AddAttribute(HeaderKeys.IV, CBORObject.FromObject(IV), UNPROTECTED); } if (K == null) { Debug.Assert(alg.Type == CBORType.Number); switch ((AlgorithmValuesInt)alg.AsInt32()) { case AlgorithmValuesInt.ChaCha20_Poly1305: K = new byte[256 / 8]; cbitTag = 128; break; default: throw new CoseException("Unsupported algorithm: " + alg); } s_PRNG.NextBytes(K); } // Generate key ContentKey = new KeyParameter(K); // Build the object to be hashed byte[] aad = getAADBytes(); AeadParameters parameters = new AeadParameters(ContentKey, cbitTag, IV, aad); cipher.Init(true, parameters); byte[] C = new byte[cipher.GetOutputSize(rgbContent.Length)]; int len = cipher.ProcessBytes(rgbContent, 0, rgbContent.Length, C, 0); len += cipher.DoFinal(C, len); RgbEncrypted = C; return(K); }