summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-10-20 01:20:50 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-10-20 01:20:50 +0700
commita38d91b7f3f29ff8c414278fe4c697c3567de3ca (patch)
tree4b4cf68a5a95842c3b2a8dafba5ffe85d11275b9
parentAccount for zOff correctly (diff)
downloadBouncyCastle.NET-ed25519-a38d91b7f3f29ff8c414278fe4c697c3567de3ca.tar.xz
Replace BikePolynomial with new BikeRing
- more efficient polynomial ring implementation
- refactor BikeEngine (performance)
-rw-r--r--crypto/src/pqc/crypto/bike/BikeEngine.cs192
-rw-r--r--crypto/src/pqc/crypto/bike/BikePolynomial.cs365
-rw-r--r--crypto/src/pqc/crypto/bike/BikeRing.cs314
-rw-r--r--crypto/src/pqc/crypto/bike/BikeUtilities.cs36
4 files changed, 399 insertions, 508 deletions
diff --git a/crypto/src/pqc/crypto/bike/BikeEngine.cs b/crypto/src/pqc/crypto/bike/BikeEngine.cs
index a872c68a7..94f073bbb 100644
--- a/crypto/src/pqc/crypto/bike/BikeEngine.cs
+++ b/crypto/src/pqc/crypto/bike/BikeEngine.cs
@@ -1,5 +1,5 @@
 using System;
-
+using System.Diagnostics;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Digests;
 using Org.BouncyCastle.Security;
@@ -10,29 +10,29 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
     internal sealed class BikeEngine
     {
         // degree of R
-        private int r;
+        private readonly int r;
 
         // the row weight
-        private int w;
+        private readonly int w;
 
         // Hamming weight of h0, h1
-        private int hw;
+        private readonly int hw;
 
         // the error weight
-        private int t;
+        private readonly int t;
 
         //the shared secret size
-        private int l;
+        private readonly int l;
 
         // number of iterations in BGF decoder
-        private int nbIter;
+        private readonly int nbIter;
 
         // tau
-        private int tau;
+        private readonly int tau;
 
-        private BikePolynomial reductionPoly;
-        private int L_BYTE;
-        private int R_BYTE;
+        private readonly BikeRing bikeRing;
+        private readonly int L_BYTE;
+        private readonly int R_BYTE;
 
         internal BikeEngine(int r, int w, int t, int l, int nbIter, int tau)
         {
@@ -45,9 +45,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
             this.hw = this.w / 2;
             this.L_BYTE = l / 8;
             this.R_BYTE = (r + 7) / 8;
-
-            // generate reductionPoly (X^r + 1)
-            this.reductionPoly = new BikePolynomial(r);
+            this.bikeRing = new BikeRing(r);
         }
 
         internal int SessionKeySize => L_BYTE;
@@ -59,24 +57,21 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
             return BikeUtilities.GenerateRandomByteArray(r * 2, 2 * R_BYTE, t, digest);
         }
 
-        private byte[] FunctionL(byte[] e0, byte[] e1)
+        private void FunctionL(byte[] e0, byte[] e1, byte[] result)
         {
             byte[] hashRes = new byte[48];
-            byte[] res = new byte[L_BYTE];
 
             Sha3Digest digest = new Sha3Digest(384);
             digest.BlockUpdate(e0, 0, e0.Length);
             digest.BlockUpdate(e1, 0, e1.Length);
             digest.DoFinal(hashRes, 0);
 
-            Array.Copy(hashRes, 0, res, 0, L_BYTE);
-            return res;
+            Array.Copy(hashRes, 0, result, 0, L_BYTE);
         }
 
-        private byte[] FunctionK(byte[] m, byte[] c0, byte[] c1)
+        private void FunctionK(byte[] m, byte[] c0, byte[] c1, byte[] result)
         {
             byte[] hashRes = new byte[48];
-            byte[] res = new byte[L_BYTE];
 
             Sha3Digest digest = new Sha3Digest(384);
             digest.BlockUpdate(m, 0, m.Length);
@@ -84,8 +79,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
             digest.BlockUpdate(c1, 0, c1.Length);
             digest.DoFinal(hashRes, 0);
 
-            Array.Copy(hashRes, 0, res, 0, L_BYTE);
-            return res;
+            Array.Copy(hashRes, 0, result, 0, L_BYTE);
         }
 
         /**
@@ -113,34 +107,17 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
             digest.BlockUpdate(seed1, 0, seed1.Length);
 
             // 1. Randomly generate h0, h1
-            byte[] h0Tmp = BikeUtilities.GenerateRandomByteArray(r, R_BYTE, hw, digest);
-            byte[] h1Tmp = BikeUtilities.GenerateRandomByteArray(r, R_BYTE, hw, digest);
-
-            Array.Copy(h0Tmp, 0, h0, 0, h0.Length);
-            Array.Copy(h1Tmp, 0, h1, 0, h1.Length);
-
-            byte[] h1Bits = new byte[r];
-            byte[] h0Bits = new byte[r];
-
-            BikeUtilities.FromByteArrayToBitArray(h0Bits, h0Tmp);
-            BikeUtilities.FromByteArrayToBitArray(h1Bits, h1Tmp);
+            ulong[] h0Element = bikeRing.GenerateRandom(hw, digest);
+            ulong[] h1Element = bikeRing.GenerateRandom(hw, digest);
 
-            // remove last 0 bits (most significant bits with 0 mean non-sense)
-            byte[] h0Cut = BikeUtilities.RemoveLast0Bits(h0Bits);
-            byte[] h1Cut = BikeUtilities.RemoveLast0Bits(h1Bits);
+            bikeRing.EncodeBytes(h0Element, h0);
+            bikeRing.EncodeBytes(h1Element, h1);
 
             // 2. Compute h
-            BikePolynomial h0Poly = new BikePolynomial(h0Cut);
-            BikePolynomial h1Poly = new BikePolynomial(h1Cut);
-
-            BikePolynomial h0Inv = h0Poly.ModInverseBigDeg(reductionPoly);
-            BikePolynomial hPoly = h1Poly.ModKaratsubaMultiplyBigDeg(h0Inv, reductionPoly);
-
-            // Get coefficients of hPoly
-            byte[] hTmp = hPoly.GetEncoded();
-            byte[] hByte = new byte[R_BYTE];
-            BikeUtilities.FromBitArrayToByteArray(hByte, hTmp);
-            Array.Copy(hByte, 0, h, 0, h.Length);
+            ulong[] hElement = bikeRing.Create();
+            bikeRing.Inv(h0Element, hElement);
+            bikeRing.Multiply(hElement, h1Element, hElement);
+            bikeRing.EncodeBytes(hElement, h);
 
             //3. Parse seed2 as sigma
             Array.Copy(seed2, 0, sigma, 0, sigma.Length);
@@ -172,40 +149,35 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
             BikeUtilities.FromByteArrayToBitArray(eBits, eBytes);
 
             byte[] e0Bits = Arrays.CopyOfRange(eBits, 0, r);
+            byte[] e0Bytes = new byte[R_BYTE];
+            BikeUtilities.FromBitArrayToByteArray(e0Bytes, e0Bits);
+
             byte[] e1Bits = Arrays.CopyOfRange(eBits, r, eBits.Length);
+            byte[] e1Bytes = new byte[R_BYTE];
+            BikeUtilities.FromBitArrayToByteArray(e1Bytes, e1Bits);
 
-            // remove last 0 bits (most significant bits with 0 mean no sense)
-            byte[] e0Cut = BikeUtilities.RemoveLast0Bits(e0Bits);
-            byte[] e1Cut = BikeUtilities.RemoveLast0Bits(e1Bits);
+            ulong[] e0Element = bikeRing.Create();
+            ulong[] e1Element = bikeRing.Create();
 
-            BikePolynomial e0 = new BikePolynomial(e0Cut);
-            BikePolynomial e1 = new BikePolynomial(e1Cut);
+            bikeRing.DecodeBytes(e0Bytes, e0Element);
+            bikeRing.DecodeBytes(e1Bytes, e1Element);
+
+            ulong[] hElement = bikeRing.Create();
+            bikeRing.DecodeBytes(h, hElement);
 
             // 3. Calculate c
             // calculate c0
-            byte[] h0Bits = new byte[r];
-            BikeUtilities.FromByteArrayToBitArray(h0Bits, h);
-            BikePolynomial hPoly = new BikePolynomial(BikeUtilities.RemoveLast0Bits(h0Bits));
-            BikePolynomial c0Poly = e0.Add(e1.ModKaratsubaMultiplyBigDeg(hPoly, reductionPoly));
-
-            byte[] c0Bits = c0Poly.GetEncoded();
-            byte[] c0Bytes = new byte[R_BYTE];
-            BikeUtilities.FromBitArrayToByteArray(c0Bytes, c0Bits);
-            Array.Copy(c0Bytes, 0, c0, 0, c0.Length);
+            ulong[] c0Element = bikeRing.Create();
+            bikeRing.Multiply(e1Element, hElement, c0Element);
+            bikeRing.Add(c0Element, e0Element, c0Element);
+            bikeRing.EncodeBytes(c0Element, c0);
 
             //calculate c1
-            byte[] e0Bytes = new byte[R_BYTE];
-            BikeUtilities.FromBitArrayToByteArray(e0Bytes, e0Bits);
-            byte[] e1Bytes = new byte[R_BYTE];
-            BikeUtilities.FromBitArrayToByteArray(e1Bytes, e1Bits);
-
-            byte[] tmp = FunctionL(e0Bytes, e1Bytes);
-            byte[] c1Tmp = BikeUtilities.XorBytes(m, tmp, L_BYTE);
-            Array.Copy(c1Tmp, 0, c1, 0, c1.Length);
+            FunctionL(e0Bytes, e1Bytes, c1);
+            BikeUtilities.XorTo(m, c1, L_BYTE);
 
             // 4. Calculate K
-            byte[] kTmp = FunctionK(m, c0, c1);
-            Array.Copy(kTmp, 0, k, 0, kTmp.Length);
+            FunctionK(m, c0, c1, k);
         }
 
         /**
@@ -221,18 +193,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
          **/
         internal void Decaps(byte[] k, byte[] h0, byte[] h1, byte[] sigma, byte[] c0, byte[] c1)
         {
-            //convert to bits
-            byte[] c0Bits = new byte[this.r];
-            byte[] h0Bits = new byte[this.r];
-            byte[] sigmaBits = new byte[this.l];
-
-            BikeUtilities.FromByteArrayToBitArray(c0Bits, c0);
-            BikeUtilities.FromByteArrayToBitArray(h0Bits, h0);
-            BikeUtilities.FromByteArrayToBitArray(sigmaBits, sigma);
-
-            byte[] c0Cut = BikeUtilities.RemoveLast0Bits(c0Bits);
-            byte[] h0Cut = BikeUtilities.RemoveLast0Bits(h0Bits);
-
             // Get compact version of h0, h1
             int[] h0Compact = new int[hw];
             int[] h1Compact = new int[hw];
@@ -240,10 +200,10 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
             ConvertToCompact(h1Compact, h1);
 
             // Compute syndrome
-            byte[] syndrome = ComputeSyndrome(c0Cut, h0Cut);
+            byte[] syndromeBits = ComputeSyndrome(c0, h0);
 
             // 1. Compute e'
-            byte[] ePrimeBits = BGFDecoder(syndrome, h0Compact, h1Compact);
+            byte[] ePrimeBits = BGFDecoder(syndromeBits, h0Compact, h1Compact);
             byte[] ePrimeBytes = new byte[2 * R_BYTE];
             BikeUtilities.FromBitArrayToByteArray(ePrimeBytes, ePrimeBits);
 
@@ -256,30 +216,31 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
             BikeUtilities.FromBitArrayToByteArray(e1Bytes, e1Bits);
 
             // 2. Compute m'
-            byte[] mPrime = BikeUtilities.XorBytes(c1, FunctionL(e0Bytes, e1Bytes), L_BYTE);
+            byte[] mPrime = new byte[L_BYTE];
+            FunctionL(e0Bytes, e1Bytes, mPrime);
+            BikeUtilities.XorTo(c1, mPrime, L_BYTE);
 
             // 3. Compute K
-            byte[] tmpK = new byte[l];
             byte[] wlist = FunctionH(mPrime);
             if (Arrays.AreEqual(ePrimeBytes, wlist))
             {
-                tmpK = FunctionK(mPrime, c0, c1);
+                FunctionK(mPrime, c0, c1, k);
             }
             else
             {
-                tmpK = FunctionK(sigma, c0, c1);
+                FunctionK(sigma, c0, c1, k);
             }
-            Array.Copy(tmpK, 0, k, 0, tmpK.Length);
         }
 
-        private byte[] ComputeSyndrome(byte[] h0, byte[] c0)
+        private byte[] ComputeSyndrome(byte[] c0, byte[] h0)
         {
-            BikePolynomial coPoly = new BikePolynomial(c0);
-            BikePolynomial h0Poly = new BikePolynomial(h0);
-
-            BikePolynomial s = coPoly.ModKaratsubaMultiplyBigDeg(h0Poly, reductionPoly);
-            byte[] transposedS = Transpose(s.GetEncoded());
-            return transposedS;
+            ulong[] c0Element = bikeRing.Create();
+            ulong[] h0Element = bikeRing.Create();
+            bikeRing.DecodeBytes(c0, c0Element);
+            bikeRing.DecodeBytes(h0, h0Element);
+            ulong[] sElement = bikeRing.Create();
+            bikeRing.Multiply(c0Element, h0Element, sElement);
+            return Transpose(bikeRing.EncodeBits(sElement));
         }
 
         private byte[] BGFDecoder(byte[] s, int[] h0Compact, int[] h1Compact)
@@ -314,12 +275,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
 
         private byte[] Transpose(byte[] input)
         {
-            byte[] tmp = BikeUtilities.Append0s(input, r); // append zeros to s
             byte[] output = new byte[r];
-            output[0] = tmp[0];
+            output[0] = input[0];
             for (int i = 1; i < r; i++)
             {
-                output[i] = tmp[r - i];
+                output[i] = input[r - i];
             }
             return output;
         }
@@ -332,13 +292,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
             // calculate for h0compact
             for (int j = 0; j < r; j++)
             {
-                if (Ctr(h0CompactCol, s, j) >= T)
+                int ctr = Ctr(h0CompactCol, s, j);
+                if (ctr >= T)
                 {
                     UpdateNewErrorIndex(e, j);
                     updatedIndices[j] = 1;
                     black[j] = 1;
                 }
-                else if (Ctr(h0CompactCol, s, j) >= T - tau)
+                else if (ctr >= T - tau)
                 {
                     gray[j] = 1;
                 }
@@ -347,13 +308,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
             // calculate for h1Compact
             for (int j = 0; j < r; j++)
             {
-                if (Ctr(h1CompactCol, s, j) >= T)
+                int ctr = Ctr(h1CompactCol, s, j);
+                if (ctr >= T)
                 {
                     UpdateNewErrorIndex(e, r + j);
                     updatedIndices[r + j] = 1;
                     black[r + j] = 1;
                 }
-                else if (Ctr(h1CompactCol, s, j) >= T - tau)
+                else if (ctr >= T - tau)
                 {
                     gray[r + j] = 1;
                 }
@@ -369,13 +331,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
             }
         }
 
-        private void BFMaskedIter(byte[] s, byte[] e, byte[] mask, int T, int[] h0Compact, int[] h1Compact, int[] h0CompactCol, int[] h1CompactCol)
+        private void BFMaskedIter(byte[] s, byte[] e, byte[] mask, int T, int[] h0Compact, int[] h1Compact,
+            int[] h0CompactCol, int[] h1CompactCol)
         {
             int[] updatedIndices = new int[2 * r];
 
             for (int j = 0; j < r; j++)
             {
-                if (Ctr(h0CompactCol, s, j) >= T && mask[j] == 1)
+                if (mask[j] == 1 && Ctr(h0CompactCol, s, j) >= T)
                 {
                     UpdateNewErrorIndex(e, j);
                     updatedIndices[j] = 1;
@@ -384,7 +347,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
 
             for (int j = 0; j < r; j++)
             {
-                if (Ctr(h1CompactCol, s, j) >= T && mask[r + j] == 1)
+                if (mask[r + j] == 1 && Ctr(h1CompactCol, s, j) >= T)
                 {
                     UpdateNewErrorIndex(e, r + j);
                     updatedIndices[r + j] = 1;
@@ -429,13 +392,20 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
 
         private int Ctr(int[] hCompactCol, byte[] s, int j)
         {
+            Debug.Assert(0 <= j && j < r);
+
             int count = 0;
             for (int i = 0; i < hw; i++)
             {
-                if (s[(hCompactCol[i] + j) % r] == 1)
-                {
-                    count += 1;
-                }
+                //int sPos = (hCompactCol[i] + j) % r;
+                int sPos = hCompactCol[i] + j - r;
+                sPos += (sPos >> 31) & r;
+
+                //if (s[sPos] == 1)
+                //{
+                //    count += 1;
+                //}
+                count += s[sPos];
             }
             return count;
         }
diff --git a/crypto/src/pqc/crypto/bike/BikePolynomial.cs b/crypto/src/pqc/crypto/bike/BikePolynomial.cs
deleted file mode 100644
index eea34d838..000000000
--- a/crypto/src/pqc/crypto/bike/BikePolynomial.cs
+++ /dev/null
@@ -1,365 +0,0 @@
-using System;
-
-using Org.BouncyCastle.Utilities;
-
-namespace Org.BouncyCastle.Pqc.Crypto.Bike
-{
-    internal class BikePolynomial
-    {
-        /**
-         * For the polynomial representation the map f: R->Z*,
-         * <tt>poly(X) -> [coef_0, coef_1, ...]</tt> is used, where
-         * <tt>coef_i</tt> is the <tt>i</tt>th coefficient of the polynomial
-         * represented as int (see {@link GF2mField}). The polynomials are stored
-         * as int arrays.
-         */
-        private readonly int[] m_coefficients;
-
-        /**
-         * Construct a monomial of the given degree over the finite field GF(2^m).
-         *
-         * @param field  the finite field GF(2^m)
-         * @param degree the degree of the monomial
-         */
-        internal BikePolynomial(int degree)
-        {
-            // Initial value (X^degree + 1)
-            this.m_coefficients = new int[degree + 1];
-            this.m_coefficients[degree] = 1;
-            this.m_coefficients[0] ^= 1;
-        }
-
-        /**
-         * Construct the polynomial over the given finite field GF(2^m) from the
-         * given coefficient vector.
-         *
-         * @param field  finite field GF2m
-         * @param coeffs the coefficient vector
-         */
-        private BikePolynomial(int[] coeffs)
-        {
-            this.m_coefficients = coeffs;
-        }
-
-        /**
-         * Create a polynomial over the finite field GF(2^m).
-         *
-         * @param field the finite field GF(2^m)
-         * @param enc   byte[] polynomial in byte array form
-         */
-        internal BikePolynomial(byte[] enc)
-        {
-            // decodes polynomial
-            this.m_coefficients = new int[enc.Length];
-            for (int i = 0; i < m_coefficients.Length; i++)
-            {
-                m_coefficients[i] = enc[i];
-                if ((m_coefficients[i] >> 1) != 0)
-                    throw new ArgumentException(
-                        "Error: byte array is not encoded polynomial over given finite field GF2m");
-            }
-            // if HC = 0 for non-zero polynomial, returns error
-            if ((m_coefficients.Length != 1) && (m_coefficients[m_coefficients.Length - 1] == 0))
-                throw new ArgumentException("Error: byte array is not encoded polynomial over given finite field GF2m");
-        }
-
-        /**
-         * Returns encoded polynomial, i.e., this polynomial in byte array form
-         *
-         * @return the encoded polynomial
-         */
-        internal byte[] GetEncoded()
-        {
-            byte[] res = new byte[m_coefficients.Length];
-            for (int i = 0; i < m_coefficients.Length; i++)
-            {
-                res[i] = (byte)m_coefficients[i];
-            }
-            return res;
-        }
-
-        /**
-         * Compute the sum of this polynomial and the given polynomial.
-         *
-         * @param addend the addend
-         * @return <tt>this + a</tt> (newly created)
-         */
-        internal BikePolynomial Add(BikePolynomial addend)
-        {
-            int[] resultCoeff = Add(m_coefficients, addend.m_coefficients);
-            return new BikePolynomial(NormalForm(resultCoeff));
-        }
-
-        /**
-         * Compute the sum of two polynomials a and b over the finite field
-         * <tt>GF(2^m)</tt>.
-         *
-         * @param a the first polynomial
-         * @param b the second polynomial
-         * @return a + b
-         */
-        private static int[] Add(int[] a, int[] b)
-        {
-            int[] result, addend;
-            if (a.Length < b.Length)
-            {
-                result = new int[b.Length];
-                Array.Copy(b, 0, result, 0, b.Length);
-                addend = a;
-            }
-            else
-            {
-                result = new int[a.Length];
-                Array.Copy(a, 0, result, 0, a.Length);
-                addend = b;
-            }
-
-            for (int i = addend.Length - 1; i >= 0; i--)
-            {
-                result[i] ^= addend[i];
-            }
-
-            return result;
-        }
-
-        private static void AddAt(int[] x, int[] z, int zPos)
-        {
-            for (int i = 0; i < x.Length; ++i)
-            {
-                z[zPos + i] ^= x[i];
-            }
-        }
-
-        /**
-         * Compute the result of the division of two polynomials over the field
-         * <tt>GF(2^m)</tt>.
-         *
-         * @param a the first polynomial
-         * @param f the second polynomial
-         * @return int[][] {q,r}, where a = q*f+r and deg(r) &lt; deg(f);
-         */
-        private static int[][] Div(int[] a, int[] f)
-        {
-            int df = ComputeDegree(f);
-            if (df == -1)
-                throw new ArithmeticException("Division by zero.");
-
-            int degreeR1 = ComputeDegree(a);
-            int[][] result = new int[2][];
-            result[0] = new int[System.Math.Max(0, degreeR1 - df) + 1];
-            result[1] = Arrays.CopyOf(a, degreeR1 + 1);
-
-            while (df <= degreeR1)
-            {
-                int n = degreeR1 - df;
-                result[0][n] ^= 1;
-                AddAt(f, result[1], n);
-                degreeR1 = ComputeDegree(result[1], degreeR1 - 1);
-            }
-            result[1] = NormalForm(result[1]);
-            return result;
-        }
-
-        /**
-         * Reduce a polynomial modulo another polynomial.
-         *
-         * @param a the polynomial
-         * @param f the reduction polynomial
-         * @return <tt>a mod f</tt>
-         */
-        private static int[] Mod(int[] a, int[] f)
-        {
-            int df = ComputeDegree(f);
-            if (df == -1)
-                throw new ArithmeticException("Division by zero");
-
-            int degreeR = ComputeDegree(a);
-            int[] result = Arrays.CopyOf(a, degreeR + 1);
-
-            while (df <= degreeR)
-            {
-                int n = degreeR - df;
-                AddAt(f, result, n);
-                degreeR = ComputeDegree(result, degreeR - 1);
-            }
-            return NormalForm(result);
-        }
-
-        /**
-         * Compute the degree of a polynomial.
-         *
-         * @param a the polynomial
-         * @return the degree of the polynomial <tt>a</tt>. If <tt>a</tt> is
-         *         the zero polynomial, return -1.
-         */
-        private static int ComputeDegree(int[] a)
-        {
-            return ComputeDegree(a, a.Length - 1);
-        }
-
-        private static int ComputeDegree(int[] a, int from)
-        {
-            int degree;
-            for (degree = from; degree >= 0 && a[degree] == 0; degree--)
-            {
-            }
-            return degree;
-        }
-
-        /**
-         * Strip leading zero coefficients from the given polynomial.
-         *
-         * @param a the polynomial
-         * @return the reduced polynomial
-         */
-        private static int[] NormalForm(int[] a)
-        {
-            int d = ComputeDegree(a);
-
-            // if a is the zero polynomial
-            if (d == -1)
-            {
-                // return new zero polynomial
-                return new int[1];
-            }
-
-            // if a already is in normal form
-            if (a.Length == d + 1)
-            {
-                // return a clone of a
-                return Arrays.Clone(a);
-            }
-
-            // else, reduce a
-            int[] result = new int[d + 1];
-            Array.Copy(a, 0, result, 0, d + 1);
-            return result;
-        }
-
-        /**
-         * Compute the product of this polynomial and another polynomial modulo a
-         * third polynomial.
-         *
-         * @param a another polynomial
-         * @param b the reduction polynomial
-         * @return <tt>this * a mod b</tt>
-         */
-        internal BikePolynomial ModKaratsubaMultiplyBigDeg(BikePolynomial a, BikePolynomial b)
-        {
-            int[] resultCoeff = ModKaratsubaMultiplyBigDeg(m_coefficients, a.m_coefficients, b.m_coefficients);
-            return new BikePolynomial(resultCoeff);
-        }
-
-        /**
-         * Compute the inverse of this polynomial modulo the given polynomial.
-         *
-         * @param a the reduction polynomial
-         * @return <tt>this^(-1) mod a</tt>
-         */
-        internal BikePolynomial ModInverseBigDeg(BikePolynomial a)
-        {
-            int[] resultCoeff = ModInvBigDeg(m_coefficients, a.m_coefficients);
-            return new BikePolynomial(resultCoeff);
-        }
-
-        private static int[] ModInvBigDeg(int[] b, int[] g)
-        {
-            int[] r0 = NormalForm(g);
-            int[] r1 = Mod(b, g);
-            int[] s0 = { 0 };
-            int[] s1 = { 1 };
-            int[] s2;
-            int[][] q;
-            while (ComputeDegree(r1) != -1)
-            {
-                q = Div(r0, r1);
-                r0 = NormalForm(r1);
-                r1 = NormalForm(q[1]);
-                s2 = Add(s0, ModKaratsubaMultiplyBigDeg(q[0], s1, g));
-                s0 = NormalForm(s1);
-                s1 = NormalForm(s2);
-            }
-            return s0;
-        }
-
-        /**
-         * Compute the product of two polynomials modulo a third polynomial over the
-         * finite field <tt>GF(2^m)</tt>.
-         *
-         * @param aa the first polynomial
-         * @param bb the second polynomial
-         * @param g the reduction polynomial
-         * @return <tt>a * b mod g</tt>
-         */
-        private static int[] ModKaratsubaMultiplyBigDeg(int[] aa, int[] bb, int[] g)
-        {
-            int[] a, b;
-            if (aa.Length >= bb.Length)
-            {
-                a = aa;
-                b = bb;
-            }
-            else
-            {
-                a = bb;
-                b = aa;
-            }
-
-            int n = a.Length;
-            int m = b.Length;
-
-            int[] D = new int[(n + m) / 2];
-            int[] S = new int[n + m - 1];
-            int[] T = new int[n + m - 1];
-            int[] C = new int[n + m - 1];
-
-            for (int i = 0; i < m; i++)
-            {
-                D[i] = a[i] * b[i];
-            }
-
-            for (int i = 1; i < n + m - 2; i++)
-            {
-                int pLimit = System.Math.Min(m, (i + 1) >> 1);
-                for (int p = 0; p < pLimit; p++)
-                {
-                    int q = i - p;
-                    int aq = q < a.Length ? a[q] : 0;
-
-                    if (q < m)
-                    {
-                        S[i] += (a[p] + aq) * (b[p] + b[q]);
-                        T[i] += D[p] + D[q];
-                    }
-                    else if (q < n)
-                    {
-                        S[i] += (a[p] + aq) * b[p];
-                        T[i] += D[p];
-                    }
-                }
-            }
-
-            for (int i = 0; i < n + m - 1; i++)
-            {
-                if (i == 0)
-                {
-                    C[i] = D[i] % 2;
-                }
-                else if (i == n + m - 2)
-                {
-                    C[i] = (a[a.Length - 1] * b[b.Length - 1]) % 2;
-                }
-                else if (i % 2 == 1)
-                {
-                    C[i] = (S[i] - T[i]) % 2;
-                }
-                else
-                {
-                    C[i] = (S[i] - T[i] + D[i / 2]) % 2;
-                }
-            }
-
-            return Mod(C, g);
-        }
-    }
-}
diff --git a/crypto/src/pqc/crypto/bike/BikeRing.cs b/crypto/src/pqc/crypto/bike/BikeRing.cs
new file mode 100644
index 000000000..c2b2102b8
--- /dev/null
+++ b/crypto/src/pqc/crypto/bike/BikeRing.cs
@@ -0,0 +1,314 @@
+using System;
+using System.Diagnostics;
+#if NETCOREAPP3_0_OR_GREATER
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Math.Raw;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Bike
+{
+    internal sealed class BikeRing
+    {
+        private readonly int m_bits;
+        private readonly int m_size;
+        private readonly int m_sizeExt;
+
+        internal BikeRing(int r)
+        {
+            if ((r & 0x80000001) != 1)
+                throw new ArgumentException();
+
+            m_bits = r;
+            m_size = (r + 63) >> 6;
+            m_sizeExt = m_size * 2;
+        }
+
+        internal void Add(ulong[] x, ulong[] y, ulong[] z)
+        {
+            for (int i = 0; i < Size; ++i)
+            {
+                z[i] = x[i] ^ y[i];
+            }
+        }
+
+        internal void Copy(ulong[] x, ulong[] z)
+        {
+            for (int i = 0; i < Size; ++i)
+            {
+                z[i] = x[i];
+            }
+        }
+
+        internal ulong[] Create()
+        {
+            return new ulong[Size];
+        }
+
+        internal ulong[] CreateExt()
+        {
+            return new ulong[SizeExt];
+        }
+
+        internal ulong[] DecodeBits(byte[] bs)
+        {
+            if (bs.Length > m_bits)
+                throw new ArgumentException();
+
+            ulong[] z = Create();
+            for (int i = 0; i < bs.Length; ++i)
+            {
+                ulong bit = bs[i];
+                if ((bit >> 1) != 0UL)
+                    throw new ArgumentException();
+
+                z[i >> 6] |= bit << (i & 63);
+            }
+            return z;
+        }
+
+        internal void DecodeBytes(byte[] bs, ulong[] z)
+        {
+            int partialBits = m_bits & 63;
+            Pack.LE_To_UInt64(bs, 0, z, 0, Size - 1);
+            byte[] last = new byte[8];
+            Array.Copy(bs, (Size - 1) << 3, last, 0, (partialBits + 7) >> 3);
+            z[Size - 1] = Pack.LE_To_UInt64(last);
+            Debug.Assert((z[Size - 1] >> partialBits) == 0);
+        }
+
+        internal byte[] EncodeBits(ulong[] x)
+        {
+            byte[] bs = new byte[m_bits];
+            for (int i = 0; i < m_bits; ++i)
+            {
+                bs[i] = (byte)((x[i >> 6] >> (i & 63)) & 1UL);
+            }
+            return bs;
+        }
+
+        internal void EncodeBytes(ulong[] x, byte[] bs)
+        {
+            int partialBits = m_bits & 63;
+            Debug.Assert((x[Size - 1] >> partialBits) == 0);
+            Pack.UInt64_To_LE(x, 0, Size - 1, bs, 0);
+            byte[] last = new byte[8];
+            Pack.UInt64_To_LE(x[Size - 1], last);
+            Array.Copy(last, 0, bs, (Size - 1) << 3, (partialBits + 7) >> 3);
+        }
+
+        internal ulong[] GenerateRandom(int weight, IXof digest)
+        {
+            byte[] buf = new byte[4];
+            int highest = Integers.HighestOneBit(m_bits);
+            int mask = highest | (highest - 1);
+
+            ulong[] z = Create();
+            int count = 0;
+            while (count < weight)
+            {
+                digest.Output(buf, 0, 4);
+                int candidate = (int)Pack.LE_To_UInt32(buf) & mask;
+                if (candidate < m_bits)
+                {
+                    int pos = candidate >> 6;
+                    ulong bit = 1UL << (candidate & 63);
+                    if ((z[pos] & bit) == 0UL)
+                    {
+                        z[pos] |= bit;
+                        ++count;
+                    }
+                }
+            }
+            return z;
+        }
+
+        internal void Inv(ulong[] a, ulong[] z)
+        {
+            ulong[] f = Create();
+            ulong[] g = Create();
+            ulong[] t = Create();
+
+            Copy(a, f);
+            Copy(a, t);
+
+            int rSub2 = m_bits - 2;
+            int bits = 32 - Integers.NumberOfLeadingZeros(rSub2);
+
+            for (int i = 1; i < bits; ++i)
+            {
+                SquareN(f, 1 << (i - 1), g);
+                Multiply(f, g, f);
+
+                if ((rSub2 & (1 << i)) != 0)
+                {
+                    int n = rSub2 & ((1 << i) - 1);
+                    SquareN(f, n, g);
+                    Multiply(t, g, t);
+                }
+            }
+
+            Square(t, z);
+        }
+
+        internal void Multiply(ulong[] x, ulong[] y, ulong[] z)
+        {
+            ulong[] tt = CreateExt();
+            ImplMultiplyAcc(x, y, tt);
+            Reduce(tt, z);
+        }
+
+        internal void Reduce(ulong[] tt, ulong[] z)
+        {
+            int partialBits = m_bits & 63;
+            int excessBits = 64 - partialBits;
+            ulong partialMask = ulong.MaxValue >> excessBits;
+
+            ulong c = Nat.ShiftUpBits64(Size, tt, Size, excessBits, tt[Size - 1], z, 0);
+            Debug.Assert(c == 0UL);
+
+            for (int i = 0; i < Size; ++i)
+            {
+                z[i] ^= tt[i];
+            }
+
+            z[Size - 1] &= partialMask;
+        }
+
+        internal int Size => m_size;
+
+        internal int SizeExt => m_sizeExt;
+
+        internal void Square(ulong[] x, ulong[] z)
+        {
+            ulong[] tt = CreateExt();
+            ImplSquare(x, tt);
+            Reduce(tt, z);
+        }
+
+        internal void SquareN(ulong[] x, int n, ulong[] z)
+        {
+            Debug.Assert(n > 0);
+
+            ulong[] tt = CreateExt();
+            ImplSquare(x, tt);
+            Reduce(tt, z);
+
+            while (--n > 0)
+            {
+                ImplSquare(z, tt);
+                Reduce(tt, z);
+            }
+        }
+
+        private void ImplMultiplyAcc(ulong[] x, ulong[] y, ulong[] zz)
+        {
+            ulong[] u = new ulong[16];
+
+            // Schoolbook
+
+            //for (int i = 0; i < Size; ++i)
+            //{
+            //    ulong x_i = x[i];
+
+            //    for (int j = 0; j < Size; ++j)
+            //    {
+            //        ulong y_j = y[j];
+
+            //        ImplMulwAcc(u, x_i, y_j, zz, i + j);
+            //    }
+            //}
+
+            // Arbitrary-degree Karatsuba
+
+            for (int i = 0; i < Size; ++i)
+            {
+                ImplMulwAcc(u, x[i], y[i], zz, i << 1);
+            }
+
+            ulong v0 = zz[0], v1 = zz[1];
+            for (int i = 1; i < Size; ++i)
+            {
+                v0 ^= zz[i << 1]; zz[i] = v0 ^ v1; v1 ^= zz[(i << 1) + 1];
+            }
+
+            ulong w = v0 ^ v1;
+            for (int i = 0; i < Size; ++i)
+            {
+                zz[Size + i] = zz[i] ^ w;
+            }
+
+            int last = Size - 1;
+            for (int zPos = 1; zPos < (last * 2); ++zPos)
+            {
+                int hi = System.Math.Min(last, zPos);
+                int lo = zPos - hi;
+
+                while (lo < hi)
+                {
+                    ImplMulwAcc(u, x[lo] ^ x[hi], y[lo] ^ y[hi], zz, zPos);
+
+                    ++lo;
+                    --hi;
+                }
+            }
+        }
+
+        private static void ImplMulwAcc(ulong[] u, ulong x, ulong y, ulong[] z, int zOff)
+        {
+#if NETCOREAPP3_0_OR_GREATER
+            if (Pclmulqdq.IsSupported)
+            {
+                var X = Vector128.CreateScalar(x);
+                var Y = Vector128.CreateScalar(y);
+                var Z = Pclmulqdq.CarrylessMultiply(X, Y, 0x00);
+                z[zOff    ] ^= Z.GetElement(0);
+                z[zOff + 1] ^= Z.GetElement(1);
+                return;
+            }
+#endif
+
+            //u[0] = 0;
+            u[1] = y;
+            for (int i = 2; i < 16; i += 2)
+            {
+                u[i    ] = u[i >> 1] << 1;
+                u[i + 1] = u[i     ] ^  y;
+            }
+
+            uint j = (uint)x;
+            ulong g, h = 0, l = u[j & 15]
+                              ^ u[(j >> 4) & 15] << 4;
+            int k = 56;
+            do
+            {
+                j  = (uint)(x >> k);
+                g  = u[j & 15]
+                   ^ u[(j >> 4) & 15] << 4;
+                l ^= (g << k);
+                h ^= (g >> -k);
+            }
+            while ((k -= 8) > 0);
+
+            for (int p = 0; p < 7; ++p)
+            {
+                x = (x & 0xFEFEFEFEFEFEFEFEUL) >> 1;
+                h ^= x & (ulong)((long)(y << p) >> 63);
+            }
+
+            Debug.Assert(h >> 63 == 0);
+
+            z[zOff    ] ^= l;
+            z[zOff + 1] ^= h;
+        }
+
+        private void ImplSquare(ulong[] x, ulong[] zz)
+        {
+            Interleave.Expand64To128(x, 0, Size, zz, 0);
+        }
+    }
+}
diff --git a/crypto/src/pqc/crypto/bike/BikeUtilities.cs b/crypto/src/pqc/crypto/bike/BikeUtilities.cs
index 5facdaacf..09143aea0 100644
--- a/crypto/src/pqc/crypto/bike/BikeUtilities.cs
+++ b/crypto/src/pqc/crypto/bike/BikeUtilities.cs
@@ -1,6 +1,4 @@
-using System;
-
-using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Utilities;
 
@@ -8,15 +6,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
 {
     internal class BikeUtilities
     {
-        internal static byte[] XorBytes(byte[] a, byte[] b, int size)
+        internal static void XorTo(byte[] x, byte[] z, int zLen)
         {
-            byte[] output = new byte[size];
-
-            for (int i = 0; i < size; i++)
+            for (int i = 0; i < zLen; ++i)
             {
-                output[i] = (byte)(a[i] ^ b[i]);
+                z[i] ^= x[i];
             }
-            return output;
         }
 
         internal static int GetHammingWeight(byte[] bytes)
@@ -82,29 +77,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Bike
             }
         }
 
-        internal static byte[] RemoveLast0Bits(byte[] output)
-        {
-            int lastIndexOf1 = 0;
-            for (int i = output.Length - 1; i >= 0; i--)
-            {
-                if (output[i] == 1)
-                {
-                    lastIndexOf1 = i;
-                    break;
-                }
-            }
-            byte[] res = new byte[lastIndexOf1 + 1];
-            Array.Copy(output, 0, res, 0, res.Length);
-            return res;
-        }
-
-        internal static byte[] Append0s(byte[] input, int length)
-        {
-            byte[] output = new byte[length];
-            Array.Copy(input, 0, output, 0, input.Length);
-            return output;
-        }
-
         internal static byte[] GenerateRandomByteArray(int mod, int size, int weight, IXof digest)
         {
             byte[] buf = new byte[4];