示例#1
0
        public static byte[] Encrypt(byte[] keyEnc, byte[] keyMac, byte[] message)
        {
            if (keyEnc == null)
            {
                throw new ArgumentNullException(nameof(keyEnc));
            }
            if (keyMac == null)
            {
                throw new ArgumentNullException(nameof(keyMac));
            }
            if (keyEnc.Length < 32)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(keyEnc),
                          "Encryption Key must be at least 256 bits (32 bytes)");
            }
            if (keyMac.Length < 32)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(keyMac),
                          "Mac Key must be at least 256 bits (32 bytes)");
            }
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            byte[] iv;
            byte[] cipherText;
            byte[] tag;

            using (HMAC tagGenerator = GetMac(aeMac, keyMac))
            {
                using (SymmetricAlgorithm cipher = GetCipher(aeCipher, keyEnc))
                    using (ICryptoTransform encryptor = cipher.CreateEncryptor())
                    {
                        // Since no IV was provided, a random one has been generated
                        // during the call to CreateEncryptor.
                        //
                        // But note that it only does the auto-generation once. If the cipher
                        // object were used again, a call to GenerateIV would have been
                        // required.
                        iv = cipher.IV;

                        cipherText = Transform(encryptor, message, 0, message.Length);
                    }

                // The IV and ciphertest both need to be included in the MAC to prevent
                // tampering.
                //
                // By including the algorithm identifiers, we have technically moved from
                // simple Authenticated Encryption (AE) to Authenticated Encryption with
                // Additional Data (AEAD). By including the algorithm identifiers in the
                // MAC, it becomes harder for an attacker to change them as an attempt to
                // perform a downgrade attack.

                byte[] tagData       = new byte[algorithmChoices.Length + iv.Length + cipherText.Length];
                int    tagDataOffset = 0;

                Append(algorithmChoices, tagData, ref tagDataOffset);
                Append(iv, tagData, ref tagDataOffset);
                Append(cipherText, tagData, ref tagDataOffset);

                tagGenerator.AppendData(tagData);
                tag = tagGenerator.GetHashAndReset();
            }

            // Build the final result as the concatenation of everything except the keys.
            int totalLength =
                algorithmChoices.Length +
                tag.Length +
                iv.Length +
                cipherText.Length;

            byte[] output       = new byte[totalLength];
            int    outputOffset = 0;

            Append(algorithmChoices, output, ref outputOffset);
            Append(tag, output, ref outputOffset);
            Append(iv, output, ref outputOffset);
            Append(cipherText, output, ref outputOffset);

            return(output);
        }
示例#2
0
        public static byte[] Decrypt(byte[] keyEnc, byte[] keyMac, byte[] cipherText)
        {
            if (keyEnc == null)
            {
                throw new ArgumentNullException(nameof(keyEnc));
            }
            if (keyMac == null)
            {
                throw new ArgumentNullException(nameof(keyMac));
            }
            if (keyEnc.Length < 32)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(keyEnc),
                          "Encryption Key must be at least 256 bits (32 bytes)");
            }
            if (keyMac.Length < 32)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(keyMac),
                          "Mac Key must be at least 256 bits (32 bytes)");
            }
            if (cipherText == null)
            {
                throw new ArgumentNullException(nameof(cipherText));
            }

            // The format of this message is assumed to be public, so there's no harm in
            // saying ahead of time that the message makes no sense.
            if (cipherText.Length < 2)
            {
                throw new CryptographicException();
            }

            // Use the message algorithm headers to determine what cipher algorithm and
            // MAC algorithm are going to be used. Since the same Key Derivation
            // Functions (KDFs) are being used in Decrypt as Encrypt, the keys are also
            // the same.
            AeCipher aeCipher = (AeCipher)cipherText[0];
            AeMac    aeMac    = (AeMac)cipherText[1];

            using (SymmetricAlgorithm cipher = GetCipher(aeCipher, keyEnc))
                using (HMAC tagGenerator = GetMac(aeMac, keyMac))
                {
                    int blockSizeInBytes  = cipher.BlockSize / 8;
                    int tagSizeInBytes    = tagGenerator.HashSize / 8;
                    int headerSizeInBytes = 2;
                    int tagOffset         = headerSizeInBytes;
                    int ivOffset          = tagOffset + tagSizeInBytes;
                    int cipherTextOffset  = ivOffset + blockSizeInBytes;
                    int cipherTextLength  = cipherText.Length - cipherTextOffset;
                    int minLen            = cipherTextOffset + blockSizeInBytes;

                    // Again, the minimum length is still assumed to be public knowledge,
                    // nothing has leaked out yet. The minimum length couldn't just be calculated
                    // without reading the header.
                    if (cipherText.Length < minLen)
                    {
                        throw new CryptographicException();
                    }

                    // It's very important that the MAC be calculated and verified before
                    // proceeding to decrypt the ciphertext, as this prevents any sort of
                    // information leaking out to an attacker.
                    //
                    // Don't include the tag in the calculation, though.
                    var data = new byte[cipherText.Length - tagSizeInBytes];

                    // First, everything before the tag (the cipher and MAC algorithm ids)
                    Buffer.BlockCopy(cipherText, 0, data, 0, tagOffset);
                    // Skip the data before the tag and the tag, then read everything that remains.
                    Buffer.BlockCopy(cipherText, (tagOffset + tagSizeInBytes), data, tagOffset, cipherText.Length - (tagOffset + tagSizeInBytes));

                    tagGenerator.AppendData(data);

                    byte[] generatedTag = tagGenerator.GetHashAndReset();

                    byte[] expectedPayload = new byte[tagSizeInBytes];
                    Buffer.BlockCopy(cipherText, tagOffset, expectedPayload, 0, tagSizeInBytes);

                    if (!CryptographicEquals(
                            generatedTag,
                            0,
                            cipherText,
                            tagOffset,
                            tagSizeInBytes))
                    {
                        // Assuming every tampered message (of the same length) took the same
                        // amount of time to process, we can now safely say
                        // "this data makes no sense" without giving anything away.
                        throw new CryptographicException("Mismatch in signed data");
                    }

                    // Restore the IV into the symmetricAlgorithm instance.
                    byte[] iv = new byte[blockSizeInBytes];
                    Buffer.BlockCopy(cipherText, ivOffset, iv, 0, iv.Length);
                    cipher.IV = iv;

                    using (ICryptoTransform decryptor = cipher.CreateDecryptor())
                    {
                        return(Transform(
                                   decryptor,
                                   cipherText,
                                   cipherTextOffset,
                                   cipherTextLength));
                    }
                }
        }