Using Bouncy Castle (FIPS) to encrypt/decrypt a very long stream

Viewed 301

First some background, in case I'm taking the wrong approach. I have two requirements:

  1. I want to encrypt the data written and read from AnonymousPipeServerStream and AnonymousPipeClientStream

  2. I must use a FIPS-compliant NIST-accredited cryptographic module.

I'm actually using StreamJsonRpc to read and write the pipes, so I have no control over how many bytes are read and written at once. What I'm looking for is an encrypting/decrypting Stream that I can use to wrap unencrypted streams.

I'm trying to use the FIPS-compliant Bouncy Castle .Net library to do this using AES in CTR mode, which I understand is a reasonable way to encrypt a stream.

I can't work out how to increment the counter when encrypting a long data stream, so I get an exception "counter in CTR mode out of range".

Below is a compilable console app which demonstrates what I have so far. It's strongly based on the sample code in section 3.2.2 of the Bouncy Castle BC-FNA user guide.

The code below works, but if you increase the amount of data written to the stream by changing const int COPIES = 50; to const int COPIES = 60;, it will throw an exception.

My questions:

  1. Is this a reasonable approach?
  2. If so, how can I handle large streams?

The code:

using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Fips;
using Org.BouncyCastle.Utilities.Encoders;

namespace Demo
{
    class Program
    {
        static void Main()
        {
            const int COPIES = 50;

            CryptoServicesRegistrar.SetApprovedOnlyMode(true);

            var key = new FipsAes.Key(Hex.Decode("aafd12f659cae63489b479e5076ddec2"));
            IBlockCipherService provider = CryptoServicesRegistrar.CreateService(key);

            // define IV – note 15 bytes, so max message length 255 * 16 bytes
            byte[] iv = Hex.Decode("000102030405060708090a0b0c0d0e");
            
            // define data to be encrypted.
            string text = "'Twas brillig, and the slithy toves did gyre and gymble in the wabe.\n";
            byte[] toBeEncrypted = Encoding.UTF8.GetBytes(text);

            // encrypt the data.
            var bOut          = new MemoryStream();
            var encryptorBldr = provider.CreateEncryptorBuilder(FipsAes.Ctr.WithIV(iv));
            var encryptor     = encryptorBldr.BuildCipher(bOut);
            
            using (Stream encryptingStream = encryptor.Stream)
            {
                // Write several copies a byte at a time to prove it works.
                writeMultipleCopiesOneByteAtOnce(toBeEncrypted, COPIES, encryptingStream); // Change copies to 60 and it breaks.
            }

            byte[] cipherText = bOut.ToArray();

            // decrypt the resulting cipher text
            var decryptorBldr = provider.CreateDecryptorBuilder(FipsAes.Ctr.WithIV(iv));
            var decryptor     = decryptorBldr.BuildCipher(new MemoryStream(cipherText));

            using var decIn = decryptor.Stream;
            var bytes = readAllOneByteAtOnce(decIn); // Prove that we can read the decryptor stream a byte at a time.

            string result = Encoding.UTF8.GetString(bytes); // Print decrypted text.
            Console.WriteLine(result);
        }

        // Just for test purposes. I wouldn't normally write bytes like this.
        static void writeMultipleCopiesOneByteAtOnce(byte[] data, int copies, Stream output)
        {
            for (int i = 0; i < copies; ++i)
            {
                foreach (byte b in data)
                    output.WriteByte(b);
            }
        }

        // Just for test purposes. I wouldn't normally read bytes like this.
        static byte[] readAllOneByteAtOnce(Stream stream)
        {
            using var memStream = new MemoryStream();

            while (true)
            {
                int b = stream.ReadByte();

                if (b < 0)
                    return memStream.ToArray();

                memStream.WriteByte((byte)b);
            }
        }
    }
}
0 Answers
Related