summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/crypto/digests/ParallelHash.cs390
-rw-r--r--crypto/src/crypto/digests/TupleHash.cs234
-rw-r--r--crypto/src/crypto/fpe/FpeEngine.cs146
-rw-r--r--crypto/src/crypto/fpe/FpeFf1Engine.cs103
-rw-r--r--crypto/src/crypto/fpe/FpeFf3_1Engine.cs105
-rw-r--r--crypto/src/crypto/fpe/SP80038G.cs20
-rw-r--r--crypto/src/crypto/modes/GcmSivBlockCipher.cs1861
-rw-r--r--crypto/src/crypto/util/BasicAlphabetMapper.cs150
-rw-r--r--crypto/src/crypto/util/Pack.cs98
-rw-r--r--crypto/src/security/SecureRandom.cs4
-rw-r--r--crypto/src/util/Arrays.cs1
-rw-r--r--crypto/src/util/Bytes.cs4
-rw-r--r--crypto/src/util/Integers.cs4
-rw-r--r--crypto/src/util/Longs.cs4
-rw-r--r--crypto/src/util/io/Streams.cs8
15 files changed, 1557 insertions, 1575 deletions
diff --git a/crypto/src/crypto/digests/ParallelHash.cs b/crypto/src/crypto/digests/ParallelHash.cs
index 1e42d86ab..7d9be7618 100644
--- a/crypto/src/crypto/digests/ParallelHash.cs
+++ b/crypto/src/crypto/digests/ParallelHash.cs
@@ -1,210 +1,210 @@
 using System;
-using System.Diagnostics;
 
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
-	/// <summary>
+    /// <summary>
     /// ParallelHash - a hash designed  to  support the efficient hashing of very long strings, by taking advantage,
-	/// of the parallelism available in modern processors with an optional XOF mode.
-	/// <para>
-	/// From NIST Special Publication 800-185 - SHA-3 Derived Functions:cSHAKE, KMAC, TupleHash and ParallelHash
-	/// </para>
+    /// of the parallelism available in modern processors with an optional XOF mode.
+    /// <para>
+    /// From NIST Special Publication 800-185 - SHA-3 Derived Functions:cSHAKE, KMAC, TupleHash and ParallelHash
+    /// </para>
     /// </summary>
-	public class ParallelHash
-	    : IXof, IDigest
-	{
-	    private static readonly byte[] N_PARALLEL_HASH = Strings.ToByteArray("ParallelHash");
-
-	    private readonly CShakeDigest cshake;
-	    private readonly CShakeDigest compressor;
-	    private readonly int bitLength;
-	    private readonly int outputLength;
-	    private readonly int B;
-	    private readonly byte[] buffer;
-	    private readonly byte[] compressorBuffer;
-
-	    private bool firstOutput;
-	    private int nCount;
-	    private int bufOff;
-
-	    /**
+    public class ParallelHash
+        : IXof, IDigest
+    {
+        private static readonly byte[] N_PARALLEL_HASH = Strings.ToByteArray("ParallelHash");
+
+        private readonly CShakeDigest cshake;
+        private readonly CShakeDigest compressor;
+        private readonly int bitLength;
+        private readonly int outputLength;
+        private readonly int B;
+        private readonly byte[] buffer;
+        private readonly byte[] compressorBuffer;
+
+        private bool firstOutput;
+        private int nCount;
+        private int bufOff;
+
+        /**
 	     * Base constructor.
 	     *
 	     * @param bitLength bit length of the underlying SHAKE function, 128 or 256.
 	     * @param S the customization string - available for local use.
 	     * @param B the blocksize (in bytes) for hashing.
 	     */
-	    public ParallelHash(int bitLength, byte[] S, int B): this(bitLength, S, B, bitLength * 2)
-		{
-
-		}
-
-	    public ParallelHash(int bitLength, byte[] S, int B, int outputSize)
-	    {
-		this.cshake = new CShakeDigest(bitLength, N_PARALLEL_HASH, S);
-		this.compressor = new CShakeDigest(bitLength, new byte[0], new byte[0]);
-		this.bitLength = bitLength;
-		this.B = B;
-		this.outputLength = (outputSize + 7) / 8;
-		this.buffer = new byte[B];
-		this.compressorBuffer = new byte[bitLength * 2 / 8];
-
-		Reset();
-	    }
-
-	    public ParallelHash(ParallelHash source)
-	    {
-		this.cshake = new CShakeDigest(source.cshake);
-		this.compressor = new CShakeDigest(source.compressor);
-		this.bitLength = source.bitLength;
-		this.B = source.B;
-		this.outputLength = source.outputLength;
-		this.buffer = Arrays.Clone(source.buffer);
-		this.compressorBuffer = Arrays.Clone(source.compressorBuffer);
-	    }
-
-	    public string AlgorithmName
-	    {
-		    get { return "ParallelHash" + cshake.AlgorithmName.Substring(6); }
-	    }
-
-	    public int GetByteLength()
-	    {
-		return cshake.GetByteLength();
-	    }
-
-	    public int GetDigestSize()
-	    {
-		return outputLength;
-	    }
-
-	    public void Update(byte b)
-	    {
-		buffer[bufOff++] = b;
-		if (bufOff == buffer.Length)
-		{
-		    compress();
-		}
-	    }
-
-	    public void BlockUpdate(byte[] inBuf, int inOff, int len)
-	    {
-		len = System.Math.Max(0,  len);
-
-		//
-		// fill the current word
-		//
-		int i = 0;
-		if (bufOff != 0)
-		{
-		    while (i < len && bufOff != buffer.Length)
-		    {
-			buffer[bufOff++] = inBuf[inOff + i++];
-		    }
-
-		    if (bufOff == buffer.Length)
-		    {
-			compress();
-		    }
-		}
-
-		if (i < len)
-		{
-		    while (len - i > B)
-		    {
-			compress(inBuf, inOff + i, B);
-			i += B;
-		    }
-		}
-
-		while (i < len)
-		{
-		    Update(inBuf[inOff + i++]);
-		}
-	    }
-
-	    private void compress()
-	    {
-		compress(buffer, 0, bufOff);
-		bufOff = 0;
-	    }
-
-	    private void compress(byte[] buf, int offSet, int len)
-	    {
-		compressor.BlockUpdate(buf, offSet, len);
-		compressor.DoFinal(compressorBuffer, 0, compressorBuffer.Length);
-
-		cshake.BlockUpdate(compressorBuffer, 0, compressorBuffer.Length);
-
-		nCount++;
-	    }
-
-	    private void wrapUp(int outputSize)
-	    {
-		if (bufOff != 0)
-		{
-		    compress();
-		}
-		byte[] nOut = XofUtilities.RightEncode(nCount);
-		byte[] encOut = XofUtilities.RightEncode(outputSize * 8);
-
-		cshake.BlockUpdate(nOut, 0, nOut.Length);
-		cshake.BlockUpdate(encOut, 0, encOut.Length);
-
-		firstOutput = false;
-	    }
-
-	    public int DoFinal(byte[] outBuf, int outOff)
-	    {
-		if (firstOutput)
-		{
-		    wrapUp(outputLength);
-		}
-
-		int rv = cshake.DoFinal(outBuf, outOff, GetDigestSize());
-
-		Reset();
-
-		return rv;
-	    }
-
-	    public int DoFinal(byte[] outBuf, int outOff, int outLen)
-	    {
-		if (firstOutput)
-		{
-		    wrapUp(outputLength);
-		}
-		
-		int rv = cshake.DoFinal(outBuf, outOff, outLen);
-
-		Reset();
-
-		return rv;
-	    }
-
-	    public int DoOutput(byte[] outBuf, int outOff, int outLen)
-	    {
-		if (firstOutput)
-		{
-		    wrapUp(0);
-		}
-
-		return cshake.DoOutput(outBuf, outOff, outLen);
-	    }
-
-	    public void Reset()
-	    {
-		cshake.Reset();
-		Arrays.Clear(buffer);
-
-		byte[] hdr = XofUtilities.LeftEncode(B);
-		cshake.BlockUpdate(hdr, 0, hdr.Length);
-
-		nCount = 0;
-		bufOff = 0;
-		firstOutput = true;
-	    }
-	}
+        public ParallelHash(int bitLength, byte[] S, int B)
+            : this(bitLength, S, B, bitLength * 2)
+        {
+
+        }
+
+        public ParallelHash(int bitLength, byte[] S, int B, int outputSize)
+        {
+            this.cshake = new CShakeDigest(bitLength, N_PARALLEL_HASH, S);
+            this.compressor = new CShakeDigest(bitLength, new byte[0], new byte[0]);
+            this.bitLength = bitLength;
+            this.B = B;
+            this.outputLength = (outputSize + 7) / 8;
+            this.buffer = new byte[B];
+            this.compressorBuffer = new byte[bitLength * 2 / 8];
+
+            Reset();
+        }
+
+        public ParallelHash(ParallelHash source)
+        {
+            this.cshake = new CShakeDigest(source.cshake);
+            this.compressor = new CShakeDigest(source.compressor);
+            this.bitLength = source.bitLength;
+            this.B = source.B;
+            this.outputLength = source.outputLength;
+            this.buffer = Arrays.Clone(source.buffer);
+            this.compressorBuffer = Arrays.Clone(source.compressorBuffer);
+        }
+
+        public virtual string AlgorithmName
+        {
+            get { return "ParallelHash" + cshake.AlgorithmName.Substring(6); }
+        }
+
+        public virtual int GetByteLength()
+        {
+            return cshake.GetByteLength();
+        }
+
+        public virtual int GetDigestSize()
+        {
+            return outputLength;
+        }
+
+        public virtual void Update(byte b)
+        {
+            buffer[bufOff++] = b;
+            if (bufOff == buffer.Length)
+            {
+                compress();
+            }
+        }
+
+        public virtual void BlockUpdate(byte[] inBuf, int inOff, int len)
+        {
+            len = System.Math.Max(0, len);
+
+            //
+            // fill the current word
+            //
+            int i = 0;
+            if (bufOff != 0)
+            {
+                while (i < len && bufOff != buffer.Length)
+                {
+                    buffer[bufOff++] = inBuf[inOff + i++];
+                }
+
+                if (bufOff == buffer.Length)
+                {
+                    compress();
+                }
+            }
+
+            if (i < len)
+            {
+                while (len - i > B)
+                {
+                    compress(inBuf, inOff + i, B);
+                    i += B;
+                }
+            }
+
+            while (i < len)
+            {
+                Update(inBuf[inOff + i++]);
+            }
+        }
+
+        private void compress()
+        {
+            compress(buffer, 0, bufOff);
+            bufOff = 0;
+        }
+
+        private void compress(byte[] buf, int offSet, int len)
+        {
+            compressor.BlockUpdate(buf, offSet, len);
+            compressor.DoFinal(compressorBuffer, 0, compressorBuffer.Length);
+
+            cshake.BlockUpdate(compressorBuffer, 0, compressorBuffer.Length);
+
+            nCount++;
+        }
+
+        private void wrapUp(int outputSize)
+        {
+            if (bufOff != 0)
+            {
+                compress();
+            }
+            byte[] nOut = XofUtilities.RightEncode(nCount);
+            byte[] encOut = XofUtilities.RightEncode(outputSize * 8);
+
+            cshake.BlockUpdate(nOut, 0, nOut.Length);
+            cshake.BlockUpdate(encOut, 0, encOut.Length);
+
+            firstOutput = false;
+        }
+
+        public virtual int DoFinal(byte[] outBuf, int outOff)
+        {
+            if (firstOutput)
+            {
+                wrapUp(outputLength);
+            }
+
+            int rv = cshake.DoFinal(outBuf, outOff, GetDigestSize());
+
+            Reset();
+
+            return rv;
+        }
+
+        public virtual int DoFinal(byte[] outBuf, int outOff, int outLen)
+        {
+            if (firstOutput)
+            {
+                wrapUp(outputLength);
+            }
+
+            int rv = cshake.DoFinal(outBuf, outOff, outLen);
+
+            Reset();
+
+            return rv;
+        }
+
+        public virtual int DoOutput(byte[] outBuf, int outOff, int outLen)
+        {
+            if (firstOutput)
+            {
+                wrapUp(0);
+            }
+
+            return cshake.DoOutput(outBuf, outOff, outLen);
+        }
+
+        public virtual void Reset()
+        {
+            cshake.Reset();
+            Arrays.Clear(buffer);
+
+            byte[] hdr = XofUtilities.LeftEncode(B);
+            cshake.BlockUpdate(hdr, 0, hdr.Length);
+
+            nCount = 0;
+            bufOff = 0;
+            firstOutput = true;
+        }
+    }
 }
diff --git a/crypto/src/crypto/digests/TupleHash.cs b/crypto/src/crypto/digests/TupleHash.cs
index 76bba3f94..98c2d2acf 100644
--- a/crypto/src/crypto/digests/TupleHash.cs
+++ b/crypto/src/crypto/digests/TupleHash.cs
@@ -1,134 +1,134 @@
 using System;
-using System.Diagnostics;
 
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
-	/// <summary>
+    /// <summary>
     /// TupleHash - a hash designed  to  simply  hash  a  tuple  of  input  strings,  any  or  all  of  which  may  be  empty  strings,
-	/// in  an  unambiguous way with an optional XOF mode.
-	/// <para>
-	/// From NIST Special Publication 800-185 - SHA-3 Derived Functions:cSHAKE, KMAC, TupleHash and ParallelHash
-	/// </para>
+    /// in  an  unambiguous way with an optional XOF mode.
+    /// <para>
+    /// From NIST Special Publication 800-185 - SHA-3 Derived Functions:cSHAKE, KMAC, TupleHash and ParallelHash
+    /// </para>
     /// </summary>
-	public class TupleHash
-	    : IXof, IDigest
-	{
-	    private static readonly byte[] N_TUPLE_HASH = Strings.ToByteArray("TupleHash");
+    public class TupleHash
+        : IXof, IDigest
+    {
+        private static readonly byte[] N_TUPLE_HASH = Strings.ToByteArray("TupleHash");
 
-	    private readonly CShakeDigest cshake;
-	    private readonly int bitLength;
-	    private readonly int outputLength;
+        private readonly CShakeDigest cshake;
+        private readonly int bitLength;
+        private readonly int outputLength;
 
-	    private bool firstOutput;
+        private bool firstOutput;
 
-	    /**
+        /**
 	     * Base constructor.
 	     *
 	     * @param bitLength bit length of the underlying SHAKE function, 128 or 256.
 	     * @param S         the customization string - available for local use.
 	     */
-	    public TupleHash(int bitLength, byte[] S): this(bitLength, S, bitLength * 2)
-		{
-
-		}
-
-	    public TupleHash(int bitLength, byte[] S, int outputSize)
-	    {
-		this.cshake = new CShakeDigest(bitLength, N_TUPLE_HASH, S);
-		this.bitLength = bitLength;
-		this.outputLength = (outputSize + 7) / 8;
-
-		Reset();
-	    }
-
-	    public TupleHash(TupleHash original)
-	    {
-		this.cshake = new CShakeDigest(original.cshake);
-		this.bitLength = cshake.fixedOutputLength;
-		this.outputLength = bitLength * 2 / 8;
-		this.firstOutput = original.firstOutput;
-	    }
-
-	    public string AlgorithmName
-	    {
-		   get { return "TupleHash" + cshake.AlgorithmName.Substring(6); }
-	    }
-
-	    public int GetByteLength()
-	    {
-		return cshake.GetByteLength();
-	    }
-
-	    public int GetDigestSize()
-	    {
-		return outputLength;
-	    }
-
-	    public void Update(byte b)
-	    {
-		byte[] bytes = XofUtilities.Encode(b);
-		cshake.BlockUpdate(bytes, 0, bytes.Length);
-	    }
-
-	    public void BlockUpdate(byte[] inBuf, int inOff, int len)
-	    {
-		byte[] bytes = XofUtilities.Encode(inBuf, inOff, len);
-		cshake.BlockUpdate(bytes, 0, bytes.Length);
-	    }
-
-	    private void wrapUp(int outputSize)
-	    {
-		byte[] encOut = XofUtilities.RightEncode(outputSize * 8);
-
-		cshake.BlockUpdate(encOut, 0, encOut.Length);
-
-		firstOutput = false;
-	    }
-
-	    public int DoFinal(byte[] outBuf, int outOff)
-	    {
-		if (firstOutput)
-		{
-		    wrapUp(GetDigestSize());
-		}
-		
-		int rv = cshake.DoFinal(outBuf, outOff, GetDigestSize());
-
-		Reset();
-
-		return rv;
-	    }
-
-	    public int DoFinal(byte[] outBuf, int outOff, int outLen)
-	    {
-		if (firstOutput)
-		{
-		    wrapUp(GetDigestSize());
-		}
-		
-		int rv = cshake.DoFinal(outBuf, outOff, outLen);
-
-		Reset();
-
-		return rv;
-	    }
-
-	    public int DoOutput(byte[] outBuf, int outOff, int outLen)
-	    {
-		if (firstOutput)
-		{
-		    wrapUp(0);
-		}
-
-		return cshake.DoOutput(outBuf, outOff, outLen);
-	    }
-
-	    public void Reset()
-	    {
-		cshake.Reset();
-		firstOutput = true;
-	    }
-	}
+        public TupleHash(int bitLength, byte[] S)
+            : this(bitLength, S, bitLength * 2)
+        {
+
+        }
+
+        public TupleHash(int bitLength, byte[] S, int outputSize)
+        {
+            this.cshake = new CShakeDigest(bitLength, N_TUPLE_HASH, S);
+            this.bitLength = bitLength;
+            this.outputLength = (outputSize + 7) / 8;
+
+            Reset();
+        }
+
+        public TupleHash(TupleHash original)
+        {
+            this.cshake = new CShakeDigest(original.cshake);
+            this.bitLength = cshake.fixedOutputLength;
+            this.outputLength = bitLength * 2 / 8;
+            this.firstOutput = original.firstOutput;
+        }
+
+        public virtual string AlgorithmName
+        {
+            get { return "TupleHash" + cshake.AlgorithmName.Substring(6); }
+        }
+
+        public virtual int GetByteLength()
+        {
+            return cshake.GetByteLength();
+        }
+
+        public virtual int GetDigestSize()
+        {
+            return outputLength;
+        }
+
+        public virtual void Update(byte b)
+        {
+            byte[] bytes = XofUtilities.Encode(b);
+            cshake.BlockUpdate(bytes, 0, bytes.Length);
+        }
+
+        public virtual void BlockUpdate(byte[] inBuf, int inOff, int len)
+        {
+            byte[] bytes = XofUtilities.Encode(inBuf, inOff, len);
+            cshake.BlockUpdate(bytes, 0, bytes.Length);
+        }
+
+        private void wrapUp(int outputSize)
+        {
+            byte[] encOut = XofUtilities.RightEncode(outputSize * 8);
+
+            cshake.BlockUpdate(encOut, 0, encOut.Length);
+
+            firstOutput = false;
+        }
+
+        public virtual int DoFinal(byte[] outBuf, int outOff)
+        {
+            if (firstOutput)
+            {
+                wrapUp(GetDigestSize());
+            }
+
+            int rv = cshake.DoFinal(outBuf, outOff, GetDigestSize());
+
+            Reset();
+
+            return rv;
+        }
+
+        public virtual int DoFinal(byte[] outBuf, int outOff, int outLen)
+        {
+            if (firstOutput)
+            {
+                wrapUp(GetDigestSize());
+            }
+
+            int rv = cshake.DoFinal(outBuf, outOff, outLen);
+
+            Reset();
+
+            return rv;
+        }
+
+        public virtual int DoOutput(byte[] outBuf, int outOff, int outLen)
+        {
+            if (firstOutput)
+            {
+                wrapUp(0);
+            }
+
+            return cshake.DoOutput(outBuf, outOff, outLen);
+        }
+
+        public virtual void Reset()
+        {
+            cshake.Reset();
+            firstOutput = true;
+        }
+    }
 }
diff --git a/crypto/src/crypto/fpe/FpeEngine.cs b/crypto/src/crypto/fpe/FpeEngine.cs
index 6757bad3a..5545b7dca 100644
--- a/crypto/src/crypto/fpe/FpeEngine.cs
+++ b/crypto/src/crypto/fpe/FpeEngine.cs
@@ -1,121 +1,73 @@
 using System;
-using System.Diagnostics;
 
-using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
-using Org.BouncyCastle.Crypto.Utilities;
-using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Fpe
 {
-/**
- * Base class for format-preserving encryption.
- */
-public abstract class FpeEngine
-{
-    protected IBlockCipher baseCipher;
-
-    protected bool forEncryption;
-    protected FpeParameters fpeParameters;
-
-    protected FpeEngine(IBlockCipher baseCipher)
+    /// <summary>Base class for format-preserving encryption.</summary>
+    public abstract class FpeEngine
     {
-        this.baseCipher = baseCipher;
-    }
-
-    /// <summary>
-    /// Process length bytes from inBuf, writing the output to outBuf.
-    /// </summary>
-    /// <returns>number of bytes output.</returns>
-    /// <param name="inBuf">input data.</param>  
-    /// <param name="inOff">offset in input data to start at.</param>  
-    /// <param name="length">number of bytes to process.</param>  
-    /// <param name="outBuf">destination buffer.</param>  
-    /// <param name="outOff">offset to start writing at in destination buffer.</param>  
-    public int ProcessBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff)
-    {
-        if (fpeParameters == null)
-        {
-            throw new InvalidOperationException("FPE engine not initialized");
-        }
-
-        if (length < 0)
-        {
-            throw new ArgumentException("input length cannot be negative");
-        }
+        protected readonly IBlockCipher baseCipher;
 
-        if (inBuf == null || outBuf == null)
-        {
-            throw new NullReferenceException("buffer value is null");
-        }
+        protected bool forEncryption;
+        protected FpeParameters fpeParameters;
 
-        if (inBuf.Length < inOff + length)
+        protected FpeEngine(IBlockCipher baseCipher)
         {
-            throw new DataLengthException("input buffer too short");
+            this.baseCipher = baseCipher;
         }
 
-        if (outBuf.Length < outOff + length)
-        {
-            throw new OutputLengthException("output buffer too short");
-        }
-
-        if (forEncryption)
-        {
-            return encryptBlock(inBuf, inOff, length, outBuf, outOff);
-        }
-        else
+        /// <summary>
+        /// Process length bytes from inBuf, writing the output to outBuf.
+        /// </summary>
+        /// <returns>number of bytes output.</returns>
+        /// <param name="inBuf">input data.</param>  
+        /// <param name="inOff">offset in input data to start at.</param>  
+        /// <param name="length">number of bytes to process.</param>  
+        /// <param name="outBuf">destination buffer.</param>  
+        /// <param name="outOff">offset to start writing at in destination buffer.</param>  
+        public virtual int ProcessBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff)
         {
-            return decryptBlock(inBuf, inOff, length, outBuf, outOff);
+            if (fpeParameters == null)
+                throw new InvalidOperationException("FPE engine not initialized");
+            if (length < 0)
+                throw new ArgumentException("cannot be negative", "length");
+            if (inBuf == null)
+                throw new ArgumentNullException("inBuf");
+            if (outBuf == null)
+                throw new ArgumentNullException("outBuf");
+
+            Check.DataLength(inBuf, inOff, length, "input buffer too short");
+            Check.OutputLength(outBuf, outOff, length, "output buffer too short");
+
+            if (forEncryption)
+            {
+                return EncryptBlock(inBuf, inOff, length, outBuf, outOff);
+            }
+            else
+            {
+                return DecryptBlock(inBuf, inOff, length, outBuf, outOff);
+            }
         }
-    }
 
-    protected static ushort[] toShortArray(byte[] buf)
-    {
-        if ((buf.Length & 1) != 0)
+        protected static bool IsOverrideSet(string propName)
         {
-            throw new ArgumentException("data must be an even number of bytes for a wide radix");
-        }
+            string propValue = Platform.GetEnvironmentVariable(propName);
 
-        ushort[] rv = new ushort[buf.Length / 2];
-
-        for (int i = 0; i != rv.Length; i++)
-        {
-            rv[i] = Pack.BE_To_UInt16(buf, i * 2);
+            return propValue != null && Platform.EqualsIgnoreCase("true", propValue);
         }
 
-        return rv;
-    }
-
-    protected static bool IsOverrideSet(string propName)
-    {
-        string propValue = Platform.GetEnvironmentVariable(propName);
-
-        return propValue == null || Platform.EqualsIgnoreCase("true", propValue);
-    }
-
-    protected static byte[] toByteArray(ushort[] buf)
-    {
-        byte[] rv = new byte[buf.Length * 2];
+        /// <summary>
+        /// Initialize the FPE engine for encryption/decryption.
+        /// </summary>
+        /// <returns>number of bytes output.</returns>
+        /// <param name="forEncryption">true if initialising for encryption, false otherwise.</param>  
+        /// <param name="parameters ">the key and other parameters to use to set the engine up.</param>  
+        public abstract void Init(bool forEncryption, ICipherParameters parameters);
 
-        for (int i = 0; i != buf.Length; i++)
-        {
-            Pack.UInt16_To_BE(buf[i], rv, i * 2);
-        }
+        protected abstract int EncryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff);
 
-        return rv;
+        protected abstract int DecryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff);
     }
-
-    /// <summary>
-    /// Initialize the FPE engine for encryption/decryption.
-    /// </summary>
-    /// <returns>number of bytes output.</returns>
-    /// <param name="forEncryption">true if initialising for encryption, false otherwise.</param>  
-    /// <param name="parameters ">the key and other parameters to use to set the engine up.</param>  
-    public abstract void Init(bool forEncryption, ICipherParameters parameters);
-
-    protected abstract int encryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff);
-
-    protected abstract int decryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff);
-}
 }
diff --git a/crypto/src/crypto/fpe/FpeFf1Engine.cs b/crypto/src/crypto/fpe/FpeFf1Engine.cs
index 8f34ef888..acd31b6f4 100644
--- a/crypto/src/crypto/fpe/FpeFf1Engine.cs
+++ b/crypto/src/crypto/fpe/FpeFf1Engine.cs
@@ -1,70 +1,83 @@
 using System;
-using System.Diagnostics;
 
 using Org.BouncyCastle.Crypto.Engines;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Fpe
 {
-public class FpeFf1Engine
-    : FpeEngine
-{
-    public FpeFf1Engine(): this(new AesEngine())
-    {
-    }
-
-    public FpeFf1Engine(IBlockCipher baseCipher): base(baseCipher)
+    public class FpeFf1Engine
+        : FpeEngine
     {
-        if (IsOverrideSet(SP80038G.FPE_DISABLED)
-            || IsOverrideSet(SP80038G.FF1_DISABLED))
+        public FpeFf1Engine()
+            : this(new AesEngine())
         {
-            throw new InvalidOperationException("FF1 encryption disabled");
         }
-    }
-
-    public override void Init(bool forEncryption, ICipherParameters parameters)
-    {
-        this.forEncryption = forEncryption;
 
-        this.fpeParameters = (FpeParameters)parameters;
-
-        baseCipher.Init(!fpeParameters.UseInverseFunction, fpeParameters.Key);
-    }
-
-    protected override int encryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff)
-    {
-        byte[] enc;
-
-        if (fpeParameters.Radix > 256)
+        public FpeFf1Engine(IBlockCipher baseCipher)
+            : base(baseCipher)
         {
-            enc = toByteArray(SP80038G.EncryptFF1w(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), toShortArray(inBuf), inOff, length / 2));
+            if (IsOverrideSet(SP80038G.FPE_DISABLED) ||
+                IsOverrideSet(SP80038G.FF1_DISABLED))
+            {
+                throw new InvalidOperationException("FF1 encryption disabled");
+            }
         }
-        else
+
+        public override void Init(bool forEncryption, ICipherParameters parameters)
         {
-            enc = SP80038G.EncryptFF1(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), inBuf, inOff, length);
+            this.forEncryption = forEncryption;
+            this.fpeParameters = (FpeParameters)parameters;
+
+            baseCipher.Init(!fpeParameters.UseInverseFunction, fpeParameters.Key);
         }
 
-        Array.Copy(enc, 0, outBuf, outOff, length);
+        protected override int EncryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff)
+        {
+            byte[] enc;
 
-        return length;
-    }
+            if (fpeParameters.Radix > 256)
+            {
+                if ((length & 1) != 0)
+                    throw new ArgumentException("input must be an even number of bytes for a wide radix");
 
-    protected override int decryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff)
-    {
-        byte[] dec;
+                ushort[] u16In = Pack.BE_To_UInt16(inBuf, inOff, length);
+                ushort[] u16Out = SP80038G.EncryptFF1w(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(),
+                    u16In, 0, u16In.Length);
+                enc = Pack.UInt16_To_BE(u16Out, 0, u16Out.Length);
+            }
+            else
+            {
+                enc = SP80038G.EncryptFF1(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), inBuf, inOff, length);
+            }
 
-        if (fpeParameters.Radix > 256)
-        {
-            dec = toByteArray(SP80038G.DecryptFF1w(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), toShortArray(inBuf), inOff, length / 2));
+            Array.Copy(enc, 0, outBuf, outOff, length);
+
+            return length;
         }
-        else
+
+        protected override int DecryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff)
         {
-            dec = SP80038G.DecryptFF1(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), inBuf, inOff, length);
-        }
+            byte[] dec;
+
+            if (fpeParameters.Radix > 256)
+            {
+                if ((length & 1) != 0)
+                    throw new ArgumentException("input must be an even number of bytes for a wide radix");
 
-        Array.Copy(dec, 0, outBuf, outOff, length);
+                ushort[] u16In = Pack.BE_To_UInt16(inBuf, inOff, length);
+                ushort[] u16Out = SP80038G.DecryptFF1w(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(),
+                    u16In, 0, u16In.Length);
+                dec = Pack.UInt16_To_BE(u16Out, 0, u16Out.Length);
+            }
+            else
+            {
+                dec = SP80038G.DecryptFF1(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), inBuf, inOff, length);
+            }
 
-        return length;
+            Array.Copy(dec, 0, outBuf, outOff, length);
+
+            return length;
+        }
     }
 }
-}
diff --git a/crypto/src/crypto/fpe/FpeFf3_1Engine.cs b/crypto/src/crypto/fpe/FpeFf3_1Engine.cs
index 480560bb2..aa238e46a 100644
--- a/crypto/src/crypto/fpe/FpeFf3_1Engine.cs
+++ b/crypto/src/crypto/fpe/FpeFf3_1Engine.cs
@@ -1,75 +1,86 @@
 using System;
-using System.Diagnostics;
 
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Fpe
 {
-public class FpeFf3_1Engine
-    : FpeEngine
-{
-    public FpeFf3_1Engine(): this(new AesEngine())
-    {
-    }
-
-    public FpeFf3_1Engine(IBlockCipher baseCipher): base(baseCipher)
+    public class FpeFf3_1Engine
+        : FpeEngine
     {
-        if (IsOverrideSet(SP80038G.FPE_DISABLED))
+        public FpeFf3_1Engine()
+            : this(new AesEngine())
         {
-            throw new InvalidOperationException("FPE disabled");
         }
-    }
-
-    public override void Init(bool forEncryption, ICipherParameters parameters)
-    {
-        this.forEncryption = forEncryption;
 
-        this.fpeParameters = (FpeParameters)parameters;
-
-        baseCipher.Init(!fpeParameters.UseInverseFunction, new KeyParameter(Arrays.Reverse(fpeParameters.Key.GetKey())));
-
-        if (fpeParameters.GetTweak().Length != 7)
+        public FpeFf3_1Engine(IBlockCipher baseCipher)
+            : base(baseCipher)
         {
-            throw new ArgumentException("tweak should be 56 bits");
+            if (IsOverrideSet(SP80038G.FPE_DISABLED))
+            {
+                throw new InvalidOperationException("FPE disabled");
+            }
         }
-    }
 
-    protected override int encryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff)
-    {
-        byte[] enc;
-
-        if (fpeParameters.Radix > 256)
+        public override void Init(bool forEncryption, ICipherParameters parameters)
         {
-            enc = toByteArray(SP80038G.EncryptFF3_1w(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), toShortArray(inBuf), inOff, length / 2));
+            this.forEncryption = forEncryption;
+            this.fpeParameters = (FpeParameters)parameters;
+
+            baseCipher.Init(!fpeParameters.UseInverseFunction, new KeyParameter(Arrays.Reverse(fpeParameters.Key.GetKey())));
+
+            if (fpeParameters.GetTweak().Length != 7)
+                throw new ArgumentException("tweak should be 56 bits");
         }
-        else
+
+        protected override int EncryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff)
         {
-            enc = SP80038G.EncryptFF3_1(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), inBuf, inOff, length);
-        }
+            byte[] enc;
 
-        Array.Copy(enc, 0, outBuf, outOff, length);
+            if (fpeParameters.Radix > 256)
+            {
+                if ((length & 1) != 0)
+                    throw new ArgumentException("input must be an even number of bytes for a wide radix");
 
-        return length;
-    }
+                ushort[] u16In = Pack.BE_To_UInt16(inBuf, inOff, length);
+                ushort[] u16Out = SP80038G.EncryptFF3_1w(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(),
+                    u16In, 0, u16In.Length);
+                enc = Pack.UInt16_To_BE(u16Out, 0, u16Out.Length);
+            }
+            else
+            {
+                enc = SP80038G.EncryptFF3_1(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), inBuf, inOff, length);
+            }
 
-    protected override int decryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff)
-    {
-        byte[] dec;
+            Array.Copy(enc, 0, outBuf, outOff, length);
 
-        if (fpeParameters.Radix > 256)
-        {
-            dec = toByteArray(SP80038G.DecryptFF3_1w(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), toShortArray(inBuf), inOff, length / 2));
+            return length;
         }
-        else
+
+        protected override int DecryptBlock(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff)
         {
-            dec = SP80038G.DecryptFF3_1(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), inBuf, inOff, length);
-        }
+            byte[] dec;
+
+            if (fpeParameters.Radix > 256)
+            {
+                if ((length & 1) != 0)
+                    throw new ArgumentException("input must be an even number of bytes for a wide radix");
 
-        Array.Copy(dec, 0, outBuf, outOff, length);
+                ushort[] u16In = Pack.BE_To_UInt16(inBuf, inOff, length);
+                ushort[] u16Out = SP80038G.DecryptFF3_1w(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(),
+                    u16In, 0, u16In.Length);
+                dec = Pack.UInt16_To_BE(u16Out, 0, u16Out.Length);
+            }
+            else
+            {
+                dec = SP80038G.DecryptFF3_1(baseCipher, fpeParameters.Radix, fpeParameters.GetTweak(), inBuf, inOff, length);
+            }
 
-        return length;
+            Array.Copy(dec, 0, outBuf, outOff, length);
+
+            return length;
+        }
     }
 }
-}
diff --git a/crypto/src/crypto/fpe/SP80038G.cs b/crypto/src/crypto/fpe/SP80038G.cs
index 4ce89c9b2..53efc1499 100644
--- a/crypto/src/crypto/fpe/SP80038G.cs
+++ b/crypto/src/crypto/fpe/SP80038G.cs
@@ -1,8 +1,5 @@
 using System;
-using System.Diagnostics;
 
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Utilities;
@@ -685,14 +682,13 @@ namespace Org.BouncyCastle.Crypto.Fpe
             return s;
         }
 
-	private static int Ceil(double v)
-	{
-		int rv = (int)v;
-		if ((double)rv < v)
-                {
-			return rv + 1;
-                }
-                return rv;
-	}
+	    private static int Ceil(double v)
+	    {
+		    int rv = (int)v;
+		    if ((double)rv < v)
+			    return rv + 1;
+
+            return rv;
+	    }
     }
 }
diff --git a/crypto/src/crypto/modes/GcmSivBlockCipher.cs b/crypto/src/crypto/modes/GcmSivBlockCipher.cs
index e54f1c201..10e7e774b 100644
--- a/crypto/src/crypto/modes/GcmSivBlockCipher.cs
+++ b/crypto/src/crypto/modes/GcmSivBlockCipher.cs
@@ -1,964 +1,931 @@
 using System;
-using System.Diagnostics;
+using System.IO;
 
-using Org.BouncyCastle.Utilities.IO;
-using Org.BouncyCastle.Crypto.Macs;
 using Org.BouncyCastle.Crypto.Engines;
-using Org.BouncyCastle.Crypto.Modes;
 using Org.BouncyCastle.Crypto.Modes.Gcm;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Crypto.Modes
 {
-	/**
-	 * GCM-SIV Mode.
-	 * <p>It should be noted that the specified limit of 2<sup>36</sup> bytes is not supported. This is because all bytes are
-	 * cached in a <b>ByteArrayOutputStream</b> object (which has a limit of a little less than 2<sup>31</sup> bytes),
-	 * and are output on the <b>DoFinal</b>() call (which can only process a maximum of 2<sup>31</sup> bytes).</p>
-	 * <p>The practical limit of 2<sup>31</sup> - 24 bytes is policed, and attempts to breach the limit will be rejected</p>
-	 * <p>In order to properly support the higher limit, an extended form of <b>ByteArrayOutputStream</b> would be needed
-	 * which would use multiple arrays to store the data. In addition, a new <b>doOutput</b> method would be required (similar
-	 * to that in <b>XOF</b> digests), which would allow the data to be output over multiple calls. Alternatively an extended
-	 * form of <b>ByteArrayInputStream</b> could be used to deliver the data.</p>
-	 */
-	public class GcmSivBlockCipher
-		 : IAeadBlockCipher 
-	{
-	     /**
-	      * The buffer length.
-	      */
-	     private static readonly int BUFLEN = 16;
-
-	     /**
-	      * The halfBuffer length.
-	      */
-	     private static readonly int HALFBUFLEN = BUFLEN >> 1;
-
-	     /**
-	      * The nonce length.
-	      */
-	     private static readonly int NONCELEN = 12;
-
-	     /**
-	      * The maximum data length (AEAD/PlainText). Due to implementation constraints this is restricted to the maximum
-	      * array length (https://programming.guide/java/array-maximum-length.html) minus the BUFLEN to allow for the MAC
-	      */
-	     private static readonly int MAX_DATALEN = Int32.MaxValue - 8 - BUFLEN;
-
-	     /**
-	      * The top bit mask.
-	      */
-	     private static readonly byte MASK = (byte) 0x80;
-
-	     /**
-	      * The addition constant.
-	      */
-	     private static readonly byte ADD = (byte) 0xE1;
-
-	     /**
-	      * The initialisation flag.
-	      */
-	     private static readonly int INIT = 1;
-
-	     /**
-	      * The aeadComplete flag.
-	      */
-	     private static readonly int AEAD_COMPLETE = 2;
-
-	     /**
-	      * The cipher.
-	      */
-	     private readonly IBlockCipher theCipher;
-
-	     /**
-	      * The multiplier.
-	      */
-	     private readonly IGcmMultiplier theMultiplier;
-
-	     /**
-	      * The gHash buffer.
-	      */
-	     internal readonly byte[] theGHash = new byte[BUFLEN];
-
-	     /**
-	      * The reverse buffer.
-	      */
-	     internal readonly byte[] theReverse = new byte[BUFLEN];
-
-	     /**
-	      * The aeadHasher.
-	      */
-	     private readonly GCMSIVHasher theAEADHasher;
-
-	     /**
-	      * The dataHasher.
-	      */
-	     private readonly GCMSIVHasher theDataHasher;
-
-	     /**
-	      * The plainDataStream.
-	      */
-	     private GCMSIVCache thePlain;
-
-	     /**
-	      * The encryptedDataStream (decryption only).
-	      */
-	     private GCMSIVCache theEncData;
-
-	     /**
-	      * Are we encrypting?
-	      */
-	     private bool forEncryption;
-
-	     /**
-	      * The initialAEAD.
-	      */
-	     private byte[] theInitialAEAD;
-
-	     /**
-	      * The nonce.
-	      */
-	     private byte[] theNonce;
-
-	     /**
-	      * The flags.
-	      */
-	     private int theFlags;
-
-	     /**
-	      * Constructor.
-	      */
-	     public GcmSivBlockCipher(): this(new AesEngine())
-	     {
-		 
-	     }
-
-	     /**
-	      * Constructor.
-	      * @param pCipher the underlying cipher
-	      */
-	     public GcmSivBlockCipher(IBlockCipher pCipher): this(pCipher, new Tables4kGcmMultiplier())
-	     {
-		 
-	     }
-
-	     /**
-	      * Constructor.
-	      * @param pCipher the underlying cipher
-	      * @param pMultiplier the multiplier
-	      */
-	     public GcmSivBlockCipher(IBlockCipher pCipher,
-				      IGcmMultiplier pMultiplier)
-	     {
-		 /* Ensure that the cipher is the correct size */
-		 if (pCipher.GetBlockSize() != BUFLEN)
-		 {
-		     throw new ArgumentException("Cipher required with a block size of " + BUFLEN + ".");
-		 }
-
-		 /* Store parameters */
-		 theCipher = pCipher;
-		 theMultiplier = pMultiplier;
-
-		 /* Create the hashers */
-		 theAEADHasher = new GCMSIVHasher(this);
-		 theDataHasher = new GCMSIVHasher(this);
-	     }
-
-	     public IBlockCipher GetUnderlyingCipher()
-	     {
-		 return theCipher;
-	     }
+    /**
+     * GCM-SIV Mode.
+     * <p>It should be noted that the specified limit of 2<sup>36</sup> bytes is not supported. This is because all bytes are
+     * cached in a <b>ByteArrayOutputStream</b> object (which has a limit of a little less than 2<sup>31</sup> bytes),
+     * and are output on the <b>DoFinal</b>() call (which can only process a maximum of 2<sup>31</sup> bytes).</p>
+     * <p>The practical limit of 2<sup>31</sup> - 24 bytes is policed, and attempts to breach the limit will be rejected</p>
+     * <p>In order to properly support the higher limit, an extended form of <b>ByteArrayOutputStream</b> would be needed
+     * which would use multiple arrays to store the data. In addition, a new <b>doOutput</b> method would be required (similar
+     * to that in <b>XOF</b> digests), which would allow the data to be output over multiple calls. Alternatively an extended
+     * form of <b>ByteArrayInputStream</b> could be used to deliver the data.</p>
+     */
+    public class GcmSivBlockCipher
+        : IAeadBlockCipher
+    {
+        /// <summary>The buffer length.</summary>
+        private static readonly int BUFLEN = 16;
+
+        /// <summary>The halfBuffer length.</summary>
+        private static readonly int HALFBUFLEN = BUFLEN >> 1;
+
+        /// <summary>The nonce length.</summary>
+        private static readonly int NONCELEN = 12;
+
+        /**
+        * The maximum data length (AEAD/PlainText). Due to implementation constraints this is restricted to the maximum
+        * array length (https://programming.guide/java/array-maximum-length.html) minus the BUFLEN to allow for the MAC
+        */
+        private static readonly int MAX_DATALEN = Int32.MaxValue - 8 - BUFLEN;
+
+        /**
+        * The top bit mask.
+        */
+        private static readonly byte MASK = (byte)0x80;
+
+        /**
+        * The addition constant.
+        */
+        private static readonly byte ADD = (byte)0xE1;
+
+        /**
+        * The initialisation flag.
+        */
+        private static readonly int INIT = 1;
+
+        /**
+        * The aeadComplete flag.
+        */
+        private static readonly int AEAD_COMPLETE = 2;
+
+        /**
+        * The cipher.
+        */
+        private readonly IBlockCipher theCipher;
+
+        /**
+        * The multiplier.
+        */
+        private readonly IGcmMultiplier theMultiplier;
+
+        /**
+        * The gHash buffer.
+        */
+        internal readonly byte[] theGHash = new byte[BUFLEN];
+
+        /**
+        * The reverse buffer.
+        */
+        internal readonly byte[] theReverse = new byte[BUFLEN];
+
+        /**
+        * The aeadHasher.
+        */
+        private readonly GcmSivHasher theAEADHasher;
+
+        /**
+        * The dataHasher.
+        */
+        private readonly GcmSivHasher theDataHasher;
+
+        /**
+        * The plainDataStream.
+        */
+        private GcmSivCache thePlain;
+
+        /**
+        * The encryptedDataStream (decryption only).
+        */
+        private GcmSivCache theEncData;
+
+        /**
+        * Are we encrypting?
+        */
+        private bool forEncryption;
+
+        /**
+        * The initialAEAD.
+        */
+        private byte[] theInitialAEAD;
+
+        /**
+        * The nonce.
+        */
+        private byte[] theNonce;
+
+        /**
+        * The flags.
+        */
+        private int theFlags;
+
+        /**
+        * Constructor.
+        */
+        public GcmSivBlockCipher()
+            : this(new AesEngine())
+        {
+        }
+
+        /**
+        * Constructor.
+        * @param pCipher the underlying cipher
+        */
+        public GcmSivBlockCipher(IBlockCipher pCipher)
+            : this(pCipher, new Tables4kGcmMultiplier())
+        {
+        }
+
+        /**
+        * Constructor.
+        * @param pCipher the underlying cipher
+        * @param pMultiplier the multiplier
+        */
+        public GcmSivBlockCipher(IBlockCipher pCipher, IGcmMultiplier pMultiplier)
+        {
+            /* Ensure that the cipher is the correct size */
+            if (pCipher.GetBlockSize() != BUFLEN)
+                throw new ArgumentException("Cipher required with a block size of " + BUFLEN + ".");
+
+            /* Store parameters */
+            theCipher = pCipher;
+            theMultiplier = pMultiplier;
+
+            /* Create the hashers */
+            theAEADHasher = new GcmSivHasher(this);
+            theDataHasher = new GcmSivHasher(this);
+        }
+
+        public virtual IBlockCipher GetUnderlyingCipher()
+        {
+            return theCipher;
+        }
 
         public virtual int GetBlockSize()
         {
             return theCipher.GetBlockSize();
         }
 
-	     public void Init(bool pEncrypt,
-			      ICipherParameters cipherParameters)
-	     {
-		 /* Set defaults */
-		 byte[] myInitialAEAD = null;
-		 byte[] myNonce = null;
-		 KeyParameter myKey = null;
-
-		 /* Access parameters */
-		 if (cipherParameters is AeadParameters)
-		 {
-		     AeadParameters myAEAD = (AeadParameters) cipherParameters;
-		     myInitialAEAD = myAEAD.GetAssociatedText();
-		     myNonce = myAEAD.GetNonce();
-		     myKey = myAEAD.Key;
-		 }
-		 else if (cipherParameters is ParametersWithIV)
-		 {
-		     ParametersWithIV myParms = (ParametersWithIV) cipherParameters;
-		     myNonce = myParms.GetIV();
-		     myKey = (KeyParameter) myParms.Parameters;
-		 }
-		 else
-		 {
-		     throw new ArgumentException("invalid parameters passed to GCM_SIV");
-		 }
-
-		 /* Check nonceSize */
-		 if (myNonce == null || myNonce.Length != NONCELEN)
-		 {
-		     throw new ArgumentException("Invalid nonce");
-		 }
-
-		 /* Check keysize */
-		 if (myKey == null)
-		 {
-		     throw new ArgumentException("Invalid key");
-		 }
-		 
-		 byte[] k = myKey.GetKey();
-
-		 if (k.Length != BUFLEN
-			 && k.Length != (BUFLEN << 1))
-		 {
-		     throw new ArgumentException("Invalid key");
-		 }
-
-		 /* Reset details */
-		 forEncryption = pEncrypt;
-		 theInitialAEAD = myInitialAEAD;
-		 theNonce = myNonce;
-
-		 /* Initialise the keys */
-		 deriveKeys(myKey);
-		 resetStreams();
-	     }
-
-	     public string AlgorithmName
-	     {
-		 	get { return theCipher.AlgorithmName + "-GCM-SIV"; }
-	     }
-
-	     /**
-	      * check AEAD status.
-	      * @param pLen the aeadLength
-	      */
-	     private void checkAEADStatus(int pLen)
-	     {
-		 /* Check we are initialised */
-		 if ((theFlags & INIT) == 0)
-		 {
-		     throw new InvalidOperationException("Cipher is not initialised");
-		 }
-
-		 /* Check AAD is allowed */
-		 if ((theFlags & AEAD_COMPLETE) != 0)
-		 {
-		     throw new InvalidOperationException("AEAD data cannot be processed after ordinary data");
-		 }
-
-		 /* Make sure that we haven't breached AEAD data limit */
-		 if ((long)theAEADHasher.getBytesProcessed() + Int64.MinValue
-		      > (MAX_DATALEN - pLen) + Int64.MinValue)
-		 {
-		     throw new InvalidOperationException("AEAD byte count exceeded");
-		 }
-	     }
-
-	     /**
-	      * check status.
-	      * @param pLen the dataLength
-	      */
-	     private void checkStatus(int pLen)
-	     {
-		 /* Check we are initialised */
-		 if ((theFlags & INIT) == 0)
-		 {
-		     throw new InvalidOperationException("Cipher is not initialised");
-		 }
-
-		 /* Complete the AEAD section if this is the first data */
-		 if ((theFlags & AEAD_COMPLETE) == 0)
-		 {
-		     theAEADHasher.completeHash();
-		     theFlags |= AEAD_COMPLETE;
-		 }
-
-		 /* Make sure that we haven't breached data limit */
-		 long dataLimit = MAX_DATALEN;
-		 long currBytes = thePlain.Length;
-		 if (!forEncryption)
-		 {
-		     dataLimit += BUFLEN;
-		     currBytes = theEncData.Length;
-		 }
-		 if (currBytes + System.Int64.MinValue
-		       > (dataLimit - pLen) + System.Int64.MinValue)
-		 {
-		     throw new InvalidOperationException("byte count exceeded");
-		 }
-	     }
-
-	     public void ProcessAadByte(byte pByte)
-	     {
-		 /* Check that we can supply AEAD */
-		 checkAEADStatus(1);
-
-		 /* Process the aead */
-		 theAEADHasher.updateHash(pByte);
-	     }
-
-	     public void ProcessAadBytes(byte[] pData,
-					 int pOffset,
-					 int pLen)
-	     {
-		 /* Check that we can supply AEAD */
-		 checkAEADStatus(pLen);
-
-		 /* Check input buffer */
-		 checkBuffer(pData, pOffset, pLen, false);
-
-		 /* Process the aead */
-		 theAEADHasher.updateHash(pData, pOffset, pLen);
-	     }
-
-	     public int ProcessByte(byte pByte,
-				    byte[] pOutput,
-				    int pOutOffset)
-	     {
-		 /* Check that we have initialised */
-		 checkStatus(1);
-
-		 /* Store the data */
-		 if (forEncryption)
-		 {
-		     thePlain.WriteByte(pByte);
-		     theDataHasher.updateHash(pByte);
-		 }
-		 else
-		 {
-		     theEncData.WriteByte(pByte);
-		 }
-
-		 /* No data returned */
-		 return 0;
-	     }
-
-	     public int ProcessBytes(byte[] pData,
-				     int pOffset,
-				     int pLen,
-				     byte[] pOutput,
-				     int pOutOffset)
-	     {
-		 /* Check that we have initialised */
-		 checkStatus(pLen);
-
-		 /* Check input buffer */
-		 checkBuffer(pData, pOffset, pLen, false);
-
-		 /* Store the data */
-		 if (forEncryption)
-		 {
-		     thePlain.Write(pData, pOffset, pLen);
-		     theDataHasher.updateHash(pData, pOffset, pLen);
-		 }
-		 else
-		 {
-		     theEncData.Write(pData, pOffset, pLen);
-		 }
-
-		 /* No data returned */
-		 return 0;
-	     }
-
-	     public int DoFinal(byte[] pOutput,
-				int pOffset)
-	     {
-		 /* Check that we have initialised */
-		 checkStatus(0);
-
-		 /* Check output buffer */
-		 checkBuffer(pOutput, pOffset, GetOutputSize(0), true);
-
-		 /* If we are encrypting */
-		 if (forEncryption)
-		 {
-		     /* Derive the tag */
-		     byte[] myTag = calculateTag();
-
-		     /* encrypt the plain text */
-		     int myDataLen = BUFLEN + encryptPlain(myTag, pOutput, pOffset);
-
-		     /* Add the tag to the output */
-		     Array.Copy(myTag, 0, pOutput, pOffset + thePlain.Length, BUFLEN);
-
-		     /* Reset the streams */
-		     resetStreams();
-		     return myDataLen;
-
-		     /* else we are decrypting */
-		 }
-		 else
-		 {
-		     /* decrypt to plain text */
-		     decryptPlain();
-
-		     /* Release plain text */
-		     int myDataLen = (int)thePlain.Length;
-		     byte[] mySrc = thePlain.GetBuffer();
-		     Array.Copy(mySrc, 0, pOutput, pOffset, myDataLen);
-
-		     /* Reset the streams */
-		     resetStreams();
-		     return myDataLen;
-		 }
-	     }
-
-	     public byte[] GetMac()
-	     {
-		 throw new InvalidOperationException();
-	     }
-
-	     public int GetUpdateOutputSize(int pLen)
-	     {
-		 return 0;
-	     }
-
-	     public int GetOutputSize(int pLen)
-	     {
-		 if (forEncryption) {
-		     return (int)(pLen + thePlain.Length + BUFLEN);
-		 }
-		 int myCurr = (int)(pLen + theEncData.Length);
-		 return myCurr > BUFLEN ? myCurr - BUFLEN : 0;
-	     }
-
-	     public void Reset()
-	     {
-		 resetStreams();
-	     }
-
-	     /**
-	      * Reset Streams.
-	      */
-	     private void resetStreams()
-	     {
-		 /* Clear the plainText buffer */
-		 if (thePlain != null)
-		 {
-			 thePlain.Dispose();
-			 thePlain = new GCMSIVCache();  
-		 }
-
-		 /* Reset hashers */
-		 theAEADHasher.reset();
-		 theDataHasher.reset();
-
-		 /* Recreate streams (to release memory) */
-		 thePlain = new GCMSIVCache();
-		 theEncData = forEncryption ? null : new GCMSIVCache();
-
-		 /* Initialise AEAD if required */
-		 theFlags &= ~AEAD_COMPLETE;
-		 Arrays.Fill(theGHash, (byte) 0);
-		 if (theInitialAEAD != null)
-		 {
-		     theAEADHasher.updateHash(theInitialAEAD, 0, theInitialAEAD.Length);
-		 }
-	      }
-
-	     /**
-	      * Obtain buffer length (allowing for null).
-	      * @param pBuffer the buffere
-	      * @return the length
-	      */
-	     private static int bufLength(byte[] pBuffer)
-	     {
-		 return pBuffer == null ? 0 : pBuffer.Length;
-	     }
-
-	     /**
-	      * Check buffer.
-	      * @param pBuffer the buffer
-	      * @param pOffset the offset
-	      * @param pLen the length
-	      * @param pOutput is this an output buffer?
-	      */
-	     private static void checkBuffer(byte[] pBuffer,
-					     int pOffset,
-					     int pLen,
-					     bool pOutput)
-	     {
-		 /* Access lengths */
-		 int myBufLen = bufLength(pBuffer);
-		 int myLast = pOffset + pLen;
-
-		 /* Check for negative values and buffer overflow */
-		 bool badLen = pLen < 0 || pOffset < 0 || myLast < 0;
-		 if (badLen || myLast > myBufLen)
-		 {
-		     throw pOutput
-			     ? new OutputLengthException("Output buffer too short.")
-			     : new DataLengthException("Input buffer too short.");
-		 }
-	     }
-
-	     /**
-	      * encrypt data stream.
-	      * @param pCounter the counter
-	      * @param pTarget the target buffer
-	      * @param pOffset the target offset
-	      * @return the length of data encrypted
-	      */
-	     private int encryptPlain(byte[] pCounter,
-				      byte[] pTarget,
-				      int pOffset)
-	     {
-		 /* Access buffer and length */
-		 byte[] mySrc = thePlain.GetBuffer();
-		 byte[] myCounter = Arrays.Clone(pCounter);
-		 myCounter[BUFLEN - 1] |= MASK;
-		 byte[] myMask = new byte[BUFLEN];
-		 long myRemaining = thePlain.Length;
-		 int myOff = 0;
-
-		 /* While we have data to process */
-		 while (myRemaining > 0)
-		 {
-		     /* Generate the next mask */
-		     theCipher.ProcessBlock(myCounter, 0, myMask, 0);
-
-		     /* Xor data into mask */
-		     int myLen = (int)System.Math.Min(BUFLEN, myRemaining);
-		     xorBlock(myMask, mySrc, myOff, myLen);
-
-		     /* Copy encrypted data to output */
-		     Array.Copy(myMask, 0, pTarget, pOffset + myOff, myLen);
-
-		     /* Adjust counters */
-		     myRemaining -= myLen;
-		     myOff += myLen;
-		     incrementCounter(myCounter);
-		 }
-
-		 /* Return the amount of data processed */
-		 return (int)thePlain.Length;
-	     }
-
-	     /**
-	      * decrypt data stream.
-	      * @throws InvalidCipherTextException on data too short or mac check failed
-	      */
-	     private void decryptPlain()
-	     {
-		 /* Access buffer and length */
-		 byte[] mySrc = theEncData.GetBuffer();
-		 int myRemaining = (int)theEncData.Length - BUFLEN;
-
-		 /* Check for insufficient data */
-		 if (myRemaining < 0)
-		 {
-		     throw new InvalidCipherTextException("Data too short");
-		 }
-
-		 /* Access counter */
-		 byte[] myExpected = Arrays.CopyOfRange(mySrc, myRemaining, myRemaining + BUFLEN);
-		 byte[] myCounter = Arrays.Clone(myExpected);
-		 myCounter[BUFLEN - 1] |= MASK;
-		 byte[] myMask = new byte[BUFLEN];
-		 int myOff = 0;
-
-		 /* While we have data to process */
-		 while (myRemaining > 0)
-		 {
-		     /* Generate the next mask */
-		     theCipher.ProcessBlock(myCounter, 0, myMask, 0);
-
-		     /* Xor data into mask */
-		     int myLen = System.Math.Min(BUFLEN, myRemaining);
-		     xorBlock(myMask, mySrc, myOff, myLen);
-
-		     /* Write data to plain dataStream */
-		     thePlain.Write(myMask, 0, myLen);
-		     theDataHasher.updateHash(myMask, 0, myLen);
-
-		     /* Adjust counters */
-		     myRemaining -= myLen;
-		     myOff += myLen;
-		     incrementCounter(myCounter);
-		 }
-
-		 /* Derive and check the tag */
-		 byte[] myTag = calculateTag();
-		 if (!Arrays.ConstantTimeAreEqual(myTag, myExpected))
-		 {
-		     Reset();
-		     throw new InvalidCipherTextException("mac check failed");
-		 }
-	     }
-
-	     /**
-	      * calculate tag.
-	      * @return the calculated tag
-	      */
-	     private byte[] calculateTag()
-	     {
-		 /* Complete the hash */
-		 theDataHasher.completeHash();
-		 byte[] myPolyVal = completePolyVal();
-
-		 /* calculate polyVal */
-		 byte[] myResult = new byte[BUFLEN];
-
-		 /* Fold in the nonce */
-		 for (int i = 0; i < NONCELEN; i++)
-		 {
-		     myPolyVal[i] ^= theNonce[i];
-		 }
-
-		 /* Clear top bit */
-		 myPolyVal[BUFLEN - 1] &= (byte)(MASK - 1);
-
-		 /* Calculate tag and return it */
-		 theCipher.ProcessBlock(myPolyVal, 0, myResult, 0);
-		 return myResult;
-	     }
-
-	     /**
-	      * complete polyVAL.
-	      * @return the calculated value
-	      */
-	     private byte[] completePolyVal()
-	     {
-		 /* Build the polyVal result */
-		 byte[] myResult = new byte[BUFLEN];
-		 gHashLengths();
-		 fillReverse(theGHash, 0, BUFLEN, myResult);
-		 return myResult;
-	     }
-
-	     /**
-	      * process lengths.
-	      */
-	     private void gHashLengths()
-	     {
-		 /* Create reversed bigEndian buffer to keep it simple */
-		 byte[] myIn = new byte[BUFLEN];
-		 Pack.UInt64_To_BE((ulong)Bytes.SIZE * theDataHasher.getBytesProcessed(), myIn, 0);
-		 Pack.UInt64_To_BE((ulong)Bytes.SIZE * theAEADHasher.getBytesProcessed(), myIn, (int)Longs.BYTES);
-
-		 /* hash value */
-		 gHASH(myIn);
-	     }
-
-	     /**
-	      * perform the next GHASH step.
-	      * @param pNext the next value
-	      */
-	     private void gHASH(byte[] pNext)
-	     {
-		 	xorBlock(theGHash, pNext);
-		 	theMultiplier.MultiplyH(theGHash);
-	     }
-
-	     /**
-	      * Byte reverse a buffer.
-	      * @param pInput the input buffer
-	      * @param pOffset the offset
-	      * @param pLength the length of data (<= BUFLEN)
-	      * @param pOutput the output buffer
-	      */
-	     private static void fillReverse(byte[] pInput,
-					     int pOffset,
-					     int pLength,
-					     byte[] pOutput)
-	     {
-		 /* Loop through the buffer */
-		 for (int i = 0, j = BUFLEN - 1; i < pLength; i++, j--)
-		 {
-		     /* Copy byte */
-		     pOutput[j] = pInput[pOffset + i];
-		 }
-	     }
-
-	     /**
-	      * xor a full block buffer.
-	      * @param pLeft the left operand and result
-	      * @param pRight the right operand
-	      */
-	     private static void xorBlock(byte[] pLeft,
-					  byte[] pRight)
-	     {
-		 /* Loop through the bytes */
-		 for (int i = 0; i < BUFLEN; i++)
-		 {
-		     pLeft[i] ^= pRight[i];
-		 }
-	     }
-
-	     /**
-	      * xor a partial block buffer.
-	      * @param pLeft the left operand and result
-	      * @param pRight the right operand
-	      * @param pOffset the offset in the right operand
-	      * @param pLength the length of data in the right operand
-	      */
-	     private static void xorBlock(byte[] pLeft,
-					  byte[] pRight,
-					  int pOffset,
-					  int pLength)
-					  {
-		 /* Loop through the bytes */
-		 for (int i = 0; i < pLength; i++)
-		 {
-		     pLeft[i] ^= pRight[i + pOffset];
-		 }
-	     }
-
-	     /**
-	      * increment the counter.
-	      * @param pCounter the counter to increment
-	      */
-	     private static void incrementCounter(byte[] pCounter)
-	     {
-		 /* Loop through the bytes incrementing counter */
-		 for (int i = 0; i < Integers.BYTES; i++)
-		 {
-		     if (++pCounter[i] != 0)
-		     {
-			 break;
-		     }
-		 }
-	     }
-
-	     /**
-	      * multiply by X.
-	      * @param pValue the value to adjust
-	      */
-	     private static void mulX(byte[] pValue)
-	     {
-		 /* Loop through the bytes */
-		 byte myMask = (byte) 0;
-		 for (int i = 0; i < BUFLEN; i++)
-		 {
-		     byte myValue = pValue[i];
-		     pValue[i] = (byte) (((myValue >> 1) & ~MASK) | myMask);
-		     myMask = (byte)((myValue & 1) == 0 ? 0 : MASK);
-		 }
-
-		 /* Xor in addition if last bit was set */
-		 if (myMask != 0)
-		 {
-		     pValue[0] ^= ADD;
-		 }
-	     }
-
-	     /**
-	      * Derive Keys.
-	      * @param pKey the keyGeneration key
-	      */
-	     private void deriveKeys(KeyParameter pKey)
-	     {
-		 /* Create the buffers */
-		 byte[] myIn = new byte[BUFLEN];
-		 byte[] myOut = new byte[BUFLEN];
-		 byte[] myResult = new byte[BUFLEN];
-		 byte[] myEncKey = new byte[pKey.GetKey().Length];
-
-		 /* Prepare for encryption */
-		 Array.Copy(theNonce, 0, myIn, BUFLEN - NONCELEN, NONCELEN);
-		 theCipher.Init(true, pKey);
-
-		 /* Derive authentication key */
-		 int myOff = 0;
-		 theCipher.ProcessBlock(myIn, 0, myOut, 0);
-		 Array.Copy(myOut, 0, myResult, myOff, HALFBUFLEN);
-		 myIn[0]++;
-		 myOff += HALFBUFLEN;
-		 theCipher.ProcessBlock(myIn, 0, myOut, 0);
-		 Array.Copy(myOut, 0, myResult, myOff, HALFBUFLEN);
-
-		 /* Derive encryption key */
-		 myIn[0]++;
-		 myOff = 0;
-		 theCipher.ProcessBlock(myIn, 0, myOut, 0);
-		 Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
-		 myIn[0]++;
-		 myOff += HALFBUFLEN;
-		 theCipher.ProcessBlock(myIn, 0, myOut, 0);
-		 Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
-
-		 /* If we have a 32byte key */
-		 if (myEncKey.Length == BUFLEN << 1)
-		 {
-		     /* Derive remainder of encryption key */
-		     myIn[0]++;
-		     myOff += HALFBUFLEN;
-		     theCipher.ProcessBlock(myIn, 0, myOut, 0);
-		     Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
-		     myIn[0]++;
-		     myOff += HALFBUFLEN;
-		     theCipher.ProcessBlock(myIn, 0, myOut, 0);
-		     Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
-		 }
-
-		 /* Initialise the Cipher */
-		 theCipher.Init(true, new KeyParameter(myEncKey));
-
-		 /* Initialise the multiplier */
-		 fillReverse(myResult, 0, BUFLEN, myOut);
-		 mulX(myOut);
-		 theMultiplier.Init(myOut);
-		 theFlags |= INIT;
-	     }
-
-	     /**
-	      * GCMSIVCache.
-	      */
-	     class GCMSIVCache
-		     : MemoryOutputStream
-	     {
-		 /**
-		  * number of bytes hashed.
-		  */
-		 private int numHashed;
-
-		 /**
-		  * Constructor.
-		  */
-		 internal GCMSIVCache()
-		 {
-		 }
-	     }
-
-	     /**
-	      * Hash Control.
-	      */
-	     class GCMSIVHasher
-	     {
-		 /**
-		  * Cache.
-		  */
-		 private readonly byte[] theBuffer = new byte[BUFLEN];
-
-		 /**
-		  * Single byte cache.
-		  */
-		 private readonly byte[] theByte = new byte[1];
-
-		 /**
-		  * Count of active bytes in cache.
-		  */
-		 private int numActive;
-
-		 /**
-		  * Count of hashed bytes.
-		  */
-		 private ulong numHashed;
-
-		private readonly GcmSivBlockCipher parent;
-
-		internal GCMSIVHasher(GcmSivBlockCipher parent)
-		{
-			this.parent = parent;
-		}
-
-		 /**
-		  * Obtain the count of bytes hashed.
-		  * @return the count
-		  */
-		 internal ulong getBytesProcessed()
-		 {
-		     return numHashed;
-		 }
-
-		 /**
-		  * Reset the hasher.
-		  */
-		 internal void reset()
-		 {
-		     numActive = 0;
-		     numHashed = 0;
-		 }
-
-		 /**
-		  * update hash.
-		  * @param pByte the byte
-		  */
-		 internal void updateHash(byte pByte)
-		 {
-		     theByte[0] = pByte;
-		     updateHash(theByte, 0, 1);
-		 }
-
-		 /**
-		  * update hash.
-		  * @param pBuffer the buffer
-		  * @param pOffset the offset within the buffer
-		  * @param pLen the length of data
-		  */
-		 internal void updateHash(byte[] pBuffer,
-				 int pOffset,
-				 int pLen)
-		 {
-		     /* If we should process the cache */
-		     int mySpace = BUFLEN - numActive;
-		     int numProcessed = 0;
-		     int myRemaining = pLen;
-		     if (numActive > 0
-			     && pLen >= mySpace)
-		     {
-			 /* Copy data into the cache and hash it */
-			 Array.Copy(pBuffer, pOffset, theBuffer, numActive, mySpace);
-			 fillReverse(theBuffer, 0, BUFLEN, parent.theReverse);
-			 parent.gHASH(parent.theReverse);
-
-			 /* Adjust counters */
-			 numProcessed += mySpace;
-			 myRemaining -= mySpace;
-			 numActive = 0;
-		     }
-
-		     /* While we have full blocks */
-		     while (myRemaining >= BUFLEN)
-		     {
-			 /* Access the next data */
-			 fillReverse(pBuffer, pOffset + numProcessed, BUFLEN, parent.theReverse);
-			 parent.gHASH(parent.theReverse);
-
-			 /* Adjust counters */
-			 numProcessed += mySpace;
-			 myRemaining -= mySpace;
-		     }
-
-		     /* If we have remaining data */
-		     if (myRemaining > 0)
-		     {
-			 /* Copy data into the cache */
-			 Array.Copy(pBuffer, pOffset + numProcessed, theBuffer, numActive, myRemaining);
-			 numActive += myRemaining;
-		     }
-
-		     /* Adjust the number of bytes processed */
-		     numHashed += (ulong)pLen;
-		 }
-
-		 /**
-		  * complete hash.
-		  */
-		 internal void completeHash()
-		 {
-		     /* If we have remaining data */
-		     if (numActive > 0)
-		     {
-			 /* Access the next data */
-			 Arrays.Fill(parent.theReverse, (byte) 0);
-			 fillReverse(theBuffer, 0, numActive, parent.theReverse);
-
-			 /* hash value */
-			 parent.gHASH(parent.theReverse);
-		     }
-		 }
-	     }
-	 }
+        public virtual void Init(bool pEncrypt, ICipherParameters cipherParameters)
+        {
+            /* Set defaults */
+            byte[] myInitialAEAD = null;
+            byte[] myNonce = null;
+            KeyParameter myKey = null;
+
+            /* Access parameters */
+            if (cipherParameters is AeadParameters)
+            {
+                AeadParameters myAEAD = (AeadParameters)cipherParameters;
+                myInitialAEAD = myAEAD.GetAssociatedText();
+                myNonce = myAEAD.GetNonce();
+                myKey = myAEAD.Key;
+            }
+            else if (cipherParameters is ParametersWithIV)
+            {
+                ParametersWithIV myParms = (ParametersWithIV)cipherParameters;
+                myNonce = myParms.GetIV();
+                myKey = (KeyParameter)myParms.Parameters;
+            }
+            else
+            {
+                throw new ArgumentException("invalid parameters passed to GCM_SIV");
+            }
+
+            /* Check nonceSize */
+            if (myNonce == null || myNonce.Length != NONCELEN)
+            {
+                throw new ArgumentException("Invalid nonce");
+            }
+
+            /* Check keysize */
+            if (myKey == null)
+            {
+                throw new ArgumentException("Invalid key");
+            }
+
+            byte[] k = myKey.GetKey();
+
+            if (k.Length != BUFLEN
+            && k.Length != (BUFLEN << 1))
+            {
+                throw new ArgumentException("Invalid key");
+            }
+
+            /* Reset details */
+            forEncryption = pEncrypt;
+            theInitialAEAD = myInitialAEAD;
+            theNonce = myNonce;
+
+            /* Initialise the keys */
+            deriveKeys(myKey);
+            ResetStreams();
+        }
+
+        public virtual string AlgorithmName
+        {
+            get { return theCipher.AlgorithmName + "-GCM-SIV"; }
+        }
+
+        /**
+        * check AEAD status.
+        * @param pLen the aeadLength
+        */
+        private void CheckAeadStatus(int pLen)
+        {
+            /* Check we are initialised */
+            if ((theFlags & INIT) == 0)
+            {
+                throw new InvalidOperationException("Cipher is not initialised");
+            }
+
+            /* Check AAD is allowed */
+            if ((theFlags & AEAD_COMPLETE) != 0)
+            {
+                throw new InvalidOperationException("AEAD data cannot be processed after ordinary data");
+            }
+
+            /* Make sure that we haven't breached AEAD data limit */
+            if ((long)theAEADHasher.getBytesProcessed() + Int64.MinValue > (MAX_DATALEN - pLen) + Int64.MinValue)
+            {
+                throw new InvalidOperationException("AEAD byte count exceeded");
+            }
+        }
+
+        /**
+        * check status.
+        * @param pLen the dataLength
+        */
+        private void CheckStatus(int pLen)
+        {
+            /* Check we are initialised */
+            if ((theFlags & INIT) == 0)
+            {
+                throw new InvalidOperationException("Cipher is not initialised");
+            }
+
+            /* Complete the AEAD section if this is the first data */
+            if ((theFlags & AEAD_COMPLETE) == 0)
+            {
+                theAEADHasher.completeHash();
+                theFlags |= AEAD_COMPLETE;
+            }
+
+            /* Make sure that we haven't breached data limit */
+            long dataLimit = MAX_DATALEN;
+            long currBytes = thePlain.Length;
+            if (!forEncryption)
+            {
+                dataLimit += BUFLEN;
+                currBytes = theEncData.Length;
+            }
+            if (currBytes + System.Int64.MinValue
+            > (dataLimit - pLen) + System.Int64.MinValue)
+            {
+                throw new InvalidOperationException("byte count exceeded");
+            }
+        }
+
+        public virtual void ProcessAadByte(byte pByte)
+        {
+            /* Check that we can supply AEAD */
+            CheckAeadStatus(1);
+
+            /* Process the aead */
+            theAEADHasher.updateHash(pByte);
+        }
+
+        public virtual void ProcessAadBytes(byte[] pData, int pOffset, int pLen)
+        {
+            /* Check that we can supply AEAD */
+            CheckAeadStatus(pLen);
+
+            /* Check input buffer */
+            CheckBuffer(pData, pOffset, pLen, false);
+
+            /* Process the aead */
+            theAEADHasher.updateHash(pData, pOffset, pLen);
+        }
+
+        public virtual int ProcessByte(byte pByte, byte[] pOutput, int pOutOffset)
+        {
+            /* Check that we have initialised */
+            CheckStatus(1);
+
+            /* Store the data */
+            if (forEncryption)
+            {
+                thePlain.WriteByte(pByte);
+                theDataHasher.updateHash(pByte);
+            }
+            else
+            {
+                theEncData.WriteByte(pByte);
+            }
+
+            /* No data returned */
+            return 0;
+        }
+
+        public virtual int ProcessBytes(byte[] pData, int pOffset, int pLen, byte[] pOutput, int pOutOffset)
+        {
+            /* Check that we have initialised */
+            CheckStatus(pLen);
+
+            /* Check input buffer */
+            CheckBuffer(pData, pOffset, pLen, false);
+
+            /* Store the data */
+            if (forEncryption)
+            {
+                thePlain.Write(pData, pOffset, pLen);
+                theDataHasher.updateHash(pData, pOffset, pLen);
+            }
+            else
+            {
+                theEncData.Write(pData, pOffset, pLen);
+            }
+
+            /* No data returned */
+            return 0;
+        }
+
+        public virtual int DoFinal(byte[] pOutput, int pOffset)
+        {
+            /* Check that we have initialised */
+            CheckStatus(0);
+
+            /* Check output buffer */
+            CheckBuffer(pOutput, pOffset, GetOutputSize(0), true);
+
+            /* If we are encrypting */
+            if (forEncryption)
+            {
+                /* Derive the tag */
+                byte[] myTag = calculateTag();
+
+                /* encrypt the plain text */
+                int myDataLen = BUFLEN + encryptPlain(myTag, pOutput, pOffset);
+
+                /* Add the tag to the output */
+                Array.Copy(myTag, 0, pOutput, pOffset + thePlain.Length, BUFLEN);
+
+                /* Reset the streams */
+                ResetStreams();
+                return myDataLen;
+
+                /* else we are decrypting */
+            }
+            else
+            {
+                /* decrypt to plain text */
+                decryptPlain();
+
+                /* Release plain text */
+                int myDataLen = Streams.WriteBufTo(thePlain, pOutput, pOffset);
+
+                /* Reset the streams */
+                ResetStreams();
+                return myDataLen;
+            }
+        }
+
+        public virtual byte[] GetMac()
+        {
+            throw new InvalidOperationException();
+        }
+
+        public virtual int GetUpdateOutputSize(int pLen)
+        {
+            return 0;
+        }
+
+        public virtual int GetOutputSize(int pLen)
+        {
+            if (forEncryption)
+            {
+                return (int)(pLen + thePlain.Length + BUFLEN);
+            }
+            int myCurr = (int)(pLen + theEncData.Length);
+            return myCurr > BUFLEN ? myCurr - BUFLEN : 0;
+        }
+
+        public virtual void Reset()
+        {
+            ResetStreams();
+        }
+
+        /**
+        * Reset Streams.
+        */
+        private void ResetStreams()
+        {
+            /* Clear the plainText buffer */
+            if (thePlain != null)
+            {
+                thePlain.Position = 0L;
+                Streams.WriteZeroes(thePlain, thePlain.Capacity);
+            }
+
+            /* Reset hashers */
+            theAEADHasher.Reset();
+            theDataHasher.Reset();
+
+            /* Recreate streams (to release memory) */
+            thePlain = new GcmSivCache();
+            theEncData = forEncryption ? null : new GcmSivCache();
+
+            /* Initialise AEAD if required */
+            theFlags &= ~AEAD_COMPLETE;
+            Arrays.Fill(theGHash, (byte)0);
+            if (theInitialAEAD != null)
+            {
+                theAEADHasher.updateHash(theInitialAEAD, 0, theInitialAEAD.Length);
+            }
+        }
+
+        /**
+        * Obtain buffer length (allowing for null).
+        * @param pBuffer the buffere
+        * @return the length
+        */
+        private static int bufLength(byte[] pBuffer)
+        {
+            return pBuffer == null ? 0 : pBuffer.Length;
+        }
+
+        /**
+        * Check buffer.
+        * @param pBuffer the buffer
+        * @param pOffset the offset
+        * @param pLen the length
+        * @param pOutput is this an output buffer?
+        */
+        private static void CheckBuffer(byte[] pBuffer, int pOffset, int pLen, bool pOutput)
+        {
+            /* Access lengths */
+            int myBufLen = bufLength(pBuffer);
+            int myLast = pOffset + pLen;
+
+            /* Check for negative values and buffer overflow */
+            bool badLen = pLen < 0 || pOffset < 0 || myLast < 0;
+            if (badLen || myLast > myBufLen)
+            {
+                throw pOutput
+                ? new OutputLengthException("Output buffer too short.")
+                : new DataLengthException("Input buffer too short.");
+            }
+        }
+
+        /**
+        * encrypt data stream.
+        * @param pCounter the counter
+        * @param pTarget the target buffer
+        * @param pOffset the target offset
+        * @return the length of data encrypted
+        */
+        private int encryptPlain(byte[] pCounter, byte[] pTarget, int pOffset)
+        {
+            /* Access buffer and length */
+#if PORTABLE
+            byte[] thePlainBuf = thePlain.ToArray();
+            int thePlainLen = thePlainBuf.Length;
+#else
+            byte[] thePlainBuf = thePlain.GetBuffer();
+            int thePlainLen = (int)thePlain.Length;
+#endif
+
+            byte[] mySrc = thePlainBuf;
+            byte[] myCounter = Arrays.Clone(pCounter);
+            myCounter[BUFLEN - 1] |= MASK;
+            byte[] myMask = new byte[BUFLEN];
+            long myRemaining = thePlainLen;
+            int myOff = 0;
+
+            /* While we have data to process */
+            while (myRemaining > 0)
+            {
+                /* Generate the next mask */
+                theCipher.ProcessBlock(myCounter, 0, myMask, 0);
+
+                /* Xor data into mask */
+                int myLen = (int)System.Math.Min(BUFLEN, myRemaining);
+                xorBlock(myMask, mySrc, myOff, myLen);
+
+                /* Copy encrypted data to output */
+                Array.Copy(myMask, 0, pTarget, pOffset + myOff, myLen);
+
+                /* Adjust counters */
+                myRemaining -= myLen;
+                myOff += myLen;
+                incrementCounter(myCounter);
+            }
+
+            /* Return the amount of data processed */
+            return thePlainLen;
+        }
+
+        /**
+        * decrypt data stream.
+        * @throws InvalidCipherTextException on data too short or mac check failed
+        */
+        private void decryptPlain()
+        {
+            /* Access buffer and length */
+#if PORTABLE
+            byte[] theEncDataBuf = theEncData.ToArray();
+            int theEncDataLen = theEncDataBuf.Length;
+#else
+            byte[] theEncDataBuf = theEncData.GetBuffer();
+            int theEncDataLen = (int)theEncData.Length;
+#endif
+
+            byte[] mySrc = theEncDataBuf;
+            int myRemaining = theEncDataLen - BUFLEN;
+
+            /* Check for insufficient data */
+            if (myRemaining < 0)
+            {
+                throw new InvalidCipherTextException("Data too short");
+            }
+
+            /* Access counter */
+            byte[] myExpected = Arrays.CopyOfRange(mySrc, myRemaining, myRemaining + BUFLEN);
+            byte[] myCounter = Arrays.Clone(myExpected);
+            myCounter[BUFLEN - 1] |= MASK;
+            byte[] myMask = new byte[BUFLEN];
+            int myOff = 0;
+
+            /* While we have data to process */
+            while (myRemaining > 0)
+            {
+                /* Generate the next mask */
+                theCipher.ProcessBlock(myCounter, 0, myMask, 0);
+
+                /* Xor data into mask */
+                int myLen = System.Math.Min(BUFLEN, myRemaining);
+                xorBlock(myMask, mySrc, myOff, myLen);
+
+                /* Write data to plain dataStream */
+                thePlain.Write(myMask, 0, myLen);
+                theDataHasher.updateHash(myMask, 0, myLen);
+
+                /* Adjust counters */
+                myRemaining -= myLen;
+                myOff += myLen;
+                incrementCounter(myCounter);
+            }
+
+            /* Derive and check the tag */
+            byte[] myTag = calculateTag();
+            if (!Arrays.ConstantTimeAreEqual(myTag, myExpected))
+            {
+                Reset();
+                throw new InvalidCipherTextException("mac check failed");
+            }
+        }
+
+        /**
+        * calculate tag.
+        * @return the calculated tag
+        */
+        private byte[] calculateTag()
+        {
+            /* Complete the hash */
+            theDataHasher.completeHash();
+            byte[] myPolyVal = completePolyVal();
+
+            /* calculate polyVal */
+            byte[] myResult = new byte[BUFLEN];
+
+            /* Fold in the nonce */
+            for (int i = 0; i < NONCELEN; i++)
+            {
+                myPolyVal[i] ^= theNonce[i];
+            }
+
+            /* Clear top bit */
+            myPolyVal[BUFLEN - 1] &= (byte)(MASK - 1);
+
+            /* Calculate tag and return it */
+            theCipher.ProcessBlock(myPolyVal, 0, myResult, 0);
+            return myResult;
+        }
+
+        /**
+        * complete polyVAL.
+        * @return the calculated value
+        */
+        private byte[] completePolyVal()
+        {
+            /* Build the polyVal result */
+            byte[] myResult = new byte[BUFLEN];
+            gHashLengths();
+            fillReverse(theGHash, 0, BUFLEN, myResult);
+            return myResult;
+        }
+
+        /**
+        * process lengths.
+        */
+        private void gHashLengths()
+        {
+            /* Create reversed bigEndian buffer to keep it simple */
+            byte[] myIn = new byte[BUFLEN];
+            Pack.UInt64_To_BE((ulong)Bytes.NumBits * theDataHasher.getBytesProcessed(), myIn, 0);
+            Pack.UInt64_To_BE((ulong)Bytes.NumBits * theAEADHasher.getBytesProcessed(), myIn, Longs.NumBytes);
+
+            /* hash value */
+            gHASH(myIn);
+        }
+
+        /**
+        * perform the next GHASH step.
+        * @param pNext the next value
+        */
+        private void gHASH(byte[] pNext)
+        {
+            xorBlock(theGHash, pNext);
+            theMultiplier.MultiplyH(theGHash);
+        }
+
+        /**
+        * Byte reverse a buffer.
+        * @param pInput the input buffer
+        * @param pOffset the offset
+        * @param pLength the length of data (<= BUFLEN)
+        * @param pOutput the output buffer
+        */
+        private static void fillReverse(byte[] pInput, int pOffset, int pLength, byte[] pOutput)
+        {
+            /* Loop through the buffer */
+            for (int i = 0, j = BUFLEN - 1; i < pLength; i++, j--)
+            {
+                /* Copy byte */
+                pOutput[j] = pInput[pOffset + i];
+            }
+        }
+
+        /**
+        * xor a full block buffer.
+        * @param pLeft the left operand and result
+        * @param pRight the right operand
+        */
+        private static void xorBlock(byte[] pLeft, byte[] pRight)
+        {
+            /* Loop through the bytes */
+            for (int i = 0; i < BUFLEN; i++)
+            {
+                pLeft[i] ^= pRight[i];
+            }
+        }
+
+        /**
+        * xor a partial block buffer.
+        * @param pLeft the left operand and result
+        * @param pRight the right operand
+        * @param pOffset the offset in the right operand
+        * @param pLength the length of data in the right operand
+        */
+        private static void xorBlock(byte[] pLeft, byte[] pRight, int pOffset, int pLength)
+        {
+            /* Loop through the bytes */
+            for (int i = 0; i < pLength; i++)
+            {
+                pLeft[i] ^= pRight[i + pOffset];
+            }
+        }
+
+        /**
+        * increment the counter.
+        * @param pCounter the counter to increment
+        */
+        private static void incrementCounter(byte[] pCounter)
+        {
+            /* Loop through the bytes incrementing counter */
+            for (int i = 0; i < Integers.NumBytes; i++)
+            {
+                if (++pCounter[i] != 0)
+                {
+                    break;
+                }
+            }
+        }
+
+        /**
+        * multiply by X.
+        * @param pValue the value to adjust
+        */
+        private static void mulX(byte[] pValue)
+        {
+            /* Loop through the bytes */
+            byte myMask = (byte)0;
+            for (int i = 0; i < BUFLEN; i++)
+            {
+                byte myValue = pValue[i];
+                pValue[i] = (byte)(((myValue >> 1) & ~MASK) | myMask);
+                myMask = (byte)((myValue & 1) == 0 ? 0 : MASK);
+            }
+
+            /* Xor in addition if last bit was set */
+            if (myMask != 0)
+            {
+                pValue[0] ^= ADD;
+            }
+        }
+
+        /**
+        * Derive Keys.
+        * @param pKey the keyGeneration key
+        */
+        private void deriveKeys(KeyParameter pKey)
+        {
+            /* Create the buffers */
+            byte[] myIn = new byte[BUFLEN];
+            byte[] myOut = new byte[BUFLEN];
+            byte[] myResult = new byte[BUFLEN];
+            byte[] myEncKey = new byte[pKey.GetKey().Length];
+
+            /* Prepare for encryption */
+            Array.Copy(theNonce, 0, myIn, BUFLEN - NONCELEN, NONCELEN);
+            theCipher.Init(true, pKey);
+
+            /* Derive authentication key */
+            int myOff = 0;
+            theCipher.ProcessBlock(myIn, 0, myOut, 0);
+            Array.Copy(myOut, 0, myResult, myOff, HALFBUFLEN);
+            myIn[0]++;
+            myOff += HALFBUFLEN;
+            theCipher.ProcessBlock(myIn, 0, myOut, 0);
+            Array.Copy(myOut, 0, myResult, myOff, HALFBUFLEN);
+
+            /* Derive encryption key */
+            myIn[0]++;
+            myOff = 0;
+            theCipher.ProcessBlock(myIn, 0, myOut, 0);
+            Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+            myIn[0]++;
+            myOff += HALFBUFLEN;
+            theCipher.ProcessBlock(myIn, 0, myOut, 0);
+            Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+
+            /* If we have a 32byte key */
+            if (myEncKey.Length == BUFLEN << 1)
+            {
+                /* Derive remainder of encryption key */
+                myIn[0]++;
+                myOff += HALFBUFLEN;
+                theCipher.ProcessBlock(myIn, 0, myOut, 0);
+                Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+                myIn[0]++;
+                myOff += HALFBUFLEN;
+                theCipher.ProcessBlock(myIn, 0, myOut, 0);
+                Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+            }
+
+            /* Initialise the Cipher */
+            theCipher.Init(true, new KeyParameter(myEncKey));
+
+            /* Initialise the multiplier */
+            fillReverse(myResult, 0, BUFLEN, myOut);
+            mulX(myOut);
+            theMultiplier.Init(myOut);
+            theFlags |= INIT;
+        }
+
+        private class GcmSivCache
+            : MemoryStream
+        {
+            internal GcmSivCache()
+            {
+            }
+        }
+
+        /**
+        * Hash Control.
+        */
+        private class GcmSivHasher
+        {
+            /**
+            * Cache.
+            */
+            private readonly byte[] theBuffer = new byte[BUFLEN];
+
+            /**
+            * Single byte cache.
+            */
+            private readonly byte[] theByte = new byte[1];
+
+            /**
+            * Count of active bytes in cache.
+            */
+            private int numActive;
+
+            /**
+            * Count of hashed bytes.
+            */
+            private ulong numHashed;
+
+            private readonly GcmSivBlockCipher parent;
+
+            internal GcmSivHasher(GcmSivBlockCipher parent)
+            {
+                this.parent = parent;
+            }
+
+            /**
+            * Obtain the count of bytes hashed.
+            * @return the count
+            */
+            internal ulong getBytesProcessed()
+            {
+                return numHashed;
+            }
+
+            /**
+            * Reset the hasher.
+            */
+            internal void Reset()
+            {
+                numActive = 0;
+                numHashed = 0;
+            }
+
+            /**
+            * update hash.
+            * @param pByte the byte
+            */
+            internal void updateHash(byte pByte)
+            {
+                theByte[0] = pByte;
+                updateHash(theByte, 0, 1);
+            }
+
+            /**
+            * update hash.
+            * @param pBuffer the buffer
+            * @param pOffset the offset within the buffer
+            * @param pLen the length of data
+            */
+            internal void updateHash(byte[] pBuffer, int pOffset, int pLen)
+            {
+                /* If we should process the cache */
+                int mySpace = BUFLEN - numActive;
+                int numProcessed = 0;
+                int myRemaining = pLen;
+                if (numActive > 0 && pLen >= mySpace)
+                {
+                    /* Copy data into the cache and hash it */
+                    Array.Copy(pBuffer, pOffset, theBuffer, numActive, mySpace);
+                    fillReverse(theBuffer, 0, BUFLEN, parent.theReverse);
+                    parent.gHASH(parent.theReverse);
+
+                    /* Adjust counters */
+                    numProcessed += mySpace;
+                    myRemaining -= mySpace;
+                    numActive = 0;
+                }
+
+                /* While we have full blocks */
+                while (myRemaining >= BUFLEN)
+                {
+                    /* Access the next data */
+                    fillReverse(pBuffer, pOffset + numProcessed, BUFLEN, parent.theReverse);
+                    parent.gHASH(parent.theReverse);
+
+                    /* Adjust counters */
+                    numProcessed += mySpace;
+                    myRemaining -= mySpace;
+                }
+
+                /* If we have remaining data */
+                if (myRemaining > 0)
+                {
+                    /* Copy data into the cache */
+                    Array.Copy(pBuffer, pOffset + numProcessed, theBuffer, numActive, myRemaining);
+                    numActive += myRemaining;
+                }
+
+                /* Adjust the number of bytes processed */
+                numHashed += (ulong)pLen;
+            }
+
+            /**
+            * complete hash.
+            */
+            internal void completeHash()
+            {
+                /* If we have remaining data */
+                if (numActive > 0)
+                {
+                    /* Access the next data */
+                    Arrays.Fill(parent.theReverse, (byte)0);
+                    fillReverse(theBuffer, 0, numActive, parent.theReverse);
+
+                    /* hash value */
+                    parent.gHASH(parent.theReverse);
+                }
+            }
+        }
+    }
 }
diff --git a/crypto/src/crypto/util/BasicAlphabetMapper.cs b/crypto/src/crypto/util/BasicAlphabetMapper.cs
index ac4011a94..b60733fc0 100644
--- a/crypto/src/crypto/util/BasicAlphabetMapper.cs
+++ b/crypto/src/crypto/util/BasicAlphabetMapper.cs
@@ -1,104 +1,106 @@
 using System;
 using System.Collections;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Crypto.Utilities
 {
-/**
- * A basic alphabet mapper that just creates a mapper based on the
- * passed in array of characters.
- */
- public class BasicAlphabetMapper
-    : IAlphabetMapper
-{
-    private Hashtable indexMap = new Hashtable();
-    private Hashtable charMap = new Hashtable();
-
     /**
-     * Base constructor.
-     *
-     * @param alphabet a string of characters making up the alphabet.
-     */    
-    public BasicAlphabetMapper(string alphabet) :
-        this(alphabet.ToCharArray())
-    {
-    }
-
-    /**
-     * Base constructor.
-     *
-     * @param alphabet an array of characters making up the alphabet.
+     * A basic alphabet mapper that just creates a mapper based on the
+     * passed in array of characters.
      */
-    public BasicAlphabetMapper(char[] alphabet)
+    public class BasicAlphabetMapper
+       : IAlphabetMapper
     {
-        for (int i = 0; i != alphabet.Length; i++)
+        private readonly IDictionary indexMap = Platform.CreateHashtable();
+        private readonly IDictionary charMap = Platform.CreateHashtable();
+
+        /**
+         * Base constructor.
+         *
+         * @param alphabet a string of characters making up the alphabet.
+         */
+        public BasicAlphabetMapper(string alphabet) :
+            this(alphabet.ToCharArray())
         {
-            if (indexMap.ContainsKey(alphabet[i]))
-            {
-                throw new ArgumentException("duplicate key detected in alphabet: " + alphabet[i]);
-            }
-            indexMap.Add(alphabet[i], i);
-            charMap.Add(i, alphabet[i]);
         }
-    }
-
-    public int Radix
-    {
-        get { return indexMap.Count; }
-    }
-
-    public byte[] ConvertToIndexes(char[] input)
-    {
-        byte[] outBuf;
 
-        if (indexMap.Count <= 256)
+        /**
+         * Base constructor.
+         *
+         * @param alphabet an array of characters making up the alphabet.
+         */
+        public BasicAlphabetMapper(char[] alphabet)
         {
-            outBuf = new byte[input.Length];
-            for (int i = 0; i != input.Length; i++)
+            for (int i = 0; i != alphabet.Length; i++)
             {
-                outBuf[i] = (byte)(int)indexMap[input[i]];
+                if (indexMap.Contains(alphabet[i]))
+                {
+                    throw new ArgumentException("duplicate key detected in alphabet: " + alphabet[i]);
+                }
+                indexMap.Add(alphabet[i], i);
+                charMap.Add(i, alphabet[i]);
             }
         }
-        else
+
+        public int Radix
         {
-            outBuf = new byte[input.Length * 2];
-            for (int i = 0; i != input.Length; i++)
-            {
-                int idx = (int)indexMap[input[i]];
-                outBuf[i * 2] = (byte)((idx >> 8) & 0xff);
-                outBuf[i * 2  + 1] = (byte)(idx & 0xff);
-            }
+            get { return indexMap.Count; }
         }
 
-        return outBuf;
-    }
-
-    public char[] ConvertToChars(byte[] input)
-    {
-        char[] outBuf;
-
-        if (charMap.Count <= 256)
+        public byte[] ConvertToIndexes(char[] input)
         {
-            outBuf = new char[input.Length];
-            for (int i = 0; i != input.Length; i++)
+            byte[] outBuf;
+
+            if (indexMap.Count <= 256)
+            {
+                outBuf = new byte[input.Length];
+                for (int i = 0; i != input.Length; i++)
+                {
+                    outBuf[i] = (byte)(int)indexMap[input[i]];
+                }
+            }
+            else
             {
-                outBuf[i] = (char)charMap[input[i] & 0xff];
+                outBuf = new byte[input.Length * 2];
+                for (int i = 0; i != input.Length; i++)
+                {
+                    int idx = (int)indexMap[input[i]];
+                    outBuf[i * 2] = (byte)((idx >> 8) & 0xff);
+                    outBuf[i * 2 + 1] = (byte)(idx & 0xff);
+                }
             }
+
+            return outBuf;
         }
-        else
+
+        public char[] ConvertToChars(byte[] input)
         {
-            if ((input.Length & 0x1) != 0)
+            char[] outBuf;
+
+            if (charMap.Count <= 256)
             {
-                throw new ArgumentException("two byte radix and input string odd.Length");
+                outBuf = new char[input.Length];
+                for (int i = 0; i != input.Length; i++)
+                {
+                    outBuf[i] = (char)charMap[input[i] & 0xff];
+                }
             }
-            
-            outBuf = new char[input.Length / 2];
-            for (int i = 0; i != input.Length; i += 2)
+            else
             {
-                outBuf[i / 2] = (char)charMap[((input[i] << 8) & 0xff00) | (input[i + 1] & 0xff)];
+                if ((input.Length & 0x1) != 0)
+                {
+                    throw new ArgumentException("two byte radix and input string odd.Length");
+                }
+
+                outBuf = new char[input.Length / 2];
+                for (int i = 0; i != input.Length; i += 2)
+                {
+                    outBuf[i / 2] = (char)charMap[((input[i] << 8) & 0xff00) | (input[i + 1] & 0xff)];
+                }
             }
-        }
 
-        return outBuf;
+            return outBuf;
+        }
     }
 }
-}
diff --git a/crypto/src/crypto/util/Pack.cs b/crypto/src/crypto/util/Pack.cs
index 30b4ec8ae..8ba738d4f 100644
--- a/crypto/src/crypto/util/Pack.cs
+++ b/crypto/src/crypto/util/Pack.cs
@@ -20,11 +20,41 @@ namespace Org.BouncyCastle.Crypto.Utilities
             bs[off + 1] = (byte)(n);
         }
 
-        internal static ushort BE_To_UInt16(byte[] bs)
+        internal static void UInt16_To_BE(ushort[] ns, byte[] bs, int off)
         {
-            uint n = (uint)bs[0] << 8
-                | (uint)bs[1];
-            return (ushort)n;
+            for (int i = 0; i < ns.Length; ++i)
+            {
+                UInt16_To_BE(ns[i], bs, off);
+                off += 2;
+            }
+        }
+
+        internal static void UInt16_To_BE(ushort[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
+        {
+            for (int i = 0; i < nsLen; ++i)
+            {
+                UInt16_To_BE(ns[nsOff + i], bs, bsOff);
+                bsOff += 2;
+            }
+        }
+
+        internal static byte[] UInt16_To_BE(ushort n)
+        {
+            byte[] bs = new byte[2];
+            UInt16_To_BE(n, bs, 0);
+            return bs;
+        }
+
+        internal static byte[] UInt16_To_BE(ushort[] ns)
+        {
+            return UInt16_To_BE(ns, 0, ns.Length);
+        }
+
+        internal static byte[] UInt16_To_BE(ushort[] ns, int nsOff, int nsLen)
+        {
+            byte[] bs = new byte[2 * nsLen];
+            UInt16_To_BE(ns, nsOff, nsLen, bs, 0);
+            return bs;
         }
 
         internal static ushort BE_To_UInt16(byte[] bs, int off)
@@ -34,11 +64,27 @@ namespace Org.BouncyCastle.Crypto.Utilities
             return (ushort)n;
         }
 
-        internal static byte[] UInt32_To_BE(uint n)
+        internal static void BE_To_UInt16(byte[] bs, int bsOff, ushort[] ns, int nsOff)
         {
-            byte[] bs = new byte[4];
-            UInt32_To_BE(n, bs, 0);
-            return bs;
+            ns[nsOff] = BE_To_UInt16(bs, bsOff);
+        }
+
+        internal static ushort[] BE_To_UInt16(byte[] bs)
+        {
+            return BE_To_UInt16(bs, 0, bs.Length);
+        }
+
+        internal static ushort[] BE_To_UInt16(byte[] bs, int off, int len)
+        {
+            if ((len & 1) != 0)
+                throw new ArgumentException("must be a multiple of 2", "len");
+
+            ushort[] ns = new ushort[len / 2];
+            for (int i = 0; i < len; i += 2)
+            {
+                BE_To_UInt16(bs, off + i, ns, i >> 1);
+            }
+            return ns;
         }
 
         internal static void UInt32_To_BE(uint n, byte[] bs)
@@ -57,13 +103,6 @@ namespace Org.BouncyCastle.Crypto.Utilities
             bs[off + 3] = (byte)(n);
         }
 
-        internal static byte[] UInt32_To_BE(uint[] ns)
-        {
-            byte[] bs = new byte[4 * ns.Length];
-            UInt32_To_BE(ns, bs, 0);
-            return bs;
-        }
-
         internal static void UInt32_To_BE(uint[] ns, byte[] bs, int off)
         {
             for (int i = 0; i < ns.Length; ++i)
@@ -73,7 +112,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
-        public static void UInt32_To_BE(uint[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
+        internal static void UInt32_To_BE(uint[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
         {
             for (int i = 0; i < nsLen; ++i)
             {
@@ -82,6 +121,20 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
+        internal static byte[] UInt32_To_BE(uint n)
+        {
+            byte[] bs = new byte[4];
+            UInt32_To_BE(n, bs, 0);
+            return bs;
+        }
+
+        internal static byte[] UInt32_To_BE(uint[] ns)
+        {
+            byte[] bs = new byte[4 * ns.Length];
+            UInt32_To_BE(ns, bs, 0);
+            return bs;
+        }
+
         internal static uint BE_To_UInt32(byte[] bs)
         {
             return (uint)bs[0] << 24
@@ -107,7 +160,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
-        public static void BE_To_UInt32(byte[] bs, int bsOff, uint[] ns, int nsOff, int nsLen)
+        internal static void BE_To_UInt32(byte[] bs, int bsOff, uint[] ns, int nsOff, int nsLen)
         {
             for (int i = 0; i < nsLen; ++i)
             {
@@ -151,7 +204,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
-        public static void UInt64_To_BE(ulong[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
+        internal static void UInt64_To_BE(ulong[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
         {
             for (int i = 0; i < nsLen; ++i)
             {
@@ -160,13 +213,6 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
-        internal static ulong BE_To_UInt64(byte[] bs)
-        {
-            uint hi = BE_To_UInt32(bs);
-            uint lo = BE_To_UInt32(bs, 4);
-            return ((ulong)hi << 32) | (ulong)lo;
-        }
-
         internal static ulong BE_To_UInt64(byte[] bs, int off)
         {
             uint hi = BE_To_UInt32(bs, off);
@@ -183,7 +229,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
-        public static void BE_To_UInt64(byte[] bs, int bsOff, ulong[] ns, int nsOff, int nsLen)
+        internal static void BE_To_UInt64(byte[] bs, int bsOff, ulong[] ns, int nsOff, int nsLen)
         {
             for (int i = 0; i < nsLen; ++i)
             {
diff --git a/crypto/src/security/SecureRandom.cs b/crypto/src/security/SecureRandom.cs
index 8c6b74d5b..982fbf3a0 100644
--- a/crypto/src/security/SecureRandom.cs
+++ b/crypto/src/security/SecureRandom.cs
@@ -246,14 +246,14 @@ namespace Org.BouncyCastle.Security
         {
             byte[] bytes = new byte[4];
             NextBytes(bytes);
-            return (int)Pack.BE_To_UInt32(bytes);
+            return (int)Pack.BE_To_UInt32(bytes, 0);
         }
 
         public virtual long NextLong()
         {
             byte[] bytes = new byte[8];
             NextBytes(bytes);
-            return (long)Pack.BE_To_UInt64(bytes);
+            return (long)Pack.BE_To_UInt64(bytes, 0);
         }
     }
 }
diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs
index 78c4e8ffc..fdf08a90c 100644
--- a/crypto/src/util/Arrays.cs
+++ b/crypto/src/util/Arrays.cs
@@ -705,6 +705,7 @@ namespace Org.BouncyCastle.Utilities
             return rv;
         }
 
+        [CLSCompliantAttribute(false)]
         public static ushort[] Concatenate(ushort[] a, ushort[] b)
         {
             if (a == null)
diff --git a/crypto/src/util/Bytes.cs b/crypto/src/util/Bytes.cs
index 0d73d67ae..ecde85dde 100644
--- a/crypto/src/util/Bytes.cs
+++ b/crypto/src/util/Bytes.cs
@@ -4,7 +4,7 @@ namespace Org.BouncyCastle.Utilities
 {
     public abstract class Bytes
     {
-        public static readonly uint BYTES = 1;
-        public static readonly uint SIZE = 8;
+        public const int NumBits = 8;
+        public const int NumBytes = 1;
     }
 }
diff --git a/crypto/src/util/Integers.cs b/crypto/src/util/Integers.cs
index b7bd25ce6..11045d9e3 100644
--- a/crypto/src/util/Integers.cs
+++ b/crypto/src/util/Integers.cs
@@ -6,8 +6,8 @@ namespace Org.BouncyCastle.Utilities
 {
     public abstract class Integers
     {
-        public static readonly uint BYTES = 4;
-        public static readonly uint SIZE = 32;
+        public const int NumBits = 32;
+        public const int NumBytes = 4;
 
         private static readonly byte[] DeBruijnTZ = {
             0x00, 0x01, 0x02, 0x18, 0x03, 0x13, 0x06, 0x19, 0x16, 0x04, 0x14, 0x0A,
diff --git a/crypto/src/util/Longs.cs b/crypto/src/util/Longs.cs
index 892e57137..91dee2b50 100644
--- a/crypto/src/util/Longs.cs
+++ b/crypto/src/util/Longs.cs
@@ -6,8 +6,8 @@ namespace Org.BouncyCastle.Utilities
 {
     public abstract class Longs
     {
-        public static readonly uint BYTES = 8;
-        public static readonly uint SIZE = 64;
+        public const int NumBits = 64;
+        public const int NumBytes = 8;
 
         public static long Reverse(long i)
         {
diff --git a/crypto/src/util/io/Streams.cs b/crypto/src/util/io/Streams.cs
index 503a1b4f1..506b2489f 100644
--- a/crypto/src/util/io/Streams.cs
+++ b/crypto/src/util/io/Streams.cs
@@ -100,15 +100,9 @@ namespace Org.BouncyCastle.Utilities.IO
         /// <exception cref="IOException"></exception>
         public static int WriteBufTo(MemoryStream buf, byte[] output, int offset)
         {
-#if PORTABLE
-            byte[] bytes = buf.ToArray();
-            bytes.CopyTo(output, offset);
-            return bytes.Length;
-#else
             int size = (int)buf.Length;
-            buf.WriteTo(new MemoryStream(output, offset, size, true));
+            WriteBufTo(buf, new MemoryStream(output, offset, size));
             return size;
-#endif
         }
 
         public static void WriteZeroes(Stream outStr, long count)