diff options
Diffstat (limited to 'crypto/src/pqc')
34 files changed, 4101 insertions, 78 deletions
diff --git a/crypto/src/pqc/crypto/bike/BikeEngine.cs b/crypto/src/pqc/crypto/bike/BikeEngine.cs new file mode 100644 index 000000000..7df2703c9 --- /dev/null +++ b/crypto/src/pqc/crypto/bike/BikeEngine.cs @@ -0,0 +1,546 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Pqc.Math.LinearAlgebra; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Pqc.Crypto.Bike +{ + public class BikeEngine + { + // degree of R + private int r; + + // the row weight + private int w; + + // Hamming weight of h0, h1 + private int hw; + + // the error weight + private int t; + + //the shared secret size + private int l; + + // number of iterations in BGF decoder + private int nbIter; + + // tau + private int tau; + + private GF2mField field; + private PolynomialGF2mSmallM reductionPoly; + private int L_BYTE; + private int R_BYTE; + + public BikeEngine(int r, int w, int t, int l, int nbIter, int tau) + { + this.r = r; + this.w = w; + this.t = t; + this.l = l; + this.nbIter = nbIter; + this.tau = tau; + this.hw = this.w / 2; + this.L_BYTE = l / 8; + this.R_BYTE = (r + 7) / 8; + + // finite field GF(2) + GF2mField field = new GF2mField(1); + this.field = field; + + // generate reductionPoly (X^r + 1) + PolynomialGF2mSmallM poly = new PolynomialGF2mSmallM(field, r); + this.reductionPoly = poly.AddMonomial(0); + } + + public int GetSessionKeySize() + { + return L_BYTE; + } + + private byte[] FunctionH(byte[] seed) + { + IXof digest = new ShakeDigest(256); + digest.BlockUpdate(seed, 0, seed.Length); + byte[] wlist = BikeRandomGenerator.GenerateRandomByteArray(r * 2, 2 * R_BYTE, t, digest); + return wlist; + } + + private byte[] FunctionL(byte[] e0, byte[] e1) + { + 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; + } + + private byte[] FunctionK(byte[] m, byte[] c0, byte[] c1) + { + byte[] hashRes = new byte[48]; + byte[] res = new byte[L_BYTE]; + + Sha3Digest digest = new Sha3Digest(384); + digest.BlockUpdate(m, 0, m.Length); + digest.BlockUpdate(c0, 0, c0.Length); + digest.BlockUpdate(c1, 0, c1.Length); + digest.DoFinal(hashRes, 0); + + Array.Copy(hashRes, 0, res, 0, L_BYTE); + return res; + } + + /** + Generate key pairs + - Secret key : (h0, h1, sigma) + - Public key: h + * @param h0 h0 + * @param h1 h1 + * @param sigma sigma + * @param h h + * @param random Secure Random + **/ + public void GenKeyPair(byte[] h0, byte[] h1, byte[] sigma, byte[] h, SecureRandom random) + { + // Randomly generate seeds + byte[] seeds = new byte[64]; + random.NextBytes(seeds); + + byte[] seed1 = new byte[L_BYTE]; + byte[] seed2 = new byte[L_BYTE]; + Array.Copy(seeds, 0, seed1, 0, seed1.Length); + Array.Copy(seeds, seed1.Length, seed2, 0, seed2.Length); + + IXof digest = new ShakeDigest(256); + digest.BlockUpdate(seed1, 0, seed1.Length); + + // 1. Randomly generate h0, h1 + byte[] h0Tmp = BikeRandomGenerator.GenerateRandomByteArray(r, R_BYTE, hw, digest); + byte[] h1Tmp = BikeRandomGenerator.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]; + + Utils.FromByteArrayToBitArray(h0Bits, h0Tmp); + Utils.FromByteArrayToBitArray(h1Bits, h1Tmp); + + // remove last 0 bits (most significant bits with 0 mean non-sense) + byte[] h0Cut = Utils.RemoveLast0Bits(h0Bits); + byte[] h1Cut = Utils.RemoveLast0Bits(h1Bits); + + // 2. Compute h + PolynomialGF2mSmallM h0Poly = new PolynomialGF2mSmallM(this.field, h0Cut); + PolynomialGF2mSmallM h1Poly = new PolynomialGF2mSmallM(this.field, h1Cut); + + PolynomialGF2mSmallM h0Inv = h0Poly.ModInverseBigDeg(reductionPoly); + PolynomialGF2mSmallM hPoly = h1Poly.ModKaratsubaMultiplyBigDeg(h0Inv, reductionPoly); + + // Get coefficients of hPoly + byte[] hTmp = hPoly.GetEncoded(); + byte[] hByte = new byte[R_BYTE]; + Utils.FromBitArrayToByteArray(hByte, hTmp); + Array.Copy(hByte, 0, h, 0, h.Length); + + //3. Parse seed2 as sigma + Array.Copy(seed2, 0, sigma, 0, sigma.Length); + } + + /** + KEM Encapsulation + - Input: h + - Output: (c0,c1,k) + * @param c0 ciphertext + * @param c1 ciphertext + * @param k session key + * @param h public key + * @param random Secure Random + **/ + public void Encaps(byte[] c0, byte[] c1, byte[] k, byte[] h, SecureRandom random) + { + byte[] seeds = new byte[64]; + random.NextBytes(seeds); + + // 1. Randomly generate m by using seed1 + byte[] m = new byte[L_BYTE]; + Array.Copy(seeds, 0, m, 0, m.Length); + + // 2. Calculate e0, e1 + byte[] eBytes = FunctionH(m); + + byte[] eBits = new byte[2 * r]; + Utils.FromByteArrayToBitArray(eBits, eBytes); + + byte[] e0Bits = Arrays.CopyOfRange(eBits, 0, r); + byte[] e1Bits = Arrays.CopyOfRange(eBits, r, eBits.Length); + + // remove last 0 bits (most significant bits with 0 mean no sense) + byte[] e0Cut = Utils.RemoveLast0Bits(e0Bits); + byte[] e1Cut = Utils.RemoveLast0Bits(e1Bits); + + PolynomialGF2mSmallM e0 = new PolynomialGF2mSmallM(field, e0Cut); + PolynomialGF2mSmallM e1 = new PolynomialGF2mSmallM(field, e1Cut); + + // 3. Calculate c + // calculate c0 + byte[] h0Bits = new byte[r]; + Utils.FromByteArrayToBitArray(h0Bits, h); + PolynomialGF2mSmallM hPoly = new PolynomialGF2mSmallM(field, Utils.RemoveLast0Bits(h0Bits)); + PolynomialGF2mSmallM c0Poly = e0.add(e1.ModKaratsubaMultiplyBigDeg(hPoly, reductionPoly)); + + byte[] c0Bits = c0Poly.GetEncoded(); + byte[] c0Bytes = new byte[R_BYTE]; + Utils.FromBitArrayToByteArray(c0Bytes, c0Bits); + Array.Copy(c0Bytes, 0, c0, 0, c0.Length); + + //calculate c1 + byte[] e0Bytes = new byte[R_BYTE]; + Utils.FromBitArrayToByteArray(e0Bytes, e0Bits); + byte[] e1Bytes = new byte[R_BYTE]; + Utils.FromBitArrayToByteArray(e1Bytes, e1Bits); + + byte[] tmp = FunctionL(e0Bytes, e1Bytes); + byte[] c1Tmp = Utils.XorBytes(m, tmp, L_BYTE); + Array.Copy(c1Tmp, 0, c1, 0, c1.Length); + + // 4. Calculate K + byte[] kTmp = FunctionK(m, c0, c1); + Array.Copy(kTmp, 0, k, 0, kTmp.Length); + } + + /** + KEM Decapsulation + - Input: (h0, h1, sigma), (c0, c1) + - Output: k + * @param h0 private key + * @param h1 private key + * @param sigma private key + * @param c0 ciphertext + * @param c1 ciphertext + * @param k session key + **/ + public 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]; + + Utils.FromByteArrayToBitArray(c0Bits, c0); + Utils.FromByteArrayToBitArray(h0Bits, h0); + Utils.FromByteArrayToBitArray(sigmaBits, sigma); + + byte[] c0Cut = Utils.RemoveLast0Bits(c0Bits); + byte[] h0Cut = Utils.RemoveLast0Bits(h0Bits); + + // Get compact version of h0, h1 + int[] h0Compact = new int[hw]; + int[] h1Compact = new int[hw]; + ConvertToCompact(h0Compact, h0); + ConvertToCompact(h1Compact, h1); + + // Compute syndrome + byte[] syndrome = ComputeSyndrome(c0Cut, h0Cut); + + // 1. Compute e' + byte[] ePrimeBits = BGFDecoder(syndrome, h0Compact, h1Compact); + byte[] ePrimeBytes = new byte[2 * R_BYTE]; + Utils.FromBitArrayToByteArray(ePrimeBytes, ePrimeBits); + + byte[] e0Bits = Arrays.CopyOfRange(ePrimeBits, 0, r); + byte[] e1Bits = Arrays.CopyOfRange(ePrimeBits, r, ePrimeBits.Length); + + byte[] e0Bytes = new byte[R_BYTE]; + Utils.FromBitArrayToByteArray(e0Bytes, e0Bits); + byte[] e1Bytes = new byte[R_BYTE]; + Utils.FromBitArrayToByteArray(e1Bytes, e1Bits); + + // 2. Compute m' + byte[] mPrime = Utils.XorBytes(c1, FunctionL(e0Bytes, e1Bytes), L_BYTE); + + // 3. Compute K + byte[] tmpK = new byte[l]; + byte[] wlist = FunctionH(mPrime); + if (Arrays.AreEqual(ePrimeBytes, wlist)) + { + tmpK = FunctionK(mPrime, c0, c1); + } + else + { + tmpK = FunctionK(sigma, c0, c1); + } + Array.Copy(tmpK, 0, k, 0, tmpK.Length); + } + + private byte[] ComputeSyndrome(byte[] h0, byte[] c0) + { + PolynomialGF2mSmallM coPoly = new PolynomialGF2mSmallM(field, c0); + PolynomialGF2mSmallM h0Poly = new PolynomialGF2mSmallM(field, h0); + + PolynomialGF2mSmallM s = coPoly.ModKaratsubaMultiplyBigDeg(h0Poly, reductionPoly); + byte[] transposedS = Transpose(s.GetEncoded()); + return transposedS; + } + + private byte[] BGFDecoder(byte[] s, int[] h0Compact, int[] h1Compact) + { + byte[] e = new byte[2 * r]; + + // Get compact column version + int[] h0CompactCol = GetColumnFromCompactVersion(h0Compact); + int[] h1CompactCol = GetColumnFromCompactVersion(h1Compact); + + for (int i = 1; i <= nbIter; i++) + { + byte[] black = new byte[2 * r]; + byte[] gray = new byte[2 * r]; + + int T = Threshold(Utils.GetHammingWeight(s), i, r); + + BFIter(s, e, T, h0Compact, h1Compact, h0CompactCol, h1CompactCol, black, gray); + + if (i == 1) + { + BFMaskedIter(s, e, black, (hw + 1) / 2 + 1, h0Compact, h1Compact, h0CompactCol, h1CompactCol); + BFMaskedIter(s, e, gray, (hw + 1) / 2 + 1, h0Compact, h1Compact, h0CompactCol, h1CompactCol); + } + } + if (Utils.GetHammingWeight(s) == 0) + { + return e; + } + else return null; + } + + private byte[] Transpose(byte[] input) + { + byte[] tmp = Utils.Append0s(input, r); // append zeros to s + byte[] output = new byte[r]; + output[0] = tmp[0]; + for (int i = 1; i < r; i++) + { + output[i] = tmp[r - i]; + } + return output; + } + private void BFIter(byte[] s, byte[] e, int T, int[] h0Compact, int[] h1Compact, int[] h0CompactCol, int[] h1CompactCol, byte[] black, byte[] gray) + { + int[] updatedIndices = new int[2 * r]; + + // calculate for h0compact + for (int j = 0; j < r; j++) + { + if (Ctr(h0CompactCol, s, j) >= T) + { + UpdateNewErrorIndex(e, j); + updatedIndices[j] = 1; + black[j] = 1; + } + else if (Ctr(h0CompactCol, s, j) >= T - tau) + { + gray[j] = 1; + } + } + + // calculate for h1Compact + for (int j = 0; j < r; j++) + { + if (Ctr(h1CompactCol, s, j) >= T) + { + UpdateNewErrorIndex(e, r + j); + updatedIndices[r + j] = 1; + black[r + j] = 1; + } + else if (Ctr(h1CompactCol, s, j) >= T - tau) + { + gray[r + j] = 1; + } + } + + // recompute syndrome + for (int i = 0; i < 2 * r; i++) + { + if (updatedIndices[i] == 1) + { + RecomputeSyndrome(s, i, h0Compact, h1Compact); + } + } + } + + 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) + { + UpdateNewErrorIndex(e, j); + updatedIndices[j] = 1; + } + } + + for (int j = 0; j < r; j++) + { + if (Ctr(h1CompactCol, s, j) >= T && mask[r + j] == 1) + { + UpdateNewErrorIndex(e, r + j); + updatedIndices[r + j] = 1; + } + } + + // recompute syndrome + for (int i = 0; i < 2 * r; i++) + { + if (updatedIndices[i] == 1) + { + RecomputeSyndrome(s, i, h0Compact, h1Compact); + } + } + } + + private int Threshold(int hammingWeight, int i, int r) + { + double d = 0; + int floorD = 0; + int res = 0; + switch (r) + { + case 12323: + d = 0.0069722 * hammingWeight + 13.530; + floorD = (int) System.Math.Floor(d); + res = floorD > 36 ? floorD : 36; + break; + case 24659: + d = 0.005265 * hammingWeight + 15.2588; + floorD = (int) System.Math.Floor(d); + res = floorD > 52 ? floorD : 52; + break; + case 40973: + d = 0.00402312 * hammingWeight + 17.8785; + floorD = (int) System.Math.Floor(d); + res = floorD > 69 ? floorD : 69; + break; + } + return res; + } + + private int Ctr(int[] hCompactCol, byte[] s, int j) + { + int count = 0; + for (int i = 0; i < hw; i++) + { + if (s[(hCompactCol[i] + j) % r] == 1) + { + count += 1; + } + } + return count; + } + + // Convert a polynomial in GF2 to an array of positions of which the coefficients of the polynomial are equals to 1 + private void ConvertToCompact(int[] compactVersion, byte[] h) + { + // maximum size of this array is the Hamming weight of the polynomial + int count = 0; + for (int i = 0; i < R_BYTE; i++) + { + for (int j = 0; j < 8; j++) + { + if ((i * 8 + j) == this.r) + { + break; + } + + if (((h[i] >> j) & 1) == 1) + { + compactVersion[count++] = i * 8 + j; + } + } + } + } + + private int[] GetColumnFromCompactVersion(int[] hCompact) + { + int[] hCompactColumn = new int[hw]; + if (hCompact[0] == 0) + { + hCompactColumn[0] = 0; + for (int i = 1; i < hw; i++) + { + hCompactColumn[i] = r - hCompact[hw - i]; + } + } + else + { + for (int i = 0; i < hw; i++) + { + hCompactColumn[i] = r - hCompact[hw - 1 - i]; + } + } + return hCompactColumn; + } + + private void RecomputeSyndrome(byte[] syndrome, int index, int[] h0Compact, int[] h1Compact) + { + if (index < r) + { + for (int i = 0; i < hw; i++) + { + if (h0Compact[i] <= index) + { + syndrome[index - h0Compact[i]] ^= 1; + } + else + { + syndrome[r + index - h0Compact[i]] ^= 1; + } + } + } + else + { + for (int i = 0; i < hw; i++) + { + if (h1Compact[i] <= (index - r)) + { + syndrome[(index - r) - h1Compact[i]] ^= 1; + } + else + { + syndrome[r - h1Compact[i] + (index - r)] ^= 1; + } + } + } + } + + private void UpdateNewErrorIndex(byte[] e, int index) + { + int newIndex = index; + if (index != 0 && index != r) + { + if (index > r) + { + newIndex = 2 * r - index + r; + } + else + { + newIndex = r - index; + } + } + e[newIndex] ^= 1; + } + } +} diff --git a/crypto/src/pqc/crypto/bike/BikeKemExtractor.cs b/crypto/src/pqc/crypto/bike/BikeKemExtractor.cs new file mode 100644 index 000000000..bcc0c124c --- /dev/null +++ b/crypto/src/pqc/crypto/bike/BikeKemExtractor.cs @@ -0,0 +1,48 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Pqc.Crypto.Bike +{ + public class BikeKemExtractor : IEncapsulatedSecretExtractor + { + private BikeEngine engine; + + private BikeKeyParameters key; + private int defaultKeySize; + + public BikeKemExtractor(BikePrivateKeyParameters privParams) + { + this.key = privParams; + initCipher(key.Parameters); + } + + private void initCipher(BikeParameters param) + { + engine = param.BIKEEngine; + defaultKeySize = param.DefaultKeySize; + } + + public byte[] ExtractSecret(byte[] encapsulation) + { + byte[] session_key = new byte[engine.GetSessionKeySize()]; + BikePrivateKeyParameters secretKey = (BikePrivateKeyParameters)key; + + // Extract c0, c1 from encapsulation c + byte[] c0 = Arrays.CopyOfRange(encapsulation, 0, secretKey.Parameters.RByte); + byte[] c1 = Arrays.CopyOfRange(encapsulation, secretKey.Parameters.RByte, encapsulation.Length); + + byte[] h0 = secretKey.GetH0(); + byte[] h1 = secretKey.GetH1(); + byte[] sigma = secretKey.GetSigma(); + + engine.Decaps(session_key, h0, h1, sigma, c0, c1); + return Arrays.CopyOfRange(session_key, 0, defaultKeySize / 8); + } + + public int EncapsulationLength => key.Parameters.RByte + key.Parameters.LByte; + + } +} diff --git a/crypto/src/pqc/crypto/bike/BikeKemGenerator.cs b/crypto/src/pqc/crypto/bike/BikeKemGenerator.cs new file mode 100644 index 000000000..461cdae97 --- /dev/null +++ b/crypto/src/pqc/crypto/bike/BikeKemGenerator.cs @@ -0,0 +1,85 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Pqc.Crypto.Bike +{ + public class BikeKemGenerator : IEncapsulatedSecretGenerator + { + private SecureRandom sr; + public BikeKemGenerator(SecureRandom random) + { + this.sr = random; + } + + public ISecretWithEncapsulation GenerateEncapsulated(AsymmetricKeyParameter recipientKey) + { + BikePublicKeyParameters key = (BikePublicKeyParameters)recipientKey; + BikeEngine engine = key.Parameters.BIKEEngine; + + byte[] K = new byte[key.Parameters.LByte]; + byte[] c0 = new byte[key.Parameters.RByte]; + byte[] c1 = new byte[key.Parameters.LByte]; + byte[] h = key.PublicKey; + + engine.Encaps(c0, c1, K, h, sr); + + byte[] cipherText = Arrays.Concatenate(c0, c1); + + return new SecretWithEncapsulationImpl(Arrays.CopyOfRange(K, 0, key.Parameters.DefaultKeySize / 8), cipherText); + } + + private class SecretWithEncapsulationImpl : ISecretWithEncapsulation + { + private volatile bool hasBeenDestroyed = false; + + private byte[] sessionKey; + private byte[] cipher_text; + + public SecretWithEncapsulationImpl(byte[] sessionKey, byte[] cipher_text) + { + this.sessionKey = sessionKey; + this.cipher_text = cipher_text; + } + + public byte[] GetSecret() + { + CheckDestroyed(); + return Arrays.Clone(sessionKey); + } + + public byte[] GetEncapsulation() + { + CheckDestroyed(); + + return Arrays.Clone(cipher_text); + } + + public void Dispose() + { + if (!hasBeenDestroyed) + { + hasBeenDestroyed = true; + Arrays.Clear(sessionKey); + Arrays.Clear(cipher_text); + } + } + + public bool IsDestroyed() + { + return hasBeenDestroyed; + } + + void CheckDestroyed() + { + if (IsDestroyed()) + { + throw new Exception("data has been destroyed"); + } + } + } + } +} diff --git a/crypto/src/pqc/crypto/bike/BikeKeyGenerationParameters.cs b/crypto/src/pqc/crypto/bike/BikeKeyGenerationParameters.cs new file mode 100644 index 000000000..226d939fc --- /dev/null +++ b/crypto/src/pqc/crypto/bike/BikeKeyGenerationParameters.cs @@ -0,0 +1,22 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Pqc.Crypto.Bike +{ + public class BikeKeyGenerationParameters : KeyGenerationParameters + { + private BikeParameters param; + + public BikeKeyGenerationParameters( + SecureRandom random, + BikeParameters param) : base(random, 256) + { + this.param = param; + } + + public BikeParameters Parameters => param; + } +} diff --git a/crypto/src/pqc/crypto/bike/BikeKeyPairGenerator.cs b/crypto/src/pqc/crypto/bike/BikeKeyPairGenerator.cs new file mode 100644 index 000000000..f1b786336 --- /dev/null +++ b/crypto/src/pqc/crypto/bike/BikeKeyPairGenerator.cs @@ -0,0 +1,76 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Pqc.Crypto.Bike +{ + public class BikeKeyPairGenerator : IAsymmetricCipherKeyPairGenerator + { + private SecureRandom random; + + // block length + private int r; + + // the row weight + private int w; + + // Hamming weight of h0, h1 + private int hw; + + // the error weight + private int t; + + //the shared secret size + private int l; + + // number of iterations in BGF decoder + private int nbIter; + + // tau + private int tau; + private int L_BYTE; + private int R_BYTE; + + private BikeKeyGenerationParameters bikeKeyGenerationParameters; + public void Init(KeyGenerationParameters param) + { + this.bikeKeyGenerationParameters = (BikeKeyGenerationParameters)param; + this.random = param.Random; + + // get parameters + this.r = this.bikeKeyGenerationParameters.Parameters.R; + this.w = this.bikeKeyGenerationParameters.Parameters.W; + this.l = this.bikeKeyGenerationParameters.Parameters.L; + this.t = this.bikeKeyGenerationParameters.Parameters.T; + this.nbIter = this.bikeKeyGenerationParameters.Parameters.NbIter; + this.tau = this.bikeKeyGenerationParameters.Parameters.Tau; + this.hw = w / 2; + this.L_BYTE = l / 8; + this.R_BYTE = (r + 7) / 8; + } + + private AsymmetricCipherKeyPair GenKeyPair() + { + BikeEngine engine = bikeKeyGenerationParameters.Parameters.BIKEEngine; + byte[] h0 = new byte[R_BYTE]; + byte[] h1 = new byte[R_BYTE]; + byte[] h = new byte[R_BYTE]; + byte[] sigma = new byte[L_BYTE]; + + engine.GenKeyPair(h0, h1, sigma, h, random); + + // form keys + BikePublicKeyParameters publicKey = new BikePublicKeyParameters(bikeKeyGenerationParameters.Parameters, h); + BikePrivateKeyParameters privateKey = new BikePrivateKeyParameters(bikeKeyGenerationParameters.Parameters, h0, h1, sigma); + + return new AsymmetricCipherKeyPair(publicKey, privateKey); + } + + public AsymmetricCipherKeyPair GenerateKeyPair() + { + return GenKeyPair(); + } + } +} diff --git a/crypto/src/pqc/crypto/bike/BikeKeyParameters.cs b/crypto/src/pqc/crypto/bike/BikeKeyParameters.cs new file mode 100644 index 000000000..0ee8279a9 --- /dev/null +++ b/crypto/src/pqc/crypto/bike/BikeKeyParameters.cs @@ -0,0 +1,21 @@ +using Org.BouncyCastle.Crypto; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Pqc.Crypto.Bike +{ + public class BikeKeyParameters : AsymmetricKeyParameter + { + private BikeParameters param; + + public BikeKeyParameters( + bool isPrivate, + BikeParameters param) : base(isPrivate) + { + this.param = param; + } + + public BikeParameters Parameters => param; + } +} diff --git a/crypto/src/pqc/crypto/bike/BikeParameters.cs b/crypto/src/pqc/crypto/bike/BikeParameters.cs new file mode 100644 index 000000000..e5695c48f --- /dev/null +++ b/crypto/src/pqc/crypto/bike/BikeParameters.cs @@ -0,0 +1,67 @@ +using Org.BouncyCastle.Crypto; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Pqc.Crypto.Bike +{ + public class BikeParameters : ICipherParameters + { + // 128 bits security + public static BikeParameters bike128 = new BikeParameters("bike128", 12323, 142, 134, 256, 5, 3, 128); + + // 192 bits security + public static BikeParameters bike192 = new BikeParameters("bike192",24659, 206, 199, 256, 5, 3, 192); + + // 256 bits security + public static BikeParameters bike256 = new BikeParameters("bike256",40973, 274, 264, 256, 5, 3, 256); + + private String name; + private int r; + private int w; + private int t; + private int l; + private int nbIter; + private int tau; + private int defaultKeySize; + + private BikeEngine bikeEngine; + internal BikeParameters(String name, int r, int w, int t, int l, int nbIter, int tau, int defaultKeySize) + { + this.name = name; + this.r = r; + this.w = w; + this.t = t; + this.l = l; + this.nbIter = nbIter; + this.tau = tau; + this.defaultKeySize = defaultKeySize; + this.bikeEngine = new BikeEngine(r, w, t, l, nbIter, tau); + } + + internal BikeParameters(BikeParameters param) + { + this.name = param.name; + this.r = param.r; + this.w = param.w; + this.t = param.t; + this.l = param.l; + this.nbIter = param.nbIter; + this.tau = param.tau; + this.defaultKeySize = param.defaultKeySize; + this.bikeEngine = new BikeEngine(r, w, t, l, nbIter, tau); + } + + public int R => r; + public int RByte => (r + 7) / 8; + public int LByte => l / 8; + public int W => w; + public int T => t; + public int L => l; + public int NbIter => nbIter; + public int Tau => tau; + public String Name => name; + public int DefaultKeySize => defaultKeySize; + internal BikeEngine BIKEEngine => bikeEngine; + } +} diff --git a/crypto/src/pqc/crypto/bike/BikePrivateKeyParameters.cs b/crypto/src/pqc/crypto/bike/BikePrivateKeyParameters.cs new file mode 100644 index 000000000..d8c6e4613 --- /dev/null +++ b/crypto/src/pqc/crypto/bike/BikePrivateKeyParameters.cs @@ -0,0 +1,55 @@ +using Org.BouncyCastle.Utilities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Pqc.Crypto.Bike +{ + public class BikePrivateKeyParameters : BikeKeyParameters + { + // h0 + private byte[] h0; + + // h1 + private byte[] h1; + + // sigma + private byte[] sigma; + + /** + * Constructor. + * + * @param h0 h0 + * @param h1 h1 + * @param sigma random bytes sigma + */ + public BikePrivateKeyParameters(BikeParameters bikeParameters, byte[] h0, byte[] h1, byte[] sigma) : base(true, bikeParameters) + { + this.h0 = Arrays.Clone(h0); + this.h1 = Arrays.Clone(h1); + this.sigma = Arrays.Clone(sigma); + } + + public byte[] GetH0() + { + return h0; + } + + public byte[] GetH1() + { + return h1; + } + + public byte[] GetSigma() + { + return sigma; + } + + public byte[] PrivateKey => Arrays.Concatenate(Arrays.Concatenate(h0, h1), sigma); + + public byte[] GetEncoded() + { + return PrivateKey; + } + } +} diff --git a/crypto/src/pqc/crypto/bike/BikePublicKeyParameters.cs b/crypto/src/pqc/crypto/bike/BikePublicKeyParameters.cs new file mode 100644 index 000000000..16370c3cf --- /dev/null +++ b/crypto/src/pqc/crypto/bike/BikePublicKeyParameters.cs @@ -0,0 +1,28 @@ +using Org.BouncyCastle.Utilities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Pqc.Crypto.Bike +{ + public class BikePublicKeyParameters : BikeKeyParameters + { + byte[] publicKey; + + /** + * Constructor. + * + * @param publicKey byte + */ + public BikePublicKeyParameters(BikeParameters param, byte[] publicKey) : base(false, param) + { + this.publicKey = Arrays.Clone(publicKey); + } + + public byte[] PublicKey => Arrays.Clone(publicKey); + public byte[] GetEncoded() + { + return PublicKey; + } + } +} diff --git a/crypto/src/pqc/crypto/bike/BikeRandomGenerator.cs b/crypto/src/pqc/crypto/bike/BikeRandomGenerator.cs new file mode 100644 index 000000000..4eea5774a --- /dev/null +++ b/crypto/src/pqc/crypto/bike/BikeRandomGenerator.cs @@ -0,0 +1,91 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Utilities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Pqc.Crypto.Bike +{ + class BikeRandomGenerator + { + private static int BitScanReverse(int t) + { + int res = 0; + while (t != 0) + { + t >>= 1; + res++; + } + + return res; + } + + private static int GetRandomInMod(int mod, IXof digest) + { + int mask = MaskNumber(BitScanReverse(mod)); + int res = -1; + while (true) + { + res = GetRandomNumber(digest); + res &= mask; + + if (res < mod) + { + break; + } + } + return res; + } + + private static void GenerateRandomArray(byte[] res, int mod, int weight, IXof digest) + { + int index = 0; + while (index < weight) + { + int tmp = GetRandomInMod(mod, digest); + + if (CheckBit(res, tmp) == 0) + { // check for new index + SetBit(res, tmp); + index++; + } + } + } + + private static int CheckBit(byte[] a, int position) + { + int index = position / 8; + int pos = position % 8; + return ((a[index] >> (pos)) & 0x01); + } + + + + private static void SetBit(byte[] a, int position) + { + int index = position / 8; + int pos = position % 8; + a[index] |= (byte) (1 << (pos)); + } + + public static byte[] GenerateRandomByteArray(int mod, int size, int weight, IXof digest) + { + byte[] res = new byte[size]; + GenerateRandomArray(res, mod, weight, digest); + return res; + } + + private static int MaskNumber(int n) + { + return ((1 << n) - 1); + } + + private static int GetRandomNumber(IXof digest) + { + byte[] output = new byte[4]; + digest.Output(output, 0, output.Length); + int tmp = Pack.LE_To_UInt16(output, 0); + return tmp; + } + } +} diff --git a/crypto/src/pqc/crypto/bike/Utils.cs b/crypto/src/pqc/crypto/bike/Utils.cs new file mode 100644 index 000000000..8a1a05e37 --- /dev/null +++ b/crypto/src/pqc/crypto/bike/Utils.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Pqc.Crypto.Bike +{ + class Utils + { + internal static byte[] XorBytes(byte[] a, byte[] b, int size) + { + byte[] output = new byte[size]; + + for (int i = 0; i < size; i++) + { + output[i] = (byte)(a[i] ^ b[i]); + } + return output; + } + + internal static int GetHammingWeight(byte[] bytes) + { + int hammingWeight = 0; + for (int i = 0; i < bytes.Length; i++) + { + hammingWeight += bytes[i]; + } + return hammingWeight; + } + + internal static void FromByteArrayToBitArray(byte[] output, byte[] input) + { + int max = (output.Length / 8); + for (int i = 0; i < max; i++) + { + for (int j = 0; j != 8; j++) + { + output[i * 8 + j] = (byte)UnsignedRightBitShiftInt(input[i] & (1 << j), j); + } + } + if (output.Length % 8 != 0) + { + int off = max * 8; + int count = 0; + while (off < output.Length) + { + output[off++] = (byte)(UnsignedRightBitShiftInt(input[max] & (1 << count), count)); + count++; + } + } + } + + internal static void FromBitArrayToByteArray(byte[] output, byte[] input) + { + int count = 0; + int pos = 0; + long len = input.Length; + while (count < len) + { + if (count + 8 >= input.Length) + {// last set of bits cannot have enough 8 bits + int b = input[count]; + for (int j = input.Length - count - 1; j >= 1; j--) + { //bin in reversed order + b |= input[count + j] << j; + } + output[pos] = (byte)b; + } + else + { + int b = input[count]; + for (int j = 7; j >= 1; j--) + { //bin in reversed order + b |= input[count + j] << j; + } + output[pos] = (byte)b; + } + + count += 8; + pos++; + } + } + + 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 int UnsignedRightBitShiftInt(int a, int b) + { + uint tmp = (uint)a; + tmp >>= b; + return (int)tmp; + } + + internal static long UnsignedRightBitShiftLong(long a, int b) + { + ulong tmp = (ulong)a; + tmp >>= b; + return (long)tmp; + } + } +} diff --git a/crypto/src/pqc/crypto/saber/SABERKEMExtractor.cs b/crypto/src/pqc/crypto/saber/SABERKEMExtractor.cs index 83d6f9d65..7199b9dab 100644 --- a/crypto/src/pqc/crypto/saber/SABERKEMExtractor.cs +++ b/crypto/src/pqc/crypto/saber/SABERKEMExtractor.cs @@ -3,20 +3,20 @@ using Org.BouncyCastle.Crypto; namespace Org.BouncyCastle.Pqc.Crypto.Saber { - public class SABERKEMExtractor + public class SaberKemExtractor : IEncapsulatedSecretExtractor { private SABEREngine engine; - private SABERKeyParameters key; + private SaberKeyParameters key; - public SABERKEMExtractor(SABERKeyParameters privParams) + public SaberKemExtractor(SaberKeyParameters privParams) { this.key = privParams; InitCipher(key.GetParameters()); } - private void InitCipher(SABERParameters param) + private void InitCipher(SaberParameters param) { engine = param.GetEngine(); } @@ -24,7 +24,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Saber public byte[] ExtractSecret(byte[] encapsulation) { byte[] session_key = new byte[engine.GetSessionKeySize()]; - engine.crypto_kem_dec(session_key, encapsulation, ((SABERPrivateKeyParameters) key).GetPrivateKey()); + engine.crypto_kem_dec(session_key, encapsulation, ((SaberPrivateKeyParameters) key).GetPrivateKey()); return session_key; } diff --git a/crypto/src/pqc/crypto/saber/SABERKEMGenerator.cs b/crypto/src/pqc/crypto/saber/SABERKEMGenerator.cs index 4baf1d0c6..0919b4dea 100644 --- a/crypto/src/pqc/crypto/saber/SABERKEMGenerator.cs +++ b/crypto/src/pqc/crypto/saber/SABERKEMGenerator.cs @@ -7,20 +7,20 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Pqc.Crypto.Saber { - public class SABERKEMGenerator + public class SaberKemGenerator : IEncapsulatedSecretGenerator { // the source of randomness private SecureRandom sr; - public SABERKEMGenerator(SecureRandom random) + public SaberKemGenerator(SecureRandom random) { this.sr = random; } public ISecretWithEncapsulation GenerateEncapsulated(AsymmetricKeyParameter recipientKey) { - SABERPublicKeyParameters key = (SABERPublicKeyParameters) recipientKey; + SaberPublicKeyParameters key = (SaberPublicKeyParameters) recipientKey; SABEREngine engine = key.GetParameters().GetEngine(); byte[] cipher_text = new byte[engine.GetCipherTextSize()]; byte[] sessionKey = new byte[engine.GetSessionKeySize()]; diff --git a/crypto/src/pqc/crypto/saber/SABERKeyGenerationParameters.cs b/crypto/src/pqc/crypto/saber/SABERKeyGenerationParameters.cs index 48851076d..038c191ef 100644 --- a/crypto/src/pqc/crypto/saber/SABERKeyGenerationParameters.cs +++ b/crypto/src/pqc/crypto/saber/SABERKeyGenerationParameters.cs @@ -4,22 +4,19 @@ using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Pqc.Crypto.Saber { - public class SABERKeyGenerationParameters + public class SaberKeyGenerationParameters : KeyGenerationParameters { - private SABERParameters parameters; + private SaberParameters parameters; - public SABERKeyGenerationParameters( + public SaberKeyGenerationParameters( SecureRandom random, - SABERParameters saberParameters) + SaberParameters saberParameters) : base(random, 256) { this.parameters = saberParameters; } - public SABERParameters GetParameters() - { - return parameters; - } + public SaberParameters Parameters => parameters; } } \ No newline at end of file diff --git a/crypto/src/pqc/crypto/saber/SABERKeyPairGenerator.cs b/crypto/src/pqc/crypto/saber/SABERKeyPairGenerator.cs index 79b59ee1d..73209b18b 100644 --- a/crypto/src/pqc/crypto/saber/SABERKeyPairGenerator.cs +++ b/crypto/src/pqc/crypto/saber/SABERKeyPairGenerator.cs @@ -4,10 +4,10 @@ using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Pqc.Crypto.Saber { - public class SABERKeyPairGenerator + public class SaberKeyPairGenerator : IAsymmetricCipherKeyPairGenerator { - private SABERKeyGenerationParameters saberParams; + private SaberKeyGenerationParameters saberParams; private int l; @@ -16,21 +16,21 @@ namespace Org.BouncyCastle.Pqc.Crypto.Saber private void Initialize( KeyGenerationParameters param) { - this.saberParams = (SABERKeyGenerationParameters) param; + this.saberParams = (SaberKeyGenerationParameters) param; this.random = param.Random; - this.l = this.saberParams.GetParameters().L; + this.l = this.saberParams.Parameters.L; } private AsymmetricCipherKeyPair GenKeyPair() { - SABEREngine engine = saberParams.GetParameters().GetEngine(); + SABEREngine engine = saberParams.Parameters.GetEngine(); byte[] sk = new byte[engine.GetPrivateKeySize()]; byte[] pk = new byte[engine.GetPublicKeySize()]; engine.crypto_kem_keypair(pk, sk, random); - SABERPublicKeyParameters pubKey = new SABERPublicKeyParameters(saberParams.GetParameters(), pk); - SABERPrivateKeyParameters privKey = new SABERPrivateKeyParameters(saberParams.GetParameters(), sk); + SaberPublicKeyParameters pubKey = new SaberPublicKeyParameters(saberParams.Parameters, pk); + SaberPrivateKeyParameters privKey = new SaberPrivateKeyParameters(saberParams.Parameters, sk); return new AsymmetricCipherKeyPair(pubKey, privKey); } diff --git a/crypto/src/pqc/crypto/saber/SABERKeyParameters.cs b/crypto/src/pqc/crypto/saber/SABERKeyParameters.cs index f44e21e00..e5a9e767e 100644 --- a/crypto/src/pqc/crypto/saber/SABERKeyParameters.cs +++ b/crypto/src/pqc/crypto/saber/SABERKeyParameters.cs @@ -3,20 +3,20 @@ using Org.BouncyCastle.Crypto; namespace Org.BouncyCastle.Pqc.Crypto.Saber { - public class SABERKeyParameters + public class SaberKeyParameters : AsymmetricKeyParameter { - private SABERParameters parameters; + private SaberParameters parameters; - public SABERKeyParameters( + public SaberKeyParameters( bool isPrivate, - SABERParameters parameters) + SaberParameters parameters) : base(isPrivate) { this.parameters = parameters; } - public SABERParameters GetParameters() + public SaberParameters GetParameters() { return parameters; } diff --git a/crypto/src/pqc/crypto/saber/SABERParameters.cs b/crypto/src/pqc/crypto/saber/SABERParameters.cs index f558e29bd..357430d50 100644 --- a/crypto/src/pqc/crypto/saber/SABERParameters.cs +++ b/crypto/src/pqc/crypto/saber/SABERParameters.cs @@ -4,27 +4,27 @@ using Org.BouncyCastle.Crypto; namespace Org.BouncyCastle.Pqc.Crypto.Saber { - public sealed class SABERParameters + public sealed class SaberParameters : ICipherParameters { - public static SABERParameters lightsaberkem128r3 = new SABERParameters("lightsaberkem128r3", 2, 128); - public static SABERParameters saberkem128r3 = new SABERParameters("saberkem128r3", 3, 128); - public static SABERParameters firesaberkem128r3 = new SABERParameters("firesaberkem128r3", 4, 128); + public static SaberParameters lightsaberkem128r3 = new SaberParameters("lightsaberkem128r3", 2, 128); + public static SaberParameters saberkem128r3 = new SaberParameters("saberkem128r3", 3, 128); + public static SaberParameters firesaberkem128r3 = new SaberParameters("firesaberkem128r3", 4, 128); - public static SABERParameters lightsaberkem192r3 = new SABERParameters("lightsaberkem192r3", 2, 192); - public static SABERParameters saberkem192r3 = new SABERParameters("saberkem192r3", 3, 192); - public static SABERParameters firesaberkem192r3 = new SABERParameters("firesaberkem192r3", 4, 192); + public static SaberParameters lightsaberkem192r3 = new SaberParameters("lightsaberkem192r3", 2, 192); + public static SaberParameters saberkem192r3 = new SaberParameters("saberkem192r3", 3, 192); + public static SaberParameters firesaberkem192r3 = new SaberParameters("firesaberkem192r3", 4, 192); - public static SABERParameters lightsaberkem256r3 = new SABERParameters("lightsaberkem256r3", 2, 256); - public static SABERParameters saberkem256r3 = new SABERParameters("saberkem256r3", 3, 256); - public static SABERParameters firesaberkem256r3 = new SABERParameters("firesaberkem256r3", 4, 256); + public static SaberParameters lightsaberkem256r3 = new SaberParameters("lightsaberkem256r3", 2, 256); + public static SaberParameters saberkem256r3 = new SaberParameters("saberkem256r3", 3, 256); + public static SaberParameters firesaberkem256r3 = new SaberParameters("firesaberkem256r3", 4, 256); private string name; private int l; private int defaultKeySize; private SABEREngine engine; - public SABERParameters(string name, int l, int defaultKeySize) + public SaberParameters(string name, int l, int defaultKeySize) { this.name = name; this.l = l; diff --git a/crypto/src/pqc/crypto/saber/SABERPrivateKeyParameters.cs b/crypto/src/pqc/crypto/saber/SABERPrivateKeyParameters.cs index 9dc9d1393..ec4add8b5 100644 --- a/crypto/src/pqc/crypto/saber/SABERPrivateKeyParameters.cs +++ b/crypto/src/pqc/crypto/saber/SABERPrivateKeyParameters.cs @@ -2,8 +2,8 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Pqc.Crypto.Saber { - public class SABERPrivateKeyParameters - : SABERKeyParameters + public class SaberPrivateKeyParameters + : SaberKeyParameters { private byte[] privateKey; @@ -12,7 +12,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Saber return Arrays.Clone(privateKey); } - public SABERPrivateKeyParameters(SABERParameters parameters, byte[] privateKey) + public SaberPrivateKeyParameters(SaberParameters parameters, byte[] privateKey) : base(true, parameters) { this.privateKey = Arrays.Clone(privateKey); diff --git a/crypto/src/pqc/crypto/saber/SABERPublicKeyParameters.cs b/crypto/src/pqc/crypto/saber/SABERPublicKeyParameters.cs index d8dc16cb4..dcac1ec3c 100644 --- a/crypto/src/pqc/crypto/saber/SABERPublicKeyParameters.cs +++ b/crypto/src/pqc/crypto/saber/SABERPublicKeyParameters.cs @@ -2,8 +2,8 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Pqc.Crypto.Saber { - public class SABERPublicKeyParameters - : SABERKeyParameters + public class SaberPublicKeyParameters + : SaberKeyParameters { public byte[] publicKey; @@ -14,7 +14,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Saber return PublicKey; } - public SABERPublicKeyParameters(SABERParameters parameters, byte[] publicKey) + public SaberPublicKeyParameters(SaberParameters parameters, byte[] publicKey) : base(false, parameters) { this.publicKey = Arrays.Clone(publicKey); diff --git a/crypto/src/pqc/crypto/utils/PqcUtilities.cs b/crypto/src/pqc/crypto/utils/PqcUtilities.cs index 26ced321a..d0873d702 100644 --- a/crypto/src/pqc/crypto/utils/PqcUtilities.cs +++ b/crypto/src/pqc/crypto/utils/PqcUtilities.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.BC; +using Org.BouncyCastle.Pqc.Crypto.Bike; using Org.BouncyCastle.Pqc.Crypto.Cmce; using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium; using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber; @@ -19,8 +20,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities private readonly static Dictionary<CmceParameters, DerObjectIdentifier> mcElieceOids = new Dictionary<CmceParameters, DerObjectIdentifier>(); private readonly static Dictionary<DerObjectIdentifier, CmceParameters> mcElieceParams = new Dictionary<DerObjectIdentifier, CmceParameters>(); - private readonly static Dictionary<SABERParameters, DerObjectIdentifier> saberOids = new Dictionary<SABERParameters, DerObjectIdentifier>(); - private readonly static Dictionary<DerObjectIdentifier, SABERParameters> saberParams = new Dictionary<DerObjectIdentifier, SABERParameters>(); + private readonly static Dictionary<SaberParameters, DerObjectIdentifier> saberOids = new Dictionary<SaberParameters, DerObjectIdentifier>(); + private readonly static Dictionary<DerObjectIdentifier, SaberParameters> saberParams = new Dictionary<DerObjectIdentifier, SaberParameters>(); private readonly static Dictionary<PicnicParameters, DerObjectIdentifier> picnicOids = new Dictionary<PicnicParameters, DerObjectIdentifier>(); private readonly static Dictionary<DerObjectIdentifier, PicnicParameters> picnicParams = new Dictionary<DerObjectIdentifier, PicnicParameters>(); @@ -37,7 +38,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities private readonly static Dictionary<FalconParameters, DerObjectIdentifier> falconOids = new Dictionary<FalconParameters, DerObjectIdentifier>(); private readonly static Dictionary<DerObjectIdentifier, FalconParameters> falconParams = new Dictionary<DerObjectIdentifier, FalconParameters>(); - + private readonly static Dictionary<BikeParameters, DerObjectIdentifier> bikeOids = new Dictionary<BikeParameters, DerObjectIdentifier>(); + private readonly static Dictionary<DerObjectIdentifier, BikeParameters> bikeParams = new Dictionary<DerObjectIdentifier, BikeParameters>(); + static PqcUtilities() { // CMCE @@ -63,25 +66,25 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities mcElieceParams[BCObjectIdentifiers.mceliece8192128_r3] = CmceParameters.mceliece8192128r3; mcElieceParams[BCObjectIdentifiers.mceliece8192128f_r3] = CmceParameters.mceliece8192128fr3; - saberOids[SABERParameters.lightsaberkem128r3] = BCObjectIdentifiers.lightsaberkem128r3; - saberOids[SABERParameters.saberkem128r3] = BCObjectIdentifiers.saberkem128r3; - saberOids[SABERParameters.firesaberkem128r3] = BCObjectIdentifiers.firesaberkem128r3; - saberOids[SABERParameters.lightsaberkem192r3] = BCObjectIdentifiers.lightsaberkem192r3; - saberOids[SABERParameters.saberkem192r3] = BCObjectIdentifiers.saberkem192r3; - saberOids[SABERParameters.firesaberkem192r3] = BCObjectIdentifiers.firesaberkem192r3; - saberOids[SABERParameters.lightsaberkem256r3] = BCObjectIdentifiers.lightsaberkem256r3; - saberOids[SABERParameters.saberkem256r3] = BCObjectIdentifiers.saberkem256r3; - saberOids[SABERParameters.firesaberkem256r3] = BCObjectIdentifiers.firesaberkem256r3; + saberOids[SaberParameters.lightsaberkem128r3] = BCObjectIdentifiers.lightsaberkem128r3; + saberOids[SaberParameters.saberkem128r3] = BCObjectIdentifiers.saberkem128r3; + saberOids[SaberParameters.firesaberkem128r3] = BCObjectIdentifiers.firesaberkem128r3; + saberOids[SaberParameters.lightsaberkem192r3] = BCObjectIdentifiers.lightsaberkem192r3; + saberOids[SaberParameters.saberkem192r3] = BCObjectIdentifiers.saberkem192r3; + saberOids[SaberParameters.firesaberkem192r3] = BCObjectIdentifiers.firesaberkem192r3; + saberOids[SaberParameters.lightsaberkem256r3] = BCObjectIdentifiers.lightsaberkem256r3; + saberOids[SaberParameters.saberkem256r3] = BCObjectIdentifiers.saberkem256r3; + saberOids[SaberParameters.firesaberkem256r3] = BCObjectIdentifiers.firesaberkem256r3; - saberParams[BCObjectIdentifiers.lightsaberkem128r3] = SABERParameters.lightsaberkem128r3; - saberParams[BCObjectIdentifiers.saberkem128r3] = SABERParameters.saberkem128r3; - saberParams[BCObjectIdentifiers.firesaberkem128r3] = SABERParameters.firesaberkem128r3; - saberParams[BCObjectIdentifiers.lightsaberkem192r3] = SABERParameters.lightsaberkem192r3; - saberParams[BCObjectIdentifiers.saberkem192r3] = SABERParameters.saberkem192r3; - saberParams[BCObjectIdentifiers.firesaberkem192r3] = SABERParameters.firesaberkem192r3; - saberParams[BCObjectIdentifiers.lightsaberkem256r3] = SABERParameters.lightsaberkem256r3; - saberParams[BCObjectIdentifiers.saberkem256r3] = SABERParameters.saberkem256r3; - saberParams[BCObjectIdentifiers.firesaberkem256r3] = SABERParameters.firesaberkem256r3; + saberParams[BCObjectIdentifiers.lightsaberkem128r3] = SaberParameters.lightsaberkem128r3; + saberParams[BCObjectIdentifiers.saberkem128r3] = SaberParameters.saberkem128r3; + saberParams[BCObjectIdentifiers.firesaberkem128r3] = SaberParameters.firesaberkem128r3; + saberParams[BCObjectIdentifiers.lightsaberkem192r3] = SaberParameters.lightsaberkem192r3; + saberParams[BCObjectIdentifiers.saberkem192r3] = SaberParameters.saberkem192r3; + saberParams[BCObjectIdentifiers.firesaberkem192r3] = SaberParameters.firesaberkem192r3; + saberParams[BCObjectIdentifiers.lightsaberkem256r3] = SaberParameters.lightsaberkem256r3; + saberParams[BCObjectIdentifiers.saberkem256r3] = SaberParameters.saberkem256r3; + saberParams[BCObjectIdentifiers.firesaberkem256r3] = SaberParameters.firesaberkem256r3; picnicOids[PicnicParameters.picnicl1fs] = BCObjectIdentifiers.picnicl1fs; @@ -162,6 +165,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities dilithiumParams[BCObjectIdentifiers.dilithium2_aes] = DilithiumParameters.Dilithium2Aes; dilithiumParams[BCObjectIdentifiers.dilithium3_aes] = DilithiumParameters.Dilithium3Aes; dilithiumParams[BCObjectIdentifiers.dilithium5_aes] = DilithiumParameters.Dilithium5Aes; + + bikeOids[BikeParameters.bike128] = BCObjectIdentifiers.bike128; + bikeOids[BikeParameters.bike192] = BCObjectIdentifiers.bike192; + bikeOids[BikeParameters.bike256] = BCObjectIdentifiers.bike256; + + bikeParams[BCObjectIdentifiers.bike128] = BikeParameters.bike128; + bikeParams[BCObjectIdentifiers.bike192] = BikeParameters.bike192; + bikeParams[BCObjectIdentifiers.bike256] = BikeParameters.bike256; } public static DerObjectIdentifier McElieceOidLookup(CmceParameters parameters) @@ -174,11 +185,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities return mcElieceParams[oid]; } - internal static DerObjectIdentifier SaberOidLookup(SABERParameters parameters) + internal static DerObjectIdentifier SaberOidLookup(SaberParameters parameters) { return saberOids[parameters]; } - internal static SABERParameters SaberParamsLookup(DerObjectIdentifier oid) + internal static SaberParameters SaberParamsLookup(DerObjectIdentifier oid) { return saberParams[oid]; } @@ -243,5 +254,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities return sikeParams[oid]; } + internal static DerObjectIdentifier BikeOidLookup(BikeParameters parameters) + { + return bikeOids[parameters]; + } + + internal static BikeParameters BikeParamsLookup(DerObjectIdentifier oid) + { + return bikeParams[oid]; + } } } diff --git a/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs b/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs index 63ae37e48..937242903 100644 --- a/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs +++ b/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs @@ -9,6 +9,7 @@ using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Math; using Org.BouncyCastle.Pqc.Asn1; +using Org.BouncyCastle.Pqc.Crypto.Bike; using Org.BouncyCastle.Pqc.Crypto.Cmce; using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium; using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber; @@ -90,9 +91,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities if (algOID.On(BCObjectIdentifiers.pqc_kem_saber)) { byte[] keyEnc = Asn1OctetString.GetInstance(keyInfo.ParsePrivateKey()).GetOctets(); - SABERParameters spParams = PqcUtilities.SaberParamsLookup(keyInfo.PrivateKeyAlgorithm.Algorithm); + SaberParameters spParams = PqcUtilities.SaberParamsLookup(keyInfo.PrivateKeyAlgorithm.Algorithm); - return new SABERPrivateKeyParameters(spParams, keyEnc); + return new SaberPrivateKeyParameters(spParams, keyEnc); } if (algOID.On(BCObjectIdentifiers.picnic)) { @@ -108,6 +109,18 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities return new SIKEPrivateKeyParameters(sikeParams, keyEnc); } + if (algOID.On(BCObjectIdentifiers.pqc_kem_bike)) + { + byte[] keyEnc = Asn1OctetString.GetInstance(keyInfo.ParsePrivateKey()).GetOctets(); + BikeParameters bikeParams = PqcUtilities.BikeParamsLookup(keyInfo.PrivateKeyAlgorithm.Algorithm); + + byte[] h0 = Arrays.CopyOfRange(keyEnc, 0, bikeParams.RByte); + byte[] h1 = Arrays.CopyOfRange(keyEnc, bikeParams.RByte, 2 * bikeParams.RByte); + byte[] sigma = Arrays.CopyOfRange(keyEnc, 2 * bikeParams.RByte, keyEnc.Length); + + + return new BikePrivateKeyParameters(bikeParams, h0, h1, sigma); + } if (algOID.Equals(BCObjectIdentifiers.kyber512) || algOID.Equals(BCObjectIdentifiers.kyber512_aes) || algOID.Equals(BCObjectIdentifiers.kyber768) diff --git a/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs b/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs index 010d9f0e3..61b02f009 100644 --- a/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs +++ b/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs @@ -13,6 +13,7 @@ using Org.BouncyCastle.Pqc.Crypto.Lms; using Org.BouncyCastle.Pqc.Crypto.Picnic; using Org.BouncyCastle.Pqc.Crypto.Saber; using Org.BouncyCastle.Pqc.Crypto.Sike; +using Org.BouncyCastle.Pqc.Crypto.Bike; using Org.BouncyCastle.Pqc.Crypto.SphincsPlus; using Org.BouncyCastle.Utilities; @@ -85,9 +86,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities parameters.Alpha, parameters.S, CmcePub); return new PrivateKeyInfo(algorithmIdentifier, CmcePriv, attributes); } - if (privateKey is SABERPrivateKeyParameters) + if (privateKey is SaberPrivateKeyParameters) { - SABERPrivateKeyParameters parameters = (SABERPrivateKeyParameters)privateKey; + SaberPrivateKeyParameters parameters = (SaberPrivateKeyParameters)privateKey; byte[] encoding = parameters.GetEncoded(); @@ -169,6 +170,15 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities return new PrivateKeyInfo(algorithmIdentifier, new DerSequence(v), attributes, new DerSequence(vPub).GetEncoded()); } + if (privateKey is BikePrivateKeyParameters) + { + BikePrivateKeyParameters parameters = (BikePrivateKeyParameters)privateKey; + + byte[] encoding = parameters.GetEncoded(); + + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PqcUtilities.BikeOidLookup(parameters.Parameters)); + return new PrivateKeyInfo(algorithmIdentifier, new DerOctetString(encoding), attributes); + } throw new ArgumentException("Class provided is not convertible: " + Platform.GetTypeName(privateKey)); } diff --git a/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs b/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs index a5aaca92c..3f352bf04 100644 --- a/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs +++ b/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs @@ -9,6 +9,7 @@ using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Math; using Org.BouncyCastle.Pqc.Asn1; +using Org.BouncyCastle.Pqc.Crypto.Bike; using Org.BouncyCastle.Pqc.Crypto.Cmce; using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium; using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber; @@ -93,6 +94,10 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities converters[BCObjectIdentifiers.kyber768_aes] = new KyberConverter(); converters[BCObjectIdentifiers.kyber1024] = new KyberConverter(); converters[BCObjectIdentifiers.kyber1024_aes] = new KyberConverter(); + + converters[BCObjectIdentifiers.bike128] = new BikeConverter(); + converters[BCObjectIdentifiers.bike192] = new BikeConverter(); + converters[BCObjectIdentifiers.bike256] = new BikeConverter(); } /// <summary> Create a public key from a SubjectPublicKeyInfo encoding</summary> @@ -180,9 +185,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities byte[] keyEnc = DerOctetString.GetInstance( DerSequence.GetInstance(keyInfo.ParsePublicKey())[0]).GetOctets(); - SABERParameters saberParams = PqcUtilities.SaberParamsLookup(keyInfo.AlgorithmID.Algorithm); + SaberParameters saberParams = PqcUtilities.SaberParamsLookup(keyInfo.AlgorithmID.Algorithm); - return new SABERPublicKeyParameters(saberParams, keyEnc); + return new SaberPublicKeyParameters(saberParams, keyEnc); } } @@ -287,5 +292,17 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities } } } + + private class BikeConverter: SubjectPublicKeyInfoConverter + { + internal override AsymmetricKeyParameter GetPublicKeyParameters(SubjectPublicKeyInfo keyInfo, object defaultParams) + { + byte[] keyEnc = DerOctetString.GetInstance(keyInfo.ParsePublicKey()).GetOctets(); + + BikeParameters bikeParams = PqcUtilities.BikeParamsLookup(keyInfo.AlgorithmID.Algorithm); + + return new BikePublicKeyParameters(bikeParams, keyEnc); + } + } } } \ No newline at end of file diff --git a/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs b/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs index 8aa09af06..75a89c3f0 100644 --- a/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs +++ b/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs @@ -5,6 +5,7 @@ using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Math; using Org.BouncyCastle.Pqc.Asn1; +using Org.BouncyCastle.Pqc.Crypto.Bike; using Org.BouncyCastle.Pqc.Crypto.Cmce; using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium; using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber; @@ -62,9 +63,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities // https://datatracker.ietf.org/doc/draft-uni-qsckeys/ return new SubjectPublicKeyInfo(algorithmIdentifier, new CmcePublicKey(encoding)); } - if (publicKey is SABERPublicKeyParameters) + if (publicKey is SaberPublicKeyParameters) { - SABERPublicKeyParameters parameters = (SABERPublicKeyParameters)publicKey; + SaberPublicKeyParameters parameters = (SaberPublicKeyParameters)publicKey; byte[] encoding = parameters.GetEncoded(); @@ -120,9 +121,18 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities v.Add(new DerOctetString(parameters.T1)); return new SubjectPublicKeyInfo(algorithmIdentifier, new DerSequence(v)); } - - throw new ArgumentException("Class provided no convertible: " + Platform.GetTypeName(publicKey)); + if (publicKey is BikePublicKeyParameters) + { + BikePublicKeyParameters parameters = (BikePublicKeyParameters)publicKey; + + + byte[] encoding = parameters.GetEncoded(); + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PqcUtilities.BikeOidLookup(parameters.Parameters)); + return new SubjectPublicKeyInfo(algorithmIdentifier, new DerOctetString(encoding)); + } + + throw new ArgumentException("Class provided no convertible: " + Platform.GetTypeName(publicKey)); } private static void ExtractBytes( diff --git a/crypto/src/pqc/math/linearalgebra/GF2mField.cs b/crypto/src/pqc/math/linearalgebra/GF2mField.cs new file mode 100644 index 000000000..e8182bf6f --- /dev/null +++ b/crypto/src/pqc/math/linearalgebra/GF2mField.cs @@ -0,0 +1,370 @@ +using Org.BouncyCastle.Security; +using System; + +namespace Org.BouncyCastle.Pqc.Math.LinearAlgebra +{ + public class GF2mField + { + + /* + * degree - degree of the field polynomial - the field polynomial ring - + * polynomial ring over the finite field GF(2) + */ + + private int degree = 0; + + private int polynomial; + + /** + * create a finite field GF(2^m) + * + * @param degree the degree of the field + */ + public GF2mField(int degree) + { + if (degree >= 32) + { + throw new ArgumentException( + " Error: the degree of field is too large "); + } + if (degree < 1) + { + throw new ArgumentException( + " Error: the degree of field is non-positive "); + } + this.degree = degree; + polynomial = PolynomialRingGF2.GetIrreduciblePolynomial(degree); + } + + /** + * create a finite field GF(2^m) with the fixed field polynomial + * + * @param degree the degree of the field + * @param poly the field polynomial + */ + public GF2mField(int degree, int poly) + { + if (degree != PolynomialRingGF2.Degree(poly)) + { + throw new ArgumentException( + " Error: the degree is not correct"); + } + if (!PolynomialRingGF2.IsIrreducible(poly)) + { + throw new ArgumentException( + " Error: given polynomial is reducible"); + } + this.degree = degree; + polynomial = poly; + + } + + public GF2mField(byte[] enc) + { + if (enc.Length != 4) + { + throw new ArgumentException( + "byte array is not an encoded finite field"); + } + polynomial = LittleEndianConversions.OS2IP(enc); + if (!PolynomialRingGF2.IsIrreducible(polynomial)) + { + throw new ArgumentException( + "byte array is not an encoded finite field"); + } + + degree = PolynomialRingGF2.Degree(polynomial); + } + + public GF2mField(GF2mField field) + { + degree = field.degree; + polynomial = field.polynomial; + } + + /** + * return degree of the field + * + * @return degree of the field + */ + public int GetDegree() + { + return degree; + } + + /** + * return the field polynomial + * + * @return the field polynomial + */ + public int GetPolynomial() + { + return polynomial; + } + + /** + * return the encoded form of this field + * + * @return the field in byte array form + */ + public byte[] GetEncoded() + { + return LittleEndianConversions.I2OSP(polynomial); + } + + /** + * Return sum of two elements + * + * @param a + * @param b + * @return a+b + */ + public int add(int a, int b) + { + return a ^ b; + } + + /** + * Return product of two elements + * + * @param a + * @param b + * @return a*b + */ + public int Mult(int a, int b) + { + return PolynomialRingGF2.modMultiply(a, b, polynomial); + } + + /** + * compute exponentiation a^k + * + * @param a a field element a + * @param k k degree + * @return a^k + */ + public int Exp(int a, int k) + { + if (k == 0) + { + return 1; + } + if (a == 0) + { + return 0; + } + if (a == 1) + { + return 1; + } + int result = 1; + if (k < 0) + { + a = Inverse(a); + k = -k; + } + while (k != 0) + { + if ((k & 1) == 1) + { + result = Mult(result, a); + } + a = Mult(a, a); + //k >>>= 1; + uint kTmp = (uint)k; + kTmp >>= 1; + k = (int) kTmp; + } + return result; + } + + /** + * compute the multiplicative inverse of a + * + * @param a a field element a + * @return a<sup>-1</sup> + */ + public int Inverse(int a) + { + int d = (1 << degree) - 2; + + return Exp(a, d); + } + + /** + * compute the square root of an integer + * + * @param a a field element a + * @return a<sup>1/2</sup> + */ + public int SqRoot(int a) + { + for (int i = 1; i < degree; i++) + { + a = Mult(a, a); + } + return a; + } + + /** + * create a random field element using PRNG sr + * + * @param sr SecureRandom + * @return a random element + */ + public int GetRandomElement(SecureRandom sr) + { + int result = RandUtils.NextInt(sr, 1 << degree); + return result; + } + + /** + * create a random non-zero field element + * + * @return a random element + */ + //public int getRandomNonZeroElement() + //{ + // return getRandomNonZeroElement(CryptoServicesRegistrar.getSecureRandom()); + //} + + /** + * create a random non-zero field element using PRNG sr + * + * @param sr SecureRandom + * @return a random non-zero element + */ + public int GetRandomNonZeroElement(SecureRandom sr) + { + int controltime = 1 << 20; + int count = 0; + int result = RandUtils.NextInt(sr, 1 << degree); + while ((result == 0) && (count < controltime)) + { + result = RandUtils.NextInt(sr, 1 << degree); + count++; + } + if (count == controltime) + { + result = 1; + } + return result; + } + + /** + * @return true if e is encoded element of this field and false otherwise + */ + public bool IsElementOfThisField(int e) + { + // e is encoded element of this field iff 0<= e < |2^m| + if (degree == 31) + { + return e >= 0; + } + return e >= 0 && e < (1 << degree); + } + + /* + * help method for visual control + */ + public String ElementToStr(int a) + { + String s = ""; + for (int i = 0; i < degree; i++) + { + if (((byte)a & 0x01) == 0) + { + s = "0" + s; + } + else + { + s = "1" + s; + } + //a >>>= 1; + uint aTmp = (uint)a; + aTmp >>= 1; + a = (int)aTmp; + } + return s; + } + + /** + * checks if given object is equal to this field. + * <p> + * The method returns false whenever the given object is not GF2m. + * + * @param other object + * @return true or false + */ + public bool Equals(Object other) + { + if ((other == null) || !(other is GF2mField)) + { + return false; + } + + GF2mField otherField = (GF2mField)other; + + if ((degree == otherField.degree) + && (polynomial == otherField.polynomial)) + { + return true; + } + + return false; + } + + public int HashCode() + { + return polynomial; + } + + /** + * Returns a human readable form of this field. + * + * @return a human readable form of this field. + */ + public String ToString() + { + String str = "Finite Field GF(2^" + degree + ") = " + "GF(2)[X]/<" + + PolyToString(polynomial) + "> "; + return str; + } + + private static String PolyToString(int p) + { + String str = ""; + if (p == 0) + { + str = "0"; + } + else + { + byte b = (byte)(p & 0x01); + if (b == 1) + { + str = "1"; + } + //p >>>= 1; + uint pTmp = (uint)p; + pTmp >>= 1; + p = (int)pTmp; + int i = 1; + while (p != 0) + { + b = (byte)(p & 0x01); + if (b == 1) + { + str = str + "+x^" + i; + } + //p >>>= 1; + pTmp = (uint) p; + pTmp >>= 1; + p = (int)pTmp; + i++; + } + } + return str; + } + } +} \ No newline at end of file diff --git a/crypto/src/pqc/math/linearalgebra/GF2mVector.cs b/crypto/src/pqc/math/linearalgebra/GF2mVector.cs new file mode 100644 index 000000000..f0e44ebe6 --- /dev/null +++ b/crypto/src/pqc/math/linearalgebra/GF2mVector.cs @@ -0,0 +1,221 @@ +using System; + +namespace Org.BouncyCastle.Pqc.Math.LinearAlgebra +{ + /** + * This class implements vectors over the finite field + * <tt>GF(2<sup>m</sup>)</tt> for small <tt>m</tt> (i.e., + * <tt>1<m<32</tt>). It extends the abstract class {@link Vector}. + */ +public class GF2mVector : Vector +{ + + /** + * the finite field this vector is defined over + */ + private GF2mField field; + + /** + * the element array + */ + private int[] vector; + + /** + * creates the vector over GF(2^m) of given length and with elements from + * array v (beginning at the first bit) + * + * @param field finite field + * @param v array with elements of vector + */ + public GF2mVector(GF2mField field, byte[] v) + { + this.field = new GF2mField(field); + + // decode vector + int d = 8; + int count = 1; + while (field.GetDegree() > d) + { + count++; + d += 8; + } + + if ((v.Length % count) != 0) + { + throw new ArgumentException( + "Byte array is not an encoded vector over the given finite field."); + } + + length = v.Length / count; + vector = new int[length]; + count = 0; + for (int i = 0; i < vector.Length; i++) + { + for (int j = 0; j < d; j += 8) + { + vector[i] |= (v[count++] & 0xff) << j; + } + if (!field.IsElementOfThisField(vector[i])) + { + throw new ArgumentException( + "Byte array is not an encoded vector over the given finite field."); + } + } + } + + /** + * Create a new vector over <tt>GF(2<sup>m</sup>)</tt> of the given + * length and element array. + * + * @param field the finite field <tt>GF(2<sup>m</sup>)</tt> + * @param vector the element array + */ + public GF2mVector(GF2mField field, int[] vector) + { + this.field = field; + length = vector.Length; + for (int i = vector.Length - 1; i >= 0; i--) + { + if (!field.IsElementOfThisField(vector[i])) + { + throw new ArithmeticException( + "Element array is not specified over the given finite field."); + } + } + this.vector = IntUtils.Clone(vector); + } + + /** + * Copy constructor. + * + * @param other another {@link GF2mVector} + */ + public GF2mVector(GF2mVector other) + { + field = new GF2mField(other.field); + length = other.length; + vector = IntUtils.Clone(other.vector); + } + + /** + * @return the finite field this vector is defined over + */ + public GF2mField GetField() + { + return field; + } + + /** + * @return int[] form of this vector + */ + public int[] GetIntArrayForm() + { + return IntUtils.Clone(vector); + } + + /** + * @return a byte array encoding of this vector + */ + public override byte[] GetEncoded() + { + int d = 8; + int count = 1; + while (field.GetDegree() > d) + { + count++; + d += 8; + } + + byte[] res = new byte[vector.Length * count]; + count = 0; + for (int i = 0; i < vector.Length; i++) + { + for (int j = 0; j < d; j += 8) + { + res[count++] = (byte)(Utils.UnsignedRightBitShiftInt(vector[i], j)); + } + } + + return res; + } + + /** + * @return whether this is the zero vector (i.e., all elements are zero) + */ + public override bool IsZero() + { + for (int i = vector.Length - 1; i >= 0; i--) + { + if (vector[i] != 0) + { + return false; + } + } + return true; + } + + /** + * Add another vector to this vector. Method is not yet implemented. + * + * @param addend the other vector + * @return <tt>this + addend</tt> + * @throws ArithmeticException if the other vector is not defined over the same field as + * this vector. + * <p> + * TODO: implement this method + */ + public override Vector Add(Vector addend) + { + throw new SystemException("not implemented"); + } + + /** + * Multiply this vector with a permutation. + * + * @param p the permutation + * @return <tt>this*p = p*this</tt> + */ + public override Vector Multiply(Permutation p) + { + int[] pVec = p.GetVector(); + if (length != pVec.Length) + { + throw new ArithmeticException( + "permutation size and vector size mismatch"); + } + + int[] result = new int[length]; + for (int i = 0; i < pVec.Length; i++) + { + result[i] = vector[pVec[i]]; + } + + return new GF2mVector(field, result); + } + + /** + * Compare this vector with another object. + * + * @param other the other object + * @return the result of the comparison + */ + public override bool Equals(Object other) + { + + if (!(other is GF2mVector)) + { + return false; + } + GF2mVector otherVec = (GF2mVector)other; + + if (!field.Equals(otherVec.field)) + { + return false; + } + + return IntUtils.Equals(vector, otherVec.vector); + } + + + } +} diff --git a/crypto/src/pqc/math/linearalgebra/IntUtils.cs b/crypto/src/pqc/math/linearalgebra/IntUtils.cs new file mode 100644 index 000000000..0a7671df6 --- /dev/null +++ b/crypto/src/pqc/math/linearalgebra/IntUtils.cs @@ -0,0 +1,159 @@ +using Org.BouncyCastle.Utilities; +using System; + +namespace Org.BouncyCastle.Pqc.Math.LinearAlgebra +{ + public class IntUtils + { + + /** + * Default constructor (private). + */ + private IntUtils() + { + // empty + } + + /** + * Compare two int arrays. No null checks are performed. + * + * @param left the first int array + * @param right the second int array + * @return the result of the comparison + */ + public static bool Equals(int[] left, int[] right) + { + return Arrays.AreEqual(left, right); + } + + /** + * Return a clone of the given int array. No null checks are performed. + * + * @param array the array to clone + * @return the clone of the given array + */ + public static int[] Clone(int[] array) + { + return Arrays.Clone(array); + } + + /** + * Fill the given int array with the given value. + * + * @param array the array + * @param value the value + */ + public static void Fill(int[] array, int value) + { + Arrays.Fill(array, value); + } + + /** + * Sorts this array of integers according to the Quicksort algorithm. After + * calling this method this array is sorted in ascending order with the + * smallest integer taking position 0 in the array. + * <p> + * This implementation is based on the quicksort algorithm as described in + * <code>Data Structures In Java</code> by Thomas A. Standish, Chapter 10, + * ISBN 0-201-30564-X. + * + * @param source the array of integers that needs to be sorted. + */ + public static void Quicksort(int[] source) + { + Quicksort(source, 0, source.Length - 1); + } + + /** + * Sort a subarray of a source array. The subarray is specified by its start + * and end index. + * + * @param source the int array to be sorted + * @param left the start index of the subarray + * @param right the end index of the subarray + */ + public static void Quicksort(int[] source, int left, int right) + { + if (right > left) + { + int index = Partition(source, left, right, right); + Quicksort(source, left, index - 1); + Quicksort(source, index + 1, right); + } + } + + /** + * Split a subarray of a source array into two partitions. The left + * partition contains elements that have value less than or equal to the + * pivot element, the right partition contains the elements that have larger + * value. + * + * @param source the int array whose subarray will be splitted + * @param left the start position of the subarray + * @param right the end position of the subarray + * @param pivotIndex the index of the pivot element inside the array + * @return the new index of the pivot element inside the array + */ + private static int Partition(int[] source, int left, int right, + int pivotIndex) + { + + int pivot = source[pivotIndex]; + source[pivotIndex] = source[right]; + source[right] = pivot; + + int index = left; + int tmp = 0; + for (int i = left; i < right; i++) + { + if (source[i] <= pivot) + { + tmp = source[index]; + source[index] = source[i]; + source[i] = tmp; + index++; + } + } + + tmp = source[index]; + source[index] = source[right]; + source[right] = tmp; + + return index; + } + + /** + * Generates a subarray of a given int array. + * + * @param input - + * the input int array + * @param start - + * the start index + * @param end - + * the end index + * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt> to + * <tt>end</tt> + */ + public static int[] SubArray( int[] input, int start, + int end) + { + int[] result = new int[end - start]; + Array.Copy(input, start, result, 0, end - start); + return result; + } + + /** + * @param input an int array + * @return a human readable form of the given int array + */ + public static String ToString(int[] input) + { + String result = ""; + for (int i = 0; i < input.Length; i++) + { + result += input[i] + " "; + } + return result; + } + } +} diff --git a/crypto/src/pqc/math/linearalgebra/LittleEndianConversions.cs b/crypto/src/pqc/math/linearalgebra/LittleEndianConversions.cs new file mode 100644 index 000000000..5b3215070 --- /dev/null +++ b/crypto/src/pqc/math/linearalgebra/LittleEndianConversions.cs @@ -0,0 +1,195 @@ + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Pqc.Math.LinearAlgebra +{ + /** + * This is a utility class containing data type conversions using little-endian + * byte order. + * + */ + class LittleEndianConversions + { + /** + * Default constructor (private). + */ + private LittleEndianConversions() + { + // empty + } + + /** + * Convert an octet string of length 4 to an integer. No length checking is + * performed. + * + * @param input the byte array holding the octet string + * @return an integer representing the octet string <tt>input</tt> + * @throws ArithmeticException if the length of the given octet string is larger than 4. + */ + public static int OS2IP(byte[] input) + { + return (int)Pack.LE_To_UInt32(input); + } + + /** + * Convert an byte array of length 4 beginning at <tt>offset</tt> into an + * integer. + * + * @param input the byte array + * @param inOff the offset into the byte array + * @return the resulting integer + */ + public static int OS2IP(byte[] input, int inOff) + { + return (int)Pack.LE_To_UInt32(input, inOff); + } + + /** + * Convert a byte array of the given length beginning at <tt>offset</tt> + * into an integer. + * + * @param input the byte array + * @param inOff the offset into the byte array + * @param inLen the length of the encoding + * @return the resulting integer + */ + public static int OS2IP(byte[] input, int inOff, int inLen) + { + int result = 0; + for (int i = inLen - 1; i >= 0; i--) + { + result |= (input[inOff + i] & 0xff) << (8 * i); + } + return result; + } + + /** + * Convert a byte array of length 8 beginning at <tt>inOff</tt> into a + * long integer. + * + * @param input the byte array + * @param inOff the offset into the byte array + * @return the resulting long integer + */ + public static long OS2LIP(byte[] input, int inOff) + { + return (long)Pack.LE_To_UInt64(input, inOff); + } + + /** + * Convert an integer to an octet string of length 4. + * + * @param x the integer to convert + * @return the converted integer + */ + public static byte[] I2OSP(int x) + { + return Pack.UInt32_To_LE((uint)x); + } + + /** + * Convert an integer into a byte array beginning at the specified offset. + * + * @param value the integer to convert + * @param output the byte array to hold the result + * @param outOff the integer offset into the byte array + */ + public static void I2OSP(int value, byte[] output, int outOff) + { + Pack.UInt32_To_LE((uint)value, output, outOff); + } + + /** + * Convert an integer to a byte array beginning at the specified offset. No + * length checking is performed (i.e., if the integer cannot be encoded with + * <tt>length</tt> octets, it is truncated). + * + * @param value the integer to convert + * @param output the byte array to hold the result + * @param outOff the integer offset into the byte array + * @param outLen the length of the encoding + */ + public static void I2OSP(int value, byte[] output, int outOff, int outLen) + { + uint valueTmp = (uint)value; + for (int i = outLen - 1; i >= 0; i--) + { + output[outOff + i] = (byte)(valueTmp >> (8 * i)); + } + } + + /** + * Convert an integer to a byte array of length 8. + * + * @param input the integer to convert + * @return the converted integer + */ + public static byte[] I2OSP(long input) + { + return Pack.UInt64_To_LE((ulong)input); + } + + /** + * Convert an integer to a byte array of length 8. + * + * @param input the integer to convert + * @param output byte array holding the output + * @param outOff offset in output array where the result is stored + */ + public static void I2OSP(long input, byte[] output, int outOff) + { + Pack.UInt64_To_LE((ulong)input, output, outOff); + } + + /** + * Convert an int array to a byte array of the specified length. No length + * checking is performed (i.e., if the last integer cannot be encoded with + * <tt>length % 4</tt> octets, it is truncated). + * + * @param input the int array + * @param outLen the length of the converted array + * @return the converted array + */ + public static byte[] ToByteArray(int[] input, int outLen) + { + int intLen = input.Length; + byte[] result = new byte[outLen]; + int index = 0; + for (int i = 0; i <= intLen - 2; i++, index += 4) + { + I2OSP(input[i], result, index); + } + I2OSP(input[intLen - 1], result, index, outLen - index); + return result; + } + + /** + * Convert a byte array to an int array. + * + * @param input the byte array + * @return the converted array + */ + public static int[] ToIntArray(byte[] input) + { + int intLen = (input.Length + 3) / 4; + int lastLen = input.Length & 0x03; + int[] result = new int[intLen]; + + int index = 0; + for (int i = 0; i <= intLen - 2; i++, index += 4) + { + result[i] = OS2IP(input, index); + } + if (lastLen != 0) + { + result[intLen - 1] = OS2IP(input, index, lastLen); + } + else + { + result[intLen - 1] = OS2IP(input, index); + } + + return result; + } + } +} diff --git a/crypto/src/pqc/math/linearalgebra/Permutation.cs b/crypto/src/pqc/math/linearalgebra/Permutation.cs new file mode 100644 index 000000000..0d36958c9 --- /dev/null +++ b/crypto/src/pqc/math/linearalgebra/Permutation.cs @@ -0,0 +1,192 @@ +using Org.BouncyCastle.Security; +using System; + +namespace Org.BouncyCastle.Pqc.Math.LinearAlgebra +{ + /** + * This class implements permutations of the set {0,1,...,n-1} for some given n + * > 0, i.e., ordered sequences containing each number <tt>m</tt> (<tt>0 <= + * m < n</tt>) + * once and only once. + */ + public class Permutation + { + + /** + * perm holds the elements of the permutation vector, i.e. <tt>[perm(0), + * perm(1), ..., perm(n-1)]</tt> + */ + private int[] perm; + + /** + * Create the identity permutation of the given size. + * + * @param n the size of the permutation + */ + public Permutation(int n) + { + if (n <= 0) + { + throw new ArgumentException("invalid length"); + } + + perm = new int[n]; + for (int i = n - 1; i >= 0; i--) + { + perm[i] = i; + } + } + + /** + * Create a permutation using the given permutation vector. + * + * @param perm the permutation vector + */ + public Permutation(int[] perm) + { + if (!IsPermutation(perm)) + { + throw new ArgumentException( + "array is not a permutation vector"); + } + + this.perm = IntUtils.Clone(perm); + } + + /** + * Create a random permutation of the given size. + * + * @param n the size of the permutation + * @param sr the source of randomness + */ + public Permutation(int n, SecureRandom sr) + { + if (n <= 0) + { + throw new ArgumentException("invalid length"); + } + + perm = new int[n]; + + int[] help = new int[n]; + for (int i = 0; i < n; i++) + { + help[i] = i; + } + + int k = n; + for (int j = 0; j < n; j++) + { + int i = RandUtils.NextInt(sr, k); + k--; + perm[j] = help[i]; + help[i] = help[k]; + } + } + + + /** + * @return the permutation vector <tt>(perm(0),perm(1),...,perm(n-1))</tt> + */ + public int[] GetVector() + { + return IntUtils.Clone(perm); + } + + /** + * Compute the inverse permutation <tt>P<sup>-1</sup></tt>. + * + * @return <tt>this<sup>-1</sup></tt> + */ + public Permutation ComputeInverse() + { + Permutation result = new Permutation(perm.Length); + for (int i = perm.Length - 1; i >= 0; i--) + { + result.perm[perm[i]] = i; + } + return result; + } + + /** + * Compute the product of this permutation and another permutation. + * + * @param p the other permutation + * @return <tt>this * p</tt> + */ + public Permutation RightMultiply(Permutation p) + { + if (p.perm.Length != perm.Length) + { + throw new ArgumentException("length mismatch"); + } + Permutation result = new Permutation(perm.Length); + for (int i = perm.Length - 1; i >= 0; i--) + { + result.perm[i] = perm[p.perm[i]]; + } + return result; + } + + /** + * checks if given object is equal to this permutation. + * <p> + * The method returns false whenever the given object is not permutation. + * + * @param other - + * permutation + * @return true or false + */ + public bool equals(Object other) + { + + if (!(other is Permutation)) + { + return false; + } + Permutation otherPerm = (Permutation)other; + + return IntUtils.Equals(perm, otherPerm.perm); + } + + /** + * @return a human readable form of the permutation + */ + public String ToString() + { + String result = "[" + perm[0]; + for (int i = 1; i < perm.Length; i++) + { + result += ", " + perm[i]; + } + result += "]"; + return result; + } + + /** + * Check that the given array corresponds to a permutation of the set + * <tt>{0, 1, ..., n-1}</tt>. + * + * @param perm permutation vector + * @return true if perm represents an n-permutation and false otherwise + */ + private bool IsPermutation(int[] perm) + { + int n = perm.Length; + bool[] onlyOnce = new bool[n]; + + for (int i = 0; i < n; i++) + { + if ((perm[i] < 0) || (perm[i] >= n) || onlyOnce[perm[i]]) + { + return false; + } + onlyOnce[perm[i]] = true; + } + + return true; + } + + } + +} diff --git a/crypto/src/pqc/math/linearalgebra/PolynomialGF2mSmallM.cs b/crypto/src/pqc/math/linearalgebra/PolynomialGF2mSmallM.cs new file mode 100644 index 000000000..9dca71bee --- /dev/null +++ b/crypto/src/pqc/math/linearalgebra/PolynomialGF2mSmallM.cs @@ -0,0 +1,1266 @@ +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using System; + +namespace Org.BouncyCastle.Pqc.Math.LinearAlgebra +{ + public class PolynomialGF2mSmallM + { + + /** + * the finite field GF(2^m) + */ + private GF2mField field; + + /** + * the degree of this polynomial + */ + private int degree; + + /** + * 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 int[] coefficients; + + /* + * some types of polynomials + */ + + /** + * Constant used for polynomial construction (see constructor + * {@link #PolynomialGF2mSmallM(GF2mField, int, char, SecureRandom)}). + */ + public const char RANDOM_IRREDUCIBLE_POLYNOMIAL = 'I'; + + /** + * Construct the zero polynomial over the finite field GF(2^m). + * + * @param field the finite field GF(2^m) + */ + public PolynomialGF2mSmallM(GF2mField field) + { + this.field = field; + degree = -1; + coefficients = new int[1]; + } + + /** + * Construct a polynomial over the finite field GF(2^m). + * + * @param field the finite field GF(2^m) + * @param deg degree of polynomial + * @param typeOfPolynomial type of polynomial + * @param sr PRNG + */ + public PolynomialGF2mSmallM(GF2mField field, int deg, + char typeOfPolynomial, SecureRandom sr) + { + this.field = field; + + switch (typeOfPolynomial) + { + case PolynomialGF2mSmallM.RANDOM_IRREDUCIBLE_POLYNOMIAL: + coefficients = CreateRandomIrreduciblePolynomial(deg, sr); + break; + default: + throw new ArgumentException(" Error: type " + + typeOfPolynomial + + " is not defined for GF2smallmPolynomial"); + } + ComputeDegree(); + } + + /** + * Create an irreducible polynomial with the given degree over the field + * <tt>GF(2^m)</tt>. + * + * @param deg polynomial degree + * @param sr source of randomness + * @return the generated irreducible polynomial + */ + private int[] CreateRandomIrreduciblePolynomial(int deg, SecureRandom sr) + { + int[] resCoeff = new int[deg + 1]; + resCoeff[deg] = 1; + resCoeff[0] = field.GetRandomNonZeroElement(sr); + for (int i = 1; i < deg; i++) + { + resCoeff[i] = field.GetRandomElement(sr); + } + while (!IsIrreducible(resCoeff)) + { + int n = RandUtils.NextInt(sr, deg); + if (n == 0) + { + resCoeff[0] = field.GetRandomNonZeroElement(sr); + } + else + { + resCoeff[n] = field.GetRandomElement(sr); + } + } + return resCoeff; + } + + /** + * 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 + */ + public PolynomialGF2mSmallM(GF2mField field, int degree) + { + this.field = field; + this.degree = degree; + coefficients = new int[degree + 1]; + coefficients[degree] = 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 + */ + public PolynomialGF2mSmallM(GF2mField field, int[] coeffs) + { + this.field = field; + coefficients = NormalForm(coeffs); + ComputeDegree(); + } + + /** + * 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 + */ + public PolynomialGF2mSmallM(GF2mField field, byte[] enc) + { + this.field = field; + + // decodes polynomial + int d = 8; + int count = 1; + while (field.GetDegree() > d) + { + count++; + d += 8; + } + + if ((enc.Length % count) != 0) + { + throw new ArgumentException( + " Error: byte array is not encoded polynomial over given finite field GF2m"); + } + + coefficients = new int[enc.Length / count]; + count = 0; + for (int i = 0; i < coefficients.Length; i++) + { + for (int j = 0; j < d; j += 8) + { + coefficients[i] ^= (enc[count++] & 0x000000ff) << j; + } + if (!this.field.IsElementOfThisField(coefficients[i])) + { + 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 ((coefficients.Length != 1) + && (coefficients[coefficients.Length - 1] == 0)) + { + throw new ArgumentException( + " Error: byte array is not encoded polynomial over given finite field GF2m"); + } + ComputeDegree(); + } + + /** + * Copy constructor. + * + * @param other another {@link PolynomialGF2mSmallM} + */ + public PolynomialGF2mSmallM(PolynomialGF2mSmallM other) + { + // field needs not to be cloned since it is immutable + field = other.field; + degree = other.degree; + coefficients = IntUtils.Clone(other.coefficients); + } + + /** + * Create a polynomial over the finite field GF(2^m) out of the given + * coefficient vector. The finite field is also obtained from the + * {@link GF2mVector}. + * + * @param vect the coefficient vector + */ + public PolynomialGF2mSmallM(GF2mVector vect) + { + new PolynomialGF2mSmallM(vect.GetField(), vect.GetIntArrayForm()); + } + + /* + * ------------------------ + */ + + /** + * Return the degree of this polynomial + * + * @return int degree of this polynomial if this is zero polynomial return + * -1 + */ + public int GetDegree() + { + int d = coefficients.Length - 1; + if (coefficients[d] == 0) + { + return -1; + } + return d; + } + + /** + * @return the head coefficient of this polynomial + */ + public int GetHeadCoefficient() + { + if (degree == -1) + { + return 0; + } + return coefficients[degree]; + } + + /** + * Return the head coefficient of a polynomial. + * + * @param a the polynomial + * @return the head coefficient of <tt>a</tt> + */ + private static int HeadCoefficient(int[] a) + { + int degree = ComputeDegree(a); + if (degree == -1) + { + return 0; + } + return a[degree]; + } + + /** + * Return the coefficient with the given index. + * + * @param index the index + * @return the coefficient with the given index + */ + public int GetCoefficient(int index) + { + if ((index < 0) || (index > degree)) + { + return 0; + } + return coefficients[index]; + } + + /** + * Returns encoded polynomial, i.e., this polynomial in byte array form + * + * @return the encoded polynomial + */ + public byte[] GetEncoded() + { + int d = 8; + int count = 1; + while (field.GetDegree() > d) + { + count++; + d += 8; + } + + byte[] res = new byte[coefficients.Length * count]; + count = 0; + for (int i = 0; i < coefficients.Length; i++) + { + for (int j = 0; j < d; j += 8) + { + res[count++] = (byte)(Utils.UnsignedRightBitShiftInt(coefficients[i], j)); + } + } + + return res; + } + + /** + * Evaluate this polynomial <tt>p</tt> at a value <tt>e</tt> (in + * <tt>GF(2^m)</tt>) with the Horner scheme. + * + * @param e the element of the finite field GF(2^m) + * @return <tt>this(e)</tt> + */ + public int evaluateAt(int e) + { + int result = coefficients[degree]; + for (int i = degree - 1; i >= 0; i--) + { + result = field.Mult(result, e) ^ coefficients[i]; + } + return result; + } + + /** + * Compute the sum of this polynomial and the given polynomial. + * + * @param addend the addend + * @return <tt>this + a</tt> (newly created) + */ + public PolynomialGF2mSmallM add(PolynomialGF2mSmallM addend) + { + int[] resultCoeff = Add(coefficients, addend.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Add the given polynomial to this polynomial (overwrite this). + * + * @param addend the addend + */ + public void AddToThis(PolynomialGF2mSmallM addend) + { + coefficients = Add(coefficients, addend.coefficients); + ComputeDegree(); + } + + /** + * 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 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] = field.add(result[i], addend[i]); + } + + return result; + } + + /** + * Compute the sum of this polynomial and the monomial of the given degree. + * + * @param degree the degree of the monomial + * @return <tt>this + X^k</tt> + */ + public PolynomialGF2mSmallM AddMonomial(int degree) + { + int[] monomial = new int[degree + 1]; + monomial[degree] = 1; + int[] resultCoeff = Add(coefficients, monomial); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the product of this polynomial with an element from GF(2^m). + * + * @param element an element of the finite field GF(2^m) + * @return <tt>this * element</tt> (newly created) + * @throws ArithmeticException if <tt>element</tt> is not an element of the finite + * field this polynomial is defined over. + */ + public PolynomialGF2mSmallM MultWithElement(int element) + { + if (!field.IsElementOfThisField(element)) + { + throw new ArithmeticException( + "Not an element of the finite field this polynomial is defined over."); + } + int[] resultCoeff = MultWithElement(coefficients, element); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Multiply this polynomial with an element from GF(2^m). + * + * @param element an element of the finite field GF(2^m) + * @throws ArithmeticException if <tt>element</tt> is not an element of the finite + * field this polynomial is defined over. + */ + public void MultThisWithElement(int element) + { + if (!field.IsElementOfThisField(element)) + { + throw new ArithmeticException( + "Not an element of the finite field this polynomial is defined over."); + } + coefficients = MultWithElement(coefficients, element); + ComputeDegree(); + } + + /** + * Compute the product of a polynomial a with an element from the finite + * field <tt>GF(2^m)</tt>. + * + * @param a the polynomial + * @param element an element of the finite field GF(2^m) + * @return <tt>a * element</tt> + */ + private int[] MultWithElement(int[] a, int element) + { + int degree = ComputeDegree(a); + if (degree == -1 || element == 0) + { + return new int[1]; + } + + if (element == 1) + { + return IntUtils.Clone(a); + } + + int[] result = new int[degree + 1]; + for (int i = degree; i >= 0; i--) + { + result[i] = field.Mult(a[i], element); + } + + return result; + } + + /** + * Compute the product of this polynomial with a monomial X^k. + * + * @param k the degree of the monomial + * @return <tt>this * X^k</tt> + */ + public PolynomialGF2mSmallM MultWithMonomial(int k) + { + int[] resultCoeff = MultWithMonomial(coefficients, k); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the product of a polynomial with a monomial X^k. + * + * @param a the polynomial + * @param k the degree of the monomial + * @return <tt>a * X^k</tt> + */ + private static int[] MultWithMonomial(int[] a, int k) + { + int d = ComputeDegree(a); + if (d == -1) + { + return new int[1]; + } + int[] result = new int[d + k + 1]; + Array.Copy(a, 0, result, k, d + 1); + return result; + } + + /** + * Divide this polynomial by the given polynomial. + * + * @param f a polynomial + * @return polynomial pair = {q,r} where this = q*f+r and deg(r) < + * deg(f); + */ + public PolynomialGF2mSmallM[] Div(PolynomialGF2mSmallM f) + { + int[][] resultCoeffs = Div(coefficients, f.coefficients); + return new PolynomialGF2mSmallM[]{ + new PolynomialGF2mSmallM(field, resultCoeffs[0]), + new PolynomialGF2mSmallM(field, resultCoeffs[1])}; + } + + /** + * 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) < deg(f); + */ + private int[][] Div(int[] a, int[] f) + { + int df = ComputeDegree(f); + int da = ComputeDegree(a) + 1; + if (df == -1) + { + throw new ArithmeticException("Division by zero."); + } + int[][] result = new int[2][]; + result[0] = new int[1]; + result[1] = new int[da]; + int hc = HeadCoefficient(f); + hc = field.Inverse(hc); + result[0][0] = 0; + Array.Copy(a, 0, result[1], 0, result[1].Length); + while (df <= ComputeDegree(result[1])) + { + int[] q; + int[] coeff = new int[1]; + coeff[0] = field.Mult(HeadCoefficient(result[1]), hc); + q = MultWithElement(f, coeff[0]); + int n = ComputeDegree(result[1]) - df; + q = MultWithMonomial(q, n); + coeff = MultWithMonomial(coeff, n); + result[0] = Add(coeff, result[0]); + result[1] = Add(q, result[1]); + } + return result; + } + + /** + * Return the greatest common divisor of this and a polynomial <i>f</i> + * + * @param f polynomial + * @return GCD(this, f) + */ + public PolynomialGF2mSmallM Gcd(PolynomialGF2mSmallM f) + { + int[] resultCoeff = Gcd(coefficients, f.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Return the greatest common divisor of two polynomials over the field + * <tt>GF(2^m)</tt>. + * + * @param f the first polynomial + * @param g the second polynomial + * @return <tt>gcd(f, g)</tt> + */ + private int[] Gcd(int[] f, int[] g) + { + int[] a = f; + int[] b = g; + if (ComputeDegree(a) == -1) + { + return b; + } + while (ComputeDegree(b) != -1) + { + int[] c = Mod(a, b); + a = new int[b.Length]; + Array.Copy(b, 0, a, 0, a.Length); + b = new int[c.Length]; + Array.Copy(c, 0, b, 0, b.Length); + } + int coeff = field.Inverse(HeadCoefficient(a)); + return MultWithElement(a, coeff); + } + + /** + * Compute the product of this polynomial and the given factor using a + * Karatzuba like scheme. + * + * @param factor the polynomial + * @return <tt>this * factor</tt> + */ + public PolynomialGF2mSmallM Multiply(PolynomialGF2mSmallM factor) + { + int[] resultCoeff = Multiply(coefficients, factor.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the product of two polynomials over the field <tt>GF(2^m)</tt> + * using a Karatzuba like multiplication. + * + * @param a the first polynomial + * @param b the second polynomial + * @return a * b + */ + private int[] Multiply(int[] a, int[] b) + { + int[] mult1, mult2; + if (ComputeDegree(a) < ComputeDegree(b)) + { + mult1 = b; + mult2 = a; + } + else + { + mult1 = a; + mult2 = b; + } + + mult1 = NormalForm(mult1); + mult2 = NormalForm(mult2); + + if (mult2.Length == 1) + { + return MultWithElement(mult1, mult2[0]); + } + + int d1 = mult1.Length; + int d2 = mult2.Length; + int[] result = new int[d1 + d2 - 1]; + + if (d2 != d1) + { + int[] res1 = new int[d2]; + int[] res2 = new int[d1 - d2]; + Array.Copy(mult1, 0, res1, 0, res1.Length); + Array.Copy(mult1, d2, res2, 0, res2.Length); + res1 = Multiply(res1, mult2); + res2 = Multiply(res2, mult2); + res2 = MultWithMonomial(res2, d2); + result = Add(res1, res2); + } + else + { + d2 = Utils.UnsignedRightBitShiftInt(d1 + 1, 1); + int d = d1 - d2; + int[] firstPartMult1 = new int[d2]; + int[] firstPartMult2 = new int[d2]; + int[] secondPartMult1 = new int[d]; + int[] secondPartMult2 = new int[d]; + Array.Copy(mult1, 0, firstPartMult1, 0, + firstPartMult1.Length); + Array.Copy(mult1, d2, secondPartMult1, 0, + secondPartMult1.Length); + Array.Copy(mult2, 0, firstPartMult2, 0, + firstPartMult2.Length); + Array.Copy(mult2, d2, secondPartMult2, 0, + secondPartMult2.Length); + int[] helpPoly1 = Add(firstPartMult1, secondPartMult1); + int[] helpPoly2 = Add(firstPartMult2, secondPartMult2); + int[] res1 = Multiply(firstPartMult1, firstPartMult2); + int[] res2 = Multiply(helpPoly1, helpPoly2); + int[] res3 = Multiply(secondPartMult1, secondPartMult2); + res2 = Add(res2, res1); + res2 = Add(res2, res3); + res3 = MultWithMonomial(res3, d2); + result = Add(res2, res3); + result = MultWithMonomial(result, d2); + result = Add(result, res1); + } + + return result; + } + + /* + * ---------------- PART II ---------------- + * + */ + + /** + * Check a polynomial for irreducibility over the field <tt>GF(2^m)</tt>. + * + * @param a the polynomial to check + * @return true if a is irreducible, false otherwise + */ + private bool IsIrreducible(int[] a) + { + if (a[0] == 0) + { + return false; + } + int d = ComputeDegree(a) >> 1; + int[] u = { 0, 1 }; + int[] Y = { 0, 1 }; + int fieldDegree = field.GetDegree(); + for (int i = 0; i < d; i++) + { + for (int j = fieldDegree - 1; j >= 0; j--) + { + u = ModMultiply(u, u, a); + } + u = NormalForm(u); + int[] g = Gcd(Add(u, Y), a); + if (ComputeDegree(g) != 0) + { + return false; + } + } + return true; + } + + /** + * Reduce this polynomial modulo another polynomial. + * + * @param f the reduction polynomial + * @return <tt>this mod f</tt> + */ + public PolynomialGF2mSmallM Mod(PolynomialGF2mSmallM f) + { + int[] resultCoeff = Mod(coefficients, f.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Reduce a polynomial modulo another polynomial. + * + * @param a the polynomial + * @param f the reduction polynomial + * @return <tt>a mod f</tt> + */ + private int[] Mod(int[] a, int[] f) + { + int df = ComputeDegree(f); + if (df == -1) + { + throw new ArithmeticException("Division by zero"); + } + int[] result = new int[a.Length]; + int hc = HeadCoefficient(f); + hc = field.Inverse(hc); + Array.Copy(a, 0, result, 0, result.Length); + while (df <= ComputeDegree(result)) + { + int[] q; + int coeff = field.Mult(HeadCoefficient(result), hc); + q = MultWithMonomial(f, ComputeDegree(result) - df); + q = MultWithElement(q, coeff); + result = Add(q, result); + } + 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> + */ + public PolynomialGF2mSmallM ModMultiply(PolynomialGF2mSmallM a, + PolynomialGF2mSmallM b) + { + int[] resultCoeff = ModMultiply(coefficients, a.coefficients, + b.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + + + /** + * Square this polynomial using a squaring matrix. + * + * @param matrix the squaring matrix + * @return <tt>this^2</tt> modulo the reduction polynomial implicitly + * given via the squaring matrix + */ + public PolynomialGF2mSmallM ModSquareMatrix(PolynomialGF2mSmallM[] matrix) + { + + int length = matrix.Length; + + int[] resultCoeff = new int[length]; + int[] thisSquare = new int[length]; + + // square each entry of this polynomial + for (int i = 0; i < coefficients.Length; i++) + { + thisSquare[i] = field.Mult(coefficients[i], coefficients[i]); + } + + // do matrix-vector multiplication + for (int i = 0; i < length; i++) + { + // compute scalar product of i-th row and coefficient vector + for (int j = 0; j < length; j++) + { + if (i >= matrix[j].coefficients.Length) + { + continue; + } + int scalarTerm = field.Mult(matrix[j].coefficients[i], + thisSquare[j]); + resultCoeff[i] = field.add(resultCoeff[i], scalarTerm); + } + } + + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the product of two polynomials modulo a third polynomial over the + * finite field <tt>GF(2^m)</tt>. + * + * @param a the first polynomial + * @param b the second polynomial + * @param g the reduction polynomial + * @return <tt>a * b mod g</tt> + */ + private int[] ModMultiply(int[] a, int[] b, int[] g) + { + return Mod(Multiply(a, b), g); + } + + /** + * Compute the square root of this polynomial modulo the given polynomial. + * + * @param a the reduction polynomial + * @return <tt>this^(1/2) mod a</tt> + */ + public PolynomialGF2mSmallM ModSquareRoot(PolynomialGF2mSmallM a) + { + int[] resultCoeff = IntUtils.Clone(coefficients); + int[] help = ModMultiply(resultCoeff, resultCoeff, a.coefficients); + while (!IsEqual(help, coefficients)) + { + resultCoeff = NormalForm(help); + help = ModMultiply(resultCoeff, resultCoeff, a.coefficients); + } + + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the square root of this polynomial using a square root matrix. + * + * @param matrix the matrix for computing square roots in + * <tt>(GF(2^m))^t</tt> the polynomial ring defining the + * square root matrix + * @return <tt>this^(1/2)</tt> modulo the reduction polynomial implicitly + * given via the square root matrix + */ + public PolynomialGF2mSmallM ModSquareRootMatrix( + PolynomialGF2mSmallM[] matrix) + { + + int length = matrix.Length; + + int[] resultCoeff = new int[length]; + + // do matrix multiplication + for (int i = 0; i < length; i++) + { + // compute scalar product of i-th row and j-th column + for (int j = 0; j < length; j++) + { + if (i >= matrix[j].coefficients.Length) + { + continue; + } + if (j < coefficients.Length) + { + int scalarTerm = field.Mult(matrix[j].coefficients[i], + coefficients[j]); + resultCoeff[i] = field.add(resultCoeff[i], scalarTerm); + } + } + } + + // compute the square root of each entry of the result coefficients + for (int i = 0; i < length; i++) + { + resultCoeff[i] = field.SqRoot(resultCoeff[i]); + } + + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the result of the division of this polynomial by another + * polynomial modulo a third polynomial. + * + * @param divisor the divisor + * @param modulus the reduction polynomial + * @return <tt>this * divisor^(-1) mod modulus</tt> + */ + public PolynomialGF2mSmallM ModDiv(PolynomialGF2mSmallM divisor, + PolynomialGF2mSmallM modulus) + { + int[] resultCoeff = ModDiv(coefficients, divisor.coefficients, + modulus.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the result of the division of two polynomials modulo a third + * polynomial over the field <tt>GF(2^m)</tt>. + * + * @param a the first polynomial + * @param b the second polynomial + * @param g the reduction polynomial + * @return <tt>a * b^(-1) mod g</tt> + */ + private int[] ModDiv(int[] a, int[] b, int[] g) + { + int[] r0 = NormalForm(g); + int[] r1 = Mod(b, g); + int[] s0 = { 0 }; + int[] s1 = Mod(a, g); + int[] s2; + int[][] q; + while (ComputeDegree(r1) != -1) + { + q = Div(r0, r1); + r0 = NormalForm(r1); + r1 = NormalForm(q[1]); + s2 = Add(s0, ModMultiply(q[0], s1, g)); + s0 = NormalForm(s1); + s1 = NormalForm(s2); + + } + int hc = HeadCoefficient(r0); + s0 = MultWithElement(s0, field.Inverse(hc)); + return s0; + } + + /** + * Compute the inverse of this polynomial modulo the given polynomial. + * + * @param a the reduction polynomial + * @return <tt>this^(-1) mod a</tt> + */ + public PolynomialGF2mSmallM ModInverse(PolynomialGF2mSmallM a) + { + int[] unit = { 1 }; + int[] resultCoeff = ModDiv(unit, coefficients, a.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute a polynomial pair (a,b) from this polynomial and the given + * polynomial g with the property b*this = a mod g and deg(a)<=deg(g)/2. + * + * @param g the reduction polynomial + * @return PolynomialGF2mSmallM[] {a,b} with b*this = a mod g and deg(a)<= + * deg(g)/2 + */ + public PolynomialGF2mSmallM[] ModPolynomialToFracton(PolynomialGF2mSmallM g) + { + int dg = g.degree >> 1; + int[] a0 = NormalForm(g.coefficients); + int[] a1 = Mod(coefficients, g.coefficients); + int[] b0 = { 0 }; + int[] b1 = { 1 }; + while (ComputeDegree(a1) > dg) + { + int[][] q = Div(a0, a1); + a0 = a1; + a1 = q[1]; + int[] b2 = Add(b0, ModMultiply(q[0], b1, g.coefficients)); + b0 = b1; + b1 = b2; + } + + return new PolynomialGF2mSmallM[]{ + new PolynomialGF2mSmallM(field, a1), + new PolynomialGF2mSmallM(field, b1)}; + } + + /** + * checks if given object is equal to this polynomial. + * <p> + * The method returns false whenever the given object is not polynomial over + * GF(2^m). + * + * @param other object + * @return true or false + */ + public bool equals(Object other) + { + + if (other == null || !(other is PolynomialGF2mSmallM)) + { + return false; + } + + PolynomialGF2mSmallM p = (PolynomialGF2mSmallM)other; + + if ((field.Equals(p.field)) && (degree == p.degree) + && (IsEqual(coefficients, p.coefficients))) + { + return true; + } + + return false; + } + + /** + * Compare two polynomials given as int arrays. + * + * @param a the first polynomial + * @param b the second polynomial + * @return <tt>true</tt> if <tt>a</tt> and <tt>b</tt> represent the + * same polynomials, <tt>false</tt> otherwise + */ + private static bool IsEqual(int[] a, int[] b) + { + int da = ComputeDegree(a); + int db = ComputeDegree(b); + if (da != db) + { + return false; + } + for (int i = 0; i <= da; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + return true; + } + + /** + * @return the hash code of this polynomial + */ + public int HashCode() + { + int hash = field.HashCode(); + for (int j = 0; j < coefficients.Length; j++) + { + hash = hash * 31 + coefficients[j]; + } + return hash; + } + + /** + * Returns a human readable form of the polynomial. + * + * @return a human readable form of the polynomial. + */ + public String toString() + { + String str = " Polynomial over " + field.ToString() + ": \n"; + + for (int i = 0; i < coefficients.Length; i++) + { + str = str + field.ElementToStr(coefficients[i]) + "Y^" + i + "+"; + } + str = str + ";"; + + return str; + } + + /** + * Compute the degree of this polynomial. If this is the zero polynomial, + * the degree is -1. + */ + private void ComputeDegree() + { + for (degree = coefficients.Length - 1; degree >= 0 + && coefficients[degree] == 0; degree--) + { + ; + } + } + + /** + * 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) + { + int degree; + for (degree = a.Length - 1; 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 IntUtils.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> + */ + public PolynomialGF2mSmallM ModKaratsubaMultiplyBigDeg(PolynomialGF2mSmallM a, + PolynomialGF2mSmallM b) + { + int[] resultCoeff = ModKaratsubaMultiplyBigDeg(coefficients, a.coefficients, + b.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the inverse of this polynomial modulo the given polynomial. + * + * @param a the reduction polynomial + * @return <tt>this^(-1) mod a</tt> + */ + public PolynomialGF2mSmallM ModInverseBigDeg(PolynomialGF2mSmallM a) + { + int[] unit = { 1 }; + int[] resultCoeff = ModDivBigDeg(unit, coefficients, a.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + private int[] ModDivBigDeg(int[] a, int[] b, int[] g) + { + int[] r0 = NormalForm(g); + int[] r1 = Mod(b, g); + int[] s0 = { 0 }; + int[] s1 = Mod(a, g); + 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); + } + int hc = HeadCoefficient(r0); + s0 = MultWithElement(s0, field.Inverse(hc)); + 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 int[] ModKaratsubaMultiplyBigDeg(int[] aa, int[] bb, int[] g) + { + int[] a, b; + if (aa.Length >= bb.Length) + { + a = Arrays.Clone(aa); + b = Arrays.Clone(bb); + } + else + { + a = Arrays.Clone(bb); + b = Arrays.Clone(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++) + { + for (int p = 0; p < System.Math.Min(m, i); p++) + { + int q = i - p; + if (p >= q) + { + break; + } + + int ap = a[p]; + int aq = 0; + + if (q < a.Length) + { + aq = a[q]; + } + + int bp = b[p]; + int dp = D[p]; + + if (q < m && p < m) + { + int bq = b[q]; + int dq = D[q]; + + S[i] = S[i] + (ap + aq) * (bp + bq); + T[i] = T[i] + dp + dq; + } + else if (q >= m && q < n) + { + S[i] = S[i] + ((ap + aq) * bp); + T[i] = T[i] + dp; + } + } + } + + 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; + } + } + int[] res = Mod(C, g); + return res; + } + } +} diff --git a/crypto/src/pqc/math/linearalgebra/PolynomialRingGF2.cs b/crypto/src/pqc/math/linearalgebra/PolynomialRingGF2.cs new file mode 100644 index 000000000..9bc3fcd31 --- /dev/null +++ b/crypto/src/pqc/math/linearalgebra/PolynomialRingGF2.cs @@ -0,0 +1,286 @@ +using System; + +namespace Org.BouncyCastle.Pqc.Math.LinearAlgebra +{ + /** + * This class describes operations with polynomials over finite field GF(2), i e + * polynomial ring R = GF(2)[X]. All operations are defined only for polynomials + * with degree <=32. For the polynomial representation the map f: R->Z, + * poly(X)->poly(2) is used, where integers have the binary representation. For + * example: X^7+X^3+X+1 -> (00...0010001011)=139 Also for polynomials type + * Integer is used. + * + * @see GF2mField + */ + public class PolynomialRingGF2 + { + + /** + * Default constructor (private). + */ + private PolynomialRingGF2() + { + // empty + } + + /** + * Return sum of two polyomials + * + * @param p polynomial + * @param q polynomial + * @return p+q + */ + + public static int Add(int p, int q) + { + return p ^ q; + } + + /** + * Return product of two polynomials + * + * @param p polynomial + * @param q polynomial + * @return p*q + */ + + public static long Multiply(int p, int q) + { + long result = 0; + if (q != 0) + { + long q1 = q & 0x00000000ffffffffL; + + while (p != 0) + { + byte b = (byte)(p & 0x01); + if (b == 1) + { + result ^= q1; + } + p = Utils.UnsignedRightBitShiftInt(p, 1); + q1 <<= 1; + + } + } + return result; + } + + /** + * Compute the product of two polynomials modulo a third polynomial. + * + * @param a the first polynomial + * @param b the second polynomial + * @param r the reduction polynomial + * @return <tt>a * b mod r</tt> + */ + public static int modMultiply(int a, int b, int r) + { + int result = 0; + int p = Remainder(a, r); + int q = Remainder(b, r); + if (q != 0) + { + int d = 1 << Degree(r); + + while (p != 0) + { + byte pMod2 = (byte)(p & 0x01); + if (pMod2 == 1) + { + result ^= q; + } + p = Utils.UnsignedRightBitShiftInt(p, 1); + q <<= 1; + if (q >= d) + { + q ^= r; + } + } + } + return result; + } + + /** + * Return the degree of a polynomial + * + * @param p polynomial p + * @return degree(p) + */ + + public static int Degree(int p) + { + int result = -1; + while (p != 0) + { + result++; + p = Utils.UnsignedRightBitShiftInt(p, 1); + } + return result; + } + + /** + * Return the degree of a polynomial + * + * @param p polynomial p + * @return degree(p) + */ + + public static int Degree(long p) + { + int result = 0; + while (p != 0) + { + result++; + p = Utils.UnsignedRightBitShiftLong(p, 1); + } + return result - 1; + } + + /** + * Return the remainder of a polynomial division of two polynomials. + * + * @param p dividend + * @param q divisor + * @return <tt>p mod q</tt> + */ + public static int Remainder(int p, int q) + { + int result = p; + + if (q == 0) + { + // -DM Console.Error.WriteLine + Console.Error.WriteLine("Error: to be divided by 0"); + return 0; + } + + while (Degree(result) >= Degree(q)) + { + result ^= q << (Degree(result) - Degree(q)); + } + + return result; + } + + /** + * Return the rest of devision two polynomials + * + * @param p polinomial + * @param q polinomial + * @return p mod q + */ + + public static int Rest(long p, int q) + { + long p1 = p; + if (q == 0) + { + // -DM Console.Error.WriteLine + Console.Error.WriteLine("Error: to be divided by 0"); + return 0; + } + long q1 = q & 0x00000000ffffffffL; + + while ((Utils.UnsignedRightBitShiftLong(p1, 32)) != 0) + { + p1 ^= q1 << (Degree(p1) - Degree(q1)); + } + + int result = (int)(p1 & 0xffffffff); + while (Degree(result) >= Degree(q)) + { + result ^= q << (Degree(result) - Degree(q)); + } + + return result; + } + + /** + * Return the greatest common divisor of two polynomials + * + * @param p polinomial + * @param q polinomial + * @return GCD(p, q) + */ + + public static int Gcd(int p, int q) + { + int a, b, c; + a = p; + b = q; + while (b != 0) + { + c = Remainder(a, b); + a = b; + b = c; + + } + return a; + } + + /** + * Checking polynomial for irreducibility + * + * @param p polinomial + * @return true if p is irreducible and false otherwise + */ + + public static bool IsIrreducible(int p) + { + if (p == 0) + { + return false; + } + uint tmpDeg = (uint)Degree(p); + int d = (int) tmpDeg >> 1; + int u = 2; + for (int i = 0; i < d; i++) + { + u = modMultiply(u, u, p); + if (Gcd(u ^ 2, p) != 1) + { + return false; + } + } + return true; + } + + /** + * Creates irreducible polynomial with degree d + * + * @param deg polynomial degree + * @return irreducible polynomial p + */ + public static int GetIrreduciblePolynomial(int deg) + { + if (deg < 0) + { + // -DM Console.Error.WriteLine + Console.Error.WriteLine("The Degree is negative"); + return 0; + } + if (deg > 31) + { + // -DM Console.Error.WriteLine + Console.Error.WriteLine("The Degree is more then 31"); + return 0; + } + if (deg == 0) + { + return 1; + } + int a = 1 << deg; + a++; + int b = 1 << (deg + 1); + for (int i = a; i < b; i += 2) + { + if (IsIrreducible(i)) + { + return i; + } + } + return 0; + } + } +} diff --git a/crypto/src/pqc/math/linearalgebra/RandUtils.cs b/crypto/src/pqc/math/linearalgebra/RandUtils.cs new file mode 100644 index 000000000..f7b7b8588 --- /dev/null +++ b/crypto/src/pqc/math/linearalgebra/RandUtils.cs @@ -0,0 +1,27 @@ +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Pqc.Math.LinearAlgebra +{ + public class RandUtils + { + public static int NextInt(SecureRandom rand, int n) + { + + if ((n & -n) == n) // i.e., n is a power of 2 + { + return (int)((n * (long)(Utils.UnsignedRightBitShiftInt(rand.NextInt(), 1))) >> 31); + } + + int bits, value; + do + { + bits = Utils.UnsignedRightBitShiftInt(rand.NextInt() ,1); + value = bits % n; + } + while (bits - value + (n - 1) < 0); + + return value; + } + } + +} diff --git a/crypto/src/pqc/math/linearalgebra/Utils.cs b/crypto/src/pqc/math/linearalgebra/Utils.cs new file mode 100644 index 000000000..eb2760f82 --- /dev/null +++ b/crypto/src/pqc/math/linearalgebra/Utils.cs @@ -0,0 +1,20 @@ + +namespace Org.BouncyCastle.Pqc.Math.LinearAlgebra +{ + class Utils + { + internal static int UnsignedRightBitShiftInt(int a, int b) + { + uint tmp = (uint) a; + tmp >>= b; + return (int) tmp; + } + + internal static long UnsignedRightBitShiftLong(long a, int b) + { + ulong tmp = (ulong)a; + tmp >>= b; + return (long) tmp; + } + } +} diff --git a/crypto/src/pqc/math/linearalgebra/Vector.cs b/crypto/src/pqc/math/linearalgebra/Vector.cs new file mode 100644 index 000000000..e50c54792 --- /dev/null +++ b/crypto/src/pqc/math/linearalgebra/Vector.cs @@ -0,0 +1,62 @@ +using System; + +namespace Org.BouncyCastle.Pqc.Math.LinearAlgebra +{ + /** + * This abstract class defines vectors. It holds the length of vector. + */ + public abstract class Vector + { + + /** + * the length of this vector + */ + protected int length; + + /** + * @return the length of this vector + */ + public int GetLength() + { + return length; + } + + /** + * @return this vector as byte array + */ + public abstract byte[] GetEncoded(); + + /** + * Return whether this is the zero vector (i.e., all elements are zero). + * + * @return <tt>true</tt> if this is the zero vector, <tt>false</tt> + * otherwise + */ + public abstract bool IsZero(); + + /** + * Add another vector to this vector. + * + * @param addend the other vector + * @return <tt>this + addend</tt> + */ + public abstract Vector Add(Vector addend); + + /** + * Multiply this vector with a permutation. + * + * @param p the permutation + * @return <tt>this*p = p*this</tt> + */ + public abstract Vector Multiply(Permutation p); + + /** + * Check if the given object is equal to this vector. + * + * @param other vector + * @return the result of the comparison + */ + public abstract bool Equals(Object other); + + } +} |