Esempio n. 1
0
        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);
        }
Esempio n. 2
0
        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 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);
        }
        private KeyParameter InitRecordMAC(ChaCha20Poly1305 cipher)
        {
            byte[] zeroes = StringToByteArray(
                "00000000000000000000000000000000"
                + "00000000000000000000000000000000"
                + "00000000000000000000000000000000"
                + "00000000000000000000000000000000");

            byte[] firstBlock = new byte[64];
            cipher.ProcessBytes(zeroes, 0, firstBlock.Length, firstBlock, 0);

            Console.WriteLine("ChaCha OutBytes");
            Console.WriteLine(ByteArrayToString(firstBlock));

            // NOTE: The BC implementation puts 'r' after 'k'
            //Array.Copy(firstBlock, 0, firstBlock, 32, 16);
            //KeyParameter macKey = new KeyParameter(firstBlock, 16, 32);
            //Poly1305KeyGenerator.clamp(macKey.getKey());

            // 8th January, 2018 21:05
            //
            // The above code is from the github HAP-Java implementation. The problem was that the clamp() operator
            // wasn't having any effect! I'm guessing it's because the getKey() returns a new instance each time.
            // To work around this, I create a buffer, clamp it and then create a KeyParameter with the new byte[]
            // How the f**k I spotted this I'll never know.
            //

            KeyParameter macKey = new KeyParameter(firstBlock, 0, 32);

            var key = macKey.GetKey();

            //Console.WriteLine(ByteArrayToString(key));

            Poly1305KeyGenerator.Clamp(key);

            //Console.WriteLine(ByteArrayToString(key));

            Poly1305KeyGenerator.CheckKey(key);

            return(new KeyParameter(key));
        }
Esempio n. 5
0
        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;
        }
Esempio n. 6
0
        private void CheckTestCase(
            ChaCha20Poly1305 encCipher,
            ChaCha20Poly1305 decCipher,
            string testName,
            byte[]              SA,
            byte[]              P,
            byte[]              C,
            byte[]              T)
        {
            byte[] enc = new byte[encCipher.GetOutputSize(P.Length)];
            if (SA != null)
            {
                encCipher.ProcessAadBytes(SA, 0, SA.Length);
            }
            int len = encCipher.ProcessBytes(P, 0, P.Length, enc, 0);

            len += encCipher.DoFinal(enc, len);

            if (enc.Length != len)
            {
                Fail("encryption reported incorrect length: " + testName);
            }

            byte[] mac = encCipher.GetMac();

            byte[] data = new byte[P.Length];
            Array.Copy(enc, 0, data, 0, data.Length);
            byte[] tail = new byte[enc.Length - P.Length];
            Array.Copy(enc, P.Length, tail, 0, tail.Length);

            if (!AreEqual(C, data))
            {
                Fail("incorrect encrypt in: " + testName);
            }

            if (!AreEqual(T, mac))
            {
                Fail("getMac() returned wrong mac in: " + testName);
            }

            if (!AreEqual(T, tail))
            {
                Fail("stream contained wrong mac in: " + testName);
            }

            byte[] dec = new byte[decCipher.GetOutputSize(enc.Length)];
            if (SA != null)
            {
                decCipher.ProcessAadBytes(SA, 0, SA.Length);
            }
            len  = decCipher.ProcessBytes(enc, 0, enc.Length, dec, 0);
            len += decCipher.DoFinal(dec, len);
            mac  = decCipher.GetMac();

            data = new byte[C.Length];
            Array.Copy(dec, 0, data, 0, data.Length);

            if (!AreEqual(P, data))
            {
                Fail("incorrect decrypt in: " + testName);
            }
        }
Esempio n. 7
0
        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");
            }
        }
Esempio n. 8
0
        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);
        }