Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Tyley <roberto.tyley@gmail.com>2014-07-15 01:38:01 +0400
committerRoberto Tyley <roberto.tyley@gmail.com>2014-07-26 11:23:17 +0400
commit7cb752aaf746dc0b473afeb9e892b7fbc12666c5 (patch)
treecc4f91ddc18332b5adbe82e3fcb040d976c90105 /core/src/main/java/org/spongycastle/pqc
parent551830f8ea5177042af2c7dd1fc90888bc67387d (diff)
Execute become-spongy.sh
https://github.com/rtyley/spongycastle/blob/3040af/become-spongy.sh
Diffstat (limited to 'core/src/main/java/org/spongycastle/pqc')
-rw-r--r--core/src/main/java/org/spongycastle/pqc/asn1/GMSSPrivateKey.java1312
-rw-r--r--core/src/main/java/org/spongycastle/pqc/asn1/GMSSPublicKey.java74
-rw-r--r--core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PrivateKey.java173
-rw-r--r--core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PublicKey.java96
-rw-r--r--core/src/main/java/org/spongycastle/pqc/asn1/McEliecePrivateKey.java197
-rw-r--r--core/src/main/java/org/spongycastle/pqc/asn1/McEliecePublicKey.java97
-rw-r--r--core/src/main/java/org/spongycastle/pqc/asn1/PQCObjectIdentifiers.java46
-rw-r--r--core/src/main/java/org/spongycastle/pqc/asn1/ParSet.java140
-rw-r--r--core/src/main/java/org/spongycastle/pqc/asn1/RainbowPrivateKey.java349
-rw-r--r--core/src/main/java/org/spongycastle/pqc/asn1/RainbowPublicKey.java174
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/DigestingMessageSigner.java117
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/MessageEncryptor.java30
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/MessageSigner.java32
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSDigestProvider.java8
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyGenerationParameters.java26
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java476
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyParameters.java22
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSLeaf.java376
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSParameters.java155
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java1041
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPublicKeyParameters.java33
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootCalc.java596
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootSig.java666
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java403
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSUtils.java145
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/Treehash.java525
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSRandom.java78
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSUtil.java151
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSVerify.java344
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSignature.java404
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/Conversions.java236
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyGenerationParameters.java25
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java119
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyParameters.java25
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Parameters.java51
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Primitives.java86
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PrivateKeyParameters.java172
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PublicKeyParameters.java97
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java218
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiDigestCipher.java128
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyGenerationParameters.java25
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java151
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyParameters.java25
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java319
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiDigestCipher.java128
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java224
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSDigestCipher.java128
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceParameters.java181
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java241
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalDigestCipher.java128
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePrivateKeyParameters.java197
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePublicKeyParameters.java96
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/IndexGenerator.java239
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java463
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyPairGenerator.java113
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyParameters.java20
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionParameters.java410
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPrivateKeyParameters.java199
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPublicKeyParameters.java131
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEngine.java495
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUParameters.java7
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigner.java263
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSignerPrng.java64
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java407
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java357
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningParameters.java269
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPrivateKeyParameters.java385
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPublicKeyParameters.java132
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/rainbow/Layer.java322
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyGenerationParameters.java26
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java413
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyParameters.java25
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowParameters.java111
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPrivateKeyParameters.java117
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPublicKeyParameters.java53
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowSigner.java301
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/ComputeInField.java490
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/GF2Field.java139
-rw-r--r--core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/RainbowUtil.java230
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigEndianConversions.java306
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigIntUtils.java138
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/ByteUtils.java414
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/CharUtils.java98
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Matrix.java1323
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Polynomial.java2039
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Vector.java539
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mField.java365
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mMatrix.java377
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mVector.java256
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nElement.java186
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nField.java292
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBElement.java1154
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBField.java546
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomial.java587
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java1021
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialField.java553
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GFElement.java158
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GoppaCode.java310
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntUtils.java202
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntegerFunctions.java1423
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/LittleEndianConversions.java230
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Matrix.java131
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Permutation.java247
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java1124
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2.java278
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java175
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/RandUtils.java25
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Vector.java69
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/BigIntEuclidean.java54
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/IntEuclidean.java51
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigDecimalPolynomial.java258
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java394
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Constants.java12
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/DenseTernaryPolynomial.java142
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/IntegerPolynomial.java1358
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/LongPolynomial2.java255
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/LongPolynomial5.java149
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ModularResultant.java46
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Polynomial.java42
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ProductFormPolynomial.java153
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Resultant.java28
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/SparseTernaryPolynomial.java320
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/TernaryPolynomial.java25
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/util/ArrayEncoder.java292
-rw-r--r--core/src/main/java/org/spongycastle/pqc/math/ntru/util/Util.java158
125 files changed, 35770 insertions, 0 deletions
diff --git a/core/src/main/java/org/spongycastle/pqc/asn1/GMSSPrivateKey.java b/core/src/main/java/org/spongycastle/pqc/asn1/GMSSPrivateKey.java
new file mode 100644
index 00000000..b56974ad
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/asn1/GMSSPrivateKey.java
@@ -0,0 +1,1312 @@
+package org.spongycastle.pqc.asn1;
+
+import java.math.BigInteger;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.pqc.crypto.gmss.GMSSLeaf;
+import org.spongycastle.pqc.crypto.gmss.GMSSParameters;
+import org.spongycastle.pqc.crypto.gmss.GMSSRootCalc;
+import org.spongycastle.pqc.crypto.gmss.GMSSRootSig;
+import org.spongycastle.pqc.crypto.gmss.Treehash;
+
+public class GMSSPrivateKey
+ extends ASN1Object
+{
+ private ASN1Primitive primitive;
+
+ private GMSSPrivateKey(ASN1Sequence mtsPrivateKey)
+ {
+ // --- Decode <index>.
+ ASN1Sequence indexPart = (ASN1Sequence)mtsPrivateKey.getObjectAt(0);
+ int[] index = new int[indexPart.size()];
+ for (int i = 0; i < indexPart.size(); i++)
+ {
+ index[i] = checkBigIntegerInIntRange(indexPart.getObjectAt(i));
+ }
+
+ // --- Decode <curSeeds>.
+ ASN1Sequence curSeedsPart = (ASN1Sequence)mtsPrivateKey.getObjectAt(1);
+ byte[][] curSeeds = new byte[curSeedsPart.size()][];
+ for (int i = 0; i < curSeeds.length; i++)
+ {
+ curSeeds[i] = ((DEROctetString)curSeedsPart.getObjectAt(i)).getOctets();
+ }
+
+ // --- Decode <nextNextSeeds>.
+ ASN1Sequence nextNextSeedsPart = (ASN1Sequence)mtsPrivateKey.getObjectAt(2);
+ byte[][] nextNextSeeds = new byte[nextNextSeedsPart.size()][];
+ for (int i = 0; i < nextNextSeeds.length; i++)
+ {
+ nextNextSeeds[i] = ((DEROctetString)nextNextSeedsPart.getObjectAt(i)).getOctets();
+ }
+
+ // --- Decode <curAuth>.
+ ASN1Sequence curAuthPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(3);
+ ASN1Sequence curAuthPart1;
+
+ byte[][][] curAuth = new byte[curAuthPart0.size()][][];
+ for (int i = 0; i < curAuth.length; i++)
+ {
+ curAuthPart1 = (ASN1Sequence)curAuthPart0.getObjectAt(i);
+ curAuth[i] = new byte[curAuthPart1.size()][];
+ for (int j = 0; j < curAuth[i].length; j++)
+ {
+ curAuth[i][j] = ((DEROctetString)curAuthPart1.getObjectAt(j)).getOctets();
+ }
+ }
+
+ // --- Decode <nextAuth>.
+ ASN1Sequence nextAuthPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(4);
+ ASN1Sequence nextAuthPart1;
+
+ byte[][][] nextAuth = new byte[nextAuthPart0.size()][][];
+ for (int i = 0; i < nextAuth.length; i++)
+ {
+ nextAuthPart1 = (ASN1Sequence)nextAuthPart0.getObjectAt(i);
+ nextAuth[i] = new byte[nextAuthPart1.size()][];
+ for (int j = 0; j < nextAuth[i].length; j++)
+ {
+ nextAuth[i][j] = ((DEROctetString)nextAuthPart1.getObjectAt(j)).getOctets();
+ }
+ }
+
+ // --- Decode <curTreehash>.
+ ASN1Sequence seqOfcurTreehash0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(5);
+ ASN1Sequence seqOfcurTreehash1;
+ ASN1Sequence seqOfcurTreehashStat;
+ ASN1Sequence seqOfcurTreehashBytes;
+ ASN1Sequence seqOfcurTreehashInts;
+ ASN1Sequence seqOfcurTreehashString;
+
+ Treehash[][] curTreehash = new Treehash[seqOfcurTreehash0.size()][];
+ /*
+ for (int i = 0; i < curTreehash.length; i++)
+ {
+ seqOfcurTreehash1 = (ASN1Sequence)seqOfcurTreehash0.getObjectAt(i);
+ curTreehash[i] = new Treehash[seqOfcurTreehash1.size()];
+ for (int j = 0; j < curTreehash[i].length; j++)
+ {
+ seqOfcurTreehashStat = (ASN1Sequence)seqOfcurTreehash1.getObjectAt(j);
+ seqOfcurTreehashString = (ASN1Sequence)seqOfcurTreehashStat
+ .getObjectAt(0);
+ seqOfcurTreehashBytes = (ASN1Sequence)seqOfcurTreehashStat
+ .getObjectAt(1);
+ seqOfcurTreehashInts = (ASN1Sequence)seqOfcurTreehashStat
+ .getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfcurTreehashString.getObjectAt(0)).getString();
+ name[1] = ((DERIA5String)seqOfcurTreehashString.getObjectAt(1)).getString();
+
+ int tailLength = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(1));
+ byte[][] statByte = new byte[3 + tailLength][];
+ statByte[0] = ((DEROctetString)seqOfcurTreehashBytes.getObjectAt(0)).getOctets();
+
+ if (statByte[0].length == 0)
+ { // if null was encoded
+ statByte[0] = null;
+ }
+
+ statByte[1] = ((DEROctetString)seqOfcurTreehashBytes.getObjectAt(1)).getOctets();
+ statByte[2] = ((DEROctetString)seqOfcurTreehashBytes.getObjectAt(2)).getOctets();
+ for (int k = 0; k < tailLength; k++)
+ {
+ statByte[3 + k] = ((DEROctetString)seqOfcurTreehashBytes
+ .getObjectAt(3 + k)).getOctets();
+ }
+ int[] statInt = new int[6 + tailLength];
+ statInt[0] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(0));
+ statInt[1] = tailLength;
+ statInt[2] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(3));
+ statInt[4] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(4));
+ statInt[5] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(5));
+ for (int k = 0; k < tailLength; k++)
+ {
+ statInt[6 + k] = checkBigIntegerInIntRange(seqOfcurTreehashInts.getObjectAt(6 + k));
+ }
+
+ // TODO: Check if we can do better than throwing away name[1] !!!
+ curTreehash[i][j] = new Treehash(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+ }
+
+
+ // --- Decode <nextTreehash>.
+ ASN1Sequence seqOfNextTreehash0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(6);
+ ASN1Sequence seqOfNextTreehash1;
+ ASN1Sequence seqOfNextTreehashStat;
+ ASN1Sequence seqOfNextTreehashBytes;
+ ASN1Sequence seqOfNextTreehashInts;
+ ASN1Sequence seqOfNextTreehashString;
+
+ Treehash[][] nextTreehash = new Treehash[seqOfNextTreehash0.size()][];
+
+ for (int i = 0; i < nextTreehash.length; i++)
+ {
+ seqOfNextTreehash1 = (ASN1Sequence)seqOfNextTreehash0.getObjectAt(i);
+ nextTreehash[i] = new Treehash[seqOfNextTreehash1.size()];
+ for (int j = 0; j < nextTreehash[i].length; j++)
+ {
+ seqOfNextTreehashStat = (ASN1Sequence)seqOfNextTreehash1
+ .getObjectAt(j);
+ seqOfNextTreehashString = (ASN1Sequence)seqOfNextTreehashStat
+ .getObjectAt(0);
+ seqOfNextTreehashBytes = (ASN1Sequence)seqOfNextTreehashStat
+ .getObjectAt(1);
+ seqOfNextTreehashInts = (ASN1Sequence)seqOfNextTreehashStat
+ .getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfNextTreehashString.getObjectAt(0))
+ .getString();
+ name[1] = ((DERIA5String)seqOfNextTreehashString.getObjectAt(1))
+ .getString();
+
+ int tailLength = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(1));
+
+ byte[][] statByte = new byte[3 + tailLength][];
+ statByte[0] = ((DEROctetString)seqOfNextTreehashBytes.getObjectAt(0)).getOctets();
+ if (statByte[0].length == 0)
+ { // if null was encoded
+ statByte[0] = null;
+ }
+
+ statByte[1] = ((DEROctetString)seqOfNextTreehashBytes.getObjectAt(1)).getOctets();
+ statByte[2] = ((DEROctetString)seqOfNextTreehashBytes.getObjectAt(2)).getOctets();
+ for (int k = 0; k < tailLength; k++)
+ {
+ statByte[3 + k] = ((DEROctetString)seqOfNextTreehashBytes
+ .getObjectAt(3 + k)).getOctets();
+ }
+ int[] statInt = new int[6 + tailLength];
+ statInt[0] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(0));
+
+ statInt[1] = tailLength;
+ statInt[2] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(2));
+
+ statInt[3] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(3));
+
+ statInt[4] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(4));
+
+ statInt[5] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(5));
+
+ for (int k = 0; k < tailLength; k++)
+ {
+ statInt[6 + k] = checkBigIntegerInIntRange(seqOfNextTreehashInts.getObjectAt(6 + k));
+
+ }
+ nextTreehash[i][j] = new Treehash(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+ }
+
+
+ // --- Decode <keep>.
+ ASN1Sequence keepPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(7);
+ ASN1Sequence keepPart1;
+
+ byte[][][] keep = new byte[keepPart0.size()][][];
+ for (int i = 0; i < keep.length; i++)
+ {
+ keepPart1 = (ASN1Sequence)keepPart0.getObjectAt(i);
+ keep[i] = new byte[keepPart1.size()][];
+ for (int j = 0; j < keep[i].length; j++)
+ {
+ keep[i][j] = ((DEROctetString)keepPart1.getObjectAt(j)).getOctets();
+ }
+ }
+
+ // --- Decode <curStack>.
+ ASN1Sequence curStackPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(8);
+ ASN1Sequence curStackPart1;
+
+ Vector[] curStack = new Vector[curStackPart0.size()];
+ for (int i = 0; i < curStack.length; i++)
+ {
+ curStackPart1 = (ASN1Sequence)curStackPart0.getObjectAt(i);
+ curStack[i] = new Vector();
+ for (int j = 0; j < curStackPart1.size(); j++)
+ {
+ curStack[i].addElement(((DEROctetString)curStackPart1.getObjectAt(j)).getOctets());
+ }
+ }
+
+ // --- Decode <nextStack>.
+ ASN1Sequence nextStackPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(9);
+ ASN1Sequence nextStackPart1;
+
+ Vector[] nextStack = new Vector[nextStackPart0.size()];
+ for (int i = 0; i < nextStack.length; i++)
+ {
+ nextStackPart1 = (ASN1Sequence)nextStackPart0.getObjectAt(i);
+ nextStack[i] = new Vector();
+ for (int j = 0; j < nextStackPart1.size(); j++)
+ {
+ nextStack[i].addElement(((DEROctetString)nextStackPart1
+ .getObjectAt(j)).getOctets());
+ }
+ }
+
+ // --- Decode <curRetain>.
+ ASN1Sequence curRetainPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(10);
+ ASN1Sequence curRetainPart1;
+ ASN1Sequence curRetainPart2;
+
+ Vector[][] curRetain = new Vector[curRetainPart0.size()][];
+ for (int i = 0; i < curRetain.length; i++)
+ {
+ curRetainPart1 = (ASN1Sequence)curRetainPart0.getObjectAt(i);
+ curRetain[i] = new Vector[curRetainPart1.size()];
+ for (int j = 0; j < curRetain[i].length; j++)
+ {
+ curRetainPart2 = (ASN1Sequence)curRetainPart1.getObjectAt(j);
+ curRetain[i][j] = new Vector();
+ for (int k = 0; k < curRetainPart2.size(); k++)
+ {
+ curRetain[i][j]
+ .addElement(((DEROctetString)curRetainPart2
+ .getObjectAt(k)).getOctets());
+ }
+ }
+ }
+
+ // --- Decode <nextRetain>.
+ ASN1Sequence nextRetainPart0 = (ASN1Sequence)mtsPrivateKey.getObjectAt(11);
+ ASN1Sequence nextRetainPart1;
+ ASN1Sequence nextRetainPart2;
+
+ Vector[][] nextRetain = new Vector[nextRetainPart0.size()][];
+ for (int i = 0; i < nextRetain.length; i++)
+ {
+ nextRetainPart1 = (ASN1Sequence)nextRetainPart0.getObjectAt(i);
+ nextRetain[i] = new Vector[nextRetainPart1.size()];
+ for (int j = 0; j < nextRetain[i].length; j++)
+ {
+ nextRetainPart2 = (ASN1Sequence)nextRetainPart1.getObjectAt(j);
+ nextRetain[i][j] = new Vector();
+ for (int k = 0; k < nextRetainPart2.size(); k++)
+ {
+ nextRetain[i][j]
+ .addElement(((DEROctetString)nextRetainPart2
+ .getObjectAt(k)).getOctets());
+ }
+ }
+ }
+
+ // --- Decode <nextNextLeaf>.
+ ASN1Sequence seqOfLeafs = (ASN1Sequence)mtsPrivateKey.getObjectAt(12);
+ ASN1Sequence seqOfLeafStat;
+ ASN1Sequence seqOfLeafBytes;
+ ASN1Sequence seqOfLeafInts;
+ ASN1Sequence seqOfLeafString;
+
+ GMSSLeaf[] nextNextLeaf = new GMSSLeaf[seqOfLeafs.size()];
+
+ for (int i = 0; i < nextNextLeaf.length; i++)
+ {
+ seqOfLeafStat = (ASN1Sequence)seqOfLeafs.getObjectAt(i);
+ // nextNextAuth[i]= new byte[nextNextAuthPart1.size()][];
+ seqOfLeafString = (ASN1Sequence)seqOfLeafStat.getObjectAt(0);
+ seqOfLeafBytes = (ASN1Sequence)seqOfLeafStat.getObjectAt(1);
+ seqOfLeafInts = (ASN1Sequence)seqOfLeafStat.getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfLeafString.getObjectAt(0)).getString();
+ name[1] = ((DERIA5String)seqOfLeafString.getObjectAt(1)).getString();
+ byte[][] statByte = new byte[4][];
+ statByte[0] = ((DEROctetString)seqOfLeafBytes.getObjectAt(0))
+ .getOctets();
+ statByte[1] = ((DEROctetString)seqOfLeafBytes.getObjectAt(1))
+ .getOctets();
+ statByte[2] = ((DEROctetString)seqOfLeafBytes.getObjectAt(2))
+ .getOctets();
+ statByte[3] = ((DEROctetString)seqOfLeafBytes.getObjectAt(3))
+ .getOctets();
+ int[] statInt = new int[4];
+ statInt[0] = checkBigIntegerInIntRange(seqOfLeafInts.getObjectAt(0));
+ statInt[1] = checkBigIntegerInIntRange(seqOfLeafInts.getObjectAt(1));
+ statInt[2] = checkBigIntegerInIntRange(seqOfLeafInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfLeafInts.getObjectAt(3));
+ nextNextLeaf[i] = new GMSSLeaf(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+
+ // --- Decode <upperLeaf>.
+ ASN1Sequence seqOfUpperLeafs = (ASN1Sequence)mtsPrivateKey.getObjectAt(13);
+ ASN1Sequence seqOfUpperLeafStat;
+ ASN1Sequence seqOfUpperLeafBytes;
+ ASN1Sequence seqOfUpperLeafInts;
+ ASN1Sequence seqOfUpperLeafString;
+
+ GMSSLeaf[] upperLeaf = new GMSSLeaf[seqOfUpperLeafs.size()];
+
+ for (int i = 0; i < upperLeaf.length; i++)
+ {
+ seqOfUpperLeafStat = (ASN1Sequence)seqOfUpperLeafs.getObjectAt(i);
+ seqOfUpperLeafString = (ASN1Sequence)seqOfUpperLeafStat.getObjectAt(0);
+ seqOfUpperLeafBytes = (ASN1Sequence)seqOfUpperLeafStat.getObjectAt(1);
+ seqOfUpperLeafInts = (ASN1Sequence)seqOfUpperLeafStat.getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfUpperLeafString.getObjectAt(0)).getString();
+ name[1] = ((DERIA5String)seqOfUpperLeafString.getObjectAt(1)).getString();
+ byte[][] statByte = new byte[4][];
+ statByte[0] = ((DEROctetString)seqOfUpperLeafBytes.getObjectAt(0))
+ .getOctets();
+ statByte[1] = ((DEROctetString)seqOfUpperLeafBytes.getObjectAt(1))
+ .getOctets();
+ statByte[2] = ((DEROctetString)seqOfUpperLeafBytes.getObjectAt(2))
+ .getOctets();
+ statByte[3] = ((DEROctetString)seqOfUpperLeafBytes.getObjectAt(3))
+ .getOctets();
+ int[] statInt = new int[4];
+ statInt[0] = checkBigIntegerInIntRange(seqOfUpperLeafInts.getObjectAt(0));
+ statInt[1] = checkBigIntegerInIntRange(seqOfUpperLeafInts.getObjectAt(1));
+ statInt[2] = checkBigIntegerInIntRange(seqOfUpperLeafInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfUpperLeafInts.getObjectAt(3));
+ upperLeaf[i] = new GMSSLeaf(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+
+ // --- Decode <upperTreehashLeaf>.
+ ASN1Sequence seqOfUpperTHLeafs = (ASN1Sequence)mtsPrivateKey.getObjectAt(14);
+ ASN1Sequence seqOfUpperTHLeafStat;
+ ASN1Sequence seqOfUpperTHLeafBytes;
+ ASN1Sequence seqOfUpperTHLeafInts;
+ ASN1Sequence seqOfUpperTHLeafString;
+
+ GMSSLeaf[] upperTHLeaf = new GMSSLeaf[seqOfUpperTHLeafs.size()];
+
+ for (int i = 0; i < upperTHLeaf.length; i++)
+ {
+ seqOfUpperTHLeafStat = (ASN1Sequence)seqOfUpperTHLeafs.getObjectAt(i);
+ seqOfUpperTHLeafString = (ASN1Sequence)seqOfUpperTHLeafStat.getObjectAt(0);
+ seqOfUpperTHLeafBytes = (ASN1Sequence)seqOfUpperTHLeafStat.getObjectAt(1);
+ seqOfUpperTHLeafInts = (ASN1Sequence)seqOfUpperTHLeafStat.getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfUpperTHLeafString.getObjectAt(0))
+ .getString();
+ name[1] = ((DERIA5String)seqOfUpperTHLeafString.getObjectAt(1))
+ .getString();
+ byte[][] statByte = new byte[4][];
+ statByte[0] = ((DEROctetString)seqOfUpperTHLeafBytes.getObjectAt(0))
+ .getOctets();
+ statByte[1] = ((DEROctetString)seqOfUpperTHLeafBytes.getObjectAt(1))
+ .getOctets();
+ statByte[2] = ((DEROctetString)seqOfUpperTHLeafBytes.getObjectAt(2))
+ .getOctets();
+ statByte[3] = ((DEROctetString)seqOfUpperTHLeafBytes.getObjectAt(3))
+ .getOctets();
+ int[] statInt = new int[4];
+ statInt[0] = checkBigIntegerInIntRange(seqOfUpperTHLeafInts.getObjectAt(0));
+ statInt[1] = checkBigIntegerInIntRange(seqOfUpperTHLeafInts.getObjectAt(1));
+ statInt[2] = checkBigIntegerInIntRange(seqOfUpperTHLeafInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfUpperTHLeafInts.getObjectAt(3));
+ upperTHLeaf[i] = new GMSSLeaf(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+
+ // --- Decode <minTreehash>.
+ ASN1Sequence minTreehashPart = (ASN1Sequence)mtsPrivateKey.getObjectAt(15);
+ int[] minTreehash = new int[minTreehashPart.size()];
+ for (int i = 0; i < minTreehashPart.size(); i++)
+ {
+ minTreehash[i] = checkBigIntegerInIntRange(minTreehashPart.getObjectAt(i));
+ }
+
+ // --- Decode <nextRoot>.
+ ASN1Sequence seqOfnextRoots = (ASN1Sequence)mtsPrivateKey.getObjectAt(16);
+ byte[][] nextRoot = new byte[seqOfnextRoots.size()][];
+ for (int i = 0; i < nextRoot.length; i++)
+ {
+ nextRoot[i] = ((DEROctetString)seqOfnextRoots.getObjectAt(i))
+ .getOctets();
+ }
+
+ // --- Decode <nextNextRoot>.
+ ASN1Sequence seqOfnextNextRoot = (ASN1Sequence)mtsPrivateKey.getObjectAt(17);
+ ASN1Sequence seqOfnextNextRootStat;
+ ASN1Sequence seqOfnextNextRootBytes;
+ ASN1Sequence seqOfnextNextRootInts;
+ ASN1Sequence seqOfnextNextRootString;
+ ASN1Sequence seqOfnextNextRootTreeH;
+ ASN1Sequence seqOfnextNextRootRetain;
+
+ GMSSRootCalc[] nextNextRoot = new GMSSRootCalc[seqOfnextNextRoot.size()];
+
+ for (int i = 0; i < nextNextRoot.length; i++)
+ {
+ seqOfnextNextRootStat = (ASN1Sequence)seqOfnextNextRoot.getObjectAt(i);
+ seqOfnextNextRootString = (ASN1Sequence)seqOfnextNextRootStat
+ .getObjectAt(0);
+ seqOfnextNextRootBytes = (ASN1Sequence)seqOfnextNextRootStat
+ .getObjectAt(1);
+ seqOfnextNextRootInts = (ASN1Sequence)seqOfnextNextRootStat.getObjectAt(2);
+ seqOfnextNextRootTreeH = (ASN1Sequence)seqOfnextNextRootStat
+ .getObjectAt(3);
+ seqOfnextNextRootRetain = (ASN1Sequence)seqOfnextNextRootStat
+ .getObjectAt(4);
+
+ // decode treehash of nextNextRoot
+ // ---------------------------------
+ ASN1Sequence seqOfnextNextRootTreeHStat;
+ ASN1Sequence seqOfnextNextRootTreeHBytes;
+ ASN1Sequence seqOfnextNextRootTreeHInts;
+ ASN1Sequence seqOfnextNextRootTreeHString;
+
+ Treehash[] nnRTreehash = new Treehash[seqOfnextNextRootTreeH.size()];
+
+ for (int k = 0; k < nnRTreehash.length; k++)
+ {
+ seqOfnextNextRootTreeHStat = (ASN1Sequence)seqOfnextNextRootTreeH
+ .getObjectAt(k);
+ seqOfnextNextRootTreeHString = (ASN1Sequence)seqOfnextNextRootTreeHStat
+ .getObjectAt(0);
+ seqOfnextNextRootTreeHBytes = (ASN1Sequence)seqOfnextNextRootTreeHStat
+ .getObjectAt(1);
+ seqOfnextNextRootTreeHInts = (ASN1Sequence)seqOfnextNextRootTreeHStat
+ .getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfnextNextRootTreeHString.getObjectAt(0))
+ .getString();
+ name[1] = ((DERIA5String)seqOfnextNextRootTreeHString.getObjectAt(1))
+ .getString();
+
+ int tailLength = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(1));
+
+ byte[][] statByte = new byte[3 + tailLength][];
+ statByte[0] = ((DEROctetString)seqOfnextNextRootTreeHBytes
+ .getObjectAt(0)).getOctets();
+ if (statByte[0].length == 0)
+ { // if null was encoded
+ statByte[0] = null;
+ }
+
+ statByte[1] = ((DEROctetString)seqOfnextNextRootTreeHBytes
+ .getObjectAt(1)).getOctets();
+ statByte[2] = ((DEROctetString)seqOfnextNextRootTreeHBytes
+ .getObjectAt(2)).getOctets();
+ for (int j = 0; j < tailLength; j++)
+ {
+ statByte[3 + j] = ((DEROctetString)seqOfnextNextRootTreeHBytes
+ .getObjectAt(3 + j)).getOctets();
+ }
+ int[] statInt = new int[6 + tailLength];
+ statInt[0] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(0));
+
+ statInt[1] = tailLength;
+ statInt[2] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(2));
+
+ statInt[3] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(3));
+
+ statInt[4] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(4));
+
+ statInt[5] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts.getObjectAt(5));
+
+ for (int j = 0; j < tailLength; j++)
+ {
+ statInt[6 + j] = checkBigIntegerInIntRange(seqOfnextNextRootTreeHInts
+ .getObjectAt(6 + j));
+ }
+ nnRTreehash[k] = new Treehash(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+ // ---------------------------------
+
+ // decode retain of nextNextRoot
+ // ---------------------------------
+ // ASN1Sequence seqOfnextNextRootRetainPart0 =
+ // (ASN1Sequence)seqOfnextNextRootRetain.get(0);
+ ASN1Sequence seqOfnextNextRootRetainPart1;
+
+ Vector[] nnRRetain = new Vector[seqOfnextNextRootRetain.size()];
+ for (int j = 0; j < nnRRetain.length; j++)
+ {
+ seqOfnextNextRootRetainPart1 = (ASN1Sequence)seqOfnextNextRootRetain
+ .getObjectAt(j);
+ nnRRetain[j] = new Vector();
+ for (int k = 0; k < seqOfnextNextRootRetainPart1.size(); k++)
+ {
+ nnRRetain[j]
+ .addElement(((DEROctetString)seqOfnextNextRootRetainPart1
+ .getObjectAt(k)).getOctets());
+ }
+ }
+ // ---------------------------------
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfnextNextRootString.getObjectAt(0))
+ .getString();
+ name[1] = ((DERIA5String)seqOfnextNextRootString.getObjectAt(1))
+ .getString();
+
+ int heightOfTree = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(0));
+ int tailLength = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(7));
+ byte[][] statByte = new byte[1 + heightOfTree + tailLength][];
+ statByte[0] = ((DEROctetString)seqOfnextNextRootBytes.getObjectAt(0))
+ .getOctets();
+ for (int j = 0; j < heightOfTree; j++)
+ {
+ statByte[1 + j] = ((DEROctetString)seqOfnextNextRootBytes
+ .getObjectAt(1 + j)).getOctets();
+ }
+ for (int j = 0; j < tailLength; j++)
+ {
+ statByte[1 + heightOfTree + j] = ((DEROctetString)seqOfnextNextRootBytes
+ .getObjectAt(1 + heightOfTree + j)).getOctets();
+ }
+ int[] statInt = new int[8 + heightOfTree + tailLength];
+ statInt[0] = heightOfTree;
+ statInt[1] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(1));
+ statInt[2] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(3));
+ statInt[4] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(4));
+ statInt[5] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(5));
+ statInt[6] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(6));
+ statInt[7] = tailLength;
+ for (int j = 0; j < heightOfTree; j++)
+ {
+ statInt[8 + j] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(8 + j));
+ }
+ for (int j = 0; j < tailLength; j++)
+ {
+ statInt[8 + heightOfTree + j] = checkBigIntegerInIntRange(seqOfnextNextRootInts.getObjectAt(8
+ + heightOfTree + j));
+ }
+ nextNextRoot[i] = new GMSSRootCalc(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt,
+ nnRTreehash, nnRRetain);
+ }
+
+ // --- Decode <curRootSig>.
+ ASN1Sequence seqOfcurRootSig = (ASN1Sequence)mtsPrivateKey.getObjectAt(18);
+ byte[][] curRootSig = new byte[seqOfcurRootSig.size()][];
+ for (int i = 0; i < curRootSig.length; i++)
+ {
+ curRootSig[i] = ((DEROctetString)seqOfcurRootSig.getObjectAt(i))
+ .getOctets();
+ }
+
+ // --- Decode <nextRootSig>.
+ ASN1Sequence seqOfnextRootSigs = (ASN1Sequence)mtsPrivateKey.getObjectAt(19);
+ ASN1Sequence seqOfnRSStats;
+ ASN1Sequence seqOfnRSStrings;
+ ASN1Sequence seqOfnRSInts;
+ ASN1Sequence seqOfnRSBytes;
+
+ GMSSRootSig[] nextRootSig = new GMSSRootSig[seqOfnextRootSigs.size()];
+
+ for (int i = 0; i < nextRootSig.length; i++)
+ {
+ seqOfnRSStats = (ASN1Sequence)seqOfnextRootSigs.getObjectAt(i);
+ // nextNextAuth[i]= new byte[nextNextAuthPart1.size()][];
+ seqOfnRSStrings = (ASN1Sequence)seqOfnRSStats.getObjectAt(0);
+ seqOfnRSBytes = (ASN1Sequence)seqOfnRSStats.getObjectAt(1);
+ seqOfnRSInts = (ASN1Sequence)seqOfnRSStats.getObjectAt(2);
+
+ String[] name = new String[2];
+ name[0] = ((DERIA5String)seqOfnRSStrings.getObjectAt(0)).getString();
+ name[1] = ((DERIA5String)seqOfnRSStrings.getObjectAt(1)).getString();
+ byte[][] statByte = new byte[5][];
+ statByte[0] = ((DEROctetString)seqOfnRSBytes.getObjectAt(0))
+ .getOctets();
+ statByte[1] = ((DEROctetString)seqOfnRSBytes.getObjectAt(1))
+ .getOctets();
+ statByte[2] = ((DEROctetString)seqOfnRSBytes.getObjectAt(2))
+ .getOctets();
+ statByte[3] = ((DEROctetString)seqOfnRSBytes.getObjectAt(3))
+ .getOctets();
+ statByte[4] = ((DEROctetString)seqOfnRSBytes.getObjectAt(4))
+ .getOctets();
+ int[] statInt = new int[9];
+ statInt[0] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(0));
+ statInt[1] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(1));
+ statInt[2] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(2));
+ statInt[3] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(3));
+ statInt[4] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(4));
+ statInt[5] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(5));
+ statInt[6] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(6));
+ statInt[7] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(7));
+ statInt[8] = checkBigIntegerInIntRange(seqOfnRSInts.getObjectAt(8));
+ nextRootSig[i] = new GMSSRootSig(DigestFactory.getDigest(name[0]).getClass(), statByte, statInt);
+ }
+
+ // --- Decode <name>.
+
+ // TODO: Really check, why there are multiple algorithms, we only
+ // use the first one!!!
+ ASN1Sequence namePart = (ASN1Sequence)mtsPrivateKey.getObjectAt(20);
+ String[] name = new String[namePart.size()];
+ for (int i = 0; i < name.length; i++)
+ {
+ name[i] = ((DERIA5String)namePart.getObjectAt(i)).getString();
+ }
+ */
+ }
+
+ public GMSSPrivateKey(int[] index, byte[][] currentSeed,
+ byte[][] nextNextSeed, byte[][][] currentAuthPath,
+ byte[][][] nextAuthPath, Treehash[][] currentTreehash,
+ Treehash[][] nextTreehash, Vector[] currentStack,
+ Vector[] nextStack, Vector[][] currentRetain,
+ Vector[][] nextRetain, byte[][][] keep, GMSSLeaf[] nextNextLeaf,
+ GMSSLeaf[] upperLeaf, GMSSLeaf[] upperTreehashLeaf,
+ int[] minTreehash, byte[][] nextRoot, GMSSRootCalc[] nextNextRoot,
+ byte[][] currentRootSig, GMSSRootSig[] nextRootSig,
+ GMSSParameters gmssParameterset, AlgorithmIdentifier digestAlg)
+ {
+ AlgorithmIdentifier[] names = new AlgorithmIdentifier[] { digestAlg };
+ this.primitive = encode(index, currentSeed, nextNextSeed, currentAuthPath, nextAuthPath, keep, currentTreehash, nextTreehash, currentStack, nextStack, currentRetain, nextRetain, nextNextLeaf, upperLeaf, upperTreehashLeaf, minTreehash, nextRoot, nextNextRoot, currentRootSig, nextRootSig, gmssParameterset, names);
+ }
+
+
+ // TODO: change method signature to something more integrated into BouncyCastle
+
+ /**
+ * @param index tree indices
+ * @param currentSeeds seed for the generation of private OTS keys for the
+ * current subtrees (TREE)
+ * @param nextNextSeeds seed for the generation of private OTS keys for the
+ * subtrees after next (TREE++)
+ * @param currentAuthPaths array of current authentication paths (AUTHPATH)
+ * @param nextAuthPaths array of next authentication paths (AUTHPATH+)
+ * @param keep keep array for the authPath algorithm
+ * @param currentTreehash treehash for authPath algorithm of current tree
+ * @param nextTreehash treehash for authPath algorithm of next tree (TREE+)
+ * @param currentStack shared stack for authPath algorithm of current tree
+ * @param nextStack shared stack for authPath algorithm of next tree (TREE+)
+ * @param currentRetain retain stack for authPath algorithm of current tree
+ * @param nextRetain retain stack for authPath algorithm of next tree (TREE+)
+ * @param nextNextLeaf array of upcoming leafs of the tree after next (LEAF++) of
+ * each layer
+ * @param upperLeaf needed for precomputation of upper nodes
+ * @param upperTreehashLeaf needed for precomputation of upper treehash nodes
+ * @param minTreehash index of next treehash instance to receive an update
+ * @param nextRoot the roots of the next trees (ROOT+)
+ * @param nextNextRoot the roots of the tree after next (ROOT++)
+ * @param currentRootSig array of signatures of the roots of the current subtrees
+ * (SIG)
+ * @param nextRootSig array of signatures of the roots of the next subtree
+ * (SIG+)
+ * @param gmssParameterset the GMSS Parameterset
+ * @param algorithms An array of algorithm identifiers, containing the hash function details
+ */
+ private ASN1Primitive encode(int[] index, byte[][] currentSeeds,
+ byte[][] nextNextSeeds, byte[][][] currentAuthPaths,
+ byte[][][] nextAuthPaths, byte[][][] keep,
+ Treehash[][] currentTreehash, Treehash[][] nextTreehash,
+ Vector[] currentStack, Vector[] nextStack,
+ Vector[][] currentRetain, Vector[][] nextRetain,
+ GMSSLeaf[] nextNextLeaf, GMSSLeaf[] upperLeaf,
+ GMSSLeaf[] upperTreehashLeaf, int[] minTreehash, byte[][] nextRoot,
+ GMSSRootCalc[] nextNextRoot, byte[][] currentRootSig,
+ GMSSRootSig[] nextRootSig, GMSSParameters gmssParameterset,
+ AlgorithmIdentifier[] algorithms)
+ {
+
+ ASN1EncodableVector result = new ASN1EncodableVector();
+
+ // --- Encode <index>.
+ ASN1EncodableVector indexPart = new ASN1EncodableVector();
+ for (int i = 0; i < index.length; i++)
+ {
+ indexPart.add(new ASN1Integer(index[i]));
+ }
+ result.add(new DERSequence(indexPart));
+
+ // --- Encode <curSeeds>.
+ ASN1EncodableVector curSeedsPart = new ASN1EncodableVector();
+ for (int i = 0; i < currentSeeds.length; i++)
+ {
+ curSeedsPart.add(new DEROctetString(currentSeeds[i]));
+ }
+ result.add(new DERSequence(curSeedsPart));
+
+ // --- Encode <nextNextSeeds>.
+ ASN1EncodableVector nextNextSeedsPart = new ASN1EncodableVector();
+ for (int i = 0; i < nextNextSeeds.length; i++)
+ {
+ nextNextSeedsPart.add(new DEROctetString(nextNextSeeds[i]));
+ }
+ result.add(new DERSequence(nextNextSeedsPart));
+
+ // --- Encode <curAuth>.
+ ASN1EncodableVector curAuthPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector curAuthPart1 = new ASN1EncodableVector();
+ for (int i = 0; i < currentAuthPaths.length; i++)
+ {
+ for (int j = 0; j < currentAuthPaths[i].length; j++)
+ {
+ curAuthPart0.add(new DEROctetString(currentAuthPaths[i][j]));
+ }
+ curAuthPart1.add(new DERSequence(curAuthPart0));
+ curAuthPart0 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(curAuthPart1));
+
+ // --- Encode <nextAuth>.
+ ASN1EncodableVector nextAuthPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector nextAuthPart1 = new ASN1EncodableVector();
+ for (int i = 0; i < nextAuthPaths.length; i++)
+ {
+ for (int j = 0; j < nextAuthPaths[i].length; j++)
+ {
+ nextAuthPart0.add(new DEROctetString(nextAuthPaths[i][j]));
+ }
+ nextAuthPart1.add(new DERSequence(nextAuthPart0));
+ nextAuthPart0 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(nextAuthPart1));
+
+ // --- Encode <curTreehash>.
+ ASN1EncodableVector seqOfTreehash0 = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfTreehash1 = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfStat = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfByte = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfInt = new ASN1EncodableVector();
+
+ for (int i = 0; i < currentTreehash.length; i++)
+ {
+ for (int j = 0; j < currentTreehash[i].length; j++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ int tailLength = currentTreehash[i][j].getStatInt()[1];
+
+ seqOfByte.add(new DEROctetString(currentTreehash[i][j]
+ .getStatByte()[0]));
+ seqOfByte.add(new DEROctetString(currentTreehash[i][j]
+ .getStatByte()[1]));
+ seqOfByte.add(new DEROctetString(currentTreehash[i][j]
+ .getStatByte()[2]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfByte.add(new DEROctetString(currentTreehash[i][j]
+ .getStatByte()[3 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ seqOfInt.add(new ASN1Integer(
+ currentTreehash[i][j].getStatInt()[0]));
+ seqOfInt.add(new ASN1Integer(tailLength));
+ seqOfInt.add(new ASN1Integer(
+ currentTreehash[i][j].getStatInt()[2]));
+ seqOfInt.add(new ASN1Integer(
+ currentTreehash[i][j].getStatInt()[3]));
+ seqOfInt.add(new ASN1Integer(
+ currentTreehash[i][j].getStatInt()[4]));
+ seqOfInt.add(new ASN1Integer(
+ currentTreehash[i][j].getStatInt()[5]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfInt.add(new ASN1Integer(currentTreehash[i][j]
+ .getStatInt()[6 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfTreehash1.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ seqOfTreehash0.add(new DERSequence(seqOfTreehash1));
+ seqOfTreehash1 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfTreehash0));
+
+ // --- Encode <nextTreehash>.
+ seqOfTreehash0 = new ASN1EncodableVector();
+ seqOfTreehash1 = new ASN1EncodableVector();
+ seqOfStat = new ASN1EncodableVector();
+ seqOfByte = new ASN1EncodableVector();
+ seqOfInt = new ASN1EncodableVector();
+
+ for (int i = 0; i < nextTreehash.length; i++)
+ {
+ for (int j = 0; j < nextTreehash[i].length; j++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ int tailLength = nextTreehash[i][j].getStatInt()[1];
+
+ seqOfByte.add(new DEROctetString(nextTreehash[i][j]
+ .getStatByte()[0]));
+ seqOfByte.add(new DEROctetString(nextTreehash[i][j]
+ .getStatByte()[1]));
+ seqOfByte.add(new DEROctetString(nextTreehash[i][j]
+ .getStatByte()[2]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfByte.add(new DEROctetString(nextTreehash[i][j]
+ .getStatByte()[3 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ seqOfInt
+ .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[0]));
+ seqOfInt.add(new ASN1Integer(tailLength));
+ seqOfInt
+ .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[2]));
+ seqOfInt
+ .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[3]));
+ seqOfInt
+ .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[4]));
+ seqOfInt
+ .add(new ASN1Integer(nextTreehash[i][j].getStatInt()[5]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfInt.add(new ASN1Integer(nextTreehash[i][j]
+ .getStatInt()[6 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfTreehash1.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ seqOfTreehash0.add(new DERSequence(new DERSequence(seqOfTreehash1)));
+ seqOfTreehash1 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfTreehash0));
+
+ // --- Encode <keep>.
+ ASN1EncodableVector keepPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector keepPart1 = new ASN1EncodableVector();
+ for (int i = 0; i < keep.length; i++)
+ {
+ for (int j = 0; j < keep[i].length; j++)
+ {
+ keepPart0.add(new DEROctetString(keep[i][j]));
+ }
+ keepPart1.add(new DERSequence(keepPart0));
+ keepPart0 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(keepPart1));
+
+ // --- Encode <curStack>.
+ ASN1EncodableVector curStackPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector curStackPart1 = new ASN1EncodableVector();
+ for (int i = 0; i < currentStack.length; i++)
+ {
+ for (int j = 0; j < currentStack[i].size(); j++)
+ {
+ curStackPart0.add(new DEROctetString((byte[])currentStack[i]
+ .elementAt(j)));
+ }
+ curStackPart1.add(new DERSequence(curStackPart0));
+ curStackPart0 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(curStackPart1));
+
+ // --- Encode <nextStack>.
+ ASN1EncodableVector nextStackPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector nextStackPart1 = new ASN1EncodableVector();
+ for (int i = 0; i < nextStack.length; i++)
+ {
+ for (int j = 0; j < nextStack[i].size(); j++)
+ {
+ nextStackPart0.add(new DEROctetString((byte[])nextStack[i]
+ .elementAt(j)));
+ }
+ nextStackPart1.add(new DERSequence(nextStackPart0));
+ nextStackPart0 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(nextStackPart1));
+
+ // --- Encode <curRetain>.
+ ASN1EncodableVector currentRetainPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector currentRetainPart1 = new ASN1EncodableVector();
+ ASN1EncodableVector currentRetainPart2 = new ASN1EncodableVector();
+ for (int i = 0; i < currentRetain.length; i++)
+ {
+ for (int j = 0; j < currentRetain[i].length; j++)
+ {
+ for (int k = 0; k < currentRetain[i][j].size(); k++)
+ {
+ currentRetainPart0.add(new DEROctetString(
+ (byte[])currentRetain[i][j].elementAt(k)));
+ }
+ currentRetainPart1.add(new DERSequence(currentRetainPart0));
+ currentRetainPart0 = new ASN1EncodableVector();
+ }
+ currentRetainPart2.add(new DERSequence(currentRetainPart1));
+ currentRetainPart1 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(currentRetainPart2));
+
+ // --- Encode <nextRetain>.
+ ASN1EncodableVector nextRetainPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector nextRetainPart1 = new ASN1EncodableVector();
+ ASN1EncodableVector nextRetainPart2 = new ASN1EncodableVector();
+ for (int i = 0; i < nextRetain.length; i++)
+ {
+ for (int j = 0; j < nextRetain[i].length; j++)
+ {
+ for (int k = 0; k < nextRetain[i][j].size(); k++)
+ {
+ nextRetainPart0.add(new DEROctetString(
+ (byte[])nextRetain[i][j].elementAt(k)));
+ }
+ nextRetainPart1.add(new DERSequence(nextRetainPart0));
+ nextRetainPart0 = new ASN1EncodableVector();
+ }
+ nextRetainPart2.add(new DERSequence(nextRetainPart1));
+ nextRetainPart1 = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(nextRetainPart2));
+
+ // --- Encode <nextNextLeaf>.
+ ASN1EncodableVector seqOfLeaf = new ASN1EncodableVector();
+ seqOfStat = new ASN1EncodableVector();
+ seqOfByte = new ASN1EncodableVector();
+ seqOfInt = new ASN1EncodableVector();
+
+ for (int i = 0; i < nextNextLeaf.length; i++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ byte[][] tempByte = nextNextLeaf[i].getStatByte();
+ seqOfByte.add(new DEROctetString(tempByte[0]));
+ seqOfByte.add(new DEROctetString(tempByte[1]));
+ seqOfByte.add(new DEROctetString(tempByte[2]));
+ seqOfByte.add(new DEROctetString(tempByte[3]));
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ int[] tempInt = nextNextLeaf[i].getStatInt();
+ seqOfInt.add(new ASN1Integer(tempInt[0]));
+ seqOfInt.add(new ASN1Integer(tempInt[1]));
+ seqOfInt.add(new ASN1Integer(tempInt[2]));
+ seqOfInt.add(new ASN1Integer(tempInt[3]));
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfLeaf.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfLeaf));
+
+ // --- Encode <upperLEAF>.
+ ASN1EncodableVector seqOfUpperLeaf = new ASN1EncodableVector();
+ seqOfStat = new ASN1EncodableVector();
+ seqOfByte = new ASN1EncodableVector();
+ seqOfInt = new ASN1EncodableVector();
+
+ for (int i = 0; i < upperLeaf.length; i++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ byte[][] tempByte = upperLeaf[i].getStatByte();
+ seqOfByte.add(new DEROctetString(tempByte[0]));
+ seqOfByte.add(new DEROctetString(tempByte[1]));
+ seqOfByte.add(new DEROctetString(tempByte[2]));
+ seqOfByte.add(new DEROctetString(tempByte[3]));
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ int[] tempInt = upperLeaf[i].getStatInt();
+ seqOfInt.add(new ASN1Integer(tempInt[0]));
+ seqOfInt.add(new ASN1Integer(tempInt[1]));
+ seqOfInt.add(new ASN1Integer(tempInt[2]));
+ seqOfInt.add(new ASN1Integer(tempInt[3]));
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfUpperLeaf.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfUpperLeaf));
+
+ // encode <upperTreehashLeaf>
+ ASN1EncodableVector seqOfUpperTreehashLeaf = new ASN1EncodableVector();
+ seqOfStat = new ASN1EncodableVector();
+ seqOfByte = new ASN1EncodableVector();
+ seqOfInt = new ASN1EncodableVector();
+
+ for (int i = 0; i < upperTreehashLeaf.length; i++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ byte[][] tempByte = upperTreehashLeaf[i].getStatByte();
+ seqOfByte.add(new DEROctetString(tempByte[0]));
+ seqOfByte.add(new DEROctetString(tempByte[1]));
+ seqOfByte.add(new DEROctetString(tempByte[2]));
+ seqOfByte.add(new DEROctetString(tempByte[3]));
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ int[] tempInt = upperTreehashLeaf[i].getStatInt();
+ seqOfInt.add(new ASN1Integer(tempInt[0]));
+ seqOfInt.add(new ASN1Integer(tempInt[1]));
+ seqOfInt.add(new ASN1Integer(tempInt[2]));
+ seqOfInt.add(new ASN1Integer(tempInt[3]));
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfUpperTreehashLeaf.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfUpperTreehashLeaf));
+
+ // --- Encode <minTreehash>.
+ ASN1EncodableVector minTreehashPart = new ASN1EncodableVector();
+ for (int i = 0; i < minTreehash.length; i++)
+ {
+ minTreehashPart.add(new ASN1Integer(minTreehash[i]));
+ }
+ result.add(new DERSequence(minTreehashPart));
+
+ // --- Encode <nextRoot>.
+ ASN1EncodableVector nextRootPart = new ASN1EncodableVector();
+ for (int i = 0; i < nextRoot.length; i++)
+ {
+ nextRootPart.add(new DEROctetString(nextRoot[i]));
+ }
+ result.add(new DERSequence(nextRootPart));
+
+ // --- Encode <nextNextRoot>.
+ ASN1EncodableVector seqOfnextNextRoot = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRStats = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRStrings = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRBytes = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRInts = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRTreehash = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnnRRetain = new ASN1EncodableVector();
+
+ for (int i = 0; i < nextNextRoot.length; i++)
+ {
+ seqOfnnRStats.add(new DERSequence(algorithms[0]));
+ seqOfnnRStrings = new ASN1EncodableVector();
+
+ int heightOfTree = nextNextRoot[i].getStatInt()[0];
+ int tailLength = nextNextRoot[i].getStatInt()[7];
+
+ seqOfnnRBytes.add(new DEROctetString(
+ nextNextRoot[i].getStatByte()[0]));
+ for (int j = 0; j < heightOfTree; j++)
+ {
+ seqOfnnRBytes.add(new DEROctetString(nextNextRoot[i]
+ .getStatByte()[1 + j]));
+ }
+ for (int j = 0; j < tailLength; j++)
+ {
+ seqOfnnRBytes.add(new DEROctetString(nextNextRoot[i]
+ .getStatByte()[1 + heightOfTree + j]));
+ }
+
+ seqOfnnRStats.add(new DERSequence(seqOfnnRBytes));
+ seqOfnnRBytes = new ASN1EncodableVector();
+
+ seqOfnnRInts.add(new ASN1Integer(heightOfTree));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[1]));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[2]));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[3]));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[4]));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[5]));
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[6]));
+ seqOfnnRInts.add(new ASN1Integer(tailLength));
+ for (int j = 0; j < heightOfTree; j++)
+ {
+ seqOfnnRInts.add(new ASN1Integer(
+ nextNextRoot[i].getStatInt()[8 + j]));
+ }
+ for (int j = 0; j < tailLength; j++)
+ {
+ seqOfnnRInts.add(new ASN1Integer(nextNextRoot[i].getStatInt()[8
+ + heightOfTree + j]));
+ }
+
+ seqOfnnRStats.add(new DERSequence(seqOfnnRInts));
+ seqOfnnRInts = new ASN1EncodableVector();
+
+ // add treehash of nextNextRoot object
+ // ----------------------------
+ seqOfStat = new ASN1EncodableVector();
+ seqOfByte = new ASN1EncodableVector();
+ seqOfInt = new ASN1EncodableVector();
+
+ if (nextNextRoot[i].getTreehash() != null)
+ {
+ for (int j = 0; j < nextNextRoot[i].getTreehash().length; j++)
+ {
+ seqOfStat.add(new DERSequence(algorithms[0]));
+
+ tailLength = nextNextRoot[i].getTreehash()[j].getStatInt()[1];
+
+ seqOfByte.add(new DEROctetString(nextNextRoot[i]
+ .getTreehash()[j].getStatByte()[0]));
+ seqOfByte.add(new DEROctetString(nextNextRoot[i]
+ .getTreehash()[j].getStatByte()[1]));
+ seqOfByte.add(new DEROctetString(nextNextRoot[i]
+ .getTreehash()[j].getStatByte()[2]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfByte.add(new DEROctetString(nextNextRoot[i]
+ .getTreehash()[j].getStatByte()[3 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfByte));
+ seqOfByte = new ASN1EncodableVector();
+
+ seqOfInt.add(new ASN1Integer(
+ nextNextRoot[i].getTreehash()[j].getStatInt()[0]));
+ seqOfInt.add(new ASN1Integer(tailLength));
+ seqOfInt.add(new ASN1Integer(
+ nextNextRoot[i].getTreehash()[j].getStatInt()[2]));
+ seqOfInt.add(new ASN1Integer(
+ nextNextRoot[i].getTreehash()[j].getStatInt()[3]));
+ seqOfInt.add(new ASN1Integer(
+ nextNextRoot[i].getTreehash()[j].getStatInt()[4]));
+ seqOfInt.add(new ASN1Integer(
+ nextNextRoot[i].getTreehash()[j].getStatInt()[5]));
+ for (int k = 0; k < tailLength; k++)
+ {
+ seqOfInt.add(new ASN1Integer(nextNextRoot[i]
+ .getTreehash()[j].getStatInt()[6 + k]));
+ }
+ seqOfStat.add(new DERSequence(seqOfInt));
+ seqOfInt = new ASN1EncodableVector();
+
+ seqOfnnRTreehash.add(new DERSequence(seqOfStat));
+ seqOfStat = new ASN1EncodableVector();
+ }
+ }
+ // ----------------------------
+ seqOfnnRStats.add(new DERSequence(seqOfnnRTreehash));
+ seqOfnnRTreehash = new ASN1EncodableVector();
+
+ // encode retain of nextNextRoot
+ // ----------------------------
+ // --- Encode <curRetain>.
+ currentRetainPart0 = new ASN1EncodableVector();
+ if (nextNextRoot[i].getRetain() != null)
+ {
+ for (int j = 0; j < nextNextRoot[i].getRetain().length; j++)
+ {
+ for (int k = 0; k < nextNextRoot[i].getRetain()[j].size(); k++)
+ {
+ currentRetainPart0.add(new DEROctetString(
+ (byte[])nextNextRoot[i].getRetain()[j]
+ .elementAt(k)));
+ }
+ seqOfnnRRetain.add(new DERSequence(currentRetainPart0));
+ currentRetainPart0 = new ASN1EncodableVector();
+ }
+ }
+ // ----------------------------
+ seqOfnnRStats.add(new DERSequence(seqOfnnRRetain));
+ seqOfnnRRetain = new ASN1EncodableVector();
+
+ seqOfnextNextRoot.add(new DERSequence(seqOfnnRStats));
+ seqOfnnRStats = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfnextNextRoot));
+
+ // --- Encode <curRootSig>.
+ ASN1EncodableVector curRootSigPart = new ASN1EncodableVector();
+ for (int i = 0; i < currentRootSig.length; i++)
+ {
+ curRootSigPart.add(new DEROctetString(currentRootSig[i]));
+ }
+ result.add(new DERSequence(curRootSigPart));
+
+ // --- Encode <nextRootSig>.
+ ASN1EncodableVector seqOfnextRootSigs = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnRSStats = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnRSStrings = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnRSBytes = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfnRSInts = new ASN1EncodableVector();
+
+ for (int i = 0; i < nextRootSig.length; i++)
+ {
+ seqOfnRSStats.add(new DERSequence(algorithms[0]));
+ seqOfnRSStrings = new ASN1EncodableVector();
+
+ seqOfnRSBytes.add(new DEROctetString(
+ nextRootSig[i].getStatByte()[0]));
+ seqOfnRSBytes.add(new DEROctetString(
+ nextRootSig[i].getStatByte()[1]));
+ seqOfnRSBytes.add(new DEROctetString(
+ nextRootSig[i].getStatByte()[2]));
+ seqOfnRSBytes.add(new DEROctetString(
+ nextRootSig[i].getStatByte()[3]));
+ seqOfnRSBytes.add(new DEROctetString(
+ nextRootSig[i].getStatByte()[4]));
+
+ seqOfnRSStats.add(new DERSequence(seqOfnRSBytes));
+ seqOfnRSBytes = new ASN1EncodableVector();
+
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[0]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[1]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[2]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[3]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[4]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[5]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[6]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[7]));
+ seqOfnRSInts.add(new ASN1Integer(nextRootSig[i].getStatInt()[8]));
+
+ seqOfnRSStats.add(new DERSequence(seqOfnRSInts));
+ seqOfnRSInts = new ASN1EncodableVector();
+
+ seqOfnextRootSigs.add(new DERSequence(seqOfnRSStats));
+ seqOfnRSStats = new ASN1EncodableVector();
+ }
+ result.add(new DERSequence(seqOfnextRootSigs));
+
+ // --- Encode <parameterset>.
+ ASN1EncodableVector parSetPart0 = new ASN1EncodableVector();
+ ASN1EncodableVector parSetPart1 = new ASN1EncodableVector();
+ ASN1EncodableVector parSetPart2 = new ASN1EncodableVector();
+ ASN1EncodableVector parSetPart3 = new ASN1EncodableVector();
+
+ for (int i = 0; i < gmssParameterset.getHeightOfTrees().length; i++)
+ {
+ parSetPart1.add(new ASN1Integer(
+ gmssParameterset.getHeightOfTrees()[i]));
+ parSetPart2.add(new ASN1Integer(gmssParameterset
+ .getWinternitzParameter()[i]));
+ parSetPart3.add(new ASN1Integer(gmssParameterset.getK()[i]));
+ }
+ parSetPart0.add(new ASN1Integer(gmssParameterset.getNumOfLayers()));
+ parSetPart0.add(new DERSequence(parSetPart1));
+ parSetPart0.add(new DERSequence(parSetPart2));
+ parSetPart0.add(new DERSequence(parSetPart3));
+ result.add(new DERSequence(parSetPart0));
+
+ // --- Encode <names>.
+ ASN1EncodableVector namesPart = new ASN1EncodableVector();
+
+ for (int i = 0; i < algorithms.length; i++)
+ {
+ namesPart.add(algorithms[i]);
+ }
+
+ result.add(new DERSequence(namesPart));
+ return new DERSequence(result);
+
+ }
+
+ private static int checkBigIntegerInIntRange(ASN1Encodable a)
+ {
+ BigInteger b = ((ASN1Integer)a).getValue();
+ if ((b.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) ||
+ (b.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0))
+ {
+ throw new IllegalArgumentException("BigInteger not in Range: " + b.toString());
+ }
+ return b.intValue();
+ }
+
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return this.primitive;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/asn1/GMSSPublicKey.java b/core/src/main/java/org/spongycastle/pqc/asn1/GMSSPublicKey.java
new file mode 100644
index 00000000..77992283
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/asn1/GMSSPublicKey.java
@@ -0,0 +1,74 @@
+package org.spongycastle.pqc.asn1;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.util.Arrays;
+
+/**
+ * This class implements an ASN.1 encoded GMSS public key. The ASN.1 definition
+ * of this structure is:
+ * <pre>
+ * GMSSPublicKey ::= SEQUENCE{
+ * version INTEGER
+ * publicKey OCTET STRING
+ * }
+ * </pre>
+ */
+public class GMSSPublicKey
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private byte[] publicKey;
+
+ private GMSSPublicKey(ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("size of seq = " + seq.size());
+ }
+
+ this.version = ASN1Integer.getInstance(seq.getObjectAt(0));
+ this.publicKey = ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets();
+ }
+
+ public GMSSPublicKey(byte[] publicKeyBytes)
+ {
+ this.version = new ASN1Integer(0);
+ this.publicKey = publicKeyBytes;
+ }
+
+ public static GMSSPublicKey getInstance(Object o)
+ {
+ if (o instanceof GMSSPublicKey)
+ {
+ return (GMSSPublicKey)o;
+ }
+ else if (o != null)
+ {
+ return new GMSSPublicKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public byte[] getPublicKey()
+ {
+ return Arrays.clone(publicKey);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(new DEROctetString(publicKey));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PrivateKey.java b/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PrivateKey.java
new file mode 100644
index 00000000..5f6a8b9d
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PrivateKey.java
@@ -0,0 +1,173 @@
+package org.spongycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+
+public class McElieceCCA2PrivateKey
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier oid;
+ private int n;
+ private int k;
+ private byte[] encField;
+ private byte[] encGp;
+ private byte[] encP;
+ private byte[] encH;
+ private byte[][] encqInv;
+
+
+ public McElieceCCA2PrivateKey(ASN1ObjectIdentifier oid, int n, int k, GF2mField field, PolynomialGF2mSmallM goppaPoly, Permutation p, GF2Matrix h, PolynomialGF2mSmallM[] qInv)
+ {
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ this.encField = field.getEncoded();
+ this.encGp = goppaPoly.getEncoded();
+ this.encP = p.getEncoded();
+ this.encH = h.getEncoded();
+ this.encqInv = new byte[qInv.length][];
+
+ for (int i = 0; i != qInv.length; i++)
+ {
+ encqInv[i] = qInv[i].getEncoded();
+ }
+ }
+
+ private McElieceCCA2PrivateKey(ASN1Sequence seq)
+ {
+ oid = ((ASN1ObjectIdentifier)seq.getObjectAt(0));
+
+ BigInteger bigN = ((ASN1Integer)seq.getObjectAt(1)).getValue();
+ n = bigN.intValue();
+
+ BigInteger bigK = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+ k = bigK.intValue();
+
+ encField = ((ASN1OctetString)seq.getObjectAt(3)).getOctets();
+
+ encGp = ((ASN1OctetString)seq.getObjectAt(4)).getOctets();
+
+ encP = ((ASN1OctetString)seq.getObjectAt(5)).getOctets();
+
+ encH = ((ASN1OctetString)seq.getObjectAt(6)).getOctets();
+
+ ASN1Sequence asnQInv = (ASN1Sequence)seq.getObjectAt(7);
+ encqInv = new byte[asnQInv.size()][];
+ for (int i = 0; i < asnQInv.size(); i++)
+ {
+ encqInv[i] = ((ASN1OctetString)asnQInv.getObjectAt(i)).getOctets();
+ }
+ }
+
+ public ASN1ObjectIdentifier getOID()
+ {
+ return oid;
+ }
+
+ public int getN()
+ {
+ return n;
+ }
+
+ public int getK()
+ {
+ return k;
+ }
+
+ public GF2mField getField()
+ {
+ return new GF2mField(encField);
+ }
+
+ public PolynomialGF2mSmallM getGoppaPoly()
+ {
+ return new PolynomialGF2mSmallM(this.getField(), encGp);
+ }
+
+ public Permutation getP()
+ {
+ return new Permutation(encP);
+ }
+
+ public GF2Matrix getH()
+ {
+ return new GF2Matrix(encH);
+ }
+
+ public PolynomialGF2mSmallM[] getQInv()
+ {
+ PolynomialGF2mSmallM[] qInv = new PolynomialGF2mSmallM[encqInv.length];
+ GF2mField field = this.getField();
+
+ for (int i = 0; i < encqInv.length; i++)
+ {
+ qInv[i] = new PolynomialGF2mSmallM(field, encqInv[i]);
+ }
+
+ return qInv;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ // encode <oidString>
+ v.add(oid);
+ // encode <n>
+ v.add(new ASN1Integer(n));
+
+ // encode <k>
+ v.add(new ASN1Integer(k));
+
+ // encode <field>
+ v.add(new DEROctetString(encField));
+
+ // encode <gp>
+ v.add(new DEROctetString(encGp));
+
+ // encode <p>
+ v.add(new DEROctetString(encP));
+
+ // encode <h>
+ v.add(new DEROctetString(encH));
+
+ // encode <q>
+ ASN1EncodableVector asnQInv = new ASN1EncodableVector();
+ for (int i = 0; i < encqInv.length; i++)
+ {
+ asnQInv.add(new DEROctetString(encqInv[i]));
+ }
+
+ v.add(new DERSequence(asnQInv));
+
+ return new DERSequence(v);
+ }
+
+ public static McElieceCCA2PrivateKey getInstance(Object o)
+ {
+ if (o instanceof McElieceCCA2PrivateKey)
+ {
+ return (McElieceCCA2PrivateKey)o;
+ }
+ else if (o != null)
+ {
+ return new McElieceCCA2PrivateKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PublicKey.java b/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PublicKey.java
new file mode 100644
index 00000000..186d8ca4
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/asn1/McElieceCCA2PublicKey.java
@@ -0,0 +1,96 @@
+package org.spongycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+
+public class McElieceCCA2PublicKey
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier oid;
+ private int n;
+ private int t;
+
+ private byte[] matrixG;
+
+ public McElieceCCA2PublicKey(ASN1ObjectIdentifier oid, int n, int t, GF2Matrix g)
+ {
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = g.getEncoded();
+ }
+
+ private McElieceCCA2PublicKey(ASN1Sequence seq)
+ {
+ oid = ((ASN1ObjectIdentifier)seq.getObjectAt(0));
+ BigInteger bigN = ((ASN1Integer)seq.getObjectAt(1)).getValue();
+ n = bigN.intValue();
+
+ BigInteger bigT = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+ t = bigT.intValue();
+
+ matrixG = ((ASN1OctetString)seq.getObjectAt(3)).getOctets();
+ }
+
+ public ASN1ObjectIdentifier getOID()
+ {
+ return oid;
+ }
+
+ public int getN()
+ {
+ return n;
+ }
+
+ public int getT()
+ {
+ return t;
+ }
+
+ public GF2Matrix getG()
+ {
+ return new GF2Matrix(matrixG);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ // encode <oidString>
+ v.add(oid);
+
+ // encode <n>
+ v.add(new ASN1Integer(n));
+
+ // encode <t>
+ v.add(new ASN1Integer(t));
+
+ // encode <matrixG>
+ v.add(new DEROctetString(matrixG));
+
+ return new DERSequence(v);
+ }
+
+ public static McElieceCCA2PublicKey getInstance(Object o)
+ {
+ if (o instanceof McElieceCCA2PublicKey)
+ {
+ return (McElieceCCA2PublicKey)o;
+ }
+ else if (o != null)
+ {
+ return new McElieceCCA2PublicKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/asn1/McEliecePrivateKey.java b/core/src/main/java/org/spongycastle/pqc/asn1/McEliecePrivateKey.java
new file mode 100644
index 00000000..e0ba1ed7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/asn1/McEliecePrivateKey.java
@@ -0,0 +1,197 @@
+package org.spongycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+
+public class McEliecePrivateKey
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier oid;
+ private int n;
+ private int k;
+ private byte[] encField;
+ private byte[] encGp;
+ private byte[] encSInv;
+ private byte[] encP1;
+ private byte[] encP2;
+ private byte[] encH;
+ private byte[][] encqInv;
+
+
+ public McEliecePrivateKey(ASN1ObjectIdentifier oid, int n, int k, GF2mField field, PolynomialGF2mSmallM goppaPoly, GF2Matrix sInv, Permutation p1, Permutation p2, GF2Matrix h, PolynomialGF2mSmallM[] qInv)
+ {
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ this.encField = field.getEncoded();
+ this.encGp = goppaPoly.getEncoded();
+ this.encSInv = sInv.getEncoded();
+ this.encP1 = p1.getEncoded();
+ this.encP2 = p2.getEncoded();
+ this.encH = h.getEncoded();
+ this.encqInv = new byte[qInv.length][];
+
+ for (int i = 0; i != qInv.length; i++)
+ {
+ encqInv[i] = qInv[i].getEncoded();
+ }
+ }
+
+ public static McEliecePrivateKey getInstance(Object o)
+ {
+ if (o instanceof McEliecePrivateKey)
+ {
+ return (McEliecePrivateKey)o;
+ }
+ else if (o != null)
+ {
+ return new McEliecePrivateKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private McEliecePrivateKey(ASN1Sequence seq)
+ {
+ // <oidString>
+ oid = ((ASN1ObjectIdentifier)seq.getObjectAt(0));
+
+ BigInteger bigN = ((ASN1Integer)seq.getObjectAt(1)).getValue();
+ n = bigN.intValue();
+
+ BigInteger bigK = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+ k = bigK.intValue();
+
+ encField = ((ASN1OctetString)seq.getObjectAt(3)).getOctets();
+
+ encGp = ((ASN1OctetString)seq.getObjectAt(4)).getOctets();
+
+ encSInv = ((ASN1OctetString)seq.getObjectAt(5)).getOctets();
+
+ encP1 = ((ASN1OctetString)seq.getObjectAt(6)).getOctets();
+
+ encP2 = ((ASN1OctetString)seq.getObjectAt(7)).getOctets();
+
+ encH = ((ASN1OctetString)seq.getObjectAt(8)).getOctets();
+
+ ASN1Sequence asnQInv = (ASN1Sequence)seq.getObjectAt(9);
+ encqInv = new byte[asnQInv.size()][];
+ for (int i = 0; i < asnQInv.size(); i++)
+ {
+ encqInv[i] = ((ASN1OctetString)asnQInv.getObjectAt(i)).getOctets();
+ }
+ }
+
+ public ASN1ObjectIdentifier getOID()
+ {
+ return oid;
+ }
+
+ public int getN()
+ {
+ return n;
+ }
+
+ public int getK()
+ {
+ return k;
+ }
+
+ public GF2mField getField()
+ {
+ return new GF2mField(encField);
+ }
+
+ public PolynomialGF2mSmallM getGoppaPoly()
+ {
+ return new PolynomialGF2mSmallM(this.getField(), encGp);
+ }
+
+ public GF2Matrix getSInv()
+ {
+ return new GF2Matrix(encSInv);
+ }
+
+ public Permutation getP1()
+ {
+ return new Permutation(encP1);
+ }
+
+ public Permutation getP2()
+ {
+ return new Permutation(encP2);
+ }
+
+ public GF2Matrix getH()
+ {
+ return new GF2Matrix(encH);
+ }
+
+ public PolynomialGF2mSmallM[] getQInv()
+ {
+ PolynomialGF2mSmallM[] qInv = new PolynomialGF2mSmallM[encqInv.length];
+ GF2mField field = this.getField();
+
+ for (int i = 0; i < encqInv.length; i++)
+ {
+ qInv[i] = new PolynomialGF2mSmallM(field, encqInv[i]);
+ }
+
+ return qInv;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ // encode <oidString>
+ v.add(oid);
+ // encode <n>
+ v.add(new ASN1Integer(n));
+
+ // encode <k>
+ v.add(new ASN1Integer(k));
+
+ // encode <fieldPoly>
+ v.add(new DEROctetString(encField));
+
+ // encode <goppaPoly>
+ v.add(new DEROctetString(encGp));
+
+ // encode <sInv>
+ v.add(new DEROctetString(encSInv));
+
+ // encode <p1>
+ v.add(new DEROctetString(encP1));
+
+ // encode <p2>
+ v.add(new DEROctetString(encP2));
+
+ // encode <h>
+ v.add(new DEROctetString(encH));
+
+ // encode <q>
+ ASN1EncodableVector asnQInv = new ASN1EncodableVector();
+ for (int i = 0; i < encqInv.length; i++)
+ {
+ asnQInv.add(new DEROctetString(encqInv[i]));
+ }
+
+ v.add(new DERSequence(asnQInv));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/asn1/McEliecePublicKey.java b/core/src/main/java/org/spongycastle/pqc/asn1/McEliecePublicKey.java
new file mode 100644
index 00000000..1415587d
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/asn1/McEliecePublicKey.java
@@ -0,0 +1,97 @@
+package org.spongycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+
+public class McEliecePublicKey
+ extends ASN1Object
+{
+
+ private ASN1ObjectIdentifier oid;
+ private int n;
+ private int t;
+
+ private byte[] matrixG;
+
+ public McEliecePublicKey(ASN1ObjectIdentifier oid, int n, int t, GF2Matrix g)
+ {
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = g.getEncoded();
+ }
+
+ private McEliecePublicKey(ASN1Sequence seq)
+ {
+ oid = ((ASN1ObjectIdentifier)seq.getObjectAt(0));
+ BigInteger bigN = ((ASN1Integer)seq.getObjectAt(1)).getValue();
+ n = bigN.intValue();
+
+ BigInteger bigT = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+ t = bigT.intValue();
+
+ matrixG = ((ASN1OctetString)seq.getObjectAt(3)).getOctets();
+ }
+
+ public ASN1ObjectIdentifier getOID()
+ {
+ return oid;
+ }
+
+ public int getN()
+ {
+ return n;
+ }
+
+ public int getT()
+ {
+ return t;
+ }
+
+ public GF2Matrix getG()
+ {
+ return new GF2Matrix(matrixG);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ // encode <oidString>
+ v.add(oid);
+
+ // encode <n>
+ v.add(new ASN1Integer(n));
+
+ // encode <t>
+ v.add(new ASN1Integer(t));
+
+ // encode <matrixG>
+ v.add(new DEROctetString(matrixG));
+
+ return new DERSequence(v);
+ }
+
+ public static McEliecePublicKey getInstance(Object o)
+ {
+ if (o instanceof McEliecePublicKey)
+ {
+ return (McEliecePublicKey)o;
+ }
+ else if (o != null)
+ {
+ return new McEliecePublicKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/asn1/PQCObjectIdentifiers.java b/core/src/main/java/org/spongycastle/pqc/asn1/PQCObjectIdentifiers.java
new file mode 100644
index 00000000..1a0e5ce7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/asn1/PQCObjectIdentifiers.java
@@ -0,0 +1,46 @@
+package org.spongycastle.pqc.asn1;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * PQC:
+ * <p>
+ * { iso(1) identifier-organization(3) dod(6) internet(1) private(4) 1 8301 3 1 3 5 3 ... }
+ */
+public interface PQCObjectIdentifiers
+{
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2 */
+ public static final ASN1ObjectIdentifier rainbow = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.5.3.2");
+
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.1 */
+ public static final ASN1ObjectIdentifier rainbowWithSha1 = rainbow.branch("1");
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.2 */
+ public static final ASN1ObjectIdentifier rainbowWithSha224 = rainbow.branch("2");
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.3 */
+ public static final ASN1ObjectIdentifier rainbowWithSha256 = rainbow.branch("3");
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.4 */
+ public static final ASN1ObjectIdentifier rainbowWithSha384 = rainbow.branch("4");
+ /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.5 */
+ public static final ASN1ObjectIdentifier rainbowWithSha512 = rainbow.branch("5");
+
+ /** 1.3.6.1.4.1.8301.3.1.3.3 */
+ public static final ASN1ObjectIdentifier gmss = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.3");
+
+ /** 1.3.6.1.4.1.8301.3.1.3.3.1 */
+ public static final ASN1ObjectIdentifier gmssWithSha1 = gmss.branch("1");
+ /** 1.3.6.1.4.1.8301.3.1.3.3.2 */
+ public static final ASN1ObjectIdentifier gmssWithSha224 = gmss.branch("2");
+ /** 1.3.6.1.4.1.8301.3.1.3.3.3 */
+ public static final ASN1ObjectIdentifier gmssWithSha256 = gmss.branch("3");
+ /** 1.3.6.1.4.1.8301.3.1.3.3.4 */
+ public static final ASN1ObjectIdentifier gmssWithSha384 = gmss.branch("4");
+ /** 1.3.6.1.4.1.8301.3.1.3.3.5 */
+ public static final ASN1ObjectIdentifier gmssWithSha512 = gmss.branch("5");
+
+ /** 1.3.6.1.4.1.8301.3.1.3.4.1 */
+ public static final ASN1ObjectIdentifier mcEliece = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.1");
+
+ /** 1.3.6.1.4.1.8301.3.1.3.4.2 */
+ public static final ASN1ObjectIdentifier mcElieceCca2 = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.2");
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/asn1/ParSet.java b/core/src/main/java/org/spongycastle/pqc/asn1/ParSet.java
new file mode 100644
index 00000000..d5110a2c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/asn1/ParSet.java
@@ -0,0 +1,140 @@
+package org.spongycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.util.Arrays;
+
+/**
+ * <pre>
+ * ParSet ::= SEQUENCE {
+ * T INTEGER
+ * h SEQUENCE OF INTEGER
+ * w SEQUENCE OF INTEGER
+ * K SEQUENCE OF INTEGER
+ * }
+ * </pre>
+ */
+public class ParSet
+ extends ASN1Object
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private int t;
+ private int[] h;
+ private int[] w;
+ private int[] k;
+
+ private static int checkBigIntegerInIntRangeAndPositive(BigInteger b)
+ {
+ if ((b.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) ||
+ (b.compareTo(ZERO) <= 0))
+ {
+ throw new IllegalArgumentException("BigInteger not in Range: " + b.toString());
+ }
+ return b.intValue();
+ }
+
+ private ParSet(ASN1Sequence seq)
+ {
+ if (seq.size() != 4)
+ {
+ throw new IllegalArgumentException("sie of seqOfParams = " + seq.size());
+ }
+ BigInteger asn1int = ((ASN1Integer)seq.getObjectAt(0)).getValue();
+
+ t = checkBigIntegerInIntRangeAndPositive(asn1int);
+
+ ASN1Sequence seqOfPSh = (ASN1Sequence)seq.getObjectAt(1);
+ ASN1Sequence seqOfPSw = (ASN1Sequence)seq.getObjectAt(2);
+ ASN1Sequence seqOfPSK = (ASN1Sequence)seq.getObjectAt(3);
+
+ if ((seqOfPSh.size() != t) ||
+ (seqOfPSw.size() != t) ||
+ (seqOfPSK.size() != t))
+ {
+ throw new IllegalArgumentException("invalid size of sequences");
+ }
+
+ h = new int[seqOfPSh.size()];
+ w = new int[seqOfPSw.size()];
+ k = new int[seqOfPSK.size()];
+
+ for (int i = 0; i < t; i++)
+ {
+ h[i] = checkBigIntegerInIntRangeAndPositive((((ASN1Integer)seqOfPSh.getObjectAt(i))).getValue());
+ w[i] = checkBigIntegerInIntRangeAndPositive((((ASN1Integer)seqOfPSw.getObjectAt(i))).getValue());
+ k[i] = checkBigIntegerInIntRangeAndPositive((((ASN1Integer)seqOfPSK.getObjectAt(i))).getValue());
+ }
+ }
+
+ public ParSet(int t, int[] h, int[] w, int[] k)
+ {
+ this.t = t;
+ this.h = h;
+ this.w = w;
+ this.k = k;
+ }
+
+ public static ParSet getInstance(Object o)
+ {
+ if (o instanceof ParSet)
+ {
+ return (ParSet)o;
+ }
+ else if (o != null)
+ {
+ return new ParSet(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public int getT()
+ {
+ return t;
+ }
+
+ public int[] getH()
+ {
+ return Arrays.clone(h);
+ }
+
+ public int[] getW()
+ {
+ return Arrays.clone(w);
+ }
+
+ public int[] getK()
+ {
+ return Arrays.clone(k);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seqOfPSh = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfPSw = new ASN1EncodableVector();
+ ASN1EncodableVector seqOfPSK = new ASN1EncodableVector();
+
+ for (int i = 0; i < h.length; i++)
+ {
+ seqOfPSh.add(new ASN1Integer(h[i]));
+ seqOfPSw.add(new ASN1Integer(w[i]));
+ seqOfPSK.add(new ASN1Integer(k[i]));
+ }
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(t));
+ v.add(new DERSequence(seqOfPSh));
+ v.add(new DERSequence(seqOfPSw));
+ v.add(new DERSequence(seqOfPSK));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/asn1/RainbowPrivateKey.java b/core/src/main/java/org/spongycastle/pqc/asn1/RainbowPrivateKey.java
new file mode 100644
index 00000000..96648900
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/asn1/RainbowPrivateKey.java
@@ -0,0 +1,349 @@
+package org.spongycastle.pqc.asn1;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.pqc.crypto.rainbow.Layer;
+import org.spongycastle.pqc.crypto.rainbow.util.RainbowUtil;
+
+/**
+ * Return the key data to encode in the PrivateKeyInfo structure.
+ * <p>
+ * The ASN.1 definition of the key structure is
+ * <pre>
+ * RainbowPrivateKey ::= SEQUENCE {
+ * CHOICE
+ * {
+ * oid OBJECT IDENTIFIER -- OID identifying the algorithm
+ * version INTEGER -- 0
+ * }
+ * A1inv SEQUENCE OF OCTET STRING -- inversed matrix of L1
+ * b1 OCTET STRING -- translation vector of L1
+ * A2inv SEQUENCE OF OCTET STRING -- inversed matrix of L2
+ * b2 OCTET STRING -- translation vector of L2
+ * vi OCTET STRING -- num of elmts in each Set S
+ * layers SEQUENCE OF Layer -- layers of F
+ * }
+ *
+ * Layer ::= SEQUENCE OF Poly
+ *
+ * Poly ::= SEQUENCE {
+ * alpha SEQUENCE OF OCTET STRING
+ * beta SEQUENCE OF OCTET STRING
+ * gamma OCTET STRING
+ * eta INTEGER
+ * }
+ * </pre>
+ */
+public class RainbowPrivateKey
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private ASN1ObjectIdentifier oid;
+
+ private byte[][] invA1;
+ private byte[] b1;
+ private byte[][] invA2;
+ private byte[] b2;
+ private byte[] vi;
+ private Layer[] layers;
+
+ private RainbowPrivateKey(ASN1Sequence seq)
+ {
+ // <oidString> or version
+ if (seq.getObjectAt(0) instanceof ASN1Integer)
+ {
+ version = ASN1Integer.getInstance(seq.getObjectAt(0));
+ }
+ else
+ {
+ oid = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ }
+
+ // <A1inv>
+ ASN1Sequence asnA1 = (ASN1Sequence)seq.getObjectAt(1);
+ invA1 = new byte[asnA1.size()][];
+ for (int i = 0; i < asnA1.size(); i++)
+ {
+ invA1[i] = ((ASN1OctetString)asnA1.getObjectAt(i)).getOctets();
+ }
+
+ // <b1>
+ ASN1Sequence asnb1 = (ASN1Sequence)seq.getObjectAt(2);
+ b1 = ((ASN1OctetString)asnb1.getObjectAt(0)).getOctets();
+
+ // <A2inv>
+ ASN1Sequence asnA2 = (ASN1Sequence)seq.getObjectAt(3);
+ invA2 = new byte[asnA2.size()][];
+ for (int j = 0; j < asnA2.size(); j++)
+ {
+ invA2[j] = ((ASN1OctetString)asnA2.getObjectAt(j)).getOctets();
+ }
+
+ // <b2>
+ ASN1Sequence asnb2 = (ASN1Sequence)seq.getObjectAt(4);
+ b2 = ((ASN1OctetString)asnb2.getObjectAt(0)).getOctets();
+
+ // <vi>
+ ASN1Sequence asnvi = (ASN1Sequence)seq.getObjectAt(5);
+ vi = ((ASN1OctetString)asnvi.getObjectAt(0)).getOctets();
+
+ // <layers>
+ ASN1Sequence asnLayers = (ASN1Sequence)seq.getObjectAt(6);
+
+ byte[][][][] alphas = new byte[asnLayers.size()][][][];
+ byte[][][][] betas = new byte[asnLayers.size()][][][];
+ byte[][][] gammas = new byte[asnLayers.size()][][];
+ byte[][] etas = new byte[asnLayers.size()][];
+ // a layer:
+ for (int l = 0; l < asnLayers.size(); l++)
+ {
+ ASN1Sequence asnLayer = (ASN1Sequence)asnLayers.getObjectAt(l);
+
+ // alphas (num of alpha-2d-array = oi)
+ ASN1Sequence alphas3d = (ASN1Sequence)asnLayer.getObjectAt(0);
+ alphas[l] = new byte[alphas3d.size()][][];
+ for (int m = 0; m < alphas3d.size(); m++)
+ {
+ ASN1Sequence alphas2d = (ASN1Sequence)alphas3d.getObjectAt(m);
+ alphas[l][m] = new byte[alphas2d.size()][];
+ for (int n = 0; n < alphas2d.size(); n++)
+ {
+ alphas[l][m][n] = ((ASN1OctetString)alphas2d.getObjectAt(n)).getOctets();
+ }
+ }
+
+ // betas ....
+ ASN1Sequence betas3d = (ASN1Sequence)asnLayer.getObjectAt(1);
+ betas[l] = new byte[betas3d.size()][][];
+ for (int mb = 0; mb < betas3d.size(); mb++)
+ {
+ ASN1Sequence betas2d = (ASN1Sequence)betas3d.getObjectAt(mb);
+ betas[l][mb] = new byte[betas2d.size()][];
+ for (int nb = 0; nb < betas2d.size(); nb++)
+ {
+ betas[l][mb][nb] = ((ASN1OctetString)betas2d.getObjectAt(nb)).getOctets();
+ }
+ }
+
+ // gammas ...
+ ASN1Sequence gammas2d = (ASN1Sequence)asnLayer.getObjectAt(2);
+ gammas[l] = new byte[gammas2d.size()][];
+ for (int mg = 0; mg < gammas2d.size(); mg++)
+ {
+ gammas[l][mg] = ((ASN1OctetString)gammas2d.getObjectAt(mg)).getOctets();
+ }
+
+ // eta ...
+ etas[l] = ((ASN1OctetString)asnLayer.getObjectAt(3)).getOctets();
+ }
+
+ int numOfLayers = vi.length - 1;
+ this.layers = new Layer[numOfLayers];
+ for (int i = 0; i < numOfLayers; i++)
+ {
+ Layer l = new Layer(vi[i], vi[i + 1], RainbowUtil.convertArray(alphas[i]),
+ RainbowUtil.convertArray(betas[i]), RainbowUtil.convertArray(gammas[i]), RainbowUtil.convertArray(etas[i]));
+ this.layers[i] = l;
+
+ }
+ }
+
+ public RainbowPrivateKey(short[][] invA1, short[] b1, short[][] invA2,
+ short[] b2, int[] vi, Layer[] layers)
+ {
+ this.version = new ASN1Integer(1);
+ this.invA1 = RainbowUtil.convertArray(invA1);
+ this.b1 = RainbowUtil.convertArray(b1);
+ this.invA2 = RainbowUtil.convertArray(invA2);
+ this.b2 = RainbowUtil.convertArray(b2);
+ this.vi = RainbowUtil.convertIntArray(vi);
+ this.layers = layers;
+ }
+
+ public static RainbowPrivateKey getInstance(Object o)
+ {
+ if (o instanceof RainbowPrivateKey)
+ {
+ return (RainbowPrivateKey)o;
+ }
+ else if (o != null)
+ {
+ return new RainbowPrivateKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * Getter for the inverse matrix of A1.
+ *
+ * @return the A1inv inverse
+ */
+ public short[][] getInvA1()
+ {
+ return RainbowUtil.convertArray(invA1);
+ }
+
+ /**
+ * Getter for the translation part of the private quadratic map L1.
+ *
+ * @return b1 the translation part of L1
+ */
+ public short[] getB1()
+ {
+ return RainbowUtil.convertArray(b1);
+ }
+
+ /**
+ * Getter for the translation part of the private quadratic map L2.
+ *
+ * @return b2 the translation part of L2
+ */
+ public short[] getB2()
+ {
+ return RainbowUtil.convertArray(b2);
+ }
+
+ /**
+ * Getter for the inverse matrix of A2
+ *
+ * @return the A2inv
+ */
+ public short[][] getInvA2()
+ {
+ return RainbowUtil.convertArray(invA2);
+ }
+
+ /**
+ * Returns the layers contained in the private key
+ *
+ * @return layers
+ */
+ public Layer[] getLayers()
+ {
+ return this.layers;
+ }
+
+ /**
+ * Returns the array of vi-s
+ *
+ * @return the vi
+ */
+ public int[] getVi()
+ {
+ return RainbowUtil.convertArraytoInt(vi);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ // encode <oidString> or version
+ if (version != null)
+ {
+ v.add(version);
+ }
+ else
+ {
+ v.add(oid);
+ }
+
+ // encode <A1inv>
+ ASN1EncodableVector asnA1 = new ASN1EncodableVector();
+ for (int i = 0; i < invA1.length; i++)
+ {
+ asnA1.add(new DEROctetString(invA1[i]));
+ }
+ v.add(new DERSequence(asnA1));
+
+ // encode <b1>
+ ASN1EncodableVector asnb1 = new ASN1EncodableVector();
+ asnb1.add(new DEROctetString(b1));
+ v.add(new DERSequence(asnb1));
+
+ // encode <A2inv>
+ ASN1EncodableVector asnA2 = new ASN1EncodableVector();
+ for (int i = 0; i < invA2.length; i++)
+ {
+ asnA2.add(new DEROctetString(invA2[i]));
+ }
+ v.add(new DERSequence(asnA2));
+
+ // encode <b2>
+ ASN1EncodableVector asnb2 = new ASN1EncodableVector();
+ asnb2.add(new DEROctetString(b2));
+ v.add(new DERSequence(asnb2));
+
+ // encode <vi>
+ ASN1EncodableVector asnvi = new ASN1EncodableVector();
+ asnvi.add(new DEROctetString(vi));
+ v.add(new DERSequence(asnvi));
+
+ // encode <layers>
+ ASN1EncodableVector asnLayers = new ASN1EncodableVector();
+ // a layer:
+ for (int l = 0; l < layers.length; l++)
+ {
+ ASN1EncodableVector aLayer = new ASN1EncodableVector();
+
+ // alphas (num of alpha-2d-array = oi)
+ byte[][][] alphas = RainbowUtil.convertArray(layers[l].getCoeffAlpha());
+ ASN1EncodableVector alphas3d = new ASN1EncodableVector();
+ for (int i = 0; i < alphas.length; i++)
+ {
+ ASN1EncodableVector alphas2d = new ASN1EncodableVector();
+ for (int j = 0; j < alphas[i].length; j++)
+ {
+ alphas2d.add(new DEROctetString(alphas[i][j]));
+ }
+ alphas3d.add(new DERSequence(alphas2d));
+ }
+ aLayer.add(new DERSequence(alphas3d));
+
+ // betas ....
+ byte[][][] betas = RainbowUtil.convertArray(layers[l].getCoeffBeta());
+ ASN1EncodableVector betas3d = new ASN1EncodableVector();
+ for (int i = 0; i < betas.length; i++)
+ {
+ ASN1EncodableVector betas2d = new ASN1EncodableVector();
+ for (int j = 0; j < betas[i].length; j++)
+ {
+ betas2d.add(new DEROctetString(betas[i][j]));
+ }
+ betas3d.add(new DERSequence(betas2d));
+ }
+ aLayer.add(new DERSequence(betas3d));
+
+ // gammas ...
+ byte[][] gammas = RainbowUtil.convertArray(layers[l].getCoeffGamma());
+ ASN1EncodableVector asnG = new ASN1EncodableVector();
+ for (int i = 0; i < gammas.length; i++)
+ {
+ asnG.add(new DEROctetString(gammas[i]));
+ }
+ aLayer.add(new DERSequence(asnG));
+
+ // eta
+ aLayer.add(new DEROctetString(RainbowUtil.convertArray(layers[l].getCoeffEta())));
+
+ // now, layer built up. add it!
+ asnLayers.add(new DERSequence(aLayer));
+ }
+
+ v.add(new DERSequence(asnLayers));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/asn1/RainbowPublicKey.java b/core/src/main/java/org/spongycastle/pqc/asn1/RainbowPublicKey.java
new file mode 100644
index 00000000..c31aabb4
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/asn1/RainbowPublicKey.java
@@ -0,0 +1,174 @@
+package org.spongycastle.pqc.asn1;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.pqc.crypto.rainbow.util.RainbowUtil;
+
+/**
+ * This class implements an ASN.1 encoded Rainbow public key. The ASN.1 definition
+ * of this structure is:
+ * <pre>
+ * RainbowPublicKey ::= SEQUENCE {
+ * CHOICE
+ * {
+ * oid OBJECT IDENTIFIER -- OID identifying the algorithm
+ * version INTEGER -- 0
+ * }
+ * docLength Integer -- length of the code
+ * coeffquadratic SEQUENCE OF OCTET STRING -- quadratic (mixed) coefficients
+ * coeffsingular SEQUENCE OF OCTET STRING -- singular coefficients
+ * coeffscalar SEQUENCE OF OCTET STRING -- scalar coefficients
+ * }
+ * </pre>
+ */
+public class RainbowPublicKey
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private ASN1ObjectIdentifier oid;
+ private ASN1Integer docLength;
+ private byte[][] coeffQuadratic;
+ private byte[][] coeffSingular;
+ private byte[] coeffScalar;
+
+ private RainbowPublicKey(ASN1Sequence seq)
+ {
+ // <oidString> or version
+ if (seq.getObjectAt(0) instanceof ASN1Integer)
+ {
+ version = ASN1Integer.getInstance(seq.getObjectAt(0));
+ }
+ else
+ {
+ oid = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ }
+
+ docLength = ASN1Integer.getInstance(seq.getObjectAt(1));
+
+ ASN1Sequence asnCoeffQuad = ASN1Sequence.getInstance(seq.getObjectAt(2));
+ coeffQuadratic = new byte[asnCoeffQuad.size()][];
+ for (int quadSize = 0; quadSize < asnCoeffQuad.size(); quadSize++)
+ {
+ coeffQuadratic[quadSize] = ASN1OctetString.getInstance(asnCoeffQuad.getObjectAt(quadSize)).getOctets();
+ }
+
+ ASN1Sequence asnCoeffSing = (ASN1Sequence)seq.getObjectAt(3);
+ coeffSingular = new byte[asnCoeffSing.size()][];
+ for (int singSize = 0; singSize < asnCoeffSing.size(); singSize++)
+ {
+ coeffSingular[singSize] = ASN1OctetString.getInstance(asnCoeffSing.getObjectAt(singSize)).getOctets();
+ }
+
+ ASN1Sequence asnCoeffScalar = (ASN1Sequence)seq.getObjectAt(4);
+ coeffScalar = ASN1OctetString.getInstance(asnCoeffScalar.getObjectAt(0)).getOctets();
+ }
+
+ public RainbowPublicKey(int docLength, short[][] coeffQuadratic, short[][] coeffSingular, short[] coeffScalar)
+ {
+ this.version = new ASN1Integer(0);
+ this.docLength = new ASN1Integer(docLength);
+ this.coeffQuadratic = RainbowUtil.convertArray(coeffQuadratic);
+ this.coeffSingular = RainbowUtil.convertArray(coeffSingular);
+ this.coeffScalar = RainbowUtil.convertArray(coeffScalar);
+ }
+
+ public static RainbowPublicKey getInstance(Object o)
+ {
+ if (o instanceof RainbowPublicKey)
+ {
+ return (RainbowPublicKey)o;
+ }
+ else if (o != null)
+ {
+ return new RainbowPublicKey(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * @return the docLength
+ */
+ public int getDocLength()
+ {
+ return this.docLength.getValue().intValue();
+ }
+
+ /**
+ * @return the coeffquadratic
+ */
+ public short[][] getCoeffQuadratic()
+ {
+ return RainbowUtil.convertArray(coeffQuadratic);
+ }
+
+ /**
+ * @return the coeffsingular
+ */
+ public short[][] getCoeffSingular()
+ {
+ return RainbowUtil.convertArray(coeffSingular);
+ }
+
+ /**
+ * @return the coeffscalar
+ */
+ public short[] getCoeffScalar()
+ {
+ return RainbowUtil.convertArray(coeffScalar);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ // encode <oidString> or version
+ if (version != null)
+ {
+ v.add(version);
+ }
+ else
+ {
+ v.add(oid);
+ }
+
+ // encode <docLength>
+ v.add(docLength);
+
+ // encode <coeffQuadratic>
+ ASN1EncodableVector asnCoeffQuad = new ASN1EncodableVector();
+ for (int i = 0; i < coeffQuadratic.length; i++)
+ {
+ asnCoeffQuad.add(new DEROctetString(coeffQuadratic[i]));
+ }
+ v.add(new DERSequence(asnCoeffQuad));
+
+ // encode <coeffSingular>
+ ASN1EncodableVector asnCoeffSing = new ASN1EncodableVector();
+ for (int i = 0; i < coeffSingular.length; i++)
+ {
+ asnCoeffSing.add(new DEROctetString(coeffSingular[i]));
+ }
+ v.add(new DERSequence(asnCoeffSing));
+
+ // encode <coeffScalar>
+ ASN1EncodableVector asnCoeffScalar = new ASN1EncodableVector();
+ asnCoeffScalar.add(new DEROctetString(coeffScalar));
+ v.add(new DERSequence(asnCoeffScalar));
+
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/DigestingMessageSigner.java b/core/src/main/java/org/spongycastle/pqc/crypto/DigestingMessageSigner.java
new file mode 100644
index 00000000..b58a5278
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/DigestingMessageSigner.java
@@ -0,0 +1,117 @@
+package org.spongycastle.pqc.crypto;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.Signer;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+
+
+/**
+ * Implements the sign and verify functions for a Signature Scheme which can use a hash function.
+ */
+public class DigestingMessageSigner
+ implements Signer
+{
+ private final Digest messDigest;
+ private final MessageSigner messSigner;
+ private boolean forSigning;
+
+ public DigestingMessageSigner(MessageSigner messSigner, Digest messDigest)
+ {
+ this.messSigner = messSigner;
+ this.messDigest = messDigest;
+ }
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ this.forSigning = forSigning;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forSigning && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Signing Requires Private Key.");
+ }
+
+ if (!forSigning && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Verification Requires Public Key.");
+ }
+
+ reset();
+
+ messSigner.init(forSigning, param);
+ }
+
+
+ /**
+ * This function signs the message that has been updated, making use of the
+ * private key.
+ *
+ * @return the signature of the message.
+ */
+ public byte[] generateSignature()
+ {
+ if (!forSigning)
+ {
+ throw new IllegalStateException("RainbowDigestSigner not initialised for signature generation.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+
+ return messSigner.generateSignature(hash);
+ }
+
+ /**
+ * This function verifies the signature of the message that has been
+ * updated, with the aid of the public key.
+ *
+ * @param signature the signature of the message is given as a byte array.
+ * @return true if the signature has been verified, false otherwise.
+ */
+ public boolean verify(byte[] signature)
+ {
+ if (forSigning)
+ {
+ throw new IllegalStateException("RainbowDigestSigner not initialised for verification");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+
+ return messSigner.verifySignature(hash, signature);
+
+ }
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+ }
+
+ public void reset()
+ {
+ messDigest.reset();
+ }
+
+ public boolean verifySignature(byte[] signature)
+ {
+ return this.verify(signature);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/MessageEncryptor.java b/core/src/main/java/org/spongycastle/pqc/crypto/MessageEncryptor.java
new file mode 100644
index 00000000..4d5ea6b2
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/MessageEncryptor.java
@@ -0,0 +1,30 @@
+package org.spongycastle.pqc.crypto;
+
+
+import org.spongycastle.crypto.CipherParameters;
+
+public interface MessageEncryptor
+{
+
+ /**
+ *
+ * @param forEncrypting true if we are encrypting a signature, false
+ * otherwise.
+ * @param param key parameters for encryption or decryption.
+ */
+ public void init(boolean forEncrypting, CipherParameters param);
+
+ /**
+ *
+ * @param message the message to be signed.
+ * @throws Exception
+ */
+ public byte[] messageEncrypt(byte[] message) throws Exception;
+
+ /**
+ *
+ * @param cipher the cipher text of the message
+ * @throws Exception
+ */
+ public byte[] messageDecrypt(byte[] cipher) throws Exception;
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/MessageSigner.java b/core/src/main/java/org/spongycastle/pqc/crypto/MessageSigner.java
new file mode 100644
index 00000000..b09c6195
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/MessageSigner.java
@@ -0,0 +1,32 @@
+package org.spongycastle.pqc.crypto;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public interface MessageSigner
+{
+ /**
+ * initialise the signer for signature generation or signature
+ * verification.
+ *
+ * @param forSigning true if we are generating a signature, false
+ * otherwise.
+ * @param param key parameters for signature generation.
+ */
+ public void init(boolean forSigning, CipherParameters param);
+
+ /**
+ * sign the passed in message (usually the output of a hash function).
+ *
+ * @param message the message to be signed.
+ * @return the signature of the message
+ */
+ public byte[] generateSignature(byte[] message);
+
+ /**
+ * verify the message message against the signature values r and s.
+ *
+ * @param message the message that was supposed to have been signed.
+ * @param signature the signature of the message
+ */
+ public boolean verifySignature(byte[] message, byte[] signature);
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSDigestProvider.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSDigestProvider.java
new file mode 100644
index 00000000..373ca503
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSDigestProvider.java
@@ -0,0 +1,8 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import org.spongycastle.crypto.Digest;
+
+public interface GMSSDigestProvider
+{
+ Digest get();
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyGenerationParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyGenerationParameters.java
new file mode 100644
index 00000000..27d7b697
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyGenerationParameters.java
@@ -0,0 +1,26 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class GMSSKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+
+ private GMSSParameters params;
+
+ public GMSSKeyGenerationParameters(
+ SecureRandom random,
+ GMSSParameters params)
+ {
+ // XXX key size?
+ super(random, 1);
+ this.params = params;
+ }
+
+ public GMSSParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java
new file mode 100644
index 00000000..bb1ef36d
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java
@@ -0,0 +1,476 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.security.SecureRandom;
+import java.util.Vector;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.spongycastle.pqc.crypto.gmss.util.WinternitzOTSVerify;
+import org.spongycastle.pqc.crypto.gmss.util.WinternitzOTSignature;
+
+
+/**
+ * This class implements key pair generation of the generalized Merkle signature
+ * scheme (GMSS).
+ *
+ * @see GMSSSigner
+ */
+public class GMSSKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ /**
+ * The source of randomness for OTS private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+ /**
+ * The hash function used for the construction of the authentication trees
+ */
+ private Digest messDigestTree;
+
+ /**
+ * An array of the seeds for the PRGN (for main tree, and all current
+ * subtrees)
+ */
+ private byte[][] currentSeeds;
+
+ /**
+ * An array of seeds for the PRGN (for all subtrees after next)
+ */
+ private byte[][] nextNextSeeds;
+
+ /**
+ * An array of the RootSignatures
+ */
+ private byte[][] currentRootSigs;
+
+ /**
+ * Class of hash function to use
+ */
+ private GMSSDigestProvider digestProvider;
+
+ /**
+ * The length of the seed for the PRNG
+ */
+ private int mdLength;
+
+ /**
+ * the number of Layers
+ */
+ private int numLayer;
+
+
+ /**
+ * Flag indicating if the class already has been initialized
+ */
+ private boolean initialized = false;
+
+ /**
+ * Instance of GMSSParameterset
+ */
+ private GMSSParameters gmssPS;
+
+ /**
+ * An array of the heights of the authentication trees of each layer
+ */
+ private int[] heightOfTrees;
+
+ /**
+ * An array of the Winternitz parameter 'w' of each layer
+ */
+ private int[] otsIndex;
+
+ /**
+ * The parameter K needed for the authentication path computation
+ */
+ private int[] K;
+
+ private GMSSKeyGenerationParameters gmssParams;
+
+ /**
+ * The GMSS OID.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.3";
+
+ /**
+ * The standard constructor tries to generate the GMSS algorithm identifier
+ * with the corresponding OID.
+ *
+ * @param digestProvider provider for digest implementations.
+ */
+ public GMSSKeyPairGenerator(GMSSDigestProvider digestProvider)
+ {
+ this.digestProvider = digestProvider;
+ messDigestTree = digestProvider.get();
+
+ // set mdLength
+ this.mdLength = messDigestTree.getDigestSize();
+ // construct randomizer
+ this.gmssRandom = new GMSSRandom(messDigestTree);
+
+ }
+
+ /**
+ * Generates the GMSS key pair. The public key is an instance of
+ * JDKGMSSPublicKey, the private key is an instance of JDKGMSSPrivateKey.
+ *
+ * @return Key pair containing a JDKGMSSPublicKey and a JDKGMSSPrivateKey
+ */
+ private AsymmetricCipherKeyPair genKeyPair()
+ {
+ if (!initialized)
+ {
+ initializeDefault();
+ }
+
+ // initialize authenticationPaths and treehash instances
+ byte[][][] currentAuthPaths = new byte[numLayer][][];
+ byte[][][] nextAuthPaths = new byte[numLayer - 1][][];
+ Treehash[][] currentTreehash = new Treehash[numLayer][];
+ Treehash[][] nextTreehash = new Treehash[numLayer - 1][];
+
+ Vector[] currentStack = new Vector[numLayer];
+ Vector[] nextStack = new Vector[numLayer - 1];
+
+ Vector[][] currentRetain = new Vector[numLayer][];
+ Vector[][] nextRetain = new Vector[numLayer - 1][];
+
+ for (int i = 0; i < numLayer; i++)
+ {
+ currentAuthPaths[i] = new byte[heightOfTrees[i]][mdLength];
+ currentTreehash[i] = new Treehash[heightOfTrees[i] - K[i]];
+
+ if (i > 0)
+ {
+ nextAuthPaths[i - 1] = new byte[heightOfTrees[i]][mdLength];
+ nextTreehash[i - 1] = new Treehash[heightOfTrees[i] - K[i]];
+ }
+
+ currentStack[i] = new Vector();
+ if (i > 0)
+ {
+ nextStack[i - 1] = new Vector();
+ }
+ }
+
+ // initialize roots
+ byte[][] currentRoots = new byte[numLayer][mdLength];
+ byte[][] nextRoots = new byte[numLayer - 1][mdLength];
+ // initialize seeds
+ byte[][] seeds = new byte[numLayer][mdLength];
+ // initialize seeds[] by copying starting-seeds of first trees of each
+ // layer
+ for (int i = 0; i < numLayer; i++)
+ {
+ System.arraycopy(currentSeeds[i], 0, seeds[i], 0, mdLength);
+ }
+
+ // initialize rootSigs
+ currentRootSigs = new byte[numLayer - 1][mdLength];
+
+ // -------------------------
+ // -------------------------
+ // --- calculation of current authpaths and current rootsigs (AUTHPATHS,
+ // SIG)------
+ // from bottom up to the root
+ for (int h = numLayer - 1; h >= 0; h--)
+ {
+ GMSSRootCalc tree = new GMSSRootCalc(this.heightOfTrees[h], this.K[h], digestProvider);
+ try
+ {
+ // on lowest layer no lower root is available, so just call
+ // the method with null as first parameter
+ if (h == numLayer - 1)
+ {
+ tree = this.generateCurrentAuthpathAndRoot(null, currentStack[h], seeds[h], h);
+ }
+ else
+ // otherwise call the method with the former computed root
+ // value
+ {
+ tree = this.generateCurrentAuthpathAndRoot(currentRoots[h + 1], currentStack[h], seeds[h], h);
+ }
+
+ }
+ catch (Exception e1)
+ {
+ e1.printStackTrace();
+ }
+
+ // set initial values needed for the private key construction
+ for (int i = 0; i < heightOfTrees[h]; i++)
+ {
+ System.arraycopy(tree.getAuthPath()[i], 0, currentAuthPaths[h][i], 0, mdLength);
+ }
+ currentRetain[h] = tree.getRetain();
+ currentTreehash[h] = tree.getTreehash();
+ System.arraycopy(tree.getRoot(), 0, currentRoots[h], 0, mdLength);
+ }
+
+ // --- calculation of next authpaths and next roots (AUTHPATHS+, ROOTS+)
+ // ------
+ for (int h = numLayer - 2; h >= 0; h--)
+ {
+ GMSSRootCalc tree = this.generateNextAuthpathAndRoot(nextStack[h], seeds[h + 1], h + 1);
+
+ // set initial values needed for the private key construction
+ for (int i = 0; i < heightOfTrees[h + 1]; i++)
+ {
+ System.arraycopy(tree.getAuthPath()[i], 0, nextAuthPaths[h][i], 0, mdLength);
+ }
+ nextRetain[h] = tree.getRetain();
+ nextTreehash[h] = tree.getTreehash();
+ System.arraycopy(tree.getRoot(), 0, nextRoots[h], 0, mdLength);
+
+ // create seed for the Merkle tree after next (nextNextSeeds)
+ // SEEDs++
+ System.arraycopy(seeds[h + 1], 0, this.nextNextSeeds[h], 0, mdLength);
+ }
+ // ------------
+
+ // generate JDKGMSSPublicKey
+ GMSSPublicKeyParameters publicKey = new GMSSPublicKeyParameters(currentRoots[0], gmssPS);
+
+ // generate the JDKGMSSPrivateKey
+ GMSSPrivateKeyParameters privateKey = new GMSSPrivateKeyParameters(currentSeeds, nextNextSeeds, currentAuthPaths,
+ nextAuthPaths, currentTreehash, nextTreehash, currentStack, nextStack, currentRetain, nextRetain, nextRoots, currentRootSigs, gmssPS, digestProvider);
+
+ // return the KeyPair
+ return (new AsymmetricCipherKeyPair(publicKey, privateKey));
+ }
+
+ /**
+ * calculates the authpath for tree in layer h which starts with seed[h]
+ * additionally computes the rootSignature of underlaying root
+ *
+ * @param currentStack stack used for the treehash instance created by this method
+ * @param lowerRoot stores the root of the lower tree
+ * @param seed starting seeds
+ * @param h actual layer
+ */
+ private GMSSRootCalc generateCurrentAuthpathAndRoot(byte[] lowerRoot, Vector currentStack, byte[] seed, int h)
+ {
+ byte[] help = new byte[mdLength];
+
+ byte[] OTSseed = new byte[mdLength];
+ OTSseed = gmssRandom.nextSeed(seed);
+
+ WinternitzOTSignature ots;
+
+ // data structure that constructs the whole tree and stores
+ // the initial values for treehash, Auth and retain
+ GMSSRootCalc treeToConstruct = new GMSSRootCalc(this.heightOfTrees[h], this.K[h], digestProvider);
+
+ treeToConstruct.initialize(currentStack);
+
+ // generate the first leaf
+ if (h == numLayer - 1)
+ {
+ ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
+ help = ots.getPublicKey();
+ }
+ else
+ {
+ // for all layers except the lowest, generate the signature of the
+ // underlying root
+ // and reuse this signature to compute the first leaf of acual layer
+ // more efficiently (by verifiing the signature)
+ ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
+ currentRootSigs[h] = ots.getSignature(lowerRoot);
+ WinternitzOTSVerify otsver = new WinternitzOTSVerify(digestProvider.get(), otsIndex[h]);
+ help = otsver.Verify(lowerRoot, currentRootSigs[h]);
+ }
+ // update the tree with the first leaf
+ treeToConstruct.update(help);
+
+ int seedForTreehashIndex = 3;
+ int count = 0;
+
+ // update the tree 2^(H) - 1 times, from the second to the last leaf
+ for (int i = 1; i < (1 << this.heightOfTrees[h]); i++)
+ {
+ // initialize the seeds for the leaf generation with index 3 * 2^h
+ if (i == seedForTreehashIndex && count < this.heightOfTrees[h] - this.K[h])
+ {
+ treeToConstruct.initializeTreehashSeed(seed, count);
+ seedForTreehashIndex *= 2;
+ count++;
+ }
+
+ OTSseed = gmssRandom.nextSeed(seed);
+ ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
+ treeToConstruct.update(ots.getPublicKey());
+ }
+
+ if (treeToConstruct.wasFinished())
+ {
+ return treeToConstruct;
+ }
+ System.err.println("Baum noch nicht fertig konstruiert!!!");
+ return null;
+ }
+
+ /**
+ * calculates the authpath and root for tree in layer h which starts with
+ * seed[h]
+ *
+ * @param nextStack stack used for the treehash instance created by this method
+ * @param seed starting seeds
+ * @param h actual layer
+ */
+ private GMSSRootCalc generateNextAuthpathAndRoot(Vector nextStack, byte[] seed, int h)
+ {
+ byte[] OTSseed = new byte[numLayer];
+ WinternitzOTSignature ots;
+
+ // data structure that constructs the whole tree and stores
+ // the initial values for treehash, Auth and retain
+ GMSSRootCalc treeToConstruct = new GMSSRootCalc(this.heightOfTrees[h], this.K[h], this.digestProvider);
+ treeToConstruct.initialize(nextStack);
+
+ int seedForTreehashIndex = 3;
+ int count = 0;
+
+ // update the tree 2^(H) times, from the first to the last leaf
+ for (int i = 0; i < (1 << this.heightOfTrees[h]); i++)
+ {
+ // initialize the seeds for the leaf generation with index 3 * 2^h
+ if (i == seedForTreehashIndex && count < this.heightOfTrees[h] - this.K[h])
+ {
+ treeToConstruct.initializeTreehashSeed(seed, count);
+ seedForTreehashIndex *= 2;
+ count++;
+ }
+
+ OTSseed = gmssRandom.nextSeed(seed);
+ ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
+ treeToConstruct.update(ots.getPublicKey());
+ }
+
+ if (treeToConstruct.wasFinished())
+ {
+ return treeToConstruct;
+ }
+ System.err.println("N�chster Baum noch nicht fertig konstruiert!!!");
+ return null;
+ }
+
+ /**
+ * This method initializes the GMSS KeyPairGenerator using an integer value
+ * <code>keySize</code> as input. It provides a simple use of the GMSS for
+ * testing demands.
+ * <p>
+ * A given <code>keysize</code> of less than 10 creates an amount 2^10
+ * signatures. A keySize between 10 and 20 creates 2^20 signatures. Given an
+ * integer greater than 20 the key pair generator creates 2^40 signatures.
+ *
+ * @param keySize Assigns the parameters used for the GMSS signatures. There are
+ * 3 choices:<br>
+ * 1. keysize &lt;= 10: creates 2^10 signatures using the
+ * parameterset<br>
+ * P = (2, (5, 5), (3, 3), (3, 3))<br>
+ * 2. keysize &gt; 10 and &lt;= 20: creates 2^20 signatures using the
+ * parameterset<br>
+ * P = (2, (10, 10), (5, 4), (2, 2))<br>
+ * 3. keysize &gt; 20: creates 2^40 signatures using the
+ * parameterset<br>
+ * P = (2, (10, 10, 10, 10), (9, 9, 9, 3), (2, 2, 2, 2))
+ * @param secureRandom not used by GMSS, the SHA1PRNG of the SUN Provider is always
+ * used
+ */
+ public void initialize(int keySize, SecureRandom secureRandom)
+ {
+
+ KeyGenerationParameters kgp;
+ if (keySize <= 10)
+ { // create 2^10 keys
+ int[] defh = {10};
+ int[] defw = {3};
+ int[] defk = {2};
+ // XXX sec random neede?
+ kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
+ }
+ else if (keySize <= 20)
+ { // create 2^20 keys
+ int[] defh = {10, 10};
+ int[] defw = {5, 4};
+ int[] defk = {2, 2};
+ kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
+ }
+ else
+ { // create 2^40 keys, keygen lasts around 80 seconds
+ int[] defh = {10, 10, 10, 10};
+ int[] defw = {9, 9, 9, 3};
+ int[] defk = {2, 2, 2, 2};
+ kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
+ }
+
+ // call the initializer with the chosen parameters
+ this.initialize(kgp);
+
+ }
+
+
+ /**
+ * Initalizes the key pair generator using a parameter set as input
+ */
+ public void initialize(KeyGenerationParameters param)
+ {
+
+ this.gmssParams = (GMSSKeyGenerationParameters)param;
+
+ // generate GMSSParameterset
+ this.gmssPS = new GMSSParameters(gmssParams.getParameters().getNumOfLayers(), gmssParams.getParameters().getHeightOfTrees(),
+ gmssParams.getParameters().getWinternitzParameter(), gmssParams.getParameters().getK());
+
+ this.numLayer = gmssPS.getNumOfLayers();
+ this.heightOfTrees = gmssPS.getHeightOfTrees();
+ this.otsIndex = gmssPS.getWinternitzParameter();
+ this.K = gmssPS.getK();
+
+ // seeds
+ this.currentSeeds = new byte[numLayer][mdLength];
+ this.nextNextSeeds = new byte[numLayer - 1][mdLength];
+
+ // construct SecureRandom for initial seed generation
+ SecureRandom secRan = new SecureRandom();
+
+ // generation of initial seeds
+ for (int i = 0; i < numLayer; i++)
+ {
+ secRan.nextBytes(currentSeeds[i]);
+ gmssRandom.nextSeed(currentSeeds[i]);
+ }
+
+ this.initialized = true;
+ }
+
+ /**
+ * This method is called by generateKeyPair() in case that no other
+ * initialization method has been called by the user
+ */
+ private void initializeDefault()
+ {
+ int[] defh = {10, 10, 10, 10};
+ int[] defw = {3, 3, 3, 3};
+ int[] defk = {2, 2, 2, 2};
+
+ KeyGenerationParameters kgp = new GMSSKeyGenerationParameters(new SecureRandom(), new GMSSParameters(defh.length, defh, defw, defk));
+ this.initialize(kgp);
+
+ }
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.initialize(param);
+
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ return genKeyPair();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyParameters.java
new file mode 100644
index 00000000..d2dcbba7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSKeyParameters.java
@@ -0,0 +1,22 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+
+public class GMSSKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private GMSSParameters params;
+
+ public GMSSKeyParameters(
+ boolean isPrivate,
+ GMSSParameters params)
+ {
+ super(isPrivate);
+ this.params = params;
+ }
+
+ public GMSSParameters getParameters()
+ {
+ return params;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSLeaf.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSLeaf.java
new file mode 100644
index 00000000..ade340ad
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSLeaf.java
@@ -0,0 +1,376 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+
+
+/**
+ * This class implements the distributed computation of the public key of the
+ * Winternitz one-time signature scheme (OTSS). The class is used by the GMSS
+ * classes for calculation of upcoming leafs.
+ */
+public class GMSSLeaf
+{
+
+ /**
+ * The hash function used by the OTS and the PRNG
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * The length of the message digest and private key
+ */
+ private int mdsize, keysize;
+
+ /**
+ * The source of randomness for OTS private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+ /**
+ * Byte array for distributed computation of the upcoming leaf
+ */
+ private byte[] leaf;
+
+ /**
+ * Byte array for storing the concatenated hashes of private key parts
+ */
+ private byte[] concHashs;
+
+ /**
+ * indices for distributed computation
+ */
+ private int i, j;
+
+ /**
+ * storing 2^w
+ */
+ private int two_power_w;
+
+ /**
+ * Winternitz parameter w
+ */
+ private int w;
+
+ /**
+ * the amount of distributed computation steps when updateLeaf is called
+ */
+ private int steps;
+
+ /**
+ * the internal seed
+ */
+ private byte[] seed;
+
+ /**
+ * the OTS privateKey parts
+ */
+ byte[] privateKeyOTS;
+
+ /**
+ * This constructor regenerates a prior GMSSLeaf object
+ *
+ * @param digest an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding
+ * provider
+ * @param otsIndex status bytes
+ * @param numLeafs status ints
+ */
+ public GMSSLeaf(Digest digest, byte[][] otsIndex, int[] numLeafs)
+ {
+ this.i = numLeafs[0];
+ this.j = numLeafs[1];
+ this.steps = numLeafs[2];
+ this.w = numLeafs[3];
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private key and the help array
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ int checksumsize = getLog((messagesize << w) + 1);
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+ this.two_power_w = 1 << w;
+
+ // calculate steps
+ // ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
+
+ // initialize arrays
+ this.privateKeyOTS = otsIndex[0];
+ this.seed = otsIndex[1];
+ this.concHashs = otsIndex[2];
+ this.leaf = otsIndex[3];
+ }
+
+ /**
+ * The constructor precomputes some needed variables for distributed leaf
+ * calculation
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function and PRNG and the digest of the corresponding
+ * provider
+ * @param w the winterniz parameter of that tree the leaf is computed
+ * for
+ * @param numLeafs the number of leafs of the tree from where the distributed
+ * computation is called
+ */
+ GMSSLeaf(Digest digest, int w, int numLeafs)
+ {
+ this.w = w;
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private key and the help array
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ int checksumsize = getLog((messagesize << w) + 1);
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+ this.two_power_w = 1 << w;
+
+ // calculate steps
+ // ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
+ this.steps = (int)Math
+ .ceil((double)(((1 << w) - 1) * keysize + 1 + keysize)
+ / (double)(numLeafs));
+
+ // initialize arrays
+ this.seed = new byte[mdsize];
+ this.leaf = new byte[mdsize];
+ this.privateKeyOTS = new byte[mdsize];
+ this.concHashs = new byte[mdsize * keysize];
+ }
+
+ public GMSSLeaf(Digest digest, int w, int numLeafs, byte[] seed0)
+ {
+ this.w = w;
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private key and the help array
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ int checksumsize = getLog((messagesize << w) + 1);
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+ this.two_power_w = 1 << w;
+
+ // calculate steps
+ // ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
+ this.steps = (int)Math
+ .ceil((double)(((1 << w) - 1) * keysize + 1 + keysize)
+ / (double)(numLeafs));
+
+ // initialize arrays
+ this.seed = new byte[mdsize];
+ this.leaf = new byte[mdsize];
+ this.privateKeyOTS = new byte[mdsize];
+ this.concHashs = new byte[mdsize * keysize];
+
+ initLeafCalc(seed0);
+ }
+
+ private GMSSLeaf(GMSSLeaf original)
+ {
+ this.messDigestOTS = original.messDigestOTS;
+ this.mdsize = original.mdsize;
+ this.keysize = original.keysize;
+ this.gmssRandom = original.gmssRandom;
+ this.leaf = Arrays.clone(original.leaf);
+ this.concHashs = Arrays.clone(original.concHashs);
+ this.i = original.i;
+ this.j = original.j;
+ this.two_power_w = original.two_power_w;
+ this.w = original.w;
+ this.steps = original.steps;
+ this.seed = Arrays.clone(original.seed);
+ this.privateKeyOTS = Arrays.clone(original.privateKeyOTS);
+ }
+
+ /**
+ * initialize the distributed leaf calculation reset i,j and compute OTSseed
+ * with seed0
+ *
+ * @param seed0 the starting seed
+ */
+ // TODO: this really looks like it should be either always called from a constructor or nextLeaf.
+ void initLeafCalc(byte[] seed0)
+ {
+ this.i = 0;
+ this.j = 0;
+ byte[] dummy = new byte[mdsize];
+ System.arraycopy(seed0, 0, dummy, 0, seed.length);
+ this.seed = gmssRandom.nextSeed(dummy);
+ }
+
+ GMSSLeaf nextLeaf()
+ {
+ GMSSLeaf nextLeaf = new GMSSLeaf(this);
+
+ nextLeaf.updateLeafCalc();
+
+ return nextLeaf;
+ }
+
+ /**
+ * Processes <code>steps</code> steps of distributed leaf calculation
+ *
+ * @return true if leaf is completed, else false
+ */
+ private void updateLeafCalc()
+ {
+ byte[] buf = new byte[messDigestOTS.getDigestSize()];
+
+ // steps times do
+ // TODO: this really needs to be looked at, the 10000 has been added as
+ // prior to this the leaf value always ended up as zeros.
+ for (int s = 0; s < steps + 10000; s++)
+ {
+ if (i == keysize && j == two_power_w - 1)
+ { // [3] at last hash the
+ // concatenation
+ messDigestOTS.update(concHashs, 0, concHashs.length);
+ leaf = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(leaf, 0);
+ return;
+ }
+ else if (i == 0 || j == two_power_w - 1)
+ { // [1] at the
+ // beginning and
+ // when [2] is
+ // finished: get the
+ // next private key
+ // part
+ i++;
+ j = 0;
+ // get next privKey part
+ this.privateKeyOTS = gmssRandom.nextSeed(seed);
+ }
+ else
+ { // [2] hash the privKey part
+ messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length);
+ privateKeyOTS = buf;
+ messDigestOTS.doFinal(privateKeyOTS, 0);
+ j++;
+ if (j == two_power_w - 1)
+ { // after w hashes add to the
+ // concatenated array
+ System.arraycopy(privateKeyOTS, 0, concHashs, mdsize
+ * (i - 1), mdsize);
+ }
+ }
+ }
+
+ throw new IllegalStateException("unable to updateLeaf in steps: " + steps + " " + i + " " + j);
+ }
+
+ /**
+ * Returns the leaf value.
+ *
+ * @return the leaf value
+ */
+ public byte[] getLeaf()
+ {
+ return Arrays.clone(leaf);
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer <code>intValue</code>.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of <code>intValue</code>
+ */
+ private int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+ /**
+ * Returns the status byte array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status bytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ byte[][] statByte = new byte[4][];
+ statByte[0] = new byte[mdsize];
+ statByte[1] = new byte[mdsize];
+ statByte[2] = new byte[mdsize * keysize];
+ statByte[3] = new byte[mdsize];
+ statByte[0] = privateKeyOTS;
+ statByte[1] = seed;
+ statByte[2] = concHashs;
+ statByte[3] = leaf;
+
+ return statByte;
+ }
+
+ /**
+ * Returns the status int array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status ints
+ */
+ public int[] getStatInt()
+ {
+
+ int[] statInt = new int[4];
+ statInt[0] = i;
+ statInt[1] = j;
+ statInt[2] = steps;
+ statInt[3] = w;
+ return statInt;
+ }
+
+ /**
+ * Returns a String representation of the main part of this element
+ *
+ * @return a String representation of the main part of this element
+ */
+ public String toString()
+ {
+ String out = "";
+
+ for (int i = 0; i < 4; i++)
+ {
+ out = out + this.getStatInt()[i] + " ";
+ }
+ out = out + " " + this.mdsize + " " + this.keysize + " "
+ + this.two_power_w + " ";
+
+ byte[][] temp = this.getStatByte();
+ for (int i = 0; i < 4; i++)
+ {
+ if (temp[i] != null)
+ {
+ out = out + new String(Hex.encode(temp[i])) + " ";
+ }
+ else
+ {
+ out = out + "null ";
+ }
+ }
+ return out;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSParameters.java
new file mode 100644
index 00000000..c78d50e3
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSParameters.java
@@ -0,0 +1,155 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * This class provides a specification for the GMSS parameters that are used by
+ * the GMSSKeyPairGenerator and GMSSSignature classes.
+ *
+ * @see org.spongycastle.pqc.crypto.gmss.GMSSKeyPairGenerator
+ */
+public class GMSSParameters
+{
+ /**
+ * The number of authentication tree layers.
+ */
+ private int numOfLayers;
+
+ /**
+ * The height of the authentication trees of each layer.
+ */
+ private int[] heightOfTrees;
+
+ /**
+ * The Winternitz Parameter 'w' of each layer.
+ */
+ private int[] winternitzParameter;
+
+ /**
+ * The parameter K needed for the authentication path computation
+ */
+ private int[] K;
+
+ /**
+ * The constructor for the parameters of the GMSSKeyPairGenerator.
+ *
+ * @param layers the number of authentication tree layers
+ * @param heightOfTrees the height of the authentication trees
+ * @param winternitzParameter the Winternitz Parameter 'w' of each layer
+ * @param K parameter for authpath computation
+ */
+ public GMSSParameters(int layers, int[] heightOfTrees, int[] winternitzParameter, int[] K)
+ throws IllegalArgumentException
+ {
+ init(layers, heightOfTrees, winternitzParameter, K);
+ }
+
+ private void init(int layers, int[] heightOfTrees,
+ int[] winternitzParameter, int[] K)
+ throws IllegalArgumentException
+ {
+ boolean valid = true;
+ String errMsg = "";
+ this.numOfLayers = layers;
+ if ((numOfLayers != winternitzParameter.length)
+ || (numOfLayers != heightOfTrees.length)
+ || (numOfLayers != K.length))
+ {
+ valid = false;
+ errMsg = "Unexpected parameterset format";
+ }
+ for (int i = 0; i < numOfLayers; i++)
+ {
+ if ((K[i] < 2) || ((heightOfTrees[i] - K[i]) % 2 != 0))
+ {
+ valid = false;
+ errMsg = "Wrong parameter K (K >= 2 and H-K even required)!";
+ }
+
+ if ((heightOfTrees[i] < 4) || (winternitzParameter[i] < 2))
+ {
+ valid = false;
+ errMsg = "Wrong parameter H or w (H > 3 and w > 1 required)!";
+ }
+ }
+
+ if (valid)
+ {
+ this.heightOfTrees = Arrays.clone(heightOfTrees);
+ this.winternitzParameter = Arrays.clone(winternitzParameter);
+ this.K = Arrays.clone(K);
+ }
+ else
+ {
+ throw new IllegalArgumentException(errMsg);
+ }
+ }
+
+ public GMSSParameters(int keySize)
+ throws IllegalArgumentException
+ {
+ if (keySize <= 10)
+ { // create 2^10 keys
+ int[] defh = {10};
+ int[] defw = {3};
+ int[] defk = {2};
+ this.init(defh.length, defh, defw, defk);
+ }
+ else if (keySize <= 20)
+ { // create 2^20 keys
+ int[] defh = {10, 10};
+ int[] defw = {5, 4};
+ int[] defk = {2, 2};
+ this.init(defh.length, defh, defw, defk);
+ }
+ else
+ { // create 2^40 keys, keygen lasts around 80 seconds
+ int[] defh = {10, 10, 10, 10};
+ int[] defw = {9, 9, 9, 3};
+ int[] defk = {2, 2, 2, 2};
+ this.init(defh.length, defh, defw, defk);
+ }
+ }
+
+ /**
+ * Returns the number of levels of the authentication trees.
+ *
+ * @return The number of levels of the authentication trees.
+ */
+ public int getNumOfLayers()
+ {
+ return numOfLayers;
+ }
+
+ /**
+ * Returns the array of height (for each layer) of the authentication trees
+ *
+ * @return The array of height (for each layer) of the authentication trees
+ */
+ public int[] getHeightOfTrees()
+ {
+ return Arrays.clone(heightOfTrees);
+ }
+
+ /**
+ * Returns the array of WinternitzParameter (for each layer) of the
+ * authentication trees
+ *
+ * @return The array of WinternitzParameter (for each layer) of the
+ * authentication trees
+ */
+ public int[] getWinternitzParameter()
+ {
+ return Arrays.clone(winternitzParameter);
+ }
+
+ /**
+ * Returns the parameter K needed for authentication path computation
+ *
+ * @return The parameter K needed for authentication path computation
+ */
+ public int[] getK()
+ {
+ return Arrays.clone(K);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java
new file mode 100644
index 00000000..29e84b1f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java
@@ -0,0 +1,1041 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.util.Vector;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.spongycastle.pqc.crypto.gmss.util.WinternitzOTSignature;
+import org.spongycastle.util.Arrays;
+
+
+/**
+ * This class provides a specification for a GMSS private key.
+ */
+public class GMSSPrivateKeyParameters
+ extends GMSSKeyParameters
+{
+ private int[] index;
+
+ private byte[][] currentSeeds;
+ private byte[][] nextNextSeeds;
+
+ private byte[][][] currentAuthPaths;
+ private byte[][][] nextAuthPaths;
+
+ private Treehash[][] currentTreehash;
+ private Treehash[][] nextTreehash;
+
+ private Vector[] currentStack;
+ private Vector[] nextStack;
+
+ private Vector[][] currentRetain;
+ private Vector[][] nextRetain;
+
+ private byte[][][] keep;
+
+ private GMSSLeaf[] nextNextLeaf;
+ private GMSSLeaf[] upperLeaf;
+ private GMSSLeaf[] upperTreehashLeaf;
+
+ private int[] minTreehash;
+
+ private GMSSParameters gmssPS;
+
+ private byte[][] nextRoot;
+ private GMSSRootCalc[] nextNextRoot;
+
+ private byte[][] currentRootSig;
+ private GMSSRootSig[] nextRootSig;
+
+ private GMSSDigestProvider digestProvider;
+
+ private boolean used = false;
+
+ /**
+ * An array of the heights of the authentication trees of each layer
+ */
+ private int[] heightOfTrees;
+
+ /**
+ * An array of the Winternitz parameter 'w' of each layer
+ */
+ private int[] otsIndex;
+
+ /**
+ * The parameter K needed for the authentication path computation
+ */
+ private int[] K;
+
+ /**
+ * the number of Layers
+ */
+ private int numLayer;
+
+ /**
+ * The hash function used to construct the authentication trees
+ */
+ private Digest messDigestTrees;
+
+ /**
+ * The message digest length
+ */
+ private int mdLength;
+
+ /**
+ * The PRNG used for private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+
+ /**
+ * The number of leafs of one tree of each layer
+ */
+ private int[] numLeafs;
+
+
+ /**
+ * Generates a new GMSS private key
+ *
+ * @param currentSeed seed for the generation of private OTS keys for the
+ * current subtrees
+ * @param nextNextSeed seed for the generation of private OTS keys for the next
+ * subtrees
+ * @param currentAuthPath array of current authentication paths
+ * @param nextAuthPath array of next authentication paths
+ * @param currentTreehash array of current treehash instances
+ * @param nextTreehash array of next treehash instances
+ * @param currentStack array of current shared stacks
+ * @param nextStack array of next shared stacks
+ * @param currentRetain array of current retain stacks
+ * @param nextRetain array of next retain stacks
+ * @param nextRoot the roots of the next subtree
+ * @param currentRootSig array of signatures of the roots of the current subtrees
+ * @param gmssParameterset the GMSS Parameterset
+ * @see org.spongycastle.pqc.crypto.gmss.GMSSKeyPairGenerator
+ */
+
+ public GMSSPrivateKeyParameters(byte[][] currentSeed, byte[][] nextNextSeed,
+ byte[][][] currentAuthPath, byte[][][] nextAuthPath,
+ Treehash[][] currentTreehash, Treehash[][] nextTreehash,
+ Vector[] currentStack, Vector[] nextStack,
+ Vector[][] currentRetain, Vector[][] nextRetain, byte[][] nextRoot,
+ byte[][] currentRootSig, GMSSParameters gmssParameterset,
+ GMSSDigestProvider digestProvider)
+ {
+ this(null, currentSeed, nextNextSeed, currentAuthPath, nextAuthPath,
+ null, currentTreehash, nextTreehash, currentStack, nextStack,
+ currentRetain, nextRetain, null, null, null, null, nextRoot,
+ null, currentRootSig, null, gmssParameterset, digestProvider);
+ }
+
+ /**
+ * /**
+ *
+ * @param index tree indices
+ * @param keep keep array for the authPath algorithm
+ * @param currentTreehash treehash for authPath algorithm of current tree
+ * @param nextTreehash treehash for authPath algorithm of next tree (TREE+)
+ * @param currentStack shared stack for authPath algorithm of current tree
+ * @param nextStack shared stack for authPath algorithm of next tree (TREE+)
+ * @param currentRetain retain stack for authPath algorithm of current tree
+ * @param nextRetain retain stack for authPath algorithm of next tree (TREE+)
+ * @param nextNextLeaf array of upcoming leafs of the tree after next (LEAF++) of
+ * each layer
+ * @param upperLeaf needed for precomputation of upper nodes
+ * @param upperTreehashLeaf needed for precomputation of upper treehash nodes
+ * @param minTreehash index of next treehash instance to receive an update
+ * @param nextRoot the roots of the next trees (ROOT+)
+ * @param nextNextRoot the roots of the tree after next (ROOT++)
+ * @param currentRootSig array of signatures of the roots of the current subtrees
+ * (SIG)
+ * @param nextRootSig array of signatures of the roots of the next subtree
+ * (SIG+)
+ * @param gmssParameterset the GMSS Parameterset
+ */
+ public GMSSPrivateKeyParameters(int[] index, byte[][] currentSeeds,
+ byte[][] nextNextSeeds, byte[][][] currentAuthPaths,
+ byte[][][] nextAuthPaths, byte[][][] keep,
+ Treehash[][] currentTreehash, Treehash[][] nextTreehash,
+ Vector[] currentStack, Vector[] nextStack,
+ Vector[][] currentRetain, Vector[][] nextRetain,
+ GMSSLeaf[] nextNextLeaf, GMSSLeaf[] upperLeaf,
+ GMSSLeaf[] upperTreehashLeaf, int[] minTreehash, byte[][] nextRoot,
+ GMSSRootCalc[] nextNextRoot, byte[][] currentRootSig,
+ GMSSRootSig[] nextRootSig, GMSSParameters gmssParameterset,
+ GMSSDigestProvider digestProvider)
+ {
+
+ super(true, gmssParameterset);
+
+ // construct message digest
+
+ this.messDigestTrees = digestProvider.get();
+ this.mdLength = messDigestTrees.getDigestSize();
+
+
+ // Parameter
+ this.gmssPS = gmssParameterset;
+ this.otsIndex = gmssParameterset.getWinternitzParameter();
+ this.K = gmssParameterset.getK();
+ this.heightOfTrees = gmssParameterset.getHeightOfTrees();
+ // initialize numLayer
+ this.numLayer = gmssPS.getNumOfLayers();
+
+ // initialize index if null
+ if (index == null)
+ {
+ this.index = new int[numLayer];
+ for (int i = 0; i < numLayer; i++)
+ {
+ this.index[i] = 0;
+ }
+ }
+ else
+ {
+ this.index = index;
+ }
+
+ this.currentSeeds = currentSeeds;
+ this.nextNextSeeds = nextNextSeeds;
+
+ this.currentAuthPaths = currentAuthPaths;
+ this.nextAuthPaths = nextAuthPaths;
+
+ // initialize keep if null
+ if (keep == null)
+ {
+ this.keep = new byte[numLayer][][];
+ for (int i = 0; i < numLayer; i++)
+ {
+ this.keep[i] = new byte[(int)Math.floor(heightOfTrees[i] / 2)][mdLength];
+ }
+ }
+ else
+ {
+ this.keep = keep;
+ }
+
+ // initialize stack if null
+ if (currentStack == null)
+ {
+ this.currentStack = new Vector[numLayer];
+ for (int i = 0; i < numLayer; i++)
+ {
+ this.currentStack[i] = new Vector();
+ }
+ }
+ else
+ {
+ this.currentStack = currentStack;
+ }
+
+ // initialize nextStack if null
+ if (nextStack == null)
+ {
+ this.nextStack = new Vector[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.nextStack[i] = new Vector();
+ }
+ }
+ else
+ {
+ this.nextStack = nextStack;
+ }
+
+ this.currentTreehash = currentTreehash;
+ this.nextTreehash = nextTreehash;
+
+ this.currentRetain = currentRetain;
+ this.nextRetain = nextRetain;
+
+ this.nextRoot = nextRoot;
+
+ this.digestProvider = digestProvider;
+
+ if (nextNextRoot == null)
+ {
+ this.nextNextRoot = new GMSSRootCalc[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.nextNextRoot[i] = new GMSSRootCalc(
+ this.heightOfTrees[i + 1], this.K[i + 1], this.digestProvider);
+ }
+ }
+ else
+ {
+ this.nextNextRoot = nextNextRoot;
+ }
+ this.currentRootSig = currentRootSig;
+
+ // calculate numLeafs
+ numLeafs = new int[numLayer];
+ for (int i = 0; i < numLayer; i++)
+ {
+ numLeafs[i] = 1 << heightOfTrees[i];
+ }
+ // construct PRNG
+ this.gmssRandom = new GMSSRandom(messDigestTrees);
+
+ if (numLayer > 1)
+ {
+ // construct the nextNextLeaf (LEAFs++) array for upcoming leafs in
+ // tree after next (TREE++)
+ if (nextNextLeaf == null)
+ {
+ this.nextNextLeaf = new GMSSLeaf[numLayer - 2];
+ for (int i = 0; i < numLayer - 2; i++)
+ {
+ this.nextNextLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i + 1], numLeafs[i + 2], this.nextNextSeeds[i]);
+ }
+ }
+ else
+ {
+ this.nextNextLeaf = nextNextLeaf;
+ }
+ }
+ else
+ {
+ this.nextNextLeaf = new GMSSLeaf[0];
+ }
+
+ // construct the upperLeaf array for upcoming leafs in tree over the
+ // actual
+ if (upperLeaf == null)
+ {
+ this.upperLeaf = new GMSSLeaf[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.upperLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i],
+ numLeafs[i + 1], this.currentSeeds[i]);
+ }
+ }
+ else
+ {
+ this.upperLeaf = upperLeaf;
+ }
+
+ // construct the leafs for upcoming leafs in treehashs in tree over the
+ // actual
+ if (upperTreehashLeaf == null)
+ {
+ this.upperTreehashLeaf = new GMSSLeaf[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.upperTreehashLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i], numLeafs[i + 1]);
+ }
+ }
+ else
+ {
+ this.upperTreehashLeaf = upperTreehashLeaf;
+ }
+
+ if (minTreehash == null)
+ {
+ this.minTreehash = new int[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ this.minTreehash[i] = -1;
+ }
+ }
+ else
+ {
+ this.minTreehash = minTreehash;
+ }
+
+ // construct the nextRootSig (RootSig++)
+ byte[] dummy = new byte[mdLength];
+ byte[] OTSseed = new byte[mdLength];
+ if (nextRootSig == null)
+ {
+ this.nextRootSig = new GMSSRootSig[numLayer - 1];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ System.arraycopy(currentSeeds[i], 0, dummy, 0, mdLength);
+ gmssRandom.nextSeed(dummy);
+ OTSseed = gmssRandom.nextSeed(dummy);
+ this.nextRootSig[i] = new GMSSRootSig(digestProvider.get(), otsIndex[i],
+ heightOfTrees[i + 1]);
+ this.nextRootSig[i].initSign(OTSseed, nextRoot[i]);
+ }
+ }
+ else
+ {
+ this.nextRootSig = nextRootSig;
+ }
+ }
+
+ // we assume this only gets called from nextKey so used is never copied.
+ private GMSSPrivateKeyParameters(GMSSPrivateKeyParameters original)
+ {
+ super(true, original.getParameters());
+
+ this.index = Arrays.clone(original.index);
+ this.currentSeeds = Arrays.clone(original.currentSeeds);
+ this.nextNextSeeds = Arrays.clone(original.nextNextSeeds);
+ this.currentAuthPaths = Arrays.clone(original.currentAuthPaths);
+ this.nextAuthPaths = Arrays.clone(original.nextAuthPaths);
+ this.currentTreehash = original.currentTreehash;
+ this.nextTreehash = original.nextTreehash;
+ this.currentStack = original.currentStack;
+ this.nextStack = original.nextStack;
+ this.currentRetain = original.currentRetain;
+ this.nextRetain = original.nextRetain;
+ this.keep = Arrays.clone(original.keep);
+ this.nextNextLeaf = original.nextNextLeaf;
+ this.upperLeaf = original.upperLeaf;
+ this.upperTreehashLeaf = original.upperTreehashLeaf;
+ this.minTreehash = original.minTreehash;
+ this.gmssPS = original.gmssPS;
+ this.nextRoot = Arrays.clone(original.nextRoot);
+ this.nextNextRoot = original.nextNextRoot;
+ this.currentRootSig = original.currentRootSig;
+ this.nextRootSig = original.nextRootSig;
+ this.digestProvider = original.digestProvider;
+ this.heightOfTrees = original.heightOfTrees;
+ this.otsIndex = original.otsIndex;
+ this.K = original.K;
+ this.numLayer = original.numLayer;
+ this.messDigestTrees = original.messDigestTrees;
+ this.mdLength = original.mdLength;
+ this.gmssRandom = original.gmssRandom;
+ this.numLeafs = original.numLeafs;
+ }
+
+ public boolean isUsed()
+ {
+ return this.used;
+ }
+
+ public void markUsed()
+ {
+ this.used = true;
+ }
+
+ public GMSSPrivateKeyParameters nextKey()
+ {
+ GMSSPrivateKeyParameters nKey = new GMSSPrivateKeyParameters(this);
+
+ nKey.nextKey(gmssPS.getNumOfLayers() - 1);
+
+ return nKey;
+ }
+
+ /**
+ * This method updates the GMSS private key for the next signature
+ *
+ * @param layer the layer where the next key is processed
+ */
+ private void nextKey(int layer)
+ {
+ // only for lowest layer ( other layers indices are raised in nextTree()
+ // method )
+ if (layer == numLayer - 1)
+ {
+ index[layer]++;
+ } // else System.out.println(" --- nextKey on layer " + layer + "
+ // index is now : " + index[layer]);
+
+ // if tree of this layer is depleted
+ if (index[layer] == numLeafs[layer])
+ {
+ if (numLayer != 1)
+ {
+ nextTree(layer);
+ index[layer] = 0;
+ }
+ }
+ else
+ {
+ updateKey(layer);
+ }
+ }
+
+ /**
+ * Switch to next subtree if the current one is depleted
+ *
+ * @param layer the layer where the next tree is processed
+ */
+ private void nextTree(int layer)
+ {
+ // System.out.println("NextTree method called on layer " + layer);
+ // dont create next tree for the top layer
+ if (layer > 0)
+ {
+ // raise index for upper layer
+ index[layer - 1]++;
+
+ // test if it is already the last tree
+ boolean lastTree = true;
+ int z = layer;
+ do
+ {
+ z--;
+ if (index[z] < numLeafs[z])
+ {
+ lastTree = false;
+ }
+ }
+ while (lastTree && (z > 0));
+
+ // only construct next subtree if last one is not already in use
+ if (!lastTree)
+ {
+ gmssRandom.nextSeed(currentSeeds[layer]);
+
+ // last step of distributed signature calculation
+ nextRootSig[layer - 1].updateSign();
+
+ // last step of distributed leaf calculation for nextNextLeaf
+ if (layer > 1)
+ {
+ nextNextLeaf[layer - 1 - 1] = nextNextLeaf[layer - 1 - 1].nextLeaf();
+ }
+
+ // last step of distributed leaf calculation for upper leaf
+ upperLeaf[layer - 1] = upperLeaf[layer - 1].nextLeaf();
+
+ // last step of distributed leaf calculation for all treehashs
+
+ if (minTreehash[layer - 1] >= 0)
+ {
+ upperTreehashLeaf[layer - 1] = upperTreehashLeaf[layer - 1].nextLeaf();
+ byte[] leaf = this.upperTreehashLeaf[layer - 1].getLeaf();
+ // if update is required use the precomputed leaf to update
+ // treehash
+ try
+ {
+ currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .update(this.gmssRandom, leaf);
+ // System.out.println("UUUpdated TH " +
+ // minTreehash[layer - 1]);
+ if (currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .wasFinished())
+ {
+ // System.out.println("FFFinished TH " +
+ // minTreehash[layer - 1]);
+ }
+ }
+ catch (Exception e)
+ {
+ System.out.println(e);
+ }
+ }
+
+ // last step of nextNextAuthRoot calculation
+ this.updateNextNextAuthRoot(layer);
+
+ // ******************************************************** /
+
+ // NOW: advance to next tree on layer 'layer'
+
+ // NextRootSig --> currentRootSigs
+ this.currentRootSig[layer - 1] = nextRootSig[layer - 1]
+ .getSig();
+
+ // -----------------------
+
+ // nextTreehash --> currentTreehash
+ // nextNextTreehash --> nextTreehash
+ for (int i = 0; i < heightOfTrees[layer] - K[layer]; i++)
+ {
+ this.currentTreehash[layer][i] = this.nextTreehash[layer - 1][i];
+ this.nextTreehash[layer - 1][i] = this.nextNextRoot[layer - 1]
+ .getTreehash()[i];
+ }
+
+ // NextAuthPath --> currentAuthPath
+ // nextNextAuthPath --> nextAuthPath
+ for (int i = 0; i < heightOfTrees[layer]; i++)
+ {
+ System.arraycopy(nextAuthPaths[layer - 1][i], 0,
+ currentAuthPaths[layer][i], 0, mdLength);
+ System.arraycopy(nextNextRoot[layer - 1].getAuthPath()[i],
+ 0, nextAuthPaths[layer - 1][i], 0, mdLength);
+ }
+
+ // nextRetain --> currentRetain
+ // nextNextRetain --> nextRetain
+ for (int i = 0; i < K[layer] - 1; i++)
+ {
+ this.currentRetain[layer][i] = this.nextRetain[layer - 1][i];
+ this.nextRetain[layer - 1][i] = this.nextNextRoot[layer - 1]
+ .getRetain()[i];
+ }
+
+ // nextStack --> currentStack
+ this.currentStack[layer] = this.nextStack[layer - 1];
+ // nextNextStack --> nextStack
+ this.nextStack[layer - 1] = this.nextNextRoot[layer - 1]
+ .getStack();
+
+ // nextNextRoot --> nextRoot
+ this.nextRoot[layer - 1] = this.nextNextRoot[layer - 1]
+ .getRoot();
+ // -----------------------
+
+ // -----------------
+ byte[] OTSseed = new byte[mdLength];
+ byte[] dummy = new byte[mdLength];
+ // gmssRandom.setSeed(currentSeeds[layer]);
+ System
+ .arraycopy(currentSeeds[layer - 1], 0, dummy, 0,
+ mdLength);
+ OTSseed = gmssRandom.nextSeed(dummy); // only need OTSSeed
+ OTSseed = gmssRandom.nextSeed(dummy);
+ OTSseed = gmssRandom.nextSeed(dummy);
+ // nextWinSig[layer-1]=new
+ // GMSSWinSig(OTSseed,algNames,otsIndex[layer-1],heightOfTrees[layer],nextRoot[layer-1]);
+ nextRootSig[layer - 1].initSign(OTSseed, nextRoot[layer - 1]);
+
+ // nextKey for upper layer
+ nextKey(layer - 1);
+ }
+ }
+ }
+
+ /**
+ * This method computes the authpath (AUTH) for the current tree,
+ * Additionally the root signature for the next tree (SIG+), the authpath
+ * (AUTH++) and root (ROOT++) for the tree after next in layer
+ * <code>layer</code>, and the LEAF++^1 for the next next tree in the
+ * layer above are updated This method is used by nextKey()
+ *
+ * @param layer
+ */
+ private void updateKey(int layer)
+ {
+ // ----------current tree processing of actual layer---------
+ // compute upcoming authpath for current Tree (AUTH)
+ computeAuthPaths(layer);
+
+ // -----------distributed calculations part------------
+ // not for highest tree layer
+ if (layer > 0)
+ {
+
+ // compute (partial) next leaf on TREE++ (not on layer 1 and 0)
+ if (layer > 1)
+ {
+ nextNextLeaf[layer - 1 - 1] = nextNextLeaf[layer - 1 - 1].nextLeaf();
+ }
+
+ // compute (partial) next leaf on tree above (not on layer 0)
+ upperLeaf[layer - 1] = upperLeaf[layer - 1].nextLeaf();
+
+ // compute (partial) next leaf for all treehashs on tree above (not
+ // on layer 0)
+
+ int t = (int)Math
+ .floor((double)(this.getNumLeafs(layer) * 2)
+ / (double)(this.heightOfTrees[layer - 1] - this.K[layer - 1]));
+
+ if (index[layer] % t == 1)
+ {
+ // System.out.println(" layer: " + layer + " index: " +
+ // index[layer] + " t : " + t);
+
+ // take precomputed node for treehash update
+ // ------------------------------------------------
+ if (index[layer] > 1 && minTreehash[layer - 1] >= 0)
+ {
+ byte[] leaf = this.upperTreehashLeaf[layer - 1].getLeaf();
+ // if update is required use the precomputed leaf to update
+ // treehash
+ try
+ {
+ currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .update(this.gmssRandom, leaf);
+ // System.out.println("Updated TH " + minTreehash[layer
+ // - 1]);
+ if (currentTreehash[layer - 1][minTreehash[layer - 1]]
+ .wasFinished())
+ {
+ // System.out.println("Finished TH " +
+ // minTreehash[layer - 1]);
+ }
+ }
+ catch (Exception e)
+ {
+ System.out.println(e);
+ }
+ // ------------------------------------------------
+ }
+
+ // initialize next leaf precomputation
+ // ------------------------------------------------
+
+ // get lowest index of treehashs
+ this.minTreehash[layer - 1] = getMinTreehashIndex(layer - 1);
+
+ if (this.minTreehash[layer - 1] >= 0)
+ {
+ // initialize leaf
+ byte[] seed = this.currentTreehash[layer - 1][this.minTreehash[layer - 1]]
+ .getSeedActive();
+ this.upperTreehashLeaf[layer - 1] = new GMSSLeaf(
+ this.digestProvider.get(), this.otsIndex[layer - 1], t, seed);
+ this.upperTreehashLeaf[layer - 1] = this.upperTreehashLeaf[layer - 1].nextLeaf();
+ // System.out.println("restarted treehashleaf (" + (layer -
+ // 1) + "," + this.minTreehash[layer - 1] + ")");
+ }
+ // ------------------------------------------------
+
+ }
+ else
+ {
+ // update the upper leaf for the treehash one step
+ if (this.minTreehash[layer - 1] >= 0)
+ {
+ this.upperTreehashLeaf[layer - 1] = this.upperTreehashLeaf[layer - 1].nextLeaf();
+ // if (minTreehash[layer - 1] > 3)
+ // System.out.print("#");
+ }
+ }
+
+ // compute (partial) the signature of ROOT+ (RootSig+) (not on top
+ // layer)
+ nextRootSig[layer - 1].updateSign();
+
+ // compute (partial) AUTHPATH++ & ROOT++ (not on top layer)
+ if (index[layer] == 1)
+ {
+ // init root and authpath calculation for tree after next
+ // (AUTH++, ROOT++)
+ this.nextNextRoot[layer - 1].initialize(new Vector());
+ }
+
+ // update root and authpath calculation for tree after next (AUTH++,
+ // ROOT++)
+ this.updateNextNextAuthRoot(layer);
+ }
+ // ----------- end distributed calculations part-----------------
+ }
+
+ /**
+ * This method returns the index of the next Treehash instance that should
+ * receive an update
+ *
+ * @param layer the layer of the GMSS tree
+ * @return index of the treehash instance that should get the update
+ */
+ private int getMinTreehashIndex(int layer)
+ {
+ int minTreehash = -1;
+ for (int h = 0; h < heightOfTrees[layer] - K[layer]; h++)
+ {
+ if (currentTreehash[layer][h].wasInitialized()
+ && !currentTreehash[layer][h].wasFinished())
+ {
+ if (minTreehash == -1)
+ {
+ minTreehash = h;
+ }
+ else if (currentTreehash[layer][h].getLowestNodeHeight() < currentTreehash[layer][minTreehash]
+ .getLowestNodeHeight())
+ {
+ minTreehash = h;
+ }
+ }
+ }
+ return minTreehash;
+ }
+
+ /**
+ * Computes the upcoming currentAuthpath of layer <code>layer</code> using
+ * the revisited authentication path computation of Dahmen/Schneider 2008
+ *
+ * @param layer the actual layer
+ */
+ private void computeAuthPaths(int layer)
+ {
+
+ int Phi = index[layer];
+ int H = heightOfTrees[layer];
+ int K = this.K[layer];
+
+ // update all nextSeeds for seed scheduling
+ for (int i = 0; i < H - K; i++)
+ {
+ currentTreehash[layer][i].updateNextSeed(gmssRandom);
+ }
+
+ // STEP 1 of Algorithm
+ int Tau = heightOfPhi(Phi);
+
+ byte[] OTSseed = new byte[mdLength];
+ OTSseed = gmssRandom.nextSeed(currentSeeds[layer]);
+
+ // STEP 2 of Algorithm
+ // if phi's parent on height tau + 1 if left node, store auth_tau
+ // in keep_tau.
+ // TODO check it, formerly was
+ // int L = Phi / (int) Math.floor(Math.pow(2, Tau + 1));
+ // L %= 2;
+ int L = (Phi >>> (Tau + 1)) & 1;
+
+ byte[] tempKeep = new byte[mdLength];
+ // store the keep node not in keep[layer][tau/2] because it might be in
+ // use
+ // wait until the space is freed in step 4a
+ if (Tau < H - 1 && L == 0)
+ {
+ System.arraycopy(currentAuthPaths[layer][Tau], 0, tempKeep, 0,
+ mdLength);
+ }
+
+ byte[] help = new byte[mdLength];
+ // STEP 3 of Algorithm
+ // if phi is left child, compute and store leaf for next currentAuthPath
+ // path,
+ // (obtained by veriying current signature)
+ if (Tau == 0)
+ {
+ // LEAFCALC !!!
+ if (layer == numLayer - 1)
+ { // lowest layer computes the
+ // necessary leaf completely at this
+ // time
+ WinternitzOTSignature ots = new WinternitzOTSignature(OTSseed,
+ digestProvider.get(), otsIndex[layer]);
+ help = ots.getPublicKey();
+ }
+ else
+ { // other layers use the precomputed leafs in
+ // nextNextLeaf
+ byte[] dummy = new byte[mdLength];
+ System.arraycopy(currentSeeds[layer], 0, dummy, 0, mdLength);
+ gmssRandom.nextSeed(dummy);
+ help = upperLeaf[layer].getLeaf();
+ this.upperLeaf[layer].initLeafCalc(dummy);
+
+ // WinternitzOTSVerify otsver = new
+ // WinternitzOTSVerify(algNames, otsIndex[layer]);
+ // byte[] help2 = otsver.Verify(currentRoot[layer],
+ // currentRootSig[layer]);
+ // System.out.println(" --- " + layer + " " +
+ // ByteUtils.toHexString(help) + " " +
+ // ByteUtils.toHexString(help2));
+ }
+ System.arraycopy(help, 0, currentAuthPaths[layer][0], 0, mdLength);
+ }
+ else
+ {
+ // STEP 4a of Algorithm
+ // get new left currentAuthPath node on height tau
+ byte[] toBeHashed = new byte[mdLength << 1];
+ System.arraycopy(currentAuthPaths[layer][Tau - 1], 0, toBeHashed,
+ 0, mdLength);
+ // free the shared keep[layer][tau/2]
+ System.arraycopy(keep[layer][(int)Math.floor((Tau - 1) / 2)], 0,
+ toBeHashed, mdLength, mdLength);
+ messDigestTrees.update(toBeHashed, 0, toBeHashed.length);
+ currentAuthPaths[layer][Tau] = new byte[messDigestTrees.getDigestSize()];
+ messDigestTrees.doFinal(currentAuthPaths[layer][Tau], 0);
+
+ // STEP 4b and 4c of Algorithm
+ // copy right nodes to currentAuthPath on height 0..Tau-1
+ for (int i = 0; i < Tau; i++)
+ {
+
+ // STEP 4b of Algorithm
+ // 1st: copy from treehashs
+ if (i < H - K)
+ {
+ if (currentTreehash[layer][i].wasFinished())
+ {
+ System.arraycopy(currentTreehash[layer][i]
+ .getFirstNode(), 0, currentAuthPaths[layer][i],
+ 0, mdLength);
+ currentTreehash[layer][i].destroy();
+ }
+ else
+ {
+ System.err
+ .println("Treehash ("
+ + layer
+ + ","
+ + i
+ + ") not finished when needed in AuthPathComputation");
+ }
+ }
+
+ // 2nd: copy precomputed values from Retain
+ if (i < H - 1 && i >= H - K)
+ {
+ if (currentRetain[layer][i - (H - K)].size() > 0)
+ {
+ // pop element from retain
+ System.arraycopy(currentRetain[layer][i - (H - K)]
+ .lastElement(), 0, currentAuthPaths[layer][i],
+ 0, mdLength);
+ currentRetain[layer][i - (H - K)]
+ .removeElementAt(currentRetain[layer][i
+ - (H - K)].size() - 1);
+ }
+ }
+
+ // STEP 4c of Algorithm
+ // initialize new stack at heights 0..Tau-1
+ if (i < H - K)
+ {
+ // create stacks anew
+ int startPoint = Phi + 3 * (1 << i);
+ if (startPoint < numLeafs[layer])
+ {
+ // if (layer < 2) {
+ // System.out.println("initialized TH " + i + " on layer
+ // " + layer);
+ // }
+ currentTreehash[layer][i].initialize();
+ }
+ }
+ }
+ }
+
+ // now keep space is free to use
+ if (Tau < H - 1 && L == 0)
+ {
+ System.arraycopy(tempKeep, 0,
+ keep[layer][(int)Math.floor(Tau / 2)], 0, mdLength);
+ }
+
+ // only update empty stack at height h if all other stacks have
+ // tailnodes with height >h
+ // finds active stack with lowest node height, choses lower index in
+ // case of tie
+
+ // on the lowest layer leafs must be computed at once, no precomputation
+ // is possible. So all treehash updates are done at once here
+ if (layer == numLayer - 1)
+ {
+ for (int tmp = 1; tmp <= (H - K) / 2; tmp++)
+ {
+ // index of the treehash instance that receives the next update
+ int minTreehash = getMinTreehashIndex(layer);
+
+ // if active treehash is found update with a leaf
+ if (minTreehash >= 0)
+ {
+ try
+ {
+ byte[] seed = new byte[mdLength];
+ System.arraycopy(
+ this.currentTreehash[layer][minTreehash]
+ .getSeedActive(), 0, seed, 0, mdLength);
+ byte[] seed2 = gmssRandom.nextSeed(seed);
+ WinternitzOTSignature ots = new WinternitzOTSignature(
+ seed2, this.digestProvider.get(), this.otsIndex[layer]);
+ byte[] leaf = ots.getPublicKey();
+ currentTreehash[layer][minTreehash].update(
+ this.gmssRandom, leaf);
+ }
+ catch (Exception e)
+ {
+ System.out.println(e);
+ }
+ }
+ }
+ }
+ else
+ { // on higher layers the updates are done later
+ this.minTreehash[layer] = getMinTreehashIndex(layer);
+ }
+ }
+
+ /**
+ * Returns the largest h such that 2^h | Phi
+ *
+ * @param Phi the leaf index
+ * @return The largest <code>h</code> with <code>2^h | Phi</code> if
+ * <code>Phi!=0</code> else return <code>-1</code>
+ */
+ private int heightOfPhi(int Phi)
+ {
+ if (Phi == 0)
+ {
+ return -1;
+ }
+ int Tau = 0;
+ int modul = 1;
+ while (Phi % modul == 0)
+ {
+ modul *= 2;
+ Tau += 1;
+ }
+ return Tau - 1;
+ }
+
+ /**
+ * Updates the authentication path and root calculation for the tree after
+ * next (AUTH++, ROOT++) in layer <code>layer</code>
+ *
+ * @param layer
+ */
+ private void updateNextNextAuthRoot(int layer)
+ {
+
+ byte[] OTSseed = new byte[mdLength];
+ OTSseed = gmssRandom.nextSeed(nextNextSeeds[layer - 1]);
+
+ // get the necessary leaf
+ if (layer == numLayer - 1)
+ { // lowest layer computes the necessary
+ // leaf completely at this time
+ WinternitzOTSignature ots = new WinternitzOTSignature(OTSseed,
+ digestProvider.get(), otsIndex[layer]);
+ this.nextNextRoot[layer - 1].update(nextNextSeeds[layer - 1], ots
+ .getPublicKey());
+ }
+ else
+ { // other layers use the precomputed leafs in nextNextLeaf
+ this.nextNextRoot[layer - 1].update(nextNextSeeds[layer - 1], nextNextLeaf[layer - 1].getLeaf());
+ this.nextNextLeaf[layer - 1].initLeafCalc(nextNextSeeds[layer - 1]);
+ }
+ }
+
+ public int[] getIndex()
+ {
+ return index;
+ }
+
+ /**
+ * @return The current index of layer i
+ */
+ public int getIndex(int i)
+ {
+ return index[i];
+ }
+
+ public byte[][] getCurrentSeeds()
+ {
+ return Arrays.clone(currentSeeds);
+ }
+
+ public byte[][][] getCurrentAuthPaths()
+ {
+ return Arrays.clone(currentAuthPaths);
+ }
+
+ /**
+ * @return The one-time signature of the root of the current subtree
+ */
+ public byte[] getSubtreeRootSig(int i)
+ {
+ return currentRootSig[i];
+ }
+
+
+ public GMSSDigestProvider getName()
+ {
+ return digestProvider;
+ }
+
+ /**
+ * @return The number of leafs of each tree of layer i
+ */
+ public int getNumLeafs(int i)
+ {
+ return numLeafs[i];
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPublicKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPublicKeyParameters.java
new file mode 100644
index 00000000..381ed00b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSPublicKeyParameters.java
@@ -0,0 +1,33 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+
+public class GMSSPublicKeyParameters
+ extends GMSSKeyParameters
+{
+ /**
+ * The GMSS public key
+ */
+ private byte[] gmssPublicKey;
+
+ /**
+ * The constructor.
+ *
+ * @param key a raw GMSS public key
+ * @param gmssParameterSet an instance of GMSSParameterset
+ */
+ public GMSSPublicKeyParameters(byte[] key, GMSSParameters gmssParameterSet)
+ {
+ super(false, gmssParameterSet);
+ this.gmssPublicKey = key;
+ }
+
+ /**
+ * Returns the GMSS public key
+ *
+ * @return The GMSS public key
+ */
+ public byte[] getPublicKey()
+ {
+ return gmssPublicKey;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootCalc.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootCalc.java
new file mode 100644
index 00000000..88e87e9c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootCalc.java
@@ -0,0 +1,596 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Integers;
+import org.spongycastle.util.encoders.Hex;
+
+
+/**
+ * This class computes a whole Merkle tree and saves the needed values for
+ * AuthPath computation. It is used for precomputation of the root of a
+ * following tree. After initialization, 2^H updates are required to complete
+ * the root. Every update requires one leaf value as parameter. While computing
+ * the root all initial values for the authentication path algorithm (treehash,
+ * auth, retain) are stored for later use.
+ */
+public class GMSSRootCalc
+{
+
+ /**
+ * max height of the tree
+ */
+ private int heightOfTree;
+
+ /**
+ * length of the messageDigest
+ */
+ private int mdLength;
+
+ /**
+ * the treehash instances of the tree
+ */
+ private Treehash[] treehash;
+
+ /**
+ * stores the retain nodes for authPath computation
+ */
+ private Vector[] retain;
+
+ /**
+ * finally stores the root of the tree when finished
+ */
+ private byte[] root;
+
+ /**
+ * stores the authentication path y_1(i), i = 0..H-1
+ */
+ private byte[][] AuthPath;
+
+ /**
+ * the value K for the authentication path computation
+ */
+ private int K;
+
+ /**
+ * Vector element that stores the nodes on the stack
+ */
+ private Vector tailStack;
+
+ /**
+ * stores the height of all nodes laying on the tailStack
+ */
+ private Vector heightOfNodes;
+ /**
+ * The hash function used for the construction of the authentication trees
+ */
+ private Digest messDigestTree;
+
+ /**
+ * An array of strings containing the name of the hash function used to
+ * construct the authentication trees and used by the OTS.
+ */
+ private GMSSDigestProvider digestProvider;
+
+ /**
+ * stores the index of the current node on each height of the tree
+ */
+ private int[] index;
+
+ /**
+ * true if instance was already initialized, false otherwise
+ */
+ private boolean isInitialized;
+
+ /**
+ * true it instance was finished
+ */
+ private boolean isFinished;
+
+ /**
+ * Integer that stores the index of the next seed that has to be omitted to
+ * the treehashs
+ */
+ private int indexForNextSeed;
+
+ /**
+ * temporary integer that stores the height of the next treehash instance
+ * that gets initialized with a seed
+ */
+ private int heightOfNextSeed;
+
+ /**
+ * This constructor regenerates a prior treehash object
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function and PRNG and the digest of the corresponding
+ * provider
+ * @param statByte status bytes
+ * @param statInt status ints
+ */
+ public GMSSRootCalc(Digest digest, byte[][] statByte, int[] statInt,
+ Treehash[] treeH, Vector[] ret)
+ {
+ this.messDigestTree = digestProvider.get();
+ this.digestProvider = digestProvider;
+ // decode statInt
+ this.heightOfTree = statInt[0];
+ this.mdLength = statInt[1];
+ this.K = statInt[2];
+ this.indexForNextSeed = statInt[3];
+ this.heightOfNextSeed = statInt[4];
+ if (statInt[5] == 1)
+ {
+ this.isFinished = true;
+ }
+ else
+ {
+ this.isFinished = false;
+ }
+ if (statInt[6] == 1)
+ {
+ this.isInitialized = true;
+ }
+ else
+ {
+ this.isInitialized = false;
+ }
+
+ int tailLength = statInt[7];
+
+ this.index = new int[heightOfTree];
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ this.index[i] = statInt[8 + i];
+ }
+
+ this.heightOfNodes = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.heightOfNodes.addElement(Integers.valueOf(statInt[8 + heightOfTree
+ + i]));
+ }
+
+ // decode statByte
+ this.root = statByte[0];
+
+ this.AuthPath = new byte[heightOfTree][mdLength];
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ this.AuthPath[i] = statByte[1 + i];
+ }
+
+ this.tailStack = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.tailStack.addElement(statByte[1 + heightOfTree + i]);
+ }
+
+ // decode treeH
+ this.treehash = GMSSUtils.clone(treeH);
+
+ // decode ret
+ this.retain = GMSSUtils.clone(ret);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param heightOfTree maximal height of the tree
+ * @param digestProvider an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding
+ * provider
+ */
+ public GMSSRootCalc(int heightOfTree, int K, GMSSDigestProvider digestProvider)
+ {
+ this.heightOfTree = heightOfTree;
+ this.digestProvider = digestProvider;
+ this.messDigestTree = digestProvider.get();
+ this.mdLength = messDigestTree.getDigestSize();
+ this.K = K;
+ this.index = new int[heightOfTree];
+ this.AuthPath = new byte[heightOfTree][mdLength];
+ this.root = new byte[mdLength];
+ // this.treehash = new Treehash[this.heightOfTree - this.K];
+ this.retain = new Vector[this.K - 1];
+ for (int i = 0; i < K - 1; i++)
+ {
+ this.retain[i] = new Vector();
+ }
+
+ }
+
+ /**
+ * Initializes the calculation of a new root
+ *
+ * @param sharedStack the stack shared by all treehash instances of this tree
+ */
+ public void initialize(Vector sharedStack)
+ {
+ this.treehash = new Treehash[this.heightOfTree - this.K];
+ for (int i = 0; i < this.heightOfTree - this.K; i++)
+ {
+ this.treehash[i] = new Treehash(sharedStack, i, this.digestProvider.get());
+ }
+
+ this.index = new int[heightOfTree];
+ this.AuthPath = new byte[heightOfTree][mdLength];
+ this.root = new byte[mdLength];
+
+ this.tailStack = new Vector();
+ this.heightOfNodes = new Vector();
+ this.isInitialized = true;
+ this.isFinished = false;
+
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ this.index[i] = -1;
+ }
+
+ this.retain = new Vector[this.K - 1];
+ for (int i = 0; i < K - 1; i++)
+ {
+ this.retain[i] = new Vector();
+ }
+
+ this.indexForNextSeed = 3;
+ this.heightOfNextSeed = 0;
+ }
+
+ /**
+ * updates the root with one leaf and stores needed values in retain,
+ * treehash or authpath. Additionally counts the seeds used. This method is
+ * used when performing the updates for TREE++.
+ *
+ * @param seed the initial seed for treehash: seedNext
+ * @param leaf the height of the treehash
+ */
+ public void update(byte[] seed, byte[] leaf)
+ {
+ if (this.heightOfNextSeed < (this.heightOfTree - this.K)
+ && this.indexForNextSeed - 2 == index[0])
+ {
+ this.initializeTreehashSeed(seed, this.heightOfNextSeed);
+ this.heightOfNextSeed++;
+ this.indexForNextSeed *= 2;
+ }
+ // now call the simple update
+ this.update(leaf);
+ }
+
+ /**
+ * Updates the root with one leaf and stores the needed values in retain,
+ * treehash or authpath
+ */
+ public void update(byte[] leaf)
+ {
+
+ if (isFinished)
+ {
+ System.out.print("Too much updates for Tree!!");
+ return;
+ }
+ if (!isInitialized)
+ {
+ System.err.println("GMSSRootCalc not initialized!");
+ return;
+ }
+
+ // a new leaf was omitted, so raise index on lowest layer
+ index[0]++;
+
+ // store the nodes on the lowest layer in treehash or authpath
+ if (index[0] == 1)
+ {
+ System.arraycopy(leaf, 0, AuthPath[0], 0, mdLength);
+ }
+ else if (index[0] == 3)
+ {
+ // store in treehash only if K < H
+ if (heightOfTree > K)
+ {
+ treehash[0].setFirstNode(leaf);
+ }
+ }
+
+ if ((index[0] - 3) % 2 == 0 && index[0] >= 3)
+ {
+ // store in retain if K = H
+ if (heightOfTree == K)
+ // TODO: check it
+ {
+ retain[0].insertElementAt(leaf, 0);
+ }
+ }
+
+ // if first update to this tree is made
+ if (index[0] == 0)
+ {
+ tailStack.addElement(leaf);
+ heightOfNodes.addElement(Integers.valueOf(0));
+ }
+ else
+ {
+
+ byte[] help = new byte[mdLength];
+ byte[] toBeHashed = new byte[mdLength << 1];
+
+ // store the new leaf in help
+ System.arraycopy(leaf, 0, help, 0, mdLength);
+ int helpHeight = 0;
+ // while top to nodes have same height
+ while (tailStack.size() > 0
+ && helpHeight == ((Integer)heightOfNodes.lastElement())
+ .intValue())
+ {
+
+ // help <-- hash(stack top element || help)
+ System.arraycopy(tailStack.lastElement(), 0, toBeHashed, 0,
+ mdLength);
+ tailStack.removeElementAt(tailStack.size() - 1);
+ heightOfNodes.removeElementAt(heightOfNodes.size() - 1);
+ System.arraycopy(help, 0, toBeHashed, mdLength, mdLength);
+
+ messDigestTree.update(toBeHashed, 0, toBeHashed.length);
+ help = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(help, 0);
+
+ // the new help node is one step higher
+ helpHeight++;
+ if (helpHeight < heightOfTree)
+ {
+ index[helpHeight]++;
+
+ // add index 1 element to initial authpath
+ if (index[helpHeight] == 1)
+ {
+ System.arraycopy(help, 0, AuthPath[helpHeight], 0,
+ mdLength);
+ }
+
+ if (helpHeight >= heightOfTree - K)
+ {
+ if (helpHeight == 0)
+ {
+ System.out.println("M���P");
+ }
+ // add help element to retain stack if it is a right
+ // node
+ // and not stored in treehash
+ if ((index[helpHeight] - 3) % 2 == 0
+ && index[helpHeight] >= 3)
+ // TODO: check it
+ {
+ retain[helpHeight - (heightOfTree - K)]
+ .insertElementAt(help, 0);
+ }
+ }
+ else
+ {
+ // if element is third in his line add it to treehash
+ if (index[helpHeight] == 3)
+ {
+ treehash[helpHeight].setFirstNode(help);
+ }
+ }
+ }
+ }
+ // push help element to the stack
+ tailStack.addElement(help);
+ heightOfNodes.addElement(Integers.valueOf(helpHeight));
+
+ // is the root calculation finished?
+ if (helpHeight == heightOfTree)
+ {
+ isFinished = true;
+ isInitialized = false;
+ root = (byte[])tailStack.lastElement();
+ }
+ }
+
+ }
+
+ /**
+ * initializes the seeds for the treehashs of the tree precomputed by this
+ * class
+ *
+ * @param seed the initial seed for treehash: seedNext
+ * @param index the height of the treehash
+ */
+ public void initializeTreehashSeed(byte[] seed, int index)
+ {
+ treehash[index].initializeSeed(seed);
+ }
+
+ /**
+ * Method to check whether the instance has been initialized or not
+ *
+ * @return true if treehash was already initialized
+ */
+ public boolean wasInitialized()
+ {
+ return isInitialized;
+ }
+
+ /**
+ * Method to check whether the instance has been finished or not
+ *
+ * @return true if tree has reached its maximum height
+ */
+ public boolean wasFinished()
+ {
+ return isFinished;
+ }
+
+ /**
+ * returns the authentication path of the first leaf of the tree
+ *
+ * @return the authentication path of the first leaf of the tree
+ */
+ public byte[][] getAuthPath()
+ {
+ return GMSSUtils.clone(AuthPath);
+ }
+
+ /**
+ * returns the initial treehash instances, storing value y_3(i)
+ *
+ * @return the initial treehash instances, storing value y_3(i)
+ */
+ public Treehash[] getTreehash()
+ {
+ return GMSSUtils.clone(treehash);
+ }
+
+ /**
+ * returns the retain stacks storing all right nodes near to the root
+ *
+ * @return the retain stacks storing all right nodes near to the root
+ */
+ public Vector[] getRetain()
+ {
+ return GMSSUtils.clone(retain);
+ }
+
+ /**
+ * returns the finished root value
+ *
+ * @return the finished root value
+ */
+ public byte[] getRoot()
+ {
+ return Arrays.clone(root);
+ }
+
+ /**
+ * returns the shared stack
+ *
+ * @return the shared stack
+ */
+ public Vector getStack()
+ {
+ Vector copy = new Vector();
+ for (Enumeration en = tailStack.elements(); en.hasMoreElements();)
+ {
+ copy.addElement(en.nextElement());
+ }
+ return copy;
+ }
+
+ /**
+ * Returns the status byte array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status bytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ int tailLength;
+ if (tailStack == null)
+ {
+ tailLength = 0;
+ }
+ else
+ {
+ tailLength = tailStack.size();
+ }
+ byte[][] statByte = new byte[1 + heightOfTree + tailLength][64]; //FIXME: messDigestTree.getByteLength()
+ statByte[0] = root;
+
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ statByte[1 + i] = AuthPath[i];
+ }
+ for (int i = 0; i < tailLength; i++)
+ {
+ statByte[1 + heightOfTree + i] = (byte[])tailStack.elementAt(i);
+ }
+
+ return statByte;
+ }
+
+ /**
+ * Returns the status int array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status ints
+ */
+ public int[] getStatInt()
+ {
+
+ int tailLength;
+ if (tailStack == null)
+ {
+ tailLength = 0;
+ }
+ else
+ {
+ tailLength = tailStack.size();
+ }
+ int[] statInt = new int[8 + heightOfTree + tailLength];
+ statInt[0] = heightOfTree;
+ statInt[1] = mdLength;
+ statInt[2] = K;
+ statInt[3] = indexForNextSeed;
+ statInt[4] = heightOfNextSeed;
+ if (isFinished)
+ {
+ statInt[5] = 1;
+ }
+ else
+ {
+ statInt[5] = 0;
+ }
+ if (isInitialized)
+ {
+ statInt[6] = 1;
+ }
+ else
+ {
+ statInt[6] = 0;
+ }
+ statInt[7] = tailLength;
+
+ for (int i = 0; i < heightOfTree; i++)
+ {
+ statInt[8 + i] = index[i];
+ }
+ for (int i = 0; i < tailLength; i++)
+ {
+ statInt[8 + heightOfTree + i] = ((Integer)heightOfNodes
+ .elementAt(i)).intValue();
+ }
+
+ return statInt;
+ }
+
+ /**
+ * @return a human readable version of the structure
+ */
+ public String toString()
+ {
+ String out = "";
+ int tailLength;
+ if (tailStack == null)
+ {
+ tailLength = 0;
+ }
+ else
+ {
+ tailLength = tailStack.size();
+ }
+
+ for (int i = 0; i < 8 + heightOfTree + tailLength; i++)
+ {
+ out = out + getStatInt()[i] + " ";
+ }
+ for (int i = 0; i < 1 + heightOfTree + tailLength; i++)
+ {
+ out = out + new String(Hex.encode(getStatByte()[i])) + " ";
+ }
+ out = out + " " + digestProvider.get().getDigestSize();
+ return out;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootSig.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootSig.java
new file mode 100644
index 00000000..f08529cf
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSRootSig.java
@@ -0,0 +1,666 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.spongycastle.util.encoders.Hex;
+
+
+/**
+ * This class implements the distributed signature generation of the Winternitz
+ * one-time signature scheme (OTSS), described in C.Dods, N.P. Smart, and M.
+ * Stam, "Hash Based Digital Signature Schemes", LNCS 3796, pages 96&#8211;115,
+ * 2005. The class is used by the GMSS classes.
+ */
+public class GMSSRootSig
+{
+
+ /**
+ * The hash function used by the OTS
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * The length of the message digest and private key
+ */
+ private int mdsize, keysize;
+
+ /**
+ * The private key
+ */
+ private byte[] privateKeyOTS;
+
+ /**
+ * The message bytes
+ */
+ private byte[] hash;
+
+ /**
+ * The signature bytes
+ */
+ private byte[] sign;
+
+ /**
+ * The Winternitz parameter
+ */
+ private int w;
+
+ /**
+ * The source of randomness for OTS private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+ /**
+ * Sizes of the message
+ */
+ private int messagesize;
+
+ /**
+ * Some precalculated values
+ */
+ private int k;
+
+ /**
+ * Some variables for storing the actual status of distributed signing
+ */
+ private int r, test, counter, ii;
+
+ /**
+ * variables for storing big numbers for the actual status of distributed
+ * signing
+ */
+ private long test8, big8;
+
+ /**
+ * The necessary steps of each updateSign() call
+ */
+ private int steps;
+
+ /**
+ * The checksum part
+ */
+ private int checksum;
+
+ /**
+ * The height of the tree
+ */
+ private int height;
+
+ /**
+ * The current intern OTSseed
+ */
+ private byte[] seed;
+
+ /**
+ * This constructor regenerates a prior GMSSRootSig object used by the
+ * GMSSPrivateKeyASN.1 class
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function, the digest of the PRGN and the names of the
+ * corresponding providers
+ * @param statByte status byte array
+ * @param statInt status int array
+ */
+ public GMSSRootSig(Digest digest, byte[][] statByte, int[] statInt)
+ {
+ messDigestOTS = digest;
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ this.counter = statInt[0];
+ this.test = statInt[1];
+ this.ii = statInt[2];
+ this.r = statInt[3];
+ this.steps = statInt[4];
+ this.keysize = statInt[5];
+ this.height = statInt[6];
+ this.w = statInt[7];
+ this.checksum = statInt[8];
+
+ this.mdsize = messDigestOTS.getDigestSize();
+
+ this.k = (1 << w) - 1;
+
+ int mdsizeBit = mdsize << 3;
+ this.messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+
+ this.privateKeyOTS = statByte[0];
+ this.seed = statByte[1];
+ this.hash = statByte[2];
+
+ this.sign = statByte[3];
+
+ this.test8 = ((statByte[4][0] & 0xff))
+ | ((long)(statByte[4][1] & 0xff) << 8)
+ | ((long)(statByte[4][2] & 0xff) << 16)
+ | ((long)(statByte[4][3] & 0xff)) << 24
+ | ((long)(statByte[4][4] & 0xff)) << 32
+ | ((long)(statByte[4][5] & 0xff)) << 40
+ | ((long)(statByte[4][6] & 0xff)) << 48
+ | ((long)(statByte[4][7] & 0xff)) << 56;
+
+ this.big8 = ((statByte[4][8] & 0xff))
+ | ((long)(statByte[4][9] & 0xff) << 8)
+ | ((long)(statByte[4][10] & 0xff) << 16)
+ | ((long)(statByte[4][11] & 0xff)) << 24
+ | ((long)(statByte[4][12] & 0xff)) << 32
+ | ((long)(statByte[4][13] & 0xff)) << 40
+ | ((long)(statByte[4][14] & 0xff)) << 48
+ | ((long)(statByte[4][15] & 0xff)) << 56;
+ }
+
+ /**
+ * The constructor generates the PRNG and initializes some variables
+ *
+ * @param digest an array of strings, containing the digest of the used hash
+ * function, the digest of the PRGN and the names of the
+ * corresponding providers
+ * @param w the winternitz parameter
+ * @param height the heigth of the tree
+ */
+ public GMSSRootSig(Digest digest, int w, int height)
+ {
+ messDigestOTS = digest;
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ this.mdsize = messDigestOTS.getDigestSize();
+ this.w = w;
+ this.height = height;
+
+ this.k = (1 << w) - 1;
+
+ int mdsizeBit = mdsize << 3;
+ this.messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+ }
+
+ /**
+ * This method initializes the distributed sigature calculation. Variables
+ * are reseted and necessary steps are calculated
+ *
+ * @param seed0 the initial OTSseed
+ * @param message the massage which will be signed
+ */
+ public void initSign(byte[] seed0, byte[] message)
+ {
+
+ // create hash of message m
+ this.hash = new byte[mdsize];
+ messDigestOTS.update(message, 0, message.length);
+ this.hash = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(this.hash, 0);
+
+ // variables for calculation of steps
+ byte[] messPart = new byte[mdsize];
+ System.arraycopy(hash, 0, messPart, 0, mdsize);
+ int checkPart = 0;
+ int sumH = 0;
+ int checksumsize = getLog((messagesize << w) + 1);
+
+ // ------- calculation of necessary steps ------
+ if (8 % w == 0)
+ {
+ int dt = 8 / w;
+ // message part
+ for (int a = 0; a < mdsize; a++)
+ {
+ // count necessary hashs in 'sumH'
+ for (int b = 0; b < dt; b++)
+ {
+ sumH += messPart[a] & k;
+ messPart[a] = (byte)(messPart[a] >>> w);
+ }
+ }
+ // checksum part
+ this.checksum = (messagesize << w) - sumH;
+ checkPart = checksum;
+ // count necessary hashs in 'sumH'
+ for (int b = 0; b < checksumsize; b += w)
+ {
+ sumH += checkPart & k;
+ checkPart >>>= w;
+ }
+ } // end if ( 8 % w == 0 )
+ else if (w < 8)
+ {
+ long big8;
+ int ii = 0;
+ int dt = mdsize / w;
+
+ // first d*w bytes of hash (main message part)
+ for (int i = 0; i < dt; i++)
+ {
+ big8 = 0;
+ for (int j = 0; j < w; j++)
+ {
+ big8 ^= (messPart[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ // count necessary hashs in 'sumH'
+ for (int j = 0; j < 8; j++)
+ {
+ sumH += (int)(big8 & k);
+ big8 >>>= w;
+ }
+ }
+ // rest of message part
+ dt = mdsize % w;
+ big8 = 0;
+ for (int j = 0; j < dt; j++)
+ {
+ big8 ^= (messPart[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ dt <<= 3;
+ // count necessary hashs in 'sumH'
+ for (int j = 0; j < dt; j += w)
+ {
+ sumH += (int)(big8 & k);
+ big8 >>>= w;
+ }
+ // checksum part
+ this.checksum = (messagesize << w) - sumH;
+ checkPart = checksum;
+ // count necessary hashs in 'sumH'
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ sumH += checkPart & k;
+ checkPart >>>= w;
+ }
+ }// end if(w<8)
+ else if (w < 57)
+ {
+ long big8;
+ int r = 0;
+ int s, f, rest, ii;
+
+ // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w (main
+ // message part)
+ while (r <= ((mdsize << 3) - w))
+ {
+ s = r >>> 3;
+ rest = r % 8;
+ r += w;
+ f = (r + 7) >>> 3;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < f; j++)
+ {
+ big8 ^= (messPart[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+ big8 >>>= rest;
+ // count necessary hashs in 'sumH'
+ sumH += (big8 & k);
+
+ }
+ // rest of message part
+ s = r >>> 3;
+ if (s < mdsize)
+ {
+ rest = r % 8;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < mdsize; j++)
+ {
+ big8 ^= (messPart[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ // count necessary hashs in 'sumH'
+ sumH += (big8 & k);
+ }
+ // checksum part
+ this.checksum = (messagesize << w) - sumH;
+ checkPart = checksum;
+ // count necessary hashs in 'sumH'
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ sumH += (checkPart & k);
+ checkPart >>>= w;
+ }
+ }// end if(w<57)
+
+ // calculate keysize
+ this.keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+
+ // calculate steps: 'keysize' times PRNG, 'sumH' times hashing,
+ // (1<<height)-1 updateSign() calls
+ this.steps = (int)Math.ceil((double)(keysize + sumH)
+ / (double)((1 << height)));
+ // ----------------------------
+
+ // reset variables
+ this.sign = new byte[keysize * mdsize];
+ this.counter = 0;
+ this.test = 0;
+ this.ii = 0;
+ this.test8 = 0;
+ this.r = 0;
+ // define the private key messagesize
+ this.privateKeyOTS = new byte[mdsize];
+ // copy the seed
+ this.seed = new byte[mdsize];
+ System.arraycopy(seed0, 0, this.seed, 0, mdsize);
+
+ }
+
+ /**
+ * This Method performs <code>steps</code> steps of distributed signature
+ * calculaion
+ *
+ * @return true if signature is generated completly, else false
+ */
+ public boolean updateSign()
+ {
+ // steps times do
+
+ for (int s = 0; s < steps; s++)
+ { // do 'step' times
+
+ if (counter < keysize)
+ { // generate the private key or perform
+ // the next hash
+ oneStep();
+ }
+ if (counter == keysize)
+ {// finish
+ return true;
+ }
+ }
+
+ return false; // leaf not finished yet
+ }
+
+ /**
+ * @return The private OTS key
+ */
+ public byte[] getSig()
+ {
+
+ return sign;
+ }
+
+ /**
+ * @return The one-time signature of the message, generated step by step
+ */
+ private void oneStep()
+ {
+ // -------- if (8 % w == 0) ----------
+ if (8 % w == 0)
+ {
+ if (test == 0)
+ {
+ // get current OTSprivateKey
+ this.privateKeyOTS = gmssRandom.nextSeed(seed);
+ // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize);
+
+ if (ii < mdsize)
+ { // for main message part
+ test = hash[ii] & k;
+ hash[ii] = (byte)(hash[ii] >>> w);
+ }
+ else
+ { // for checksum part
+ test = checksum & k;
+ checksum >>>= w;
+ }
+ }
+ else if (test > 0)
+ { // hash the private Key 'test' times (on
+ // time each step)
+ messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length);
+ privateKeyOTS = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(privateKeyOTS, 0);
+ test--;
+ }
+ if (test == 0)
+ { // if all hashes done copy result to siganture
+ // array
+ System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize,
+ mdsize);
+ counter++;
+
+ if (counter % (8 / w) == 0)
+ { // raise array index for main
+ // massage part
+ ii++;
+ }
+ }
+
+ }// ----- end if (8 % w == 0) -----
+ // ---------- if ( w < 8 ) ----------------
+ else if (w < 8)
+ {
+
+ if (test == 0)
+ {
+ if (counter % 8 == 0 && ii < mdsize)
+ { // after every 8th "add
+ // to signature"-step
+ big8 = 0;
+ if (counter < ((mdsize / w) << 3))
+ {// main massage
+ // (generate w*8 Bits
+ // every time) part
+ for (int j = 0; j < w; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ }
+ else
+ { // rest of massage part (once)
+ for (int j = 0; j < mdsize % w; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ }
+ }
+ if (counter == messagesize)
+ { // checksum part (once)
+ big8 = checksum;
+ }
+
+ test = (int)(big8 & k);
+ // generate current OTSprivateKey
+ this.privateKeyOTS = gmssRandom.nextSeed(seed);
+ // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize);
+
+ }
+ else if (test > 0)
+ { // hash the private Key 'test' times (on
+ // time each step)
+ messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length);
+ privateKeyOTS = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(privateKeyOTS, 0);
+ test--;
+ }
+ if (test == 0)
+ { // if all hashes done copy result to siganture
+ // array
+ System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize,
+ mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+
+ }// ------- end if(w<8)--------------------------------
+ // --------- if w < 57 -----------------------------
+ else if (w < 57)
+ {
+
+ if (test8 == 0)
+ {
+ int s, f, rest;
+ big8 = 0;
+ ii = 0;
+ rest = r % 8;
+ s = r >>> 3;
+ // --- message part---
+ if (s < mdsize)
+ {
+ if (r <= ((mdsize << 3) - w))
+ { // first message part
+ r += w;
+ f = (r + 7) >>> 3;
+ }
+ else
+ { // rest of message part (once)
+ f = mdsize;
+ r += w;
+ }
+ // generate long 'big8' with minimum w next bits of the
+ // message array
+ for (int i = s; i < f; i++)
+ {
+ big8 ^= (hash[i] & 0xff) << (ii << 3);
+ ii++;
+ }
+ // delete bits on the right side, which were used already by
+ // the last loop
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ }
+ // --- checksum part
+ else
+ {
+ test8 = (checksum & k);
+ checksum >>>= w;
+ }
+ // generate current OTSprivateKey
+ this.privateKeyOTS = gmssRandom.nextSeed(seed);
+ // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize);
+
+ }
+ else if (test8 > 0)
+ { // hash the private Key 'test' times (on
+ // time each step)
+ messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length);
+ privateKeyOTS = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(privateKeyOTS, 0);
+ test8--;
+ }
+ if (test8 == 0)
+ { // if all hashes done copy result to siganture
+ // array
+ System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize,
+ mdsize);
+ counter++;
+ }
+
+ }
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer <code>intValue</code>.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of <code>intValue</code>
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+ /**
+ * This method returns the status byte array
+ *
+ * @return statBytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ byte[][] statByte = new byte[5][mdsize];
+ statByte[0] = privateKeyOTS;
+ statByte[1] = seed;
+ statByte[2] = hash;
+ statByte[3] = sign;
+ statByte[4] = this.getStatLong();
+
+ return statByte;
+ }
+
+ /**
+ * This method returns the status int array
+ *
+ * @return statInt
+ */
+ public int[] getStatInt()
+ {
+ int[] statInt = new int[9];
+ statInt[0] = counter;
+ statInt[1] = test;
+ statInt[2] = ii;
+ statInt[3] = r;
+ statInt[4] = steps;
+ statInt[5] = keysize;
+ statInt[6] = height;
+ statInt[7] = w;
+ statInt[8] = checksum;
+ return statInt;
+ }
+
+ /**
+ * Converts the long parameters into byte arrays to store it in
+ * statByte-Array
+ */
+ public byte[] getStatLong()
+ {
+ byte[] bytes = new byte[16];
+
+ bytes[0] = (byte)((test8) & 0xff);
+ bytes[1] = (byte)((test8 >> 8) & 0xff);
+ bytes[2] = (byte)((test8 >> 16) & 0xff);
+ bytes[3] = (byte)((test8 >> 24) & 0xff);
+ bytes[4] = (byte)((test8) >> 32 & 0xff);
+ bytes[5] = (byte)((test8 >> 40) & 0xff);
+ bytes[6] = (byte)((test8 >> 48) & 0xff);
+ bytes[7] = (byte)((test8 >> 56) & 0xff);
+
+ bytes[8] = (byte)((big8) & 0xff);
+ bytes[9] = (byte)((big8 >> 8) & 0xff);
+ bytes[10] = (byte)((big8 >> 16) & 0xff);
+ bytes[11] = (byte)((big8 >> 24) & 0xff);
+ bytes[12] = (byte)((big8) >> 32 & 0xff);
+ bytes[13] = (byte)((big8 >> 40) & 0xff);
+ bytes[14] = (byte)((big8 >> 48) & 0xff);
+ bytes[15] = (byte)((big8 >> 56) & 0xff);
+
+ return bytes;
+ }
+
+ /**
+ * returns a string representation of the instance
+ *
+ * @return a string representation of the instance
+ */
+ public String toString()
+ {
+ String out = "" + this.big8 + " ";
+ int[] statInt = new int[9];
+ statInt = this.getStatInt();
+ byte[][] statByte = new byte[5][mdsize];
+ statByte = this.getStatByte();
+ for (int i = 0; i < 9; i++)
+ {
+ out = out + statInt[i] + " ";
+ }
+ for (int i = 0; i < 5; i++)
+ {
+ out = out + new String(Hex.encode(statByte[i])) + " ";
+ }
+
+ return out;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java
new file mode 100644
index 00000000..2a78d385
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java
@@ -0,0 +1,403 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageSigner;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSUtil;
+import org.spongycastle.pqc.crypto.gmss.util.WinternitzOTSVerify;
+import org.spongycastle.pqc.crypto.gmss.util.WinternitzOTSignature;
+import org.spongycastle.util.Arrays;
+
+/**
+ * This class implements the GMSS signature scheme.
+ */
+public class GMSSSigner
+ implements MessageSigner
+{
+
+ /**
+ * Instance of GMSSParameterSpec
+ */
+ //private GMSSParameterSpec gmssParameterSpec;
+
+ /**
+ * Instance of GMSSUtilities
+ */
+ private GMSSUtil gmssUtil = new GMSSUtil();
+
+
+ /**
+ * The raw GMSS public key
+ */
+ private byte[] pubKeyBytes;
+
+ /**
+ * Hash function for the construction of the authentication trees
+ */
+ private Digest messDigestTrees;
+
+ /**
+ * The length of the hash function output
+ */
+ private int mdLength;
+
+ /**
+ * The number of tree layers
+ */
+ private int numLayer;
+
+ /**
+ * The hash function used by the OTS
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * An instance of the Winternitz one-time signature
+ */
+ private WinternitzOTSignature ots;
+
+ /**
+ * Array of strings containing the name of the hash function used by the OTS
+ * and the corresponding provider name
+ */
+ private GMSSDigestProvider digestProvider;
+
+ /**
+ * The current main tree and subtree indices
+ */
+ private int[] index;
+
+ /**
+ * Array of the authentication paths for the current trees of all layers
+ */
+ private byte[][][] currentAuthPaths;
+
+ /**
+ * The one-time signature of the roots of the current subtrees
+ */
+ private byte[][] subtreeRootSig;
+
+
+ /**
+ * The GMSSParameterset
+ */
+ private GMSSParameters gmssPS;
+
+ /**
+ * The PRNG
+ */
+ private GMSSRandom gmssRandom;
+
+ GMSSKeyParameters key;
+
+ // XXX needed? Source of randomness
+ private SecureRandom random;
+
+
+ /**
+ * The standard constructor tries to generate the MerkleTree Algorithm
+ * identifier with the corresponding OID.
+ *
+ * @param digest the digest to use
+ */
+ // TODO
+ public GMSSSigner(GMSSDigestProvider digest)
+ {
+ digestProvider = digest;
+ messDigestTrees = digest.get();
+ messDigestOTS = messDigestTrees;
+ mdLength = messDigestTrees.getDigestSize();
+ gmssRandom = new GMSSRandom(messDigestTrees);
+ }
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ // XXX random needed?
+ this.random = rParam.getRandom();
+ this.key = (GMSSPrivateKeyParameters)rParam.getParameters();
+ initSign();
+
+ }
+ else
+ {
+
+ this.random = new SecureRandom();
+ this.key = (GMSSPrivateKeyParameters)param;
+ initSign();
+ }
+ }
+ else
+ {
+ this.key = (GMSSPublicKeyParameters)param;
+ initVerify();
+
+ }
+
+ }
+
+
+ /**
+ * Initializes the signature algorithm for signing a message.
+ */
+ private void initSign()
+ {
+ messDigestTrees.reset();
+ // set private key and take from it ots key, auth, tree and key
+ // counter, rootSign
+ GMSSPrivateKeyParameters gmssPrivateKey = (GMSSPrivateKeyParameters)key;
+
+ if (gmssPrivateKey.isUsed())
+ {
+ throw new IllegalStateException("Private key already used");
+ }
+
+ // check if last signature has been generated
+ if (gmssPrivateKey.getIndex(0) >= gmssPrivateKey.getNumLeafs(0))
+ {
+ throw new IllegalStateException("No more signatures can be generated");
+ }
+
+ // get Parameterset
+ this.gmssPS = gmssPrivateKey.getParameters();
+ // get numLayer
+ this.numLayer = gmssPS.getNumOfLayers();
+
+ // get OTS Instance of lowest layer
+ byte[] seed = gmssPrivateKey.getCurrentSeeds()[numLayer - 1];
+ byte[] OTSSeed = new byte[mdLength];
+ byte[] dummy = new byte[mdLength];
+ System.arraycopy(seed, 0, dummy, 0, mdLength);
+ OTSSeed = gmssRandom.nextSeed(dummy); // secureRandom.nextBytes(currentSeeds[currentSeeds.length-1]);secureRandom.nextBytes(OTSseed);
+ this.ots = new WinternitzOTSignature(OTSSeed, digestProvider.get(), gmssPS.getWinternitzParameter()[numLayer - 1]);
+
+ byte[][][] helpCurrentAuthPaths = gmssPrivateKey.getCurrentAuthPaths();
+ currentAuthPaths = new byte[numLayer][][];
+
+ // copy the main tree authentication path
+ for (int j = 0; j < numLayer; j++)
+ {
+ currentAuthPaths[j] = new byte[helpCurrentAuthPaths[j].length][mdLength];
+ for (int i = 0; i < helpCurrentAuthPaths[j].length; i++)
+ {
+ System.arraycopy(helpCurrentAuthPaths[j][i], 0, currentAuthPaths[j][i], 0, mdLength);
+ }
+ }
+
+ // copy index
+ index = new int[numLayer];
+ System.arraycopy(gmssPrivateKey.getIndex(), 0, index, 0, numLayer);
+
+ // copy subtreeRootSig
+ byte[] helpSubtreeRootSig;
+ subtreeRootSig = new byte[numLayer - 1][];
+ for (int i = 0; i < numLayer - 1; i++)
+ {
+ helpSubtreeRootSig = gmssPrivateKey.getSubtreeRootSig(i);
+ subtreeRootSig[i] = new byte[helpSubtreeRootSig.length];
+ System.arraycopy(helpSubtreeRootSig, 0, subtreeRootSig[i], 0, helpSubtreeRootSig.length);
+ }
+
+ gmssPrivateKey.markUsed();
+ }
+
+ /**
+ * Signs a message.
+ *
+ * @return the signature.
+ */
+ public byte[] generateSignature(byte[] message)
+ {
+
+ byte[] otsSig = new byte[mdLength];
+ byte[] authPathBytes;
+ byte[] indexBytes;
+
+ otsSig = ots.getSignature(message);
+
+ // get concatenated lowest layer tree authentication path
+ authPathBytes = gmssUtil.concatenateArray(currentAuthPaths[numLayer - 1]);
+
+ // put lowest layer index into a byte array
+ indexBytes = gmssUtil.intToBytesLittleEndian(index[numLayer - 1]);
+
+ // create first part of GMSS signature
+ byte[] gmssSigFirstPart = new byte[indexBytes.length + otsSig.length + authPathBytes.length];
+ System.arraycopy(indexBytes, 0, gmssSigFirstPart, 0, indexBytes.length);
+ System.arraycopy(otsSig, 0, gmssSigFirstPart, indexBytes.length, otsSig.length);
+ System.arraycopy(authPathBytes, 0, gmssSigFirstPart, (indexBytes.length + otsSig.length), authPathBytes.length);
+ // --- end first part
+
+ // --- next parts of the signature
+ // create initial array with length 0 for iteration
+ byte[] gmssSigNextPart = new byte[0];
+
+ for (int i = numLayer - 1 - 1; i >= 0; i--)
+ {
+
+ // get concatenated next tree authentication path
+ authPathBytes = gmssUtil.concatenateArray(currentAuthPaths[i]);
+
+ // put next tree index into a byte array
+ indexBytes = gmssUtil.intToBytesLittleEndian(index[i]);
+
+ // create next part of GMSS signature
+
+ // create help array and copy actual gmssSig into it
+ byte[] helpGmssSig = new byte[gmssSigNextPart.length];
+ System.arraycopy(gmssSigNextPart, 0, helpGmssSig, 0, gmssSigNextPart.length);
+ // adjust length of gmssSigNextPart for adding next part
+ gmssSigNextPart = new byte[helpGmssSig.length + indexBytes.length + subtreeRootSig[i].length + authPathBytes.length];
+
+ // copy old data (help array) and new data in gmssSigNextPart
+ System.arraycopy(helpGmssSig, 0, gmssSigNextPart, 0, helpGmssSig.length);
+ System.arraycopy(indexBytes, 0, gmssSigNextPart, helpGmssSig.length, indexBytes.length);
+ System.arraycopy(subtreeRootSig[i], 0, gmssSigNextPart, (helpGmssSig.length + indexBytes.length), subtreeRootSig[i].length);
+ System.arraycopy(authPathBytes, 0, gmssSigNextPart, (helpGmssSig.length + indexBytes.length + subtreeRootSig[i].length), authPathBytes.length);
+
+ }
+ // --- end next parts
+
+ // concatenate the two parts of the GMSS signature
+ byte[] gmssSig = new byte[gmssSigFirstPart.length + gmssSigNextPart.length];
+ System.arraycopy(gmssSigFirstPart, 0, gmssSig, 0, gmssSigFirstPart.length);
+ System.arraycopy(gmssSigNextPart, 0, gmssSig, gmssSigFirstPart.length, gmssSigNextPart.length);
+
+ // return the GMSS signature
+ return gmssSig;
+ }
+
+ /**
+ * Initializes the signature algorithm for verifying a signature.
+ */
+ private void initVerify()
+ {
+ messDigestTrees.reset();
+
+ GMSSPublicKeyParameters gmssPublicKey = (GMSSPublicKeyParameters)key;
+ pubKeyBytes = gmssPublicKey.getPublicKey();
+ gmssPS = gmssPublicKey.getParameters();
+ // get numLayer
+ this.numLayer = gmssPS.getNumOfLayers();
+
+
+ }
+
+ /**
+ * This function verifies the signature of the message that has been
+ * updated, with the aid of the public key.
+ *
+ * @param message the message
+ * @param signature the signature associated with the message
+ * @return true if the signature has been verified, false otherwise.
+ */
+ public boolean verifySignature(byte[] message, byte[] signature)
+ {
+
+ boolean success = false;
+ // int halfSigLength = signature.length >>> 1;
+ messDigestOTS.reset();
+ WinternitzOTSVerify otsVerify;
+ int otsSigLength;
+
+ byte[] help = message;
+
+ byte[] otsSig;
+ byte[] otsPublicKey;
+ byte[][] authPath;
+ byte[] dest;
+ int nextEntry = 0;
+ int index;
+ // Verify signature
+
+ // --- begin with message = 'message that was signed'
+ // and then in each step message = subtree root
+ for (int j = numLayer - 1; j >= 0; j--)
+ {
+ otsVerify = new WinternitzOTSVerify(digestProvider.get(), gmssPS.getWinternitzParameter()[j]);
+ otsSigLength = otsVerify.getSignatureLength();
+
+ message = help;
+ // get the subtree index
+ index = gmssUtil.bytesToIntLittleEndian(signature, nextEntry);
+
+ // 4 is the number of bytes in integer
+ nextEntry += 4;
+
+ // get one-time signature
+ otsSig = new byte[otsSigLength];
+ System.arraycopy(signature, nextEntry, otsSig, 0, otsSigLength);
+ nextEntry += otsSigLength;
+
+ // compute public OTS key from the one-time signature
+ otsPublicKey = otsVerify.Verify(message, otsSig);
+
+ // test if OTSsignature is correct
+ if (otsPublicKey == null)
+ {
+ System.err.println("OTS Public Key is null in GMSSSignature.verify");
+ return false;
+ }
+
+ // get authentication path from the signature
+ authPath = new byte[gmssPS.getHeightOfTrees()[j]][mdLength];
+ for (int i = 0; i < authPath.length; i++)
+ {
+ System.arraycopy(signature, nextEntry, authPath[i], 0, mdLength);
+ nextEntry = nextEntry + mdLength;
+ }
+
+ // compute the root of the subtree from the authentication path
+ help = new byte[mdLength];
+
+ help = otsPublicKey;
+
+ int count = 1 << authPath.length;
+ count = count + index;
+
+ for (int i = 0; i < authPath.length; i++)
+ {
+ dest = new byte[mdLength << 1];
+
+ if ((count % 2) == 0)
+ {
+ System.arraycopy(help, 0, dest, 0, mdLength);
+ System.arraycopy(authPath[i], 0, dest, mdLength, mdLength);
+ count = count / 2;
+ }
+ else
+ {
+ System.arraycopy(authPath[i], 0, dest, 0, mdLength);
+ System.arraycopy(help, 0, dest, mdLength, help.length);
+ count = (count - 1) / 2;
+ }
+ messDigestTrees.update(dest, 0, dest.length);
+ help = new byte[messDigestTrees.getDigestSize()];
+ messDigestTrees.doFinal(help, 0);
+ }
+ }
+
+ // now help contains the root of the maintree
+
+ // test if help is equal to the GMSS public key
+ if (Arrays.areEqual(pubKeyBytes, help))
+ {
+ success = true;
+ }
+
+ return success;
+ }
+
+
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSUtils.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSUtils.java
new file mode 100644
index 00000000..1f79bdde
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSUtils.java
@@ -0,0 +1,145 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.spongycastle.util.Arrays;
+
+class GMSSUtils
+{
+ static GMSSLeaf[] clone(GMSSLeaf[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ GMSSLeaf[] copy = new GMSSLeaf[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static GMSSRootCalc[] clone(GMSSRootCalc[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ GMSSRootCalc[] copy = new GMSSRootCalc[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static GMSSRootSig[] clone(GMSSRootSig[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ GMSSRootSig[] copy = new GMSSRootSig[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static byte[][] clone(byte[][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ byte[][] copy = new byte[data.length][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = Arrays.clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ static byte[][][] clone(byte[][][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ byte[][][] copy = new byte[data.length][][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ static Treehash[] clone(Treehash[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Treehash[] copy = new Treehash[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ static Treehash[][] clone(Treehash[][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Treehash[][] copy = new Treehash[data.length][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+
+ static Vector[] clone(Vector[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Vector[] copy = new Vector[data.length];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = new Vector();
+ for (Enumeration en = data[i].elements(); en.hasMoreElements();)
+ {
+ copy[i].addElement(en.nextElement());
+ }
+ }
+
+ return copy;
+ }
+
+ static Vector[][] clone(Vector[][] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ Vector[][] copy = new Vector[data.length][];
+
+ for (int i = 0; i != data.length; i++)
+ {
+ copy[i] = clone(data[i]);
+ }
+
+ return copy;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/Treehash.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/Treehash.java
new file mode 100644
index 00000000..82784e34
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/Treehash.java
@@ -0,0 +1,525 @@
+package org.spongycastle.pqc.crypto.gmss;
+
+import java.util.Vector;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom;
+import org.spongycastle.util.Integers;
+import org.spongycastle.util.encoders.Hex;
+
+
+/**
+ * This class implements a treehash instance for the Merkle tree traversal
+ * algorithm. The first node of the stack is stored in this instance itself,
+ * additional tail nodes are stored on a tailstack.
+ */
+public class Treehash
+{
+
+ /**
+ * max height of current treehash instance.
+ */
+ private int maxHeight;
+
+ /**
+ * Vector element that stores the nodes on the stack
+ */
+ private Vector tailStack;
+
+ /**
+ * Vector element that stores the height of the nodes on the stack
+ */
+ private Vector heightOfNodes;
+
+ /**
+ * the first node is stored in the treehash instance itself, not on stack
+ */
+ private byte[] firstNode;
+
+ /**
+ * seedActive needed for the actual node
+ */
+ private byte[] seedActive;
+
+ /**
+ * the seed needed for the next re-initialization of the treehash instance
+ */
+ private byte[] seedNext;
+
+ /**
+ * number of nodes stored on the stack and belonging to this treehash
+ * instance
+ */
+ private int tailLength;
+
+ /**
+ * the height in the tree of the first node stored in treehash
+ */
+ private int firstNodeHeight;
+
+ /**
+ * true if treehash instance was already initialized, false otherwise
+ */
+ private boolean isInitialized;
+
+ /**
+ * true if the first node's height equals the maxHeight of the treehash
+ */
+ private boolean isFinished;
+
+ /**
+ * true if the nextSeed has been initialized with index 3*2^h needed for the
+ * seed scheduling
+ */
+ private boolean seedInitialized;
+
+ /**
+ * denotes the Message Digest used by the tree to create nodes
+ */
+ private Digest messDigestTree;
+
+ /**
+ * This constructor regenerates a prior treehash object
+ *
+ * @param name an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding provider
+ * @param statByte status bytes
+ * @param statInt status ints
+ */
+ public Treehash(Digest name, byte[][] statByte, int[] statInt)
+ {
+ this.messDigestTree = name;
+
+ // decode statInt
+ this.maxHeight = statInt[0];
+ this.tailLength = statInt[1];
+ this.firstNodeHeight = statInt[2];
+
+ if (statInt[3] == 1)
+ {
+ this.isFinished = true;
+ }
+ else
+ {
+ this.isFinished = false;
+ }
+ if (statInt[4] == 1)
+ {
+ this.isInitialized = true;
+ }
+ else
+ {
+ this.isInitialized = false;
+ }
+ if (statInt[5] == 1)
+ {
+ this.seedInitialized = true;
+ }
+ else
+ {
+ this.seedInitialized = false;
+ }
+
+ this.heightOfNodes = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.heightOfNodes.addElement(Integers.valueOf(statInt[6 + i]));
+ }
+
+ // decode statByte
+ this.firstNode = statByte[0];
+ this.seedActive = statByte[1];
+ this.seedNext = statByte[2];
+
+ this.tailStack = new Vector();
+ for (int i = 0; i < tailLength; i++)
+ {
+ this.tailStack.addElement(statByte[3 + i]);
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param tailStack a vector element where the stack nodes are stored
+ * @param maxHeight maximal height of the treehash instance
+ * @param digest an array of strings, containing the name of the used hash
+ * function and PRNG and the name of the corresponding provider
+ */
+ public Treehash(Vector tailStack, int maxHeight, Digest digest)
+ {
+ this.tailStack = tailStack;
+ this.maxHeight = maxHeight;
+ this.firstNode = null;
+ this.isInitialized = false;
+ this.isFinished = false;
+ this.seedInitialized = false;
+ this.messDigestTree = digest;
+
+ this.seedNext = new byte[messDigestTree.getDigestSize()];
+ this.seedActive = new byte[messDigestTree.getDigestSize()];
+ }
+
+ /**
+ * Method to initialize the seeds needed for the precomputation of right
+ * nodes. Should be initialized with index 3*2^i for treehash_i
+ *
+ * @param seedIn
+ */
+ public void initializeSeed(byte[] seedIn)
+ {
+ System.arraycopy(seedIn, 0, this.seedNext, 0, this.messDigestTree
+ .getDigestSize());
+ this.seedInitialized = true;
+ }
+
+ /**
+ * initializes the treehash instance. The seeds must already have been
+ * initialized to work correctly.
+ */
+ public void initialize()
+ {
+ if (!this.seedInitialized)
+ {
+ System.err.println("Seed " + this.maxHeight + " not initialized");
+ return;
+ }
+
+ this.heightOfNodes = new Vector();
+ this.tailLength = 0;
+ this.firstNode = null;
+ this.firstNodeHeight = -1;
+ this.isInitialized = true;
+ System.arraycopy(this.seedNext, 0, this.seedActive, 0, messDigestTree
+ .getDigestSize());
+ }
+
+ /**
+ * Calculates one update of the treehash instance, i.e. creates a new leaf
+ * and hashes if possible
+ *
+ * @param gmssRandom an instance of the PRNG
+ * @param leaf The byte value of the leaf needed for the update
+ */
+ public void update(GMSSRandom gmssRandom, byte[] leaf)
+ {
+
+ if (this.isFinished)
+ {
+ System.err
+ .println("No more update possible for treehash instance!");
+ return;
+ }
+ if (!this.isInitialized)
+ {
+ System.err
+ .println("Treehash instance not initialized before update");
+ return;
+ }
+
+ byte[] help = new byte[this.messDigestTree.getDigestSize()];
+ int helpHeight = -1;
+
+ gmssRandom.nextSeed(this.seedActive);
+
+ // if treehash gets first update
+ if (this.firstNode == null)
+ {
+ this.firstNode = leaf;
+ this.firstNodeHeight = 0;
+ }
+ else
+ {
+ // store the new node in help array, do not push it on the stack
+ help = leaf;
+ helpHeight = 0;
+
+ // hash the nodes on the stack if possible
+ while (this.tailLength > 0
+ && helpHeight == ((Integer)heightOfNodes.lastElement())
+ .intValue())
+ {
+ // put top element of the stack and help node in array
+ // 'tobehashed'
+ // and hash them together, put result again in help array
+ byte[] toBeHashed = new byte[this.messDigestTree
+ .getDigestSize() << 1];
+
+ // pop element from stack
+ System.arraycopy(this.tailStack.lastElement(), 0, toBeHashed,
+ 0, this.messDigestTree.getDigestSize());
+ this.tailStack.removeElementAt(this.tailStack.size() - 1);
+ this.heightOfNodes
+ .removeElementAt(this.heightOfNodes.size() - 1);
+
+ System.arraycopy(help, 0, toBeHashed, this.messDigestTree
+ .getDigestSize(), this.messDigestTree
+ .getDigestSize());
+ messDigestTree.update(toBeHashed, 0, toBeHashed.length);
+ help = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(help, 0);
+
+ // increase help height, stack was reduced by one element
+ helpHeight++;
+ this.tailLength--;
+ }
+
+ // push the new node on the stack
+ this.tailStack.addElement(help);
+ this.heightOfNodes.addElement(Integers.valueOf(helpHeight));
+ this.tailLength++;
+
+ // finally check whether the top node on stack and the first node
+ // in treehash have same height. If so hash them together
+ // and store them in treehash
+ if (((Integer)heightOfNodes.lastElement()).intValue() == this.firstNodeHeight)
+ {
+ byte[] toBeHashed = new byte[this.messDigestTree
+ .getDigestSize() << 1];
+ System.arraycopy(this.firstNode, 0, toBeHashed, 0,
+ this.messDigestTree.getDigestSize());
+
+ // pop element from tailStack and copy it into help2 array
+ System.arraycopy(this.tailStack.lastElement(), 0, toBeHashed,
+ this.messDigestTree.getDigestSize(),
+ this.messDigestTree.getDigestSize());
+ this.tailStack.removeElementAt(this.tailStack.size() - 1);
+ this.heightOfNodes
+ .removeElementAt(this.heightOfNodes.size() - 1);
+
+ // store new element in firstNode, stack is then empty
+ messDigestTree.update(toBeHashed, 0, toBeHashed.length);
+ this.firstNode = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(this.firstNode, 0);
+ this.firstNodeHeight++;
+
+ // empty the stack
+ this.tailLength = 0;
+ }
+ }
+
+ // check if treehash instance is completed
+ if (this.firstNodeHeight == this.maxHeight)
+ {
+ this.isFinished = true;
+ }
+ }
+
+ /**
+ * Destroys a treehash instance after the top node was taken for
+ * authentication path.
+ */
+ public void destroy()
+ {
+ this.isInitialized = false;
+ this.isFinished = false;
+ this.firstNode = null;
+ this.tailLength = 0;
+ this.firstNodeHeight = -1;
+ }
+
+ /**
+ * Returns the height of the lowest node stored either in treehash or on the
+ * stack. It must not be set to infinity (as mentioned in the paper) because
+ * this cases are considered in the computeAuthPaths method of
+ * JDKGMSSPrivateKey
+ *
+ * @return Height of the lowest node
+ */
+ public int getLowestNodeHeight()
+ {
+ if (this.firstNode == null)
+ {
+ return this.maxHeight;
+ }
+ else if (this.tailLength == 0)
+ {
+ return this.firstNodeHeight;
+ }
+ else
+ {
+ return Math.min(this.firstNodeHeight, ((Integer)heightOfNodes
+ .lastElement()).intValue());
+ }
+ }
+
+ /**
+ * Returns the top node height
+ *
+ * @return Height of the first node, the top node
+ */
+ public int getFirstNodeHeight()
+ {
+ if (firstNode == null)
+ {
+ return maxHeight;
+ }
+ return firstNodeHeight;
+ }
+
+ /**
+ * Method to check whether the instance has been initialized or not
+ *
+ * @return true if treehash was already initialized
+ */
+ public boolean wasInitialized()
+ {
+ return this.isInitialized;
+ }
+
+ /**
+ * Method to check whether the instance has been finished or not
+ *
+ * @return true if treehash has reached its maximum height
+ */
+ public boolean wasFinished()
+ {
+ return this.isFinished;
+ }
+
+ /**
+ * returns the first node stored in treehash instance itself
+ *
+ * @return the first node stored in treehash instance itself
+ */
+ public byte[] getFirstNode()
+ {
+ return this.firstNode;
+ }
+
+ /**
+ * returns the active seed
+ *
+ * @return the active seed
+ */
+ public byte[] getSeedActive()
+ {
+ return this.seedActive;
+ }
+
+ /**
+ * This method sets the first node stored in the treehash instance itself
+ *
+ * @param hash
+ */
+ public void setFirstNode(byte[] hash)
+ {
+ if (!this.isInitialized)
+ {
+ this.initialize();
+ }
+ this.firstNode = hash;
+ this.firstNodeHeight = this.maxHeight;
+ this.isFinished = true;
+ }
+
+ /**
+ * updates the nextSeed of this treehash instance one step needed for the
+ * schedulng of the seeds
+ *
+ * @param gmssRandom the prng used for the seeds
+ */
+ public void updateNextSeed(GMSSRandom gmssRandom)
+ {
+ gmssRandom.nextSeed(seedNext);
+ }
+
+ /**
+ * Returns the tailstack
+ *
+ * @return the tailstack
+ */
+ public Vector getTailStack()
+ {
+ return this.tailStack;
+ }
+
+ /**
+ * Returns the status byte array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status bytes
+ */
+ public byte[][] getStatByte()
+ {
+
+ byte[][] statByte = new byte[3 + tailLength][this.messDigestTree
+ .getDigestSize()];
+ statByte[0] = firstNode;
+ statByte[1] = seedActive;
+ statByte[2] = seedNext;
+ for (int i = 0; i < tailLength; i++)
+ {
+ statByte[3 + i] = (byte[])tailStack.elementAt(i);
+ }
+ return statByte;
+ }
+
+ /**
+ * Returns the status int array used by the GMSSPrivateKeyASN.1 class
+ *
+ * @return The status ints
+ */
+ public int[] getStatInt()
+ {
+
+ int[] statInt = new int[6 + tailLength];
+ statInt[0] = maxHeight;
+ statInt[1] = tailLength;
+ statInt[2] = firstNodeHeight;
+ if (this.isFinished)
+ {
+ statInt[3] = 1;
+ }
+ else
+ {
+ statInt[3] = 0;
+ }
+ if (this.isInitialized)
+ {
+ statInt[4] = 1;
+ }
+ else
+ {
+ statInt[4] = 0;
+ }
+ if (this.seedInitialized)
+ {
+ statInt[5] = 1;
+ }
+ else
+ {
+ statInt[5] = 0;
+ }
+ for (int i = 0; i < tailLength; i++)
+ {
+ statInt[6 + i] = ((Integer)heightOfNodes.elementAt(i)).intValue();
+ }
+ return statInt;
+ }
+
+ /**
+ * returns a String representation of the treehash instance
+ */
+ public String toString()
+ {
+ String out = "Treehash : ";
+ for (int i = 0; i < 6 + tailLength; i++)
+ {
+ out = out + this.getStatInt()[i] + " ";
+ }
+ for (int i = 0; i < 3 + tailLength; i++)
+ {
+ if (this.getStatByte()[i] != null)
+ {
+ out = out + new String(Hex.encode((this.getStatByte()[i]))) + " ";
+ }
+ else
+ {
+ out = out + "null ";
+ }
+ }
+ out = out + " " + this.messDigestTree.getDigestSize();
+ return out;
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSRandom.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSRandom.java
new file mode 100644
index 00000000..367aae0e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSRandom.java
@@ -0,0 +1,78 @@
+package org.spongycastle.pqc.crypto.gmss.util;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * This class provides a PRNG for GMSS
+ */
+public class GMSSRandom
+{
+ /**
+ * Hash function for the construction of the authentication trees
+ */
+ private Digest messDigestTree;
+
+ /**
+ * Constructor
+ *
+ * @param messDigestTree2
+ */
+ public GMSSRandom(Digest messDigestTree2)
+ {
+
+ this.messDigestTree = messDigestTree2;
+ }
+
+ /**
+ * computes the next seed value, returns a random byte array and sets
+ * outseed to the next value
+ *
+ * @param outseed byte array in which ((1 + SEEDin +RAND) mod 2^n) will be
+ * stored
+ * @return byte array of H(SEEDin)
+ */
+ public byte[] nextSeed(byte[] outseed)
+ {
+ // RAND <-- H(SEEDin)
+ byte[] rand = new byte[outseed.length];
+ messDigestTree.update(outseed, 0, outseed.length);
+ rand = new byte[messDigestTree.getDigestSize()];
+ messDigestTree.doFinal(rand, 0);
+
+ // SEEDout <-- (1 + SEEDin +RAND) mod 2^n
+ addByteArrays(outseed, rand);
+ addOne(outseed);
+
+ // System.arraycopy(outseed, 0, outseed, 0, outseed.length);
+
+ return rand;
+ }
+
+ private void addByteArrays(byte[] a, byte[] b)
+ {
+
+ byte overflow = 0;
+ int temp;
+
+ for (int i = 0; i < a.length; i++)
+ {
+ temp = (0xFF & a[i]) + (0xFF & b[i]) + overflow;
+ a[i] = (byte)temp;
+ overflow = (byte)(temp >> 8);
+ }
+ }
+
+ private void addOne(byte[] a)
+ {
+
+ byte overflow = 1;
+ int temp;
+
+ for (int i = 0; i < a.length; i++)
+ {
+ temp = (0xFF & a[i]) + overflow;
+ a[i] = (byte)temp;
+ overflow = (byte)(temp >> 8);
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSUtil.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSUtil.java
new file mode 100644
index 00000000..57678d85
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/GMSSUtil.java
@@ -0,0 +1,151 @@
+package org.spongycastle.pqc.crypto.gmss.util;
+
+/**
+ * This class provides several methods that are required by the GMSS classes.
+ */
+public class GMSSUtil
+{
+ /**
+ * Converts a 32 bit integer into a byte array beginning at
+ * <code>offset</code> (little-endian representation)
+ *
+ * @param value the integer to convert
+ */
+ public byte[] intToBytesLittleEndian(int value)
+ {
+ byte[] bytes = new byte[4];
+
+ bytes[0] = (byte)((value) & 0xff);
+ bytes[1] = (byte)((value >> 8) & 0xff);
+ bytes[2] = (byte)((value >> 16) & 0xff);
+ bytes[3] = (byte)((value >> 24) & 0xff);
+ return bytes;
+ }
+
+ /**
+ * Converts a byte array beginning at <code>offset</code> into a 32 bit
+ * integer (little-endian representation)
+ *
+ * @param bytes the byte array
+ * @return The resulting integer
+ */
+ public int bytesToIntLittleEndian(byte[] bytes)
+ {
+
+ return ((bytes[0] & 0xff)) | ((bytes[1] & 0xff) << 8)
+ | ((bytes[2] & 0xff) << 16) | ((bytes[3] & 0xff)) << 24;
+ }
+
+ /**
+ * Converts a byte array beginning at <code>offset</code> into a 32 bit
+ * integer (little-endian representation)
+ *
+ * @param bytes the byte array
+ * @param offset the integer offset into the byte array
+ * @return The resulting integer
+ */
+ public int bytesToIntLittleEndian(byte[] bytes, int offset)
+ {
+ return ((bytes[offset++] & 0xff)) | ((bytes[offset++] & 0xff) << 8)
+ | ((bytes[offset++] & 0xff) << 16)
+ | ((bytes[offset] & 0xff)) << 24;
+ }
+
+ /**
+ * This method concatenates a 2-dimensional byte array into a 1-dimensional
+ * byte array
+ *
+ * @param arraycp a 2-dimensional byte array.
+ * @return 1-dimensional byte array with concatenated input array
+ */
+ public byte[] concatenateArray(byte[][] arraycp)
+ {
+ byte[] dest = new byte[arraycp.length * arraycp[0].length];
+ int indx = 0;
+ for (int i = 0; i < arraycp.length; i++)
+ {
+ System.arraycopy(arraycp[i], 0, dest, indx, arraycp[i].length);
+ indx = indx + arraycp[i].length;
+ }
+ return dest;
+ }
+
+ /**
+ * This method prints the values of a 2-dimensional byte array
+ *
+ * @param text a String
+ * @param array a 2-dimensional byte array
+ */
+ public void printArray(String text, byte[][] array)
+ {
+ System.out.println(text);
+ int counter = 0;
+ for (int i = 0; i < array.length; i++)
+ {
+ for (int j = 0; j < array[0].length; j++)
+ {
+ System.out.println(counter + "; " + array[i][j]);
+ counter++;
+ }
+ }
+ }
+
+ /**
+ * This method prints the values of a 1-dimensional byte array
+ *
+ * @param text a String
+ * @param array a 1-dimensional byte array.
+ */
+ public void printArray(String text, byte[] array)
+ {
+ System.out.println(text);
+ int counter = 0;
+ for (int i = 0; i < array.length; i++)
+ {
+ System.out.println(counter + "; " + array[i]);
+ counter++;
+ }
+ }
+
+ /**
+ * This method tests if an integer is a power of 2.
+ *
+ * @param testValue an integer
+ * @return <code>TRUE</code> if <code>testValue</code> is a power of 2,
+ * <code>FALSE</code> otherwise
+ */
+ public boolean testPowerOfTwo(int testValue)
+ {
+ int a = 1;
+ while (a < testValue)
+ {
+ a <<= 1;
+ }
+ if (testValue == a)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer <code>intValue</code>.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of <code>intValue</code>
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSVerify.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSVerify.java
new file mode 100644
index 00000000..0a1e52eb
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSVerify.java
@@ -0,0 +1,344 @@
+package org.spongycastle.pqc.crypto.gmss.util;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * This class implements signature verification of the Winternitz one-time
+ * signature scheme (OTSS), described in C.Dods, N.P. Smart, and M. Stam, "Hash
+ * Based Digital Signature Schemes", LNCS 3796, pages 96&#8211;115, 2005. The
+ * class is used by the GMSS classes.
+ */
+public class WinternitzOTSVerify
+{
+
+ private Digest messDigestOTS;
+
+ /**
+ * The Winternitz parameter
+ */
+ private int w;
+
+ /**
+ * The constructor
+ *
+ * @param digest the name of the hash function used by the OTS and the provider
+ * name of the hash function
+ * @param w the Winternitz parameter
+ */
+ public WinternitzOTSVerify(Digest digest, int w)
+ {
+ this.w = w;
+
+ messDigestOTS = digest;
+ }
+
+ /**
+ * @return The length of the one-time signature
+ */
+ public int getSignatureLength()
+ {
+ int mdsize = messDigestOTS.getDigestSize();
+ int size = ((mdsize << 3) + (w - 1)) / w;
+ int logs = getLog((size << w) + 1);
+ size += (logs + w - 1) / w;
+
+ return mdsize * size;
+ }
+
+ /**
+ * This method computes the public OTS key from the one-time signature of a
+ * message. This is *NOT* a complete OTS signature verification, but it
+ * suffices for usage with CMSS.
+ *
+ * @param message the message
+ * @param signature the one-time signature
+ * @return The public OTS key
+ */
+ public byte[] Verify(byte[] message, byte[] signature)
+ {
+
+ int mdsize = messDigestOTS.getDigestSize();
+ byte[] hash = new byte[mdsize]; // hash of message m
+
+ // create hash of message m
+ messDigestOTS.update(message, 0, message.length);
+ hash = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hash, 0);
+
+ int size = ((mdsize << 3) + (w - 1)) / w;
+ int logs = getLog((size << w) + 1);
+ int keysize = size + (logs + w - 1) / w;
+
+ int testKeySize = mdsize * keysize;
+
+ if (testKeySize != signature.length)
+ {
+ return null;
+ }
+
+ byte[] testKey = new byte[testKeySize];
+
+ int c = 0;
+ int counter = 0;
+ int test;
+
+ if (8 % w == 0)
+ {
+ int d = 8 / w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+
+ // verify signature
+ for (int i = 0; i < hash.length; i++)
+ {
+ for (int j = 0; j < d; j++)
+ {
+ test = hash[i] & k;
+ c += test;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ hash[i] = (byte)(hash[i] >>> w);
+ counter++;
+ }
+ }
+
+ c = (size << w) - c;
+ for (int i = 0; i < logs; i += w)
+ {
+ test = c & k;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }
+ else if (w < 8)
+ {
+ int d = mdsize / w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+ long big8;
+ int ii = 0;
+ // create signature
+ // first d*w bytes of hash
+ for (int i = 0; i < d; i++)
+ {
+ big8 = 0;
+ for (int j = 0; j < w; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ for (int j = 0; j < 8; j++)
+ {
+ test = (int)(big8 & k);
+ c += test;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+ }
+ // rest of hash
+ d = mdsize % w;
+ big8 = 0;
+ for (int j = 0; j < d; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ d <<= 3;
+ for (int j = 0; j < d; j += w)
+ {
+ test = (int)(big8 & k);
+ c += test;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+
+ // check bytes
+ c = (size << w) - c;
+ for (int i = 0; i < logs; i += w)
+ {
+ test = c & k;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }// end if(w<8)
+ else if (w < 57)
+ {
+ int d = (mdsize << 3) - w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+ long big8, test8;
+ int r = 0;
+ int s, f, rest, ii;
+ // create signature
+ // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w
+ while (r <= d)
+ {
+ s = r >>> 3;
+ rest = r % 8;
+ r += w;
+ f = (r + 7) >>> 3;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < f; j++)
+ {
+ big8 ^= (hash[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ c += test8;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test8 < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ counter++;
+
+ }
+ // rest of hash
+ s = r >>> 3;
+ if (s < mdsize)
+ {
+ rest = r % 8;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < mdsize; j++)
+ {
+ big8 ^= (hash[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ c += test8;
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test8 < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ counter++;
+ }
+ // check bytes
+ c = (size << w) - c;
+ for (int i = 0; i < logs; i += w)
+ {
+ test8 = (c & k);
+
+ System.arraycopy(signature, counter * mdsize, hlp, 0, mdsize);
+
+ while (test8 < k)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8++;
+ }
+
+ System.arraycopy(hlp, 0, testKey, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }// end if(w<57)
+
+ byte[] TKey = new byte[mdsize];
+ messDigestOTS.update(testKey, 0, testKey.length);
+ TKey = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(TKey, 0);
+
+ return TKey;
+
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer <code>intValue</code>.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base
+ * 256 of <code>intValue</code>
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSignature.java b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSignature.java
new file mode 100644
index 00000000..2ec2c1ad
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/gmss/util/WinternitzOTSignature.java
@@ -0,0 +1,404 @@
+package org.spongycastle.pqc.crypto.gmss.util;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * This class implements key pair generation and signature generation of the
+ * Winternitz one-time signature scheme (OTSS), described in C.Dods, N.P. Smart,
+ * and M. Stam, "Hash Based Digital Signature Schemes", LNCS 3796, pages
+ * 96&#8211;115, 2005. The class is used by the GMSS classes.
+ */
+
+public class WinternitzOTSignature
+{
+
+ /**
+ * The hash function used by the OTS
+ */
+ private Digest messDigestOTS;
+
+ /**
+ * The length of the message digest and private key
+ */
+ private int mdsize, keysize;
+
+ /**
+ * An array of strings, containing the name of the used hash function, the
+ * name of the PRGN and the names of the corresponding providers
+ */
+ // private String[] name = new String[2];
+ /**
+ * The private key
+ */
+ private byte[][] privateKeyOTS;
+
+ /**
+ * The Winternitz parameter
+ */
+ private int w;
+
+ /**
+ * The source of randomness for OTS private key generation
+ */
+ private GMSSRandom gmssRandom;
+
+ /**
+ * Sizes of the message and the checksum, both
+ */
+ private int messagesize, checksumsize;
+
+ /**
+ * The constructor generates an OTS key pair, using <code>seed0</code> and
+ * the PRNG
+ *
+ * @param seed0 the seed for the PRGN
+ * @param digest an array of strings, containing the name of the used hash
+ * function, the name of the PRGN and the names of the
+ * corresponding providers
+ * @param w the Winternitz parameter
+ */
+ public WinternitzOTSignature(byte[] seed0, Digest digest, int w)
+ {
+ // this.name = name;
+ this.w = w;
+
+ messDigestOTS = digest;
+
+ gmssRandom = new GMSSRandom(messDigestOTS);
+
+ // calulate keysize for private and public key and also the help
+ // array
+
+ mdsize = messDigestOTS.getDigestSize();
+ int mdsizeBit = mdsize << 3;
+ messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
+
+ checksumsize = getLog((messagesize << w) + 1);
+
+ keysize = messagesize
+ + (int)Math.ceil((double)checksumsize / (double)w);
+
+ /*
+ * mdsize = messDigestOTS.getDigestLength(); messagesize =
+ * ((mdsize<<3)+(w-1))/w;
+ *
+ * checksumsize = getlog((messagesize<<w)+1);
+ *
+ * keysize = messagesize + (checksumsize+w-1)/w;
+ */
+ // define the private key messagesize
+ privateKeyOTS = new byte[keysize][mdsize];
+
+ // gmssRandom.setSeed(seed0);
+ byte[] dummy = new byte[mdsize];
+ System.arraycopy(seed0, 0, dummy, 0, dummy.length);
+
+ // generate random bytes and
+ // assign them to the private key
+ for (int i = 0; i < keysize; i++)
+ {
+ privateKeyOTS[i] = gmssRandom.nextSeed(dummy);
+ }
+ }
+
+ /**
+ * @return The private OTS key
+ */
+ public byte[][] getPrivateKey()
+ {
+ return privateKeyOTS;
+ }
+
+ /**
+ * @return The public OTS key
+ */
+ public byte[] getPublicKey()
+ {
+ byte[] helppubKey = new byte[keysize * mdsize];
+
+ byte[] help = new byte[mdsize];
+ int two_power_t = 1 << w;
+
+ for (int i = 0; i < keysize; i++)
+ {
+ // hash w-1 time the private key and assign it to the public key
+ messDigestOTS.update(privateKeyOTS[i], 0, privateKeyOTS[i].length);
+ help = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(help, 0);
+ for (int j = 2; j < two_power_t; j++)
+ {
+ messDigestOTS.update(help, 0, help.length);
+ help = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(help, 0);
+ }
+ System.arraycopy(help, 0, helppubKey, mdsize * i, mdsize);
+ }
+
+ messDigestOTS.update(helppubKey, 0, helppubKey.length);
+ byte[] tmp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(tmp, 0);
+ return tmp;
+ }
+
+ /**
+ * @return The one-time signature of the message, generated with the private
+ * key
+ */
+ public byte[] getSignature(byte[] message)
+ {
+ byte[] sign = new byte[keysize * mdsize];
+ // byte [] message; // message m as input
+ byte[] hash = new byte[mdsize]; // hash of message m
+ int counter = 0;
+ int c = 0;
+ int test = 0;
+ // create hash of message m
+ messDigestOTS.update(message, 0, message.length);
+ hash = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hash, 0);
+
+ if (8 % w == 0)
+ {
+ int d = 8 / w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+
+ // create signature
+ for (int i = 0; i < hash.length; i++)
+ {
+ for (int j = 0; j < d; j++)
+ {
+ test = hash[i] & k;
+ c += test;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ hash[i] = (byte)(hash[i] >>> w);
+ counter++;
+ }
+ }
+
+ c = (messagesize << w) - c;
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ test = c & k;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }
+ else if (w < 8)
+ {
+ int d = mdsize / w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+ long big8;
+ int ii = 0;
+ // create signature
+ // first d*w bytes of hash
+ for (int i = 0; i < d; i++)
+ {
+ big8 = 0;
+ for (int j = 0; j < w; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ for (int j = 0; j < 8; j++)
+ {
+ test = (int)(big8 & k);
+ c += test;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+ }
+ // rest of hash
+ d = mdsize % w;
+ big8 = 0;
+ for (int j = 0; j < d; j++)
+ {
+ big8 ^= (hash[ii] & 0xff) << (j << 3);
+ ii++;
+ }
+ d <<= 3;
+ for (int j = 0; j < d; j += w)
+ {
+ test = (int)(big8 & k);
+ c += test;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ big8 >>>= w;
+ counter++;
+ }
+
+ // check bytes
+ c = (messagesize << w) - c;
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ test = c & k;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }// end if(w<8)
+ else if (w < 57)
+ {
+ int d = (mdsize << 3) - w;
+ int k = (1 << w) - 1;
+ byte[] hlp = new byte[mdsize];
+ long big8, test8;
+ int r = 0;
+ int s, f, rest, ii;
+ // create signature
+ // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w
+ while (r <= d)
+ {
+ s = r >>> 3;
+ rest = r % 8;
+ r += w;
+ f = (r + 7) >>> 3;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < f; j++)
+ {
+ big8 ^= (hash[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ c += test8;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+ while (test8 > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ counter++;
+
+ }
+ // rest of hash
+ s = r >>> 3;
+ if (s < mdsize)
+ {
+ rest = r % 8;
+ big8 = 0;
+ ii = 0;
+ for (int j = s; j < mdsize; j++)
+ {
+ big8 ^= (hash[j] & 0xff) << (ii << 3);
+ ii++;
+ }
+
+ big8 >>>= rest;
+ test8 = (big8 & k);
+ c += test8;
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+ while (test8 > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ counter++;
+ }
+ // check bytes
+ c = (messagesize << w) - c;
+ for (int i = 0; i < checksumsize; i += w)
+ {
+ test8 = (c & k);
+
+ System.arraycopy(privateKeyOTS[counter], 0, hlp, 0, mdsize);
+
+ while (test8 > 0)
+ {
+ messDigestOTS.update(hlp, 0, hlp.length);
+ hlp = new byte[messDigestOTS.getDigestSize()];
+ messDigestOTS.doFinal(hlp, 0);
+ test8--;
+ }
+ System.arraycopy(hlp, 0, sign, counter * mdsize, mdsize);
+ c >>>= w;
+ counter++;
+ }
+ }// end if(w<57)
+
+ return sign;
+ }
+
+ /**
+ * This method returns the least integer that is greater or equal to the
+ * logarithm to the base 2 of an integer <code>intValue</code>.
+ *
+ * @param intValue an integer
+ * @return The least integer greater or equal to the logarithm to the base 2
+ * of <code>intValue</code>
+ */
+ public int getLog(int intValue)
+ {
+ int log = 1;
+ int i = 2;
+ while (i < intValue)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/Conversions.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/Conversions.java
new file mode 100644
index 00000000..9772d018
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/Conversions.java
@@ -0,0 +1,236 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.math.BigInteger;
+
+import org.spongycastle.pqc.math.linearalgebra.BigIntUtils;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+import org.spongycastle.pqc.math.linearalgebra.IntegerFunctions;
+
+
+/**
+ * Provides methods for CCA2-Secure Conversions of McEliece PKCS
+ */
+final class Conversions
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ /**
+ * Default constructor (private).
+ */
+ private Conversions()
+ {
+ }
+
+ /**
+ * Encode a number between 0 and (n|t) (binomial coefficient) into a binary
+ * vector of length n with weight t. The number is given as a byte array.
+ * Only the first s bits are used, where s = floor[log(n|t)].
+ *
+ * @param n integer
+ * @param t integer
+ * @param m the message as a byte array
+ * @return the encoded message as {@link GF2Vector}
+ */
+ public static GF2Vector encode(final int n, final int t, final byte[] m)
+ {
+ if (n < t)
+ {
+ throw new IllegalArgumentException("n < t");
+ }
+
+ // compute the binomial c = (n|t)
+ BigInteger c = IntegerFunctions.binomial(n, t);
+ // get the number encoded in m
+ BigInteger i = new BigInteger(1, m);
+ // compare
+ if (i.compareTo(c) >= 0)
+ {
+ throw new IllegalArgumentException("Encoded number too large.");
+ }
+
+ GF2Vector result = new GF2Vector(n);
+
+ int nn = n;
+ int tt = t;
+ for (int j = 0; j < n; j++)
+ {
+ c = c.multiply(BigInteger.valueOf(nn - tt)).divide(
+ BigInteger.valueOf(nn));
+ nn--;
+ if (c.compareTo(i) <= 0)
+ {
+ result.setBit(j);
+ i = i.subtract(c);
+ tt--;
+ if (nn == tt)
+ {
+ c = ONE;
+ }
+ else
+ {
+ c = (c.multiply(BigInteger.valueOf(tt + 1)))
+ .divide(BigInteger.valueOf(nn - tt));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Decode a binary vector of length n and weight t into a number between 0
+ * and (n|t) (binomial coefficient). The result is given as a byte array of
+ * length floor[(s+7)/8], where s = floor[log(n|t)].
+ *
+ * @param n integer
+ * @param t integer
+ * @param vec the binary vector
+ * @return the decoded vector as a byte array
+ */
+ public static byte[] decode(int n, int t, GF2Vector vec)
+ {
+ if ((vec.getLength() != n) || (vec.getHammingWeight() != t))
+ {
+ throw new IllegalArgumentException(
+ "vector has wrong length or hamming weight");
+ }
+ int[] vecArray = vec.getVecArray();
+
+ BigInteger bc = IntegerFunctions.binomial(n, t);
+ BigInteger d = ZERO;
+ int nn = n;
+ int tt = t;
+ for (int i = 0; i < n; i++)
+ {
+ bc = bc.multiply(BigInteger.valueOf(nn - tt)).divide(
+ BigInteger.valueOf(nn));
+ nn--;
+
+ int q = i >> 5;
+ int e = vecArray[q] & (1 << (i & 0x1f));
+ if (e != 0)
+ {
+ d = d.add(bc);
+ tt--;
+ if (nn == tt)
+ {
+ bc = ONE;
+ }
+ else
+ {
+ bc = bc.multiply(BigInteger.valueOf(tt + 1)).divide(
+ BigInteger.valueOf(nn - tt));
+ }
+
+ }
+ }
+
+ return BigIntUtils.toMinimalByteArray(d);
+ }
+
+ /**
+ * Compute a message representative of a message given as a vector of length
+ * <tt>n</tt> bit and of hamming weight <tt>t</tt>. The result is a
+ * byte array of length <tt>(s+7)/8</tt>, where
+ * <tt>s = floor[log(n|t)]</tt>.
+ *
+ * @param n integer
+ * @param t integer
+ * @param m the message vector as a byte array
+ * @return a message representative for <tt>m</tt>
+ */
+ public static byte[] signConversion(int n, int t, byte[] m)
+ {
+ if (n < t)
+ {
+ throw new IllegalArgumentException("n < t");
+ }
+
+ BigInteger bc = IntegerFunctions.binomial(n, t);
+ // finds s = floor[log(binomial(n,t))]
+ int s = bc.bitLength() - 1;
+ // s = sq*8 + sr;
+ int sq = s >> 3;
+ int sr = s & 7;
+ if (sr == 0)
+ {
+ sq--;
+ sr = 8;
+ }
+
+ // n = nq*8+nr;
+ int nq = n >> 3;
+ int nr = n & 7;
+ if (nr == 0)
+ {
+ nq--;
+ nr = 8;
+ }
+ // take s bit from m
+ byte[] data = new byte[nq + 1];
+ if (m.length < data.length)
+ {
+ System.arraycopy(m, 0, data, 0, m.length);
+ for (int i = m.length; i < data.length; i++)
+ {
+ data[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(m, 0, data, 0, nq);
+ int h = (1 << nr) - 1;
+ data[nq] = (byte)(h & m[nq]);
+ }
+
+ BigInteger d = ZERO;
+ int nn = n;
+ int tt = t;
+ for (int i = 0; i < n; i++)
+ {
+ bc = (bc.multiply(new BigInteger(Integer.toString(nn - tt))))
+ .divide(new BigInteger(Integer.toString(nn)));
+ nn--;
+
+ int q = i >>> 3;
+ int r = i & 7;
+ r = 1 << r;
+ byte e = (byte)(r & data[q]);
+ if (e != 0)
+ {
+ d = d.add(bc);
+ tt--;
+ if (nn == tt)
+ {
+ bc = ONE;
+ }
+ else
+ {
+ bc = (bc
+ .multiply(new BigInteger(Integer.toString(tt + 1))))
+ .divide(new BigInteger(Integer.toString(nn - tt)));
+ }
+ }
+ }
+
+ byte[] result = new byte[sq + 1];
+ byte[] help = d.toByteArray();
+ if (help.length < result.length)
+ {
+ System.arraycopy(help, 0, result, 0, help.length);
+ for (int i = help.length; i < result.length; i++)
+ {
+ result[i] = 0;
+ }
+ }
+ else
+ {
+ System.arraycopy(help, 0, result, 0, sq);
+ result[sq] = (byte)(((1 << sr) - 1) & help[sq]);
+ }
+
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyGenerationParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyGenerationParameters.java
new file mode 100644
index 00000000..8095d291
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class McElieceCCA2KeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private McElieceCCA2Parameters params;
+
+ public McElieceCCA2KeyGenerationParameters(
+ SecureRandom random,
+ McElieceCCA2Parameters params)
+ {
+ // XXX key size?
+ super(random, 128);
+ this.params = params;
+ }
+
+ public McElieceCCA2Parameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java
new file mode 100644
index 00000000..c950bda7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java
@@ -0,0 +1,119 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode.MaMaPe;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialRingGF2m;
+
+
+/**
+ * This class implements key pair generation of the McEliece Public Key
+ * Cryptosystem (McEliecePKC).
+ */
+public class McElieceCCA2KeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2";
+
+ private McElieceCCA2KeyGenerationParameters mcElieceCCA2Params;
+
+ // the extension degree of the finite field GF(2^m)
+ private int m;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability
+ private int t;
+
+ // the field polynomial
+ private int fieldPoly;
+
+ // the source of randomness
+ private SecureRandom random;
+
+ // flag indicating whether the key pair generator has been initialized
+ private boolean initialized = false;
+
+ /**
+ * Default initialization of the key pair generator.
+ */
+ private void initializeDefault()
+ {
+ McElieceCCA2KeyGenerationParameters mcCCA2Params = new McElieceCCA2KeyGenerationParameters(new SecureRandom(), new McElieceCCA2Parameters());
+ init(mcCCA2Params);
+ }
+
+ // TODO
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.mcElieceCCA2Params = (McElieceCCA2KeyGenerationParameters)param;
+
+ // set source of randomness
+ this.random = new SecureRandom();
+
+ this.m = this.mcElieceCCA2Params.getParameters().getM();
+ this.n = this.mcElieceCCA2Params.getParameters().getN();
+ this.t = this.mcElieceCCA2Params.getParameters().getT();
+ this.fieldPoly = this.mcElieceCCA2Params.getParameters().getFieldPoly();
+ this.initialized = true;
+ }
+
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+
+ if (!initialized)
+ {
+ initializeDefault();
+ }
+
+ // finite field GF(2^m)
+ GF2mField field = new GF2mField(m, fieldPoly);
+
+ // irreducible Goppa polynomial
+ PolynomialGF2mSmallM gp = new PolynomialGF2mSmallM(field, t,
+ PolynomialGF2mSmallM.RANDOM_IRREDUCIBLE_POLYNOMIAL, random);
+ PolynomialRingGF2m ring = new PolynomialRingGF2m(field, gp);
+
+ // matrix for computing square roots in (GF(2^m))^t
+ PolynomialGF2mSmallM[] qInv = ring.getSquareRootMatrix();
+
+ // generate canonical check matrix
+ GF2Matrix h = GoppaCode.createCanonicalCheckMatrix(field, gp);
+
+ // compute short systematic form of check matrix
+ MaMaPe mmp = GoppaCode.computeSystematicForm(h, random);
+ GF2Matrix shortH = mmp.getSecondMatrix();
+ Permutation p = mmp.getPermutation();
+
+ // compute short systematic form of generator matrix
+ GF2Matrix shortG = (GF2Matrix)shortH.computeTranspose();
+
+ // obtain number of rows of G (= dimension of the code)
+ int k = shortG.getNumRows();
+
+ // generate keys
+ McElieceCCA2PublicKeyParameters pubKey = new McElieceCCA2PublicKeyParameters(OID, n, t, shortG, mcElieceCCA2Params.getParameters());
+ McElieceCCA2PrivateKeyParameters privKey = new McElieceCCA2PrivateKeyParameters(OID, n, k,
+ field, gp, p, h, qInv, mcElieceCCA2Params.getParameters());
+
+ // return key pair
+ return new AsymmetricCipherKeyPair(pubKey, privKey);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyParameters.java
new file mode 100644
index 00000000..4dc3b8d2
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2KeyParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+
+
+public class McElieceCCA2KeyParameters
+ extends AsymmetricKeyParameter
+{
+ private McElieceCCA2Parameters params;
+
+ public McElieceCCA2KeyParameters(
+ boolean isPrivate,
+ McElieceCCA2Parameters params)
+ {
+ super(isPrivate);
+ this.params = params;
+ }
+
+
+ public McElieceCCA2Parameters getParameters()
+ {
+ return params;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Parameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Parameters.java
new file mode 100644
index 00000000..11eae2e0
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Parameters.java
@@ -0,0 +1,51 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+
+/**
+ * This class provides a specification for the parameters of the CCA2-secure
+ * variants of the McEliece PKCS that are used with
+ * {@link McElieceFujisakiCipher}, {@link McElieceKobaraImaiCipher}, and
+ * {@link McEliecePointchevalCipher}.
+ *
+ * @see McElieceFujisakiCipher
+ * @see McElieceKobaraImaiCipher
+ * @see McEliecePointchevalCipher
+ */
+public class McElieceCCA2Parameters
+ extends McElieceParameters
+{
+
+
+ public Digest digest;
+
+
+ /**
+ * Construct the default parameters.
+ * The default message digest is SHA256.
+ */
+ public McElieceCCA2Parameters()
+ {
+ this.digest = new SHA256Digest();
+ }
+
+ public McElieceCCA2Parameters(int m, int t)
+ {
+ super(m, t);
+ this.digest = new SHA256Digest();
+ }
+
+ public McElieceCCA2Parameters(Digest digest)
+ {
+ this.digest = digest;
+ }
+
+ public Digest getDigest()
+ {
+ return this.digest;
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Primitives.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Primitives.java
new file mode 100644
index 00000000..66c163a2
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2Primitives.java
@@ -0,0 +1,86 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.spongycastle.pqc.math.linearalgebra.Vector;
+
+/**
+ * Core operations for the CCA-secure variants of McEliece.
+ */
+public final class McElieceCCA2Primitives
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private McElieceCCA2Primitives()
+ {
+ }
+
+ /**
+ * The McEliece encryption primitive.
+ *
+ * @param pubKey the public key
+ * @param m the message vector
+ * @param z the error vector
+ * @return <tt>m*G + z</tt>
+ */
+
+
+ public static GF2Vector encryptionPrimitive(McElieceCCA2PublicKeyParameters pubKey,
+ GF2Vector m, GF2Vector z)
+ {
+
+ GF2Matrix matrixG = pubKey.getMatrixG();
+ Vector mG = matrixG.leftMultiplyLeftCompactForm(m);
+ return (GF2Vector)mG.add(z);
+ }
+
+ /**
+ * The McEliece decryption primitive.
+ *
+ * @param privKey the private key
+ * @param c the ciphertext vector <tt>c = m*G + z</tt>
+ * @return the message vector <tt>m</tt> and the error vector <tt>z</tt>
+ */
+ public static GF2Vector[] decryptionPrimitive(
+ McElieceCCA2PrivateKeyParameters privKey, GF2Vector c)
+ {
+
+ // obtain values from private key
+ int k = privKey.getK();
+ Permutation p = privKey.getP();
+ GF2mField field = privKey.getField();
+ PolynomialGF2mSmallM gp = privKey.getGoppaPoly();
+ GF2Matrix h = privKey.getH();
+ PolynomialGF2mSmallM[] q = privKey.getQInv();
+
+ // compute inverse permutation P^-1
+ Permutation pInv = p.computeInverse();
+
+ // multiply c with permutation P^-1
+ GF2Vector cPInv = (GF2Vector)c.multiply(pInv);
+
+ // compute syndrome of cP^-1
+ GF2Vector syndVec = (GF2Vector)h.rightMultiply(cPInv);
+
+ // decode syndrome
+ GF2Vector errors = GoppaCode.syndromeDecode(syndVec, field, gp, q);
+ GF2Vector mG = (GF2Vector)cPInv.add(errors);
+
+ // multiply codeword and error vector with P
+ mG = (GF2Vector)mG.multiply(p);
+ errors = (GF2Vector)errors.multiply(p);
+
+ // extract plaintext vector (last k columns of mG)
+ GF2Vector m = mG.extractRightVector(k);
+
+ // return vectors
+ return new GF2Vector[]{m, errors};
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PrivateKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PrivateKeyParameters.java
new file mode 100644
index 00000000..a19a267b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PrivateKeyParameters.java
@@ -0,0 +1,172 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+
+/**
+ *
+ *
+ *
+ */
+public class McElieceCCA2PrivateKeyParameters
+ extends McElieceCCA2KeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the dimension of the code
+ private int k;
+
+ // the finte field GF(2^m)
+ private GF2mField field;
+
+ // the irreducible Goppa polynomial
+ private PolynomialGF2mSmallM goppaPoly;
+
+ // the permutation
+ private Permutation p;
+
+ // the canonical check matrix
+ private GF2Matrix h;
+
+ // the matrix used to compute square roots in (GF(2^m))^t
+ private PolynomialGF2mSmallM[] qInv;
+
+ /**
+ * Constructor.
+ *
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param field the finite field <tt>GF(2<sup>m</sup>)</tt>
+ * @param gp the irreducible Goppa polynomial
+ * @param p the permutation
+ * @param h the canonical check matrix
+ * @param qInv the matrix used to compute square roots in
+ * <tt>(GF(2^m))^t</tt>
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PrivateKeyParameters(String oid, int n, int k, GF2mField field,
+ PolynomialGF2mSmallM gp, Permutation p, GF2Matrix h,
+ PolynomialGF2mSmallM[] qInv, McElieceCCA2Parameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ this.field = field;
+ this.goppaPoly = gp;
+ this.p = p;
+ this.h = h;
+ this.qInv = qInv;
+ }
+
+ /**
+ * Constructor used by the {@link McElieceKeyFactory}.
+ *
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param encFieldPoly the encoded field polynomial defining the finite field
+ * <tt>GF(2<sup>m</sup>)</tt>
+ * @param encGoppaPoly the encoded irreducible Goppa polynomial
+ * @param encP the encoded permutation
+ * @param encH the encoded canonical check matrix
+ * @param encQInv the encoded matrix used to compute square roots in
+ * <tt>(GF(2^m))^t</tt>
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PrivateKeyParameters(String oid, int n, int k, byte[] encFieldPoly,
+ byte[] encGoppaPoly, byte[] encP, byte[] encH, byte[][] encQInv, McElieceCCA2Parameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ field = new GF2mField(encFieldPoly);
+ goppaPoly = new PolynomialGF2mSmallM(field, encGoppaPoly);
+ p = new Permutation(encP);
+ h = new GF2Matrix(encH);
+ qInv = new PolynomialGF2mSmallM[encQInv.length];
+ for (int i = 0; i < encQInv.length; i++)
+ {
+ qInv[i] = new PolynomialGF2mSmallM(field, encQInv[i]);
+ }
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return k;
+ }
+
+ /**
+ * @return the degree of the Goppa polynomial (error correcting capability)
+ */
+ public int getT()
+ {
+ return goppaPoly.getDegree();
+ }
+
+ /**
+ * @return the finite field
+ */
+ public GF2mField getField()
+ {
+ return field;
+ }
+
+ /**
+ * @return the irreducible Goppa polynomial
+ */
+ public PolynomialGF2mSmallM getGoppaPoly()
+ {
+ return goppaPoly;
+ }
+
+ /**
+ * @return the permutation P
+ */
+ public Permutation getP()
+ {
+ return p;
+ }
+
+ /**
+ * @return the canonical check matrix H
+ */
+ public GF2Matrix getH()
+ {
+ return h;
+ }
+
+ /**
+ * @return the matrix used to compute square roots in <tt>(GF(2^m))^t</tt>
+ */
+ public PolynomialGF2mSmallM[] getQInv()
+ {
+ return qInv;
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PublicKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PublicKeyParameters.java
new file mode 100644
index 00000000..9b5ba0a6
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceCCA2PublicKeyParameters.java
@@ -0,0 +1,97 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+
+/**
+ *
+ *
+ *
+ */
+public class McElieceCCA2PublicKeyParameters
+ extends McElieceCCA2KeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability of the code
+ private int t;
+
+ // the generator matrix
+ private GF2Matrix matrixG;
+
+ /**
+ * Constructor.
+ *
+ * @param n length of the code
+ * @param t error correction capability
+ * @param matrix generator matrix
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PublicKeyParameters(String oid, int n, int t, GF2Matrix matrix, McElieceCCA2Parameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = new GF2Matrix(matrix);
+ }
+
+ /**
+ * Constructor (used by {@link McElieceKeyFactory}).
+ *
+ * @param n length of the code
+ * @param t error correction capability of the code
+ * @param encMatrix encoded generator matrix
+ * @param params McElieceCCA2Parameters
+ */
+ public McElieceCCA2PublicKeyParameters(String oid, int n, int t, byte[] encMatrix, McElieceCCA2Parameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.matrixG = new GF2Matrix(encMatrix);
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the error correction capability of the code
+ */
+ public int getT()
+ {
+ return t;
+ }
+
+ /**
+ * @return the generator matrix
+ */
+ public GF2Matrix getMatrixG()
+ {
+ return matrixG;
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return matrixG.getNumRows();
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java
new file mode 100644
index 00000000..810a69c8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java
@@ -0,0 +1,218 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.prng.DigestRandomGenerator;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+import org.spongycastle.pqc.math.linearalgebra.ByteUtils;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+
+/**
+ * This class implements the Fujisaki/Okamoto conversion of the McEliecePKCS.
+ * Fujisaki and Okamoto propose hybrid encryption that merges a symmetric
+ * encryption scheme which is secure in the find-guess model with an asymmetric
+ * one-way encryption scheme which is sufficiently probabilistic to obtain a
+ * public key cryptosystem which is CCA2-secure. For details, see D. Engelbert,
+ * R. Overbeck, A. Schmidt, "A summary of the development of the McEliece
+ * Cryptosystem", technical report.
+ */
+public class McElieceFujisakiCipher
+ implements MessageEncryptor
+{
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.1";
+
+ private static final String DEFAULT_PRNG_NAME = "SHA1PRNG";
+
+ private Digest messDigest;
+
+ private SecureRandom sr;
+
+ /**
+ * The McEliece main parameters
+ */
+ private int n, k, t;
+
+ McElieceCCA2KeyParameters key;
+
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)param;
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McElieceCCA2PrivateKeyParameters)param;
+ this.initCipherDecrypt((McElieceCCA2PrivateKeyParameters)key);
+ }
+
+ }
+
+
+ public int getKeySize(McElieceCCA2KeyParameters key)
+ throws IllegalArgumentException
+ {
+
+ if (key instanceof McElieceCCA2PublicKeyParameters)
+ {
+ return ((McElieceCCA2PublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McElieceCCA2PrivateKeyParameters)
+ {
+ return ((McElieceCCA2PrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+
+ }
+
+
+ private void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
+ {
+ this.sr = sr != null ? sr : new SecureRandom();
+ this.messDigest = pubKey.getParameters().getDigest();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+ }
+
+
+ public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+ {
+ this.messDigest = privKey.getParameters().getDigest();
+ n = privKey.getN();
+ t = privKey.getT();
+ }
+
+
+ public byte[] messageEncrypt(byte[] input)
+ throws Exception
+ {
+
+ // generate random vector r of length k bits
+ GF2Vector r = new GF2Vector(k, sr);
+
+ // convert r to byte array
+ byte[] rBytes = r.getEncoded();
+
+ // compute (r||input)
+ byte[] rm = ByteUtils.concatenate(rBytes, input);
+
+ // compute H(r||input)
+ messDigest.update(rm, 0, rm.length);
+ byte[] hrm = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hrm, 0);
+
+ // convert H(r||input) to error vector z
+ GF2Vector z = Conversions.encode(n, t, hrm);
+
+ // compute c1 = E(r, z)
+ byte[] c1 = McElieceCCA2Primitives.encryptionPrimitive((McElieceCCA2PublicKeyParameters)key, r, z)
+ .getEncoded();
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rBytes);
+
+ // generate random c2
+ byte[] c2 = new byte[input.length];
+ sr0.nextBytes(c2);
+
+ // XOR with input
+ for (int i = 0; i < input.length; i++)
+ {
+ c2[i] ^= input[i];
+ }
+
+ // return (c1||c2)
+ return ByteUtils.concatenate(c1, c2);
+ }
+
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+
+ int c1Len = (n + 7) >> 3;
+ int c2Len = input.length - c1Len;
+
+ // split ciphertext (c1||c2)
+ byte[][] c1c2 = ByteUtils.split(input, c1Len);
+ byte[] c1 = c1c2[0];
+ byte[] c2 = c1c2[1];
+
+ // decrypt c1 ...
+ GF2Vector hrmVec = GF2Vector.OS2VP(n, c1);
+ GF2Vector[] decC1 = McElieceCCA2Primitives.decryptionPrimitive((McElieceCCA2PrivateKeyParameters)key,
+ hrmVec);
+ byte[] rBytes = decC1[0].getEncoded();
+ // ... and obtain error vector z
+ GF2Vector z = decC1[1];
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rBytes);
+
+ // generate random sequence
+ byte[] mBytes = new byte[c2Len];
+ sr0.nextBytes(mBytes);
+
+ // XOR with c2 to obtain m
+ for (int i = 0; i < c2Len; i++)
+ {
+ mBytes[i] ^= c2[i];
+ }
+
+ // compute H(r||m)
+ byte[] rmBytes = ByteUtils.concatenate(rBytes, mBytes);
+ byte[] hrm = new byte[messDigest.getDigestSize()];
+ messDigest.update(rmBytes, 0, rmBytes.length);
+ messDigest.doFinal(hrm, 0);
+
+
+ // compute Conv(H(r||m))
+ hrmVec = Conversions.encode(n, t, hrm);
+
+ // check that Conv(H(m||r)) = z
+ if (!hrmVec.equals(z))
+ {
+
+ throw new Exception("Bad Padding: invalid ciphertext");
+
+ }
+
+ // return plaintext m
+ return mBytes;
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiDigestCipher.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiDigestCipher.java
new file mode 100644
index 00000000..9a5577ce
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceFujisakiDigestCipher.java
@@ -0,0 +1,128 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McElieceFujisakiDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCCA2Cipher;
+
+ private boolean forEncrypting;
+
+
+ public McElieceFujisakiDigestCipher(MessageEncryptor mcElieceCCA2Cipher, Digest messDigest)
+ {
+ this.mcElieceCCA2Cipher = mcElieceCCA2Cipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCCA2Cipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McElieceFujisakiDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCCA2Cipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McElieceFujisakiDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCCA2Cipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyGenerationParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyGenerationParameters.java
new file mode 100644
index 00000000..129a704c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyGenerationParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class McElieceKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private McElieceParameters params;
+
+ public McElieceKeyGenerationParameters(
+ SecureRandom random,
+ McElieceParameters params)
+ {
+ // XXX key size?
+ super(random, 256);
+ this.params = params;
+ }
+
+ public McElieceParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java
new file mode 100644
index 00000000..07db4cd6
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java
@@ -0,0 +1,151 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode.MaMaPe;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialRingGF2m;
+
+
+/**
+ * This class implements key pair generation of the McEliece Public Key
+ * Cryptosystem (McEliecePKC).
+ */
+public class McElieceKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+
+
+ public McElieceKeyPairGenerator()
+ {
+
+ }
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ private static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.1";
+
+ private McElieceKeyGenerationParameters mcElieceParams;
+
+ // the extension degree of the finite field GF(2^m)
+ private int m;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability
+ private int t;
+
+ // the field polynomial
+ private int fieldPoly;
+
+ // the source of randomness
+ private SecureRandom random;
+
+ // flag indicating whether the key pair generator has been initialized
+ private boolean initialized = false;
+
+
+ /**
+ * Default initialization of the key pair generator.
+ */
+ private void initializeDefault()
+ {
+ McElieceKeyGenerationParameters mcParams = new McElieceKeyGenerationParameters(new SecureRandom(), new McElieceParameters());
+ initialize(mcParams);
+ }
+
+ private void initialize(
+ KeyGenerationParameters param)
+ {
+ this.mcElieceParams = (McElieceKeyGenerationParameters)param;
+
+ // set source of randomness
+ this.random = new SecureRandom();
+
+ this.m = this.mcElieceParams.getParameters().getM();
+ this.n = this.mcElieceParams.getParameters().getN();
+ this.t = this.mcElieceParams.getParameters().getT();
+ this.fieldPoly = this.mcElieceParams.getParameters().getFieldPoly();
+ this.initialized = true;
+ }
+
+
+ private AsymmetricCipherKeyPair genKeyPair()
+ {
+
+ if (!initialized)
+ {
+ initializeDefault();
+ }
+
+ // finite field GF(2^m)
+ GF2mField field = new GF2mField(m, fieldPoly);
+
+ // irreducible Goppa polynomial
+ PolynomialGF2mSmallM gp = new PolynomialGF2mSmallM(field, t,
+ PolynomialGF2mSmallM.RANDOM_IRREDUCIBLE_POLYNOMIAL, random);
+ PolynomialRingGF2m ring = new PolynomialRingGF2m(field, gp);
+
+ // matrix used to compute square roots in (GF(2^m))^t
+ PolynomialGF2mSmallM[] sqRootMatrix = ring.getSquareRootMatrix();
+
+ // generate canonical check matrix
+ GF2Matrix h = GoppaCode.createCanonicalCheckMatrix(field, gp);
+
+ // compute short systematic form of check matrix
+ MaMaPe mmp = GoppaCode.computeSystematicForm(h, random);
+ GF2Matrix shortH = mmp.getSecondMatrix();
+ Permutation p1 = mmp.getPermutation();
+
+ // compute short systematic form of generator matrix
+ GF2Matrix shortG = (GF2Matrix)shortH.computeTranspose();
+
+ // extend to full systematic form
+ GF2Matrix gPrime = shortG.extendLeftCompactForm();
+
+ // obtain number of rows of G (= dimension of the code)
+ int k = shortG.getNumRows();
+
+ // generate random invertible (k x k)-matrix S and its inverse S^-1
+ GF2Matrix[] matrixSandInverse = GF2Matrix
+ .createRandomRegularMatrixAndItsInverse(k, random);
+
+ // generate random permutation P2
+ Permutation p2 = new Permutation(n, random);
+
+ // compute public matrix G=S*G'*P2
+ GF2Matrix g = (GF2Matrix)matrixSandInverse[0].rightMultiply(gPrime);
+ g = (GF2Matrix)g.rightMultiply(p2);
+
+
+ // generate keys
+ McEliecePublicKeyParameters pubKey = new McEliecePublicKeyParameters(OID, n, t, g, mcElieceParams.getParameters());
+ McEliecePrivateKeyParameters privKey = new McEliecePrivateKeyParameters(OID, n, k,
+ field, gp, matrixSandInverse[1], p1, p2, h, sqRootMatrix, mcElieceParams.getParameters());
+
+ // return key pair
+ return new AsymmetricCipherKeyPair(pubKey, privKey);
+ }
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.initialize(param);
+
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ return genKeyPair();
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyParameters.java
new file mode 100644
index 00000000..a8b2ce84
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKeyParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+
+
+public class McElieceKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private McElieceParameters params;
+
+ public McElieceKeyParameters(
+ boolean isPrivate,
+ McElieceParameters params)
+ {
+ super(isPrivate);
+ this.params = params;
+ }
+
+
+ public McElieceParameters getParameters()
+ {
+ return params;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java
new file mode 100644
index 00000000..0be981be
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java
@@ -0,0 +1,319 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.prng.DigestRandomGenerator;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+import org.spongycastle.pqc.math.linearalgebra.ByteUtils;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+import org.spongycastle.pqc.math.linearalgebra.IntegerFunctions;
+
+/**
+ * This class implements the Kobara/Imai conversion of the McEliecePKCS. This is
+ * a conversion of the McEliecePKCS which is CCA2-secure. For details, see D.
+ * Engelbert, R. Overbeck, A. Schmidt, "A summary of the development of the
+ * McEliece Cryptosystem", technical report.
+ */
+public class McElieceKobaraImaiCipher
+ implements MessageEncryptor
+{
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.3";
+
+ private static final String DEFAULT_PRNG_NAME = "SHA1PRNG";
+
+ /**
+ * A predetermined public constant.
+ */
+ public static final byte[] PUBLIC_CONSTANT = "a predetermined public constant"
+ .getBytes();
+
+
+ private Digest messDigest;
+
+ private SecureRandom sr;
+
+ McElieceCCA2KeyParameters key;
+
+ /**
+ * The McEliece main parameters
+ */
+ private int n, k, t;
+
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)param;
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McElieceCCA2PrivateKeyParameters)param;
+ this.initCipherDecrypt((McElieceCCA2PrivateKeyParameters)key);
+ }
+
+ }
+
+ /**
+ * Return the key size of the given key object.
+ *
+ * @param key the McElieceCCA2KeyParameters object
+ * @return the key size of the given key object
+ */
+ public int getKeySize(McElieceCCA2KeyParameters key)
+ {
+ if (key instanceof McElieceCCA2PublicKeyParameters)
+ {
+ return ((McElieceCCA2PublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McElieceCCA2PrivateKeyParameters)
+ {
+ return ((McElieceCCA2PrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+ }
+
+ private void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
+ {
+ this.messDigest = pubKey.getParameters().getDigest();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+
+ }
+
+ public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+ {
+ this.messDigest = privKey.getParameters().getDigest();
+ n = privKey.getN();
+ k = privKey.getK();
+ t = privKey.getT();
+ }
+
+ public byte[] messageEncrypt(byte[] input)
+ throws Exception
+ {
+
+ int c2Len = messDigest.getDigestSize();
+ int c4Len = k >> 3;
+ int c5Len = (IntegerFunctions.binomial(n, t).bitLength() - 1) >> 3;
+
+
+ int mLen = c4Len + c5Len - c2Len - PUBLIC_CONSTANT.length;
+ if (input.length > mLen)
+ {
+ mLen = input.length;
+ }
+
+ int c1Len = mLen + PUBLIC_CONSTANT.length;
+ int c6Len = c1Len + c2Len - c4Len - c5Len;
+
+ // compute (m||const)
+ byte[] mConst = new byte[c1Len];
+ System.arraycopy(input, 0, mConst, 0, input.length);
+ System.arraycopy(PUBLIC_CONSTANT, 0, mConst, mLen,
+ PUBLIC_CONSTANT.length);
+
+ // generate random r of length c2Len bytes
+ byte[] r = new byte[c2Len];
+ sr.nextBytes(r);
+
+ // get PRNG object
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(r);
+
+ // generate random sequence ...
+ byte[] c1 = new byte[c1Len];
+ sr0.nextBytes(c1);
+
+ // ... and XOR with (m||const) to obtain c1
+ for (int i = c1Len - 1; i >= 0; i--)
+ {
+ c1[i] ^= mConst[i];
+ }
+
+ // compute H(c1) ...
+ byte[] c2 = new byte[messDigest.getDigestSize()];
+ messDigest.update(c1, 0, c1.length);
+ messDigest.doFinal(c2, 0);
+
+ // ... and XOR with r
+ for (int i = c2Len - 1; i >= 0; i--)
+ {
+ c2[i] ^= r[i];
+ }
+
+ // compute (c2||c1)
+ byte[] c2c1 = ByteUtils.concatenate(c2, c1);
+
+ // split (c2||c1) into (c6||c5||c4), where c4Len is k/8 bytes, c5Len is
+ // floor[log(n|t)]/8 bytes, and c6Len is c1Len+c2Len-c4Len-c5Len (may be
+ // 0).
+ byte[] c6 = new byte[0];
+ if (c6Len > 0)
+ {
+ c6 = new byte[c6Len];
+ System.arraycopy(c2c1, 0, c6, 0, c6Len);
+ }
+
+ byte[] c5 = new byte[c5Len];
+ System.arraycopy(c2c1, c6Len, c5, 0, c5Len);
+
+ byte[] c4 = new byte[c4Len];
+ System.arraycopy(c2c1, c6Len + c5Len, c4, 0, c4Len);
+
+ // convert c4 to vector over GF(2)
+ GF2Vector c4Vec = GF2Vector.OS2VP(k, c4);
+
+ // convert c5 to error vector z
+ GF2Vector z = Conversions.encode(n, t, c5);
+
+ // compute encC4 = E(c4, z)
+ byte[] encC4 = McElieceCCA2Primitives.encryptionPrimitive((McElieceCCA2PublicKeyParameters)key,
+ c4Vec, z).getEncoded();
+
+ // if c6Len > 0
+ if (c6Len > 0)
+ {
+ // return (c6||encC4)
+ return ByteUtils.concatenate(c6, encC4);
+ }
+ // else, return encC4
+ return encC4;
+ }
+
+
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+
+ int nDiv8 = n >> 3;
+
+ if (input.length < nDiv8)
+ {
+ throw new Exception("Bad Padding: Ciphertext too short.");
+ }
+
+ int c2Len = messDigest.getDigestSize();
+ int c4Len = k >> 3;
+ int c6Len = input.length - nDiv8;
+
+ // split cipher text (c6||encC4), where c6 may be empty
+ byte[] c6, encC4;
+ if (c6Len > 0)
+ {
+ byte[][] c6EncC4 = ByteUtils.split(input, c6Len);
+ c6 = c6EncC4[0];
+ encC4 = c6EncC4[1];
+ }
+ else
+ {
+ c6 = new byte[0];
+ encC4 = input;
+ }
+
+ // convert encC4 into vector over GF(2)
+ GF2Vector encC4Vec = GF2Vector.OS2VP(n, encC4);
+
+ // decrypt encC4Vec to obtain c4 and error vector z
+ GF2Vector[] c4z = McElieceCCA2Primitives.decryptionPrimitive((McElieceCCA2PrivateKeyParameters)key,
+ encC4Vec);
+ byte[] c4 = c4z[0].getEncoded();
+ GF2Vector z = c4z[1];
+
+ // if length of c4 is greater than c4Len (because of padding) ...
+ if (c4.length > c4Len)
+ {
+ // ... truncate the padding bytes
+ c4 = ByteUtils.subArray(c4, 0, c4Len);
+ }
+
+ // compute c5 = Conv^-1(z)
+ byte[] c5 = Conversions.decode(n, t, z);
+
+ // compute (c6||c5||c4)
+ byte[] c6c5c4 = ByteUtils.concatenate(c6, c5);
+ c6c5c4 = ByteUtils.concatenate(c6c5c4, c4);
+
+ // split (c6||c5||c4) into (c2||c1), where c2Len = mdLen and c1Len =
+ // input.length-c2Len bytes.
+ int c1Len = c6c5c4.length - c2Len;
+ byte[][] c2c1 = ByteUtils.split(c6c5c4, c2Len);
+ byte[] c2 = c2c1[0];
+ byte[] c1 = c2c1[1];
+
+ // compute H(c1) ...
+ byte[] rPrime = new byte[messDigest.getDigestSize()];
+ messDigest.update(c1, 0, c1.length);
+ messDigest.doFinal(rPrime, 0);
+
+ // ... and XOR with c2 to obtain r'
+ for (int i = c2Len - 1; i >= 0; i--)
+ {
+ rPrime[i] ^= c2[i];
+ }
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rPrime);
+
+ // generate random sequence R(r') ...
+ byte[] mConstPrime = new byte[c1Len];
+ sr0.nextBytes(mConstPrime);
+
+ // ... and XOR with c1 to obtain (m||const')
+ for (int i = c1Len - 1; i >= 0; i--)
+ {
+ mConstPrime[i] ^= c1[i];
+ }
+
+ if (mConstPrime.length < c1Len)
+ {
+ throw new Exception("Bad Padding: invalid ciphertext");
+ }
+
+ byte[][] temp = ByteUtils.split(mConstPrime, c1Len
+ - PUBLIC_CONSTANT.length);
+ byte[] mr = temp[0];
+ byte[] constPrime = temp[1];
+
+ if (!ByteUtils.equals(constPrime, PUBLIC_CONSTANT))
+ {
+ throw new Exception("Bad Padding: invalid ciphertext");
+ }
+
+ return mr;
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiDigestCipher.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiDigestCipher.java
new file mode 100644
index 00000000..7df9cc0d
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceKobaraImaiDigestCipher.java
@@ -0,0 +1,128 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McElieceKobaraImaiDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCCA2Cipher;
+
+ private boolean forEncrypting;
+
+
+ public McElieceKobaraImaiDigestCipher(MessageEncryptor mcElieceCCA2Cipher, Digest messDigest)
+ {
+ this.mcElieceCCA2Cipher = mcElieceCCA2Cipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCCA2Cipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McElieceKobaraImaiDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCCA2Cipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McElieceKobaraImaiDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCCA2Cipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java
new file mode 100644
index 00000000..2e843aa0
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java
@@ -0,0 +1,224 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.GoppaCode;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+import org.spongycastle.pqc.math.linearalgebra.Vector;
+
+/**
+ * This class implements the McEliece Public Key cryptosystem (McEliecePKCS). It
+ * was first described in R.J. McEliece, "A public key cryptosystem based on
+ * algebraic coding theory", DSN progress report, 42-44:114-116, 1978. The
+ * McEliecePKCS is the first cryptosystem which is based on error correcting
+ * codes. The trapdoor for the McEliece cryptosystem using Goppa codes is the
+ * knowledge of the Goppa polynomial used to generate the code.
+ */
+public class McEliecePKCSCipher
+ implements MessageEncryptor
+{
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.1";
+
+
+ // the source of randomness
+ private SecureRandom sr;
+
+ // the McEliece main parameters
+ private int n, k, t;
+
+ // The maximum number of bytes the cipher can decrypt
+ public int maxPlainTextSize;
+
+ // The maximum number of bytes the cipher can encrypt
+ public int cipherTextSize;
+
+ McElieceKeyParameters key;
+
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McEliecePublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McEliecePublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McEliecePublicKeyParameters)param;
+ this.initCipherEncrypt((McEliecePublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McEliecePrivateKeyParameters)param;
+ this.initCipherDecrypt((McEliecePrivateKeyParameters)key);
+ }
+
+ }
+
+
+ /**
+ * Return the key size of the given key object.
+ *
+ * @param key the McElieceKeyParameters object
+ * @return the keysize of the given key object
+ */
+
+ public int getKeySize(McElieceKeyParameters key)
+ {
+
+ if (key instanceof McEliecePublicKeyParameters)
+ {
+ return ((McEliecePublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McEliecePrivateKeyParameters)
+ {
+ return ((McEliecePrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+
+ }
+
+
+ public void initCipherEncrypt(McEliecePublicKeyParameters pubKey)
+ {
+ this.sr = sr != null ? sr : new SecureRandom();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+ cipherTextSize = n >> 3;
+ maxPlainTextSize = (k >> 3);
+ }
+
+
+ public void initCipherDecrypt(McEliecePrivateKeyParameters privKey)
+ {
+ n = privKey.getN();
+ k = privKey.getK();
+
+ maxPlainTextSize = (k >> 3);
+ cipherTextSize = n >> 3;
+ }
+
+ /**
+ * Encrypt a plain text.
+ *
+ * @param input the plain text
+ * @return the cipher text
+ */
+ public byte[] messageEncrypt(byte[] input)
+ {
+ GF2Vector m = computeMessageRepresentative(input);
+ GF2Vector z = new GF2Vector(n, t, sr);
+
+ GF2Matrix g = ((McEliecePublicKeyParameters)key).getG();
+ Vector mG = g.leftMultiply(m);
+ GF2Vector mGZ = (GF2Vector)mG.add(z);
+
+ return mGZ.getEncoded();
+ }
+
+ private GF2Vector computeMessageRepresentative(byte[] input)
+ {
+ byte[] data = new byte[maxPlainTextSize + ((k & 0x07) != 0 ? 1 : 0)];
+ System.arraycopy(input, 0, data, 0, input.length);
+ data[input.length] = 0x01;
+ return GF2Vector.OS2VP(k, data);
+ }
+
+ /**
+ * Decrypt a cipher text.
+ *
+ * @param input the cipher text
+ * @return the plain text
+ * @throws Exception if the cipher text is invalid.
+ */
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+ GF2Vector vec = GF2Vector.OS2VP(n, input);
+ McEliecePrivateKeyParameters privKey = (McEliecePrivateKeyParameters)key;
+ GF2mField field = privKey.getField();
+ PolynomialGF2mSmallM gp = privKey.getGoppaPoly();
+ GF2Matrix sInv = privKey.getSInv();
+ Permutation p1 = privKey.getP1();
+ Permutation p2 = privKey.getP2();
+ GF2Matrix h = privKey.getH();
+ PolynomialGF2mSmallM[] qInv = privKey.getQInv();
+
+ // compute permutation P = P1 * P2
+ Permutation p = p1.rightMultiply(p2);
+
+ // compute P^-1
+ Permutation pInv = p.computeInverse();
+
+ // compute c P^-1
+ GF2Vector cPInv = (GF2Vector)vec.multiply(pInv);
+
+ // compute syndrome of c P^-1
+ GF2Vector syndrome = (GF2Vector)h.rightMultiply(cPInv);
+
+ // decode syndrome
+ GF2Vector z = GoppaCode.syndromeDecode(syndrome, field, gp, qInv);
+ GF2Vector mSG = (GF2Vector)cPInv.add(z);
+
+ // multiply codeword with P1 and error vector with P
+ mSG = (GF2Vector)mSG.multiply(p1);
+ z = (GF2Vector)z.multiply(p);
+
+ // extract mS (last k columns of mSG)
+ GF2Vector mS = mSG.extractRightVector(k);
+
+ // compute plaintext vector
+ GF2Vector mVec = (GF2Vector)sInv.leftMultiply(mS);
+
+ // compute and return plaintext
+ return computeMessage(mVec);
+ }
+
+ private byte[] computeMessage(GF2Vector mr)
+ throws Exception
+ {
+ byte[] mrBytes = mr.getEncoded();
+ // find first non-zero byte
+ int index;
+ for (index = mrBytes.length - 1; index >= 0 && mrBytes[index] == 0; index--)
+ {
+ ;
+ }
+
+ // check if padding byte is valid
+ if (mrBytes[index] != 0x01)
+ {
+ throw new Exception("Bad Padding: invalid ciphertext");
+ }
+
+ // extract and return message
+ byte[] mBytes = new byte[index];
+ System.arraycopy(mrBytes, 0, mBytes, 0, index);
+ return mBytes;
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSDigestCipher.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSDigestCipher.java
new file mode 100644
index 00000000..901fe688
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePKCSDigestCipher.java
@@ -0,0 +1,128 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McEliecePKCSDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCipher;
+
+ private boolean forEncrypting;
+
+
+ public McEliecePKCSDigestCipher(MessageEncryptor mcElieceCipher, Digest messDigest)
+ {
+ this.mcElieceCipher = mcElieceCipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePKCSDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePKCSDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceParameters.java
new file mode 100644
index 00000000..d49f9279
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McElieceParameters.java
@@ -0,0 +1,181 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialRingGF2;
+
+public class McElieceParameters
+ implements CipherParameters
+{
+
+ /**
+ * The default extension degree
+ */
+ public static final int DEFAULT_M = 11;
+
+ /**
+ * The default error correcting capability.
+ */
+ public static final int DEFAULT_T = 50;
+
+ /**
+ * extension degree of the finite field GF(2^m)
+ */
+ private int m;
+
+ /**
+ * error correction capability of the code
+ */
+ private int t;
+
+ /**
+ * length of the code
+ */
+ private int n;
+
+ /**
+ * the field polynomial
+ */
+ private int fieldPoly;
+
+ /**
+ * Constructor. Set the default parameters: extension degree.
+ */
+ public McElieceParameters()
+ {
+ this(DEFAULT_M, DEFAULT_T);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param keysize the length of a Goppa code
+ * @throws IllegalArgumentException if <tt>keysize &lt; 1</tt>.
+ */
+ public McElieceParameters(int keysize)
+ throws IllegalArgumentException
+ {
+ if (keysize < 1)
+ {
+ throw new IllegalArgumentException("key size must be positive");
+ }
+ m = 0;
+ n = 1;
+ while (n < keysize)
+ {
+ n <<= 1;
+ m++;
+ }
+ t = n >>> 1;
+ t /= m;
+ fieldPoly = PolynomialRingGF2.getIrreduciblePolynomial(m);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param m degree of the finite field GF(2^m)
+ * @param t error correction capability of the code
+ * @throws IllegalArgumentException if <tt>m &lt; 1</tt> or <tt>m &gt; 32</tt> or
+ * <tt>t &lt; 0</tt> or <tt>t &gt; n</tt>.
+ */
+ public McElieceParameters(int m, int t)
+ throws IllegalArgumentException
+ {
+ if (m < 1)
+ {
+ throw new IllegalArgumentException("m must be positive");
+ }
+ if (m > 32)
+ {
+ throw new IllegalArgumentException("m is too large");
+ }
+ this.m = m;
+ n = 1 << m;
+ if (t < 0)
+ {
+ throw new IllegalArgumentException("t must be positive");
+ }
+ if (t > n)
+ {
+ throw new IllegalArgumentException("t must be less than n = 2^m");
+ }
+ this.t = t;
+ fieldPoly = PolynomialRingGF2.getIrreduciblePolynomial(m);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param m degree of the finite field GF(2^m)
+ * @param t error correction capability of the code
+ * @param poly the field polynomial
+ * @throws IllegalArgumentException if <tt>m &lt; 1</tt> or <tt>m &gt; 32</tt> or
+ * <tt>t &lt; 0</tt> or <tt>t &gt; n</tt> or
+ * <tt>poly</tt> is not an irreducible field polynomial.
+ */
+ public McElieceParameters(int m, int t, int poly)
+ throws IllegalArgumentException
+ {
+ this.m = m;
+ if (m < 1)
+ {
+ throw new IllegalArgumentException("m must be positive");
+ }
+ if (m > 32)
+ {
+ throw new IllegalArgumentException(" m is too large");
+ }
+ this.n = 1 << m;
+ this.t = t;
+ if (t < 0)
+ {
+ throw new IllegalArgumentException("t must be positive");
+ }
+ if (t > n)
+ {
+ throw new IllegalArgumentException("t must be less than n = 2^m");
+ }
+ if ((PolynomialRingGF2.degree(poly) == m)
+ && (PolynomialRingGF2.isIrreducible(poly)))
+ {
+ this.fieldPoly = poly;
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "polynomial is not a field polynomial for GF(2^m)");
+ }
+ }
+
+ /**
+ * @return the extension degree of the finite field GF(2^m)
+ */
+ public int getM()
+ {
+ return m;
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the error correction capability of the code
+ */
+ public int getT()
+ {
+ return t;
+ }
+
+ /**
+ * @return the field polynomial
+ */
+ public int getFieldPoly()
+ {
+ return fieldPoly;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java
new file mode 100644
index 00000000..ba58258b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java
@@ -0,0 +1,241 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.prng.DigestRandomGenerator;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+import org.spongycastle.pqc.math.linearalgebra.ByteUtils;
+import org.spongycastle.pqc.math.linearalgebra.GF2Vector;
+
+/**
+ * This class implements the Pointcheval conversion of the McEliecePKCS.
+ * Pointcheval presents a generic technique to make a CCA2-secure cryptosystem
+ * from any partially trapdoor one-way function in the random oracle model. For
+ * details, see D. Engelbert, R. Overbeck, A. Schmidt, "A summary of the
+ * development of the McEliece Cryptosystem", technical report.
+ */
+public class McEliecePointchevalCipher
+ implements MessageEncryptor
+{
+
+
+ /**
+ * The OID of the algorithm.
+ */
+ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.2";
+
+ private Digest messDigest;
+
+ private SecureRandom sr;
+
+ /**
+ * The McEliece main parameters
+ */
+ private int n, k, t;
+
+ McElieceCCA2KeyParameters key;
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.sr = rParam.getRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)rParam.getParameters();
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+
+ }
+ else
+ {
+ this.sr = new SecureRandom();
+ this.key = (McElieceCCA2PublicKeyParameters)param;
+ this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
+ }
+ }
+ else
+ {
+ this.key = (McElieceCCA2PrivateKeyParameters)param;
+ this.initCipherDecrypt((McElieceCCA2PrivateKeyParameters)key);
+ }
+
+ }
+
+ /**
+ * Return the key size of the given key object.
+ *
+ * @param key the McElieceCCA2KeyParameters object
+ * @return the key size of the given key object
+ * @throws IllegalArgumentException if the key is invalid
+ */
+ public int getKeySize(McElieceCCA2KeyParameters key)
+ throws IllegalArgumentException
+ {
+
+ if (key instanceof McElieceCCA2PublicKeyParameters)
+ {
+ return ((McElieceCCA2PublicKeyParameters)key).getN();
+
+ }
+ if (key instanceof McElieceCCA2PrivateKeyParameters)
+ {
+ return ((McElieceCCA2PrivateKeyParameters)key).getN();
+ }
+ throw new IllegalArgumentException("unsupported type");
+
+ }
+
+
+ protected int decryptOutputSize(int inLen)
+ {
+ return 0;
+ }
+
+ protected int encryptOutputSize(int inLen)
+ {
+ return 0;
+ }
+
+
+ public void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
+ {
+ this.sr = sr != null ? sr : new SecureRandom();
+ this.messDigest = pubKey.getParameters().getDigest();
+ n = pubKey.getN();
+ k = pubKey.getK();
+ t = pubKey.getT();
+ }
+
+ public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+ {
+ this.messDigest = privKey.getParameters().getDigest();
+ n = privKey.getN();
+ k = privKey.getK();
+ t = privKey.getT();
+ }
+
+ public byte[] messageEncrypt(byte[] input)
+ throws Exception
+ {
+
+ int kDiv8 = k >> 3;
+
+ // generate random r of length k div 8 bytes
+ byte[] r = new byte[kDiv8];
+ sr.nextBytes(r);
+
+ // generate random vector r' of length k bits
+ GF2Vector rPrime = new GF2Vector(k, sr);
+
+ // convert r' to byte array
+ byte[] rPrimeBytes = rPrime.getEncoded();
+
+ // compute (input||r)
+ byte[] mr = ByteUtils.concatenate(input, r);
+
+ // compute H(input||r)
+ messDigest.update(mr, 0, mr.length);
+ byte[] hmr = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hmr, 0);
+
+
+ // convert H(input||r) to error vector z
+ GF2Vector z = Conversions.encode(n, t, hmr);
+
+ // compute c1 = E(rPrime, z)
+ byte[] c1 = McElieceCCA2Primitives.encryptionPrimitive((McElieceCCA2PublicKeyParameters)key, rPrime,
+ z).getEncoded();
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rPrimeBytes);
+
+ // generate random c2
+ byte[] c2 = new byte[input.length + kDiv8];
+ sr0.nextBytes(c2);
+
+ // XOR with input
+ for (int i = 0; i < input.length; i++)
+ {
+ c2[i] ^= input[i];
+ }
+ // XOR with r
+ for (int i = 0; i < kDiv8; i++)
+ {
+ c2[input.length + i] ^= r[i];
+ }
+
+ // return (c1||c2)
+ return ByteUtils.concatenate(c1, c2);
+ }
+
+ public byte[] messageDecrypt(byte[] input)
+ throws Exception
+ {
+
+ int c1Len = (n + 7) >> 3;
+ int c2Len = input.length - c1Len;
+
+ // split cipher text (c1||c2)
+ byte[][] c1c2 = ByteUtils.split(input, c1Len);
+ byte[] c1 = c1c2[0];
+ byte[] c2 = c1c2[1];
+
+ // decrypt c1 ...
+ GF2Vector c1Vec = GF2Vector.OS2VP(n, c1);
+ GF2Vector[] c1Dec = McElieceCCA2Primitives.decryptionPrimitive((McElieceCCA2PrivateKeyParameters)key,
+ c1Vec);
+ byte[] rPrimeBytes = c1Dec[0].getEncoded();
+ // ... and obtain error vector z
+ GF2Vector z = c1Dec[1];
+
+ // get PRNG object
+ DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
+
+ // seed PRNG with r'
+ sr0.addSeedMaterial(rPrimeBytes);
+
+ // generate random sequence
+ byte[] mrBytes = new byte[c2Len];
+ sr0.nextBytes(mrBytes);
+
+ // XOR with c2 to obtain (m||r)
+ for (int i = 0; i < c2Len; i++)
+ {
+ mrBytes[i] ^= c2[i];
+ }
+
+ // compute H(m||r)
+ messDigest.update(mrBytes, 0, mrBytes.length);
+ byte[] hmr = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hmr, 0);
+
+ // compute Conv(H(m||r))
+ c1Vec = Conversions.encode(n, t, hmr);
+
+ // check that Conv(H(m||r)) = z
+ if (!c1Vec.equals(z))
+ {
+ throw new Exception("Bad Padding: Invalid ciphertext.");
+ }
+
+ // split (m||r) to obtain m
+ int kDiv8 = k >> 3;
+ byte[][] mr = ByteUtils.split(mrBytes, c2Len - kDiv8);
+
+ // return plain text m
+ return mr[0];
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalDigestCipher.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalDigestCipher.java
new file mode 100644
index 00000000..bcfd1d87
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePointchevalDigestCipher.java
@@ -0,0 +1,128 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageEncryptor;
+
+// TODO should implement some interface?
+public class McEliecePointchevalDigestCipher
+{
+
+ private final Digest messDigest;
+
+ private final MessageEncryptor mcElieceCCA2Cipher;
+
+ private boolean forEncrypting;
+
+
+ public McEliecePointchevalDigestCipher(MessageEncryptor mcElieceCCA2Cipher, Digest messDigest)
+ {
+ this.mcElieceCCA2Cipher = mcElieceCCA2Cipher;
+ this.messDigest = messDigest;
+ }
+
+
+ public void init(boolean forEncrypting,
+ CipherParameters param)
+ {
+
+ this.forEncrypting = forEncrypting;
+ AsymmetricKeyParameter k;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ k = (AsymmetricKeyParameter)((ParametersWithRandom)param).getParameters();
+ }
+ else
+ {
+ k = (AsymmetricKeyParameter)param;
+ }
+
+ if (forEncrypting && k.isPrivate())
+ {
+ throw new IllegalArgumentException("Encrypting Requires Public Key.");
+ }
+
+ if (!forEncrypting && !k.isPrivate())
+ {
+ throw new IllegalArgumentException("Decrypting Requires Private Key.");
+ }
+
+ reset();
+
+ mcElieceCCA2Cipher.init(forEncrypting, param);
+ }
+
+
+ public byte[] messageEncrypt()
+ {
+ if (!forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePointchevalDigestCipher not initialised for encrypting.");
+ }
+
+ byte[] hash = new byte[messDigest.getDigestSize()];
+ messDigest.doFinal(hash, 0);
+ byte[] enc = null;
+
+ try
+ {
+ enc = mcElieceCCA2Cipher.messageEncrypt(hash);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return enc;
+ }
+
+
+ public byte[] messageDecrypt(byte[] ciphertext)
+ {
+ byte[] output = null;
+ if (forEncrypting)
+ {
+ throw new IllegalStateException("McEliecePointchevalDigestCipher not initialised for decrypting.");
+ }
+
+
+ try
+ {
+ output = mcElieceCCA2Cipher.messageDecrypt(ciphertext);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ return output;
+ }
+
+
+ public void update(byte b)
+ {
+ messDigest.update(b);
+
+ }
+
+ public void update(byte[] in, int off, int len)
+ {
+ messDigest.update(in, off, len);
+
+ }
+
+
+ public void reset()
+ {
+ messDigest.reset();
+
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePrivateKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePrivateKeyParameters.java
new file mode 100644
index 00000000..3c4ef324
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePrivateKeyParameters.java
@@ -0,0 +1,197 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+import org.spongycastle.pqc.math.linearalgebra.GF2mField;
+import org.spongycastle.pqc.math.linearalgebra.Permutation;
+import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
+
+
+public class McEliecePrivateKeyParameters
+ extends McElieceKeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the dimension of the code, where <tt>k &gt;= n - mt</tt>
+ private int k;
+
+ // the underlying finite field
+ private GF2mField field;
+
+ // the irreducible Goppa polynomial
+ private PolynomialGF2mSmallM goppaPoly;
+
+ // a k x k random binary non-singular matrix
+ private GF2Matrix sInv;
+
+ // the permutation used to generate the systematic check matrix
+ private Permutation p1;
+
+ // the permutation used to compute the public generator matrix
+ private Permutation p2;
+
+ // the canonical check matrix of the code
+ private GF2Matrix h;
+
+ // the matrix used to compute square roots in <tt>(GF(2^m))^t</tt>
+ private PolynomialGF2mSmallM[] qInv;
+
+ /**
+ * Constructor.
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param field the field polynomial defining the finite field
+ * <tt>GF(2<sup>m</sup>)</tt>
+ * @param goppaPoly the irreducible Goppa polynomial
+ * @param sInv the matrix <tt>S<sup>-1</sup></tt>
+ * @param p1 the permutation used to generate the systematic check
+ * matrix
+ * @param p2 the permutation used to compute the public generator
+ * matrix
+ * @param h the canonical check matrix
+ * @param qInv the matrix used to compute square roots in
+ * <tt>(GF(2<sup>m</sup>))<sup>t</sup></tt>
+ * @param params McElieceParameters
+ */
+ public McEliecePrivateKeyParameters(String oid, int n, int k, GF2mField field,
+ PolynomialGF2mSmallM goppaPoly, GF2Matrix sInv, Permutation p1,
+ Permutation p2, GF2Matrix h, PolynomialGF2mSmallM[] qInv, McElieceParameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.k = k;
+ this.n = n;
+ this.field = field;
+ this.goppaPoly = goppaPoly;
+ this.sInv = sInv;
+ this.p1 = p1;
+ this.p2 = p2;
+ this.h = h;
+ this.qInv = qInv;
+ }
+
+ /**
+ * Constructor (used by the {@link McElieceKeyFactory}).
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param k the dimension of the code
+ * @param encField the encoded field polynomial defining the finite field
+ * <tt>GF(2<sup>m</sup>)</tt>
+ * @param encGoppaPoly the encoded irreducible Goppa polynomial
+ * @param encSInv the encoded matrix <tt>S<sup>-1</sup></tt>
+ * @param encP1 the encoded permutation used to generate the systematic
+ * check matrix
+ * @param encP2 the encoded permutation used to compute the public
+ * generator matrix
+ * @param encH the encoded canonical check matrix
+ * @param encQInv the encoded matrix used to compute square roots in
+ * <tt>(GF(2<sup>m</sup>))<sup>t</sup></tt>
+ * @param params McElieceParameters
+ */
+ public McEliecePrivateKeyParameters(String oid, int n, int k, byte[] encField,
+ byte[] encGoppaPoly, byte[] encSInv, byte[] encP1, byte[] encP2,
+ byte[] encH, byte[][] encQInv, McElieceParameters params)
+ {
+ super(true, params);
+ this.oid = oid;
+ this.n = n;
+ this.k = k;
+ field = new GF2mField(encField);
+ goppaPoly = new PolynomialGF2mSmallM(field, encGoppaPoly);
+ sInv = new GF2Matrix(encSInv);
+ p1 = new Permutation(encP1);
+ p2 = new Permutation(encP2);
+ h = new GF2Matrix(encH);
+ qInv = new PolynomialGF2mSmallM[encQInv.length];
+ for (int i = 0; i < encQInv.length; i++)
+ {
+ qInv[i] = new PolynomialGF2mSmallM(field, encQInv[i]);
+ }
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return k;
+ }
+
+ /**
+ * @return the finite field <tt>GF(2<sup>m</sup>)</tt>
+ */
+ public GF2mField getField()
+ {
+ return field;
+ }
+
+ /**
+ * @return the irreducible Goppa polynomial
+ */
+ public PolynomialGF2mSmallM getGoppaPoly()
+ {
+ return goppaPoly;
+ }
+
+ /**
+ * @return the k x k random binary non-singular matrix S^-1
+ */
+ public GF2Matrix getSInv()
+ {
+ return sInv;
+ }
+
+ /**
+ * @return the permutation used to generate the systematic check matrix
+ */
+ public Permutation getP1()
+ {
+ return p1;
+ }
+
+ /**
+ * @return the permutation used to compute the public generator matrix
+ */
+ public Permutation getP2()
+ {
+ return p2;
+ }
+
+ /**
+ * @return the canonical check matrix H
+ */
+ public GF2Matrix getH()
+ {
+ return h;
+ }
+
+ /**
+ * @return the matrix used to compute square roots in
+ * <tt>(GF(2<sup>m</sup>))<sup>t</sup></tt>
+ */
+ public PolynomialGF2mSmallM[] getQInv()
+ {
+ return qInv;
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePublicKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePublicKeyParameters.java
new file mode 100644
index 00000000..9f011243
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/mceliece/McEliecePublicKeyParameters.java
@@ -0,0 +1,96 @@
+package org.spongycastle.pqc.crypto.mceliece;
+
+import org.spongycastle.pqc.math.linearalgebra.GF2Matrix;
+
+
+public class McEliecePublicKeyParameters
+ extends McElieceKeyParameters
+{
+
+ // the OID of the algorithm
+ private String oid;
+
+ // the length of the code
+ private int n;
+
+ // the error correction capability of the code
+ private int t;
+
+ // the generator matrix
+ private GF2Matrix g;
+
+ /**
+ * Constructor (used by {@link McElieceKeyFactory}).
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param t the error correction capability of the code
+ * @param g the generator matrix
+ * @param params McElieceParameters
+ */
+ public McEliecePublicKeyParameters(String oid, int n, int t, GF2Matrix g, McElieceParameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.g = new GF2Matrix(g);
+ }
+
+ /**
+ * Constructor (used by {@link McElieceKeyFactory}).
+ *
+ * @param oid
+ * @param n the length of the code
+ * @param t the error correction capability of the code
+ * @param encG the encoded generator matrix
+ * @param params McElieceParameters
+ */
+ public McEliecePublicKeyParameters(String oid, int t, int n, byte[] encG, McElieceParameters params)
+ {
+ super(false, params);
+ this.oid = oid;
+ this.n = n;
+ this.t = t;
+ this.g = new GF2Matrix(encG);
+ }
+
+ /**
+ * @return the length of the code
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * @return the error correction capability of the code
+ */
+ public int getT()
+ {
+ return t;
+ }
+
+ /**
+ * @return the generator matrix
+ */
+ public GF2Matrix getG()
+ {
+ return g;
+ }
+
+ public String getOIDString()
+ {
+ return oid;
+
+ }
+
+ /**
+ * @return the dimension of the code
+ */
+ public int getK()
+ {
+ return g.getNumRows();
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/IndexGenerator.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/IndexGenerator.java
new file mode 100644
index 00000000..01caf970
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/IndexGenerator.java
@@ -0,0 +1,239 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.util.Arrays;
+
+/**
+ * An implementation of the Index Generation Function in IEEE P1363.1.
+ */
+public class IndexGenerator
+{
+ private byte[] seed;
+ private int N;
+ private int c;
+ private int minCallsR;
+ private int totLen;
+ private int remLen;
+ private BitString buf;
+ private int counter;
+ private boolean initialized;
+ private Digest hashAlg;
+ private int hLen;
+
+ /**
+ * Constructs a new index generator.
+ *
+ * @param seed a seed of arbitrary length to initialize the index generator with
+ * @param params NtruEncrypt parameters
+ */
+ IndexGenerator(byte[] seed, NTRUEncryptionParameters params)
+ {
+ this.seed = seed;
+ N = params.N;
+ c = params.c;
+ minCallsR = params.minCallsR;
+
+ totLen = 0;
+ remLen = 0;
+ counter = 0;
+ hashAlg = params.hashAlg;
+
+ hLen = hashAlg.getDigestSize(); // hash length
+ initialized = false;
+ }
+
+ /**
+ * Returns a number <code>i</code> such that <code>0 &lt;= i &lt; N</code>.
+ *
+ * @return
+ */
+ int nextIndex()
+ {
+ if (!initialized)
+ {
+ buf = new BitString();
+ byte[] hash = new byte[hashAlg.getDigestSize()];
+ while (counter < minCallsR)
+ {
+ appendHash(buf, hash);
+ counter++;
+ }
+ totLen = minCallsR * 8 * hLen;
+ remLen = totLen;
+ initialized = true;
+ }
+
+ while (true)
+ {
+ totLen += c;
+ BitString M = buf.getTrailing(remLen);
+ if (remLen < c)
+ {
+ int tmpLen = c - remLen;
+ int cThreshold = counter + (tmpLen + hLen - 1) / hLen;
+ byte[] hash = new byte[hashAlg.getDigestSize()];
+ while (counter < cThreshold)
+ {
+ appendHash(M, hash);
+ counter++;
+ if (tmpLen > 8 * hLen)
+ {
+ tmpLen -= 8 * hLen;
+ }
+ }
+ remLen = 8 * hLen - tmpLen;
+ buf = new BitString();
+ buf.appendBits(hash);
+ }
+ else
+ {
+ remLen -= c;
+ }
+
+ int i = M.getLeadingAsInt(c); // assume c<32
+ if (i < (1 << c) - ((1 << c) % N))
+ {
+ return i % N;
+ }
+ }
+ }
+
+ private void appendHash(BitString m, byte[] hash)
+ {
+ hashAlg.update(seed, 0, seed.length);
+
+ putInt(hashAlg, counter);
+
+ hashAlg.doFinal(hash, 0);
+
+ m.appendBits(hash);
+ }
+
+ private void putInt(Digest hashAlg, int counter)
+ {
+ hashAlg.update((byte)(counter >> 24));
+ hashAlg.update((byte)(counter >> 16));
+ hashAlg.update((byte)(counter >> 8));
+ hashAlg.update((byte)counter);
+ }
+
+ /**
+ * Represents a string of bits and supports appending, reading the head, and reading the tail.
+ */
+ public static class BitString
+ {
+ byte[] bytes = new byte[4];
+ int numBytes; // includes the last byte even if only some of its bits are used
+ int lastByteBits; // lastByteBits <= 8
+
+ /**
+ * Appends all bits in a byte array to the end of the bit string.
+ *
+ * @param bytes a byte array
+ */
+ void appendBits(byte[] bytes)
+ {
+ for (int i = 0; i != bytes.length; i++)
+ {
+ appendBits(bytes[i]);
+ }
+ }
+
+ /**
+ * Appends all bits in a byte to the end of the bit string.
+ *
+ * @param b a byte
+ */
+ public void appendBits(byte b)
+ {
+ if (numBytes == bytes.length)
+ {
+ bytes = copyOf(bytes, 2 * bytes.length);
+ }
+
+ if (numBytes == 0)
+ {
+ numBytes = 1;
+ bytes[0] = b;
+ lastByteBits = 8;
+ }
+ else if (lastByteBits == 8)
+ {
+ bytes[numBytes++] = b;
+ }
+ else
+ {
+ int s = 8 - lastByteBits;
+ bytes[numBytes - 1] |= (b & 0xFF) << lastByteBits;
+ bytes[numBytes++] = (byte)((b & 0xFF) >> s);
+ }
+ }
+
+ /**
+ * Returns the last <code>numBits</code> bits from the end of the bit string.
+ *
+ * @param numBits number of bits
+ * @return a new <code>BitString</code> of length <code>numBits</code>
+ */
+ public BitString getTrailing(int numBits)
+ {
+ BitString newStr = new BitString();
+ newStr.numBytes = (numBits + 7) / 8;
+ newStr.bytes = new byte[newStr.numBytes];
+ for (int i = 0; i < newStr.numBytes; i++)
+ {
+ newStr.bytes[i] = bytes[i];
+ }
+
+ newStr.lastByteBits = numBits % 8;
+ if (newStr.lastByteBits == 0)
+ {
+ newStr.lastByteBits = 8;
+ }
+ else
+ {
+ int s = 32 - newStr.lastByteBits;
+ newStr.bytes[newStr.numBytes - 1] = (byte)(newStr.bytes[newStr.numBytes - 1] << s >>> s);
+ }
+
+ return newStr;
+ }
+
+ /**
+ * Returns up to 32 bits from the beginning of the bit string.
+ *
+ * @param numBits number of bits
+ * @return an <code>int</code> whose lower <code>numBits</code> bits are the beginning of the bit string
+ */
+ public int getLeadingAsInt(int numBits)
+ {
+ int startBit = (numBytes - 1) * 8 + lastByteBits - numBits;
+ int startByte = startBit / 8;
+
+ int startBitInStartByte = startBit % 8;
+ int sum = (bytes[startByte] & 0xFF) >>> startBitInStartByte;
+ int shift = 8 - startBitInStartByte;
+ for (int i = startByte + 1; i < numBytes; i++)
+ {
+ sum |= (bytes[i] & 0xFF) << shift;
+ shift += 8;
+ }
+
+ return sum;
+ }
+
+ public byte[] getBytes()
+ {
+ return Arrays.clone(bytes);
+ }
+ }
+
+ private static byte[] copyOf(byte[] src, int len)
+ {
+ byte[] tmp = new byte[len];
+
+ System.arraycopy(src, 0, tmp, 0, len < src.length ? len : src.length);
+
+ return tmp;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java
new file mode 100644
index 00000000..76cb839d
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java
@@ -0,0 +1,463 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruEncrypt. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUEncryptionKeyGenerationParameters
+ extends KeyGenerationParameters
+ implements Cloneable
+{
+ /**
+ * A conservative (in terms of security) parameter set that gives 256 bits of security and is optimized for key size.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters EES1087EP2 = new NTRUEncryptionKeyGenerationParameters(1087, 2048, 120, 120, 256, 13, 25, 14, true, new byte[]{0, 6, 3}, true, false, new SHA512Digest());
+
+ /**
+ * A conservative (in terms of security) parameter set that gives 256 bits of security and is a tradeoff between key size and encryption/decryption speed.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters EES1171EP1 = new NTRUEncryptionKeyGenerationParameters(1171, 2048, 106, 106, 256, 13, 20, 15, true, new byte[]{0, 6, 4}, true, false, new SHA512Digest());
+
+ /**
+ * A conservative (in terms of security) parameter set that gives 256 bits of security and is optimized for encryption/decryption speed.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters EES1499EP1 = new NTRUEncryptionKeyGenerationParameters(1499, 2048, 79, 79, 256, 13, 17, 19, true, new byte[]{0, 6, 5}, true, false, new SHA512Digest());
+
+ /**
+ * A parameter set that gives 128 bits of security and uses simple ternary polynomials.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_439 = new NTRUEncryptionKeyGenerationParameters(439, 2048, 146, 130, 128, 9, 32, 9, true, new byte[]{0, 7, 101}, true, false, new SHA256Digest());
+
+ /**
+ * Like <code>APR2011_439</code>, this parameter set gives 128 bits of security but uses product-form polynomials and <code>f=1+pF</code>.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_439_FAST = new NTRUEncryptionKeyGenerationParameters(439, 2048, 9, 8, 5, 130, 128, 9, 32, 9, true, new byte[]{0, 7, 101}, true, true, new SHA256Digest());
+
+ /**
+ * A parameter set that gives 256 bits of security and uses simple ternary polynomials.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_743 = new NTRUEncryptionKeyGenerationParameters(743, 2048, 248, 220, 256, 10, 27, 14, true, new byte[]{0, 7, 105}, false, false, new SHA512Digest());
+
+ /**
+ * Like <code>APR2011_743</code>, this parameter set gives 256 bits of security but uses product-form polynomials and <code>f=1+pF</code>.
+ */
+ public static final NTRUEncryptionKeyGenerationParameters APR2011_743_FAST = new NTRUEncryptionKeyGenerationParameters(743, 2048, 11, 11, 15, 220, 256, 10, 27, 14, true, new byte[]{0, 7, 105}, false, true, new SHA512Digest());
+
+ public int N, q, df, df1, df2, df3;
+ public int dr;
+ public int dr1;
+ public int dr2;
+ public int dr3;
+ public int dg;
+ int llen;
+ public int maxMsgLenBytes;
+ public int db;
+ public int bufferLenBits;
+ int bufferLenTrits;
+ public int dm0;
+ public int pkLen;
+ public int c;
+ public int minCallsR;
+ public int minCallsMask;
+ public boolean hashSeed;
+ public byte[] oid;
+ public boolean sparse;
+ public boolean fastFp;
+ public int polyType;
+ public Digest hashAlg;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. <code>polyType=SIMPLE</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df number of ones in the private polynomial <code>f</code>
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial <code>m'</code> in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.spongycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether <code>f=1+p*F</code> for a ternary <code>F</code> (true) or <code>f</code> is ternary (false)
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUEncryptionKeyGenerationParameters(int N, int q, int df, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ super(new SecureRandom(), db);
+ this.N = N;
+ this.q = q;
+ this.df = df;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. <code>polyType=PRODUCT</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df1 number of ones in the private polynomial <code>f1</code>
+ * @param df2 number of ones in the private polynomial <code>f2</code>
+ * @param df3 number of ones in the private polynomial <code>f3</code>
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial <code>m'</code> in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.spongycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether <code>f=1+p*F</code> for a ternary <code>F</code> (true) or <code>f</code> is ternary (false)
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>
+ */
+ public NTRUEncryptionKeyGenerationParameters(int N, int q, int df1, int df2, int df3, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ super(new SecureRandom(), db);
+
+ this.N = N;
+ this.q = q;
+ this.df1 = df1;
+ this.df2 = df2;
+ this.df3 = df3;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ private void init()
+ {
+ dr = df;
+ dr1 = df1;
+ dr2 = df2;
+ dr3 = df3;
+ dg = N / 3;
+ llen = 1; // ceil(log2(maxMsgLenBytes))
+ maxMsgLenBytes = N * 3 / 2 / 8 - llen - db / 8 - 1;
+ bufferLenBits = (N * 3 / 2 + 7) / 8 * 8 + 1;
+ bufferLenTrits = N - 1;
+ pkLen = db;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws java.io.IOException
+ */
+ public NTRUEncryptionKeyGenerationParameters(InputStream is)
+ throws IOException
+ {
+ super(new SecureRandom(), -1);
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ df = dis.readInt();
+ df1 = dis.readInt();
+ df2 = dis.readInt();
+ df3 = dis.readInt();
+ db = dis.readInt();
+ dm0 = dis.readInt();
+ c = dis.readInt();
+ minCallsR = dis.readInt();
+ minCallsMask = dis.readInt();
+ hashSeed = dis.readBoolean();
+ oid = new byte[3];
+ dis.read(oid);
+ sparse = dis.readBoolean();
+ fastFp = dis.readBoolean();
+ polyType = dis.read();
+
+ String alg = dis.readUTF();
+
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+
+ init();
+ }
+
+ public NTRUEncryptionParameters getEncryptionParameters()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUEncryptionParameters(N, q, df, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ else
+ {
+ return new NTRUEncryptionParameters(N, q, df1, df2, df3, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ }
+
+ public NTRUEncryptionKeyGenerationParameters clone()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUEncryptionKeyGenerationParameters(N, q, df, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ else
+ {
+ return new NTRUEncryptionKeyGenerationParameters(N, q, df1, df2, df3, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ }
+
+ /**
+ * Returns the maximum length a plaintext message can be with this parameter set.
+ *
+ * @return the maximum length in bytes
+ */
+ public int getMaxMessageLength()
+ {
+ return maxMsgLenBytes;
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws java.io.IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(df);
+ dos.writeInt(df1);
+ dos.writeInt(df2);
+ dos.writeInt(df3);
+ dos.writeInt(db);
+ dos.writeInt(dm0);
+ dos.writeInt(c);
+ dos.writeInt(minCallsR);
+ dos.writeInt(minCallsMask);
+ dos.writeBoolean(hashSeed);
+ dos.write(oid);
+ dos.writeBoolean(sparse);
+ dos.writeBoolean(fastFp);
+ dos.write(polyType);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ }
+
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + N;
+ result = prime * result + bufferLenBits;
+ result = prime * result + bufferLenTrits;
+ result = prime * result + c;
+ result = prime * result + db;
+ result = prime * result + df;
+ result = prime * result + df1;
+ result = prime * result + df2;
+ result = prime * result + df3;
+ result = prime * result + dg;
+ result = prime * result + dm0;
+ result = prime * result + dr;
+ result = prime * result + dr1;
+ result = prime * result + dr2;
+ result = prime * result + dr3;
+ result = prime * result + (fastFp ? 1231 : 1237);
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ result = prime * result + (hashSeed ? 1231 : 1237);
+ result = prime * result + llen;
+ result = prime * result + maxMsgLenBytes;
+ result = prime * result + minCallsMask;
+ result = prime * result + minCallsR;
+ result = prime * result + Arrays.hashCode(oid);
+ result = prime * result + pkLen;
+ result = prime * result + polyType;
+ result = prime * result + q;
+ result = prime * result + (sparse ? 1231 : 1237);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUEncryptionKeyGenerationParameters other = (NTRUEncryptionKeyGenerationParameters)obj;
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (bufferLenBits != other.bufferLenBits)
+ {
+ return false;
+ }
+ if (bufferLenTrits != other.bufferLenTrits)
+ {
+ return false;
+ }
+ if (c != other.c)
+ {
+ return false;
+ }
+ if (db != other.db)
+ {
+ return false;
+ }
+ if (df != other.df)
+ {
+ return false;
+ }
+ if (df1 != other.df1)
+ {
+ return false;
+ }
+ if (df2 != other.df2)
+ {
+ return false;
+ }
+ if (df3 != other.df3)
+ {
+ return false;
+ }
+ if (dg != other.dg)
+ {
+ return false;
+ }
+ if (dm0 != other.dm0)
+ {
+ return false;
+ }
+ if (dr != other.dr)
+ {
+ return false;
+ }
+ if (dr1 != other.dr1)
+ {
+ return false;
+ }
+ if (dr2 != other.dr2)
+ {
+ return false;
+ }
+ if (dr3 != other.dr3)
+ {
+ return false;
+ }
+ if (fastFp != other.fastFp)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (hashSeed != other.hashSeed)
+ {
+ return false;
+ }
+ if (llen != other.llen)
+ {
+ return false;
+ }
+ if (maxMsgLenBytes != other.maxMsgLenBytes)
+ {
+ return false;
+ }
+ if (minCallsMask != other.minCallsMask)
+ {
+ return false;
+ }
+ if (minCallsR != other.minCallsR)
+ {
+ return false;
+ }
+ if (!Arrays.equals(oid, other.oid))
+ {
+ return false;
+ }
+ if (pkLen != other.pkLen)
+ {
+ return false;
+ }
+ if (polyType != other.polyType)
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (sparse != other.sparse)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString()
+ {
+ StringBuilder output = new StringBuilder("EncryptionParameters(N=" + N + " q=" + q);
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ output.append(" polyType=SIMPLE df=" + df);
+ }
+ else
+ {
+ output.append(" polyType=PRODUCT df1=" + df1 + " df2=" + df2 + " df3=" + df3);
+ }
+ output.append(" dm0=" + dm0 + " db=" + db + " c=" + c + " minCallsR=" + minCallsR + " minCallsMask=" + minCallsMask +
+ " hashSeed=" + hashSeed + " hashAlg=" + hashAlg + " oid=" + Arrays.toString(oid) + " sparse=" + sparse + ")");
+ return output.toString();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyPairGenerator.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyPairGenerator.java
new file mode 100644
index 00000000..fd483f09
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyPairGenerator.java
@@ -0,0 +1,113 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.spongycastle.pqc.math.ntru.util.Util;
+
+/**
+ * Generates key pairs.<br>
+ * The parameter p is hardcoded to 3.
+ */
+public class NTRUEncryptionKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private NTRUEncryptionKeyGenerationParameters params;
+
+ /**
+ * Constructs a new instance with a set of encryption parameters.
+ *
+ * @param param encryption parameters
+ */
+ public void init(KeyGenerationParameters param)
+ {
+ this.params = (NTRUEncryptionKeyGenerationParameters)param;
+ }
+
+ /**
+ * Generates a new encryption key pair.
+ *
+ * @return a key pair
+ */
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ int N = params.N;
+ int q = params.q;
+ int df = params.df;
+ int df1 = params.df1;
+ int df2 = params.df2;
+ int df3 = params.df3;
+ int dg = params.dg;
+ boolean fastFp = params.fastFp;
+ boolean sparse = params.sparse;
+
+ Polynomial t;
+ IntegerPolynomial fq;
+ IntegerPolynomial fp = null;
+
+ // choose a random f that is invertible mod 3 and q
+ while (true)
+ {
+ IntegerPolynomial f;
+
+ // choose random t, calculate f and fp
+ if (fastFp)
+ {
+ // if fastFp=true, f is always invertible mod 3
+ t = params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? Util.generateRandomTernary(N, df, df, sparse, params.getRandom()) : ProductFormPolynomial.generateRandom(N, df1, df2, df3, df3, params.getRandom());
+ f = t.toIntegerPolynomial();
+ f.mult(3);
+ f.coeffs[0] += 1;
+ }
+ else
+ {
+ t = params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? Util.generateRandomTernary(N, df, df - 1, sparse, params.getRandom()) : ProductFormPolynomial.generateRandom(N, df1, df2, df3, df3 - 1, params.getRandom());
+ f = t.toIntegerPolynomial();
+ fp = f.invertF3();
+ if (fp == null)
+ {
+ continue;
+ }
+ }
+
+ fq = f.invertFq(q);
+ if (fq == null)
+ {
+ continue;
+ }
+ break;
+ }
+
+ // if fastFp=true, fp=1
+ if (fastFp)
+ {
+ fp = new IntegerPolynomial(N);
+ fp.coeffs[0] = 1;
+ }
+
+ // choose a random g that is invertible mod q
+ DenseTernaryPolynomial g;
+ while (true)
+ {
+ g = DenseTernaryPolynomial.generateRandom(N, dg, dg - 1, params.getRandom());
+ if (g.invertFq(q) != null)
+ {
+ break;
+ }
+ }
+
+ IntegerPolynomial h = g.mult(fq, q);
+ h.mult3(q);
+ h.ensurePositive(q);
+ g.clear();
+ fq.clear();
+
+ NTRUEncryptionPrivateKeyParameters priv = new NTRUEncryptionPrivateKeyParameters(h, t, fp, params.getEncryptionParameters());
+ NTRUEncryptionPublicKeyParameters pub = new NTRUEncryptionPublicKeyParameters(h, params.getEncryptionParameters());
+ return new AsymmetricCipherKeyPair(pub, priv);
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyParameters.java
new file mode 100644
index 00000000..a22eb286
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionKeyParameters.java
@@ -0,0 +1,20 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+
+public class NTRUEncryptionKeyParameters
+ extends AsymmetricKeyParameter
+{
+ final protected NTRUEncryptionParameters params;
+
+ public NTRUEncryptionKeyParameters(boolean privateKey, NTRUEncryptionParameters params)
+ {
+ super(privateKey);
+ this.params = params;
+ }
+
+ public NTRUEncryptionParameters getParameters()
+ {
+ return params;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionParameters.java
new file mode 100644
index 00000000..86dabae8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionParameters.java
@@ -0,0 +1,410 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruEncrypt. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUEncryptionParameters
+ implements Cloneable
+{
+
+ public int N, q, df, df1, df2, df3;
+ public int dr;
+ public int dr1;
+ public int dr2;
+ public int dr3;
+ public int dg;
+ int llen;
+ public int maxMsgLenBytes;
+ public int db;
+ public int bufferLenBits;
+ int bufferLenTrits;
+ public int dm0;
+ public int pkLen;
+ public int c;
+ public int minCallsR;
+ public int minCallsMask;
+ public boolean hashSeed;
+ public byte[] oid;
+ public boolean sparse;
+ public boolean fastFp;
+ public int polyType;
+ public Digest hashAlg;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. <code>polyType=SIMPLE</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df number of ones in the private polynomial <code>f</code>
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial <code>m'</code> in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.spongycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether <code>f=1+p*F</code> for a ternary <code>F</code> (true) or <code>f</code> is ternary (false)
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUEncryptionParameters(int N, int q, int df, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.df = df;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. <code>polyType=PRODUCT</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param df1 number of ones in the private polynomial <code>f1</code>
+ * @param df2 number of ones in the private polynomial <code>f2</code>
+ * @param df3 number of ones in the private polynomial <code>f3</code>
+ * @param dm0 minimum acceptable number of -1's, 0's, and 1's in the polynomial <code>m'</code> in the last encryption step
+ * @param db number of random bits to prepend to the message
+ * @param c a parameter for the Index Generation Function ({@link org.spongycastle.pqc.crypto.ntru.IndexGenerator})
+ * @param minCallsR minimum number of hash calls for the IGF to make
+ * @param minCallsMask minimum number of calls to generate the masking polynomial
+ * @param hashSeed whether to hash the seed in the MGF first (true) or use the seed directly (false)
+ * @param oid three bytes that uniquely identify the parameter set
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param fastFp whether <code>f=1+p*F</code> for a ternary <code>F</code> (true) or <code>f</code> is ternary (false)
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>
+ */
+ public NTRUEncryptionParameters(int N, int q, int df1, int df2, int df3, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.df1 = df1;
+ this.df2 = df2;
+ this.df3 = df3;
+ this.db = db;
+ this.dm0 = dm0;
+ this.c = c;
+ this.minCallsR = minCallsR;
+ this.minCallsMask = minCallsMask;
+ this.hashSeed = hashSeed;
+ this.oid = oid;
+ this.sparse = sparse;
+ this.fastFp = fastFp;
+ this.polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ private void init()
+ {
+ dr = df;
+ dr1 = df1;
+ dr2 = df2;
+ dr3 = df3;
+ dg = N / 3;
+ llen = 1; // ceil(log2(maxMsgLenBytes))
+ maxMsgLenBytes = N * 3 / 2 / 8 - llen - db / 8 - 1;
+ bufferLenBits = (N * 3 / 2 + 7) / 8 * 8 + 1;
+ bufferLenTrits = N - 1;
+ pkLen = db;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws IOException
+ */
+ public NTRUEncryptionParameters(InputStream is)
+ throws IOException
+ {
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ df = dis.readInt();
+ df1 = dis.readInt();
+ df2 = dis.readInt();
+ df3 = dis.readInt();
+ db = dis.readInt();
+ dm0 = dis.readInt();
+ c = dis.readInt();
+ minCallsR = dis.readInt();
+ minCallsMask = dis.readInt();
+ hashSeed = dis.readBoolean();
+ oid = new byte[3];
+ dis.read(oid);
+ sparse = dis.readBoolean();
+ fastFp = dis.readBoolean();
+ polyType = dis.read();
+
+ String alg = dis.readUTF();
+
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+
+ init();
+ }
+
+ public NTRUEncryptionParameters clone()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUEncryptionParameters(N, q, df, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ else
+ {
+ return new NTRUEncryptionParameters(N, q, df1, df2, df3, dm0, db, c, minCallsR, minCallsMask, hashSeed, oid, sparse, fastFp, hashAlg);
+ }
+ }
+
+ /**
+ * Returns the maximum length a plaintext message can be with this parameter set.
+ *
+ * @return the maximum length in bytes
+ */
+ public int getMaxMessageLength()
+ {
+ return maxMsgLenBytes;
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(df);
+ dos.writeInt(df1);
+ dos.writeInt(df2);
+ dos.writeInt(df3);
+ dos.writeInt(db);
+ dos.writeInt(dm0);
+ dos.writeInt(c);
+ dos.writeInt(minCallsR);
+ dos.writeInt(minCallsMask);
+ dos.writeBoolean(hashSeed);
+ dos.write(oid);
+ dos.writeBoolean(sparse);
+ dos.writeBoolean(fastFp);
+ dos.write(polyType);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ }
+
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + N;
+ result = prime * result + bufferLenBits;
+ result = prime * result + bufferLenTrits;
+ result = prime * result + c;
+ result = prime * result + db;
+ result = prime * result + df;
+ result = prime * result + df1;
+ result = prime * result + df2;
+ result = prime * result + df3;
+ result = prime * result + dg;
+ result = prime * result + dm0;
+ result = prime * result + dr;
+ result = prime * result + dr1;
+ result = prime * result + dr2;
+ result = prime * result + dr3;
+ result = prime * result + (fastFp ? 1231 : 1237);
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ result = prime * result + (hashSeed ? 1231 : 1237);
+ result = prime * result + llen;
+ result = prime * result + maxMsgLenBytes;
+ result = prime * result + minCallsMask;
+ result = prime * result + minCallsR;
+ result = prime * result + Arrays.hashCode(oid);
+ result = prime * result + pkLen;
+ result = prime * result + polyType;
+ result = prime * result + q;
+ result = prime * result + (sparse ? 1231 : 1237);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUEncryptionParameters other = (NTRUEncryptionParameters)obj;
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (bufferLenBits != other.bufferLenBits)
+ {
+ return false;
+ }
+ if (bufferLenTrits != other.bufferLenTrits)
+ {
+ return false;
+ }
+ if (c != other.c)
+ {
+ return false;
+ }
+ if (db != other.db)
+ {
+ return false;
+ }
+ if (df != other.df)
+ {
+ return false;
+ }
+ if (df1 != other.df1)
+ {
+ return false;
+ }
+ if (df2 != other.df2)
+ {
+ return false;
+ }
+ if (df3 != other.df3)
+ {
+ return false;
+ }
+ if (dg != other.dg)
+ {
+ return false;
+ }
+ if (dm0 != other.dm0)
+ {
+ return false;
+ }
+ if (dr != other.dr)
+ {
+ return false;
+ }
+ if (dr1 != other.dr1)
+ {
+ return false;
+ }
+ if (dr2 != other.dr2)
+ {
+ return false;
+ }
+ if (dr3 != other.dr3)
+ {
+ return false;
+ }
+ if (fastFp != other.fastFp)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (hashSeed != other.hashSeed)
+ {
+ return false;
+ }
+ if (llen != other.llen)
+ {
+ return false;
+ }
+ if (maxMsgLenBytes != other.maxMsgLenBytes)
+ {
+ return false;
+ }
+ if (minCallsMask != other.minCallsMask)
+ {
+ return false;
+ }
+ if (minCallsR != other.minCallsR)
+ {
+ return false;
+ }
+ if (!Arrays.equals(oid, other.oid))
+ {
+ return false;
+ }
+ if (pkLen != other.pkLen)
+ {
+ return false;
+ }
+ if (polyType != other.polyType)
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (sparse != other.sparse)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString()
+ {
+ StringBuilder output = new StringBuilder("EncryptionParameters(N=" + N + " q=" + q);
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ output.append(" polyType=SIMPLE df=" + df);
+ }
+ else
+ {
+ output.append(" polyType=PRODUCT df1=" + df1 + " df2=" + df2 + " df3=" + df3);
+ }
+ output.append(" dm0=" + dm0 + " db=" + db + " c=" + c + " minCallsR=" + minCallsR + " minCallsMask=" + minCallsMask +
+ " hashSeed=" + hashSeed + " hashAlg=" + hashAlg + " oid=" + Arrays.toString(oid) + " sparse=" + sparse + ")");
+ return output.toString();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPrivateKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPrivateKeyParameters.java
new file mode 100644
index 00000000..5b0adbea
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPrivateKeyParameters.java
@@ -0,0 +1,199 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+
+/**
+ * A NtruEncrypt private key is essentially a polynomial named <code>f</code>
+ * which takes different forms depending on whether product-form polynomials are used,
+ * and on <code>fastP</code><br>
+ * The inverse of <code>f</code> modulo <code>p</code> is precomputed on initialization.
+ */
+public class NTRUEncryptionPrivateKeyParameters
+ extends NTRUEncryptionKeyParameters
+{
+ public Polynomial t;
+ public IntegerPolynomial fp;
+ public IntegerPolynomial h;
+
+ /**
+ * Constructs a new private key from a polynomial
+ *
+ * @param h the public polynomial for the key.
+ * @param t the polynomial which determines the key: if <code>fastFp=true</code>, <code>f=1+3t</code>; otherwise, <code>f=t</code>
+ * @param fp the inverse of <code>f</code>
+ * @param params the NtruEncrypt parameters to use
+ */
+ public NTRUEncryptionPrivateKeyParameters(IntegerPolynomial h, Polynomial t, IntegerPolynomial fp, NTRUEncryptionParameters params)
+ {
+ super(true, params);
+
+ this.h = h;
+ this.t = t;
+ this.fp = fp;
+ }
+
+ /**
+ * Converts a byte array to a polynomial <code>f</code> and constructs a new private key
+ *
+ * @param b an encoded polynomial
+ * @param params the NtruEncrypt parameters to use
+ * @see #getEncoded()
+ */
+ public NTRUEncryptionPrivateKeyParameters(byte[] b, NTRUEncryptionParameters params)
+ throws IOException
+ {
+ this(new ByteArrayInputStream(b), params);
+ }
+
+ /**
+ * Reads a polynomial <code>f</code> from an input stream and constructs a new private key
+ *
+ * @param is an input stream
+ * @param params the NtruEncrypt parameters to use
+ * @see #writeTo(OutputStream)
+ */
+ public NTRUEncryptionPrivateKeyParameters(InputStream is, NTRUEncryptionParameters params)
+ throws IOException
+ {
+ super(true, params);
+
+ if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ int N = params.N;
+ int df1 = params.df1;
+ int df2 = params.df2;
+ int df3Ones = params.df3;
+ int df3NegOnes = params.fastFp ? params.df3 : params.df3 - 1;
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ t = ProductFormPolynomial.fromBinary(is, N, df1, df2, df3Ones, df3NegOnes);
+ }
+ else
+ {
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ IntegerPolynomial fInt = IntegerPolynomial.fromBinary3Tight(is, params.N);
+ t = params.sparse ? new SparseTernaryPolynomial(fInt) : new DenseTernaryPolynomial(fInt);
+ }
+
+ init();
+ }
+
+ /**
+ * Initializes <code>fp</code> from t.
+ */
+ private void init()
+ {
+ if (params.fastFp)
+ {
+ fp = new IntegerPolynomial(params.N);
+ fp.coeffs[0] = 1;
+ }
+ else
+ {
+ fp = t.toIntegerPolynomial().invertF3();
+ }
+ }
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ * @see #NTRUEncryptionPrivateKeyParameters(byte[], NTRUEncryptionParameters)
+ */
+ public byte[] getEncoded()
+ {
+ byte[] hBytes = h.toBinary(params.q);
+ byte[] tBytes;
+
+ if (t instanceof ProductFormPolynomial)
+ {
+ tBytes = ((ProductFormPolynomial)t).toBinary();
+ }
+ else
+ {
+ tBytes = t.toIntegerPolynomial().toBinary3Tight();
+ }
+
+ byte[] res = new byte[hBytes.length + tBytes.length];
+
+ System.arraycopy(hBytes, 0, res, 0, hBytes.length);
+ System.arraycopy(tBytes, 0, res, hBytes.length, tBytes.length);
+
+ return res;
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ * @see #NTRUEncryptionPrivateKeyParameters(InputStream, NTRUEncryptionParameters)
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ result = prime * result + ((t == null) ? 0 : t.hashCode());
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUEncryptionPrivateKeyParameters))
+ {
+ return false;
+ }
+ NTRUEncryptionPrivateKeyParameters other = (NTRUEncryptionPrivateKeyParameters)obj;
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ if (t == null)
+ {
+ if (other.t != null)
+ {
+ return false;
+ }
+ }
+ else if (!t.equals(other.t))
+ {
+ return false;
+ }
+ if (!h.equals(other.h))
+ {
+ return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPublicKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPublicKeyParameters.java
new file mode 100644
index 00000000..0f3abd9c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEncryptionPublicKeyParameters.java
@@ -0,0 +1,131 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+
+/**
+ * A NtruEncrypt public key is essentially a polynomial named <code>h</code>.
+ */
+public class NTRUEncryptionPublicKeyParameters
+ extends NTRUEncryptionKeyParameters
+{
+ public IntegerPolynomial h;
+
+ /**
+ * Constructs a new public key from a polynomial
+ *
+ * @param h the polynomial <code>h</code> which determines the key
+ * @param params the NtruEncrypt parameters to use
+ */
+ public NTRUEncryptionPublicKeyParameters(IntegerPolynomial h, NTRUEncryptionParameters params)
+ {
+ super(false, params);
+
+ this.h = h;
+ }
+
+ /**
+ * Converts a byte array to a polynomial <code>h</code> and constructs a new public key
+ *
+ * @param b an encoded polynomial
+ * @param params the NtruEncrypt parameters to use
+ * @see #getEncoded()
+ */
+ public NTRUEncryptionPublicKeyParameters(byte[] b, NTRUEncryptionParameters params)
+ {
+ super(false, params);
+
+ h = IntegerPolynomial.fromBinary(b, params.N, params.q);
+ }
+
+ /**
+ * Reads a polynomial <code>h</code> from an input stream and constructs a new public key
+ *
+ * @param is an input stream
+ * @param params the NtruEncrypt parameters to use
+ * @see #writeTo(OutputStream)
+ */
+ public NTRUEncryptionPublicKeyParameters(InputStream is, NTRUEncryptionParameters params)
+ throws IOException
+ {
+ super(false, params);
+
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ }
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ * @see #NTRUEncryptionPublicKeyParameters(byte[], NTRUEncryptionParameters)
+ */
+ public byte[] getEncoded()
+ {
+ return h.toBinary(params.q);
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ * @see #NTRUEncryptionPublicKeyParameters(InputStream, NTRUEncryptionParameters)
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUEncryptionPublicKeyParameters))
+ {
+ return false;
+ }
+ NTRUEncryptionPublicKeyParameters other = (NTRUEncryptionPublicKeyParameters)obj;
+ if (h == null)
+ {
+ if (other.h != null)
+ {
+ return false;
+ }
+ }
+ else if (!h.equals(other.h))
+ {
+ return false;
+ }
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEngine.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEngine.java
new file mode 100644
index 00000000..571057e2
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUEngine.java
@@ -0,0 +1,495 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.TernaryPolynomial;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Encrypts, decrypts data and generates key pairs.<br>
+ * The parameter p is hardcoded to 3.
+ */
+public class NTRUEngine
+ implements AsymmetricBlockCipher
+{
+ private boolean forEncryption;
+ private NTRUEncryptionParameters params;
+ private NTRUEncryptionPublicKeyParameters pubKey;
+ private NTRUEncryptionPrivateKeyParameters privKey;
+ private SecureRandom random;
+
+ /**
+ * Constructs a new instance with a set of encryption parameters.
+ *
+ */
+ public NTRUEngine()
+ {
+ }
+
+ public void init(boolean forEncryption, CipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+ if (forEncryption)
+ {
+ if (parameters instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)parameters;
+
+ this.random = p.getRandom();
+ this.pubKey = (NTRUEncryptionPublicKeyParameters)p.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ this.pubKey = (NTRUEncryptionPublicKeyParameters)parameters;
+ }
+
+ this.params = pubKey.getParameters();
+ }
+ else
+ {
+ this.privKey = (NTRUEncryptionPrivateKeyParameters)parameters;
+ this.params = privKey.getParameters();
+ }
+ }
+
+ public int getInputBlockSize()
+ {
+ return params.maxMsgLenBytes;
+ }
+
+ public int getOutputBlockSize()
+ {
+ return ((params.N * log2(params.q)) + 7) / 8;
+ }
+
+ public byte[] processBlock(byte[] in, int inOff, int len)
+ throws InvalidCipherTextException
+ {
+ byte[] tmp = new byte[len];
+
+ System.arraycopy(in, inOff, tmp, 0, len);
+
+ if (forEncryption)
+ {
+ return encrypt(tmp, pubKey);
+ }
+ else
+ {
+ return decrypt(tmp, privKey);
+ }
+ }
+
+ /**
+ * Encrypts a message.<br/>
+ * See P1363.1 section 9.2.2.
+ *
+ * @param m The message to encrypt
+ * @param pubKey the public key to encrypt the message with
+ * @return the encrypted message
+ */
+ private byte[] encrypt(byte[] m, NTRUEncryptionPublicKeyParameters pubKey)
+ {
+ IntegerPolynomial pub = pubKey.h;
+ int N = params.N;
+ int q = params.q;
+
+ int maxLenBytes = params.maxMsgLenBytes;
+ int db = params.db;
+ int bufferLenBits = params.bufferLenBits;
+ int dm0 = params.dm0;
+ int pkLen = params.pkLen;
+ int minCallsMask = params.minCallsMask;
+ boolean hashSeed = params.hashSeed;
+ byte[] oid = params.oid;
+
+ int l = m.length;
+ if (maxLenBytes > 255)
+ {
+ throw new IllegalArgumentException("llen values bigger than 1 are not supported");
+ }
+ if (l > maxLenBytes)
+ {
+ throw new DataLengthException("Message too long: " + l + ">" + maxLenBytes);
+ }
+
+ while (true)
+ {
+ // M = b|octL|m|p0
+ byte[] b = new byte[db / 8];
+ random.nextBytes(b);
+ byte[] p0 = new byte[maxLenBytes + 1 - l];
+ byte[] M = new byte[bufferLenBits / 8];
+
+ System.arraycopy(b, 0, M, 0, b.length);
+ M[b.length] = (byte)l;
+ System.arraycopy(m, 0, M, b.length + 1, m.length);
+ System.arraycopy(p0, 0, M, b.length + 1 + m.length, p0.length);
+
+ IntegerPolynomial mTrin = IntegerPolynomial.fromBinary3Sves(M, N);
+
+ // sData = OID|m|b|hTrunc
+ byte[] bh = pub.toBinary(q);
+ byte[] hTrunc = copyOf(bh, pkLen / 8);
+ byte[] sData = buildSData(oid, m, l, b, hTrunc);
+
+ Polynomial r = generateBlindingPoly(sData, M);
+ IntegerPolynomial R = r.mult(pub, q);
+ IntegerPolynomial R4 = (IntegerPolynomial)R.clone();
+ R4.modPositive(4);
+ byte[] oR4 = R4.toBinary(4);
+ IntegerPolynomial mask = MGF(oR4, N, minCallsMask, hashSeed);
+ mTrin.add(mask);
+ mTrin.mod3();
+
+ if (mTrin.count(-1) < dm0)
+ {
+ continue;
+ }
+ if (mTrin.count(0) < dm0)
+ {
+ continue;
+ }
+ if (mTrin.count(1) < dm0)
+ {
+ continue;
+ }
+
+ R.add(mTrin, q);
+ R.ensurePositive(q);
+ return R.toBinary(q);
+ }
+ }
+
+ private byte[] buildSData(byte[] oid, byte[] m, int l, byte[] b, byte[] hTrunc)
+ {
+ byte[] sData = new byte[oid.length + l + b.length + hTrunc.length];
+
+ System.arraycopy(oid, 0, sData, 0, oid.length);
+ System.arraycopy(m, 0, sData, oid.length, m.length);
+ System.arraycopy(b, 0, sData, oid.length + m.length, b.length);
+ System.arraycopy(hTrunc, 0, sData, oid.length + m.length + b.length, hTrunc.length);
+ return sData;
+ }
+
+ protected IntegerPolynomial encrypt(IntegerPolynomial m, TernaryPolynomial r, IntegerPolynomial pubKey)
+ {
+ IntegerPolynomial e = r.mult(pubKey, params.q);
+ e.add(m, params.q);
+ e.ensurePositive(params.q);
+ return e;
+ }
+
+ /**
+ * Deterministically generates a blinding polynomial from a seed and a message representative.
+ *
+ * @param seed
+ * @param M message representative
+ * @return a blinding polynomial
+ */
+ private Polynomial generateBlindingPoly(byte[] seed, byte[] M)
+ {
+ IndexGenerator ig = new IndexGenerator(seed, params);
+
+ if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ SparseTernaryPolynomial r1 = new SparseTernaryPolynomial(generateBlindingCoeffs(ig, params.dr1));
+ SparseTernaryPolynomial r2 = new SparseTernaryPolynomial(generateBlindingCoeffs(ig, params.dr2));
+ SparseTernaryPolynomial r3 = new SparseTernaryPolynomial(generateBlindingCoeffs(ig, params.dr3));
+ return new ProductFormPolynomial(r1, r2, r3);
+ }
+ else
+ {
+ int dr = params.dr;
+ boolean sparse = params.sparse;
+ int[] r = generateBlindingCoeffs(ig, dr);
+ if (sparse)
+ {
+ return new SparseTernaryPolynomial(r);
+ }
+ else
+ {
+ return new DenseTernaryPolynomial(r);
+ }
+ }
+ }
+
+ /**
+ * Generates an <code>int</code> array containing <code>dr</code> elements equal to <code>1</code>
+ * and <code>dr</code> elements equal to <code>-1</code> using an index generator.
+ *
+ * @param ig an index generator
+ * @param dr number of ones / negative ones
+ * @return an array containing numbers between <code>-1</code> and <code>1</code>
+ */
+ private int[] generateBlindingCoeffs(IndexGenerator ig, int dr)
+ {
+ int N = params.N;
+
+ int[] r = new int[N];
+ for (int coeff = -1; coeff <= 1; coeff += 2)
+ {
+ int t = 0;
+ while (t < dr)
+ {
+ int i = ig.nextIndex();
+ if (r[i] == 0)
+ {
+ r[i] = coeff;
+ t++;
+ }
+ }
+ }
+
+ return r;
+ }
+
+ /**
+ * An implementation of MGF-TP-1 from P1363.1 section 8.4.1.1.
+ *
+ * @param seed
+ * @param N
+ * @param minCallsR
+ * @param hashSeed whether to hash the seed
+ * @return
+ */
+ private IntegerPolynomial MGF(byte[] seed, int N, int minCallsR, boolean hashSeed)
+ {
+ Digest hashAlg = params.hashAlg;
+ int hashLen = hashAlg.getDigestSize();
+ byte[] buf = new byte[minCallsR * hashLen];
+ byte[] Z = hashSeed ? calcHash(hashAlg, seed) : seed;
+ int counter = 0;
+ while (counter < minCallsR)
+ {
+ hashAlg.update(Z, 0, Z.length);
+ putInt(hashAlg, counter);
+
+ byte[] hash = calcHash(hashAlg);
+ System.arraycopy(hash, 0, buf, counter * hashLen, hashLen);
+ counter++;
+ }
+
+ IntegerPolynomial i = new IntegerPolynomial(N);
+ while (true)
+ {
+ int cur = 0;
+ for (int index = 0; index != buf.length; index++)
+ {
+ int O = (int)buf[index] & 0xFF;
+ if (O >= 243) // 243 = 3^5
+ {
+ continue;
+ }
+
+ for (int terIdx = 0; terIdx < 4; terIdx++)
+ {
+ int rem3 = O % 3;
+ i.coeffs[cur] = rem3 - 1;
+ cur++;
+ if (cur == N)
+ {
+ return i;
+ }
+ O = (O - rem3) / 3;
+ }
+
+ i.coeffs[cur] = O - 1;
+ cur++;
+ if (cur == N)
+ {
+ return i;
+ }
+ }
+
+ if (cur >= N)
+ {
+ return i;
+ }
+
+ hashAlg.update(Z, 0, Z.length);
+ putInt(hashAlg, counter);
+
+ byte[] hash = calcHash(hashAlg);
+
+ buf = hash;
+
+ counter++;
+ }
+ }
+
+ private void putInt(Digest hashAlg, int counter)
+ {
+ hashAlg.update((byte)(counter >> 24));
+ hashAlg.update((byte)(counter >> 16));
+ hashAlg.update((byte)(counter >> 8));
+ hashAlg.update((byte)counter);
+ }
+
+ private byte[] calcHash(Digest hashAlg)
+ {
+ byte[] tmp = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.doFinal(tmp, 0);
+
+ return tmp;
+ }
+
+ private byte[] calcHash(Digest hashAlg, byte[] input)
+ {
+ byte[] tmp = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.update(input, 0, input.length);
+ hashAlg.doFinal(tmp, 0);
+
+ return tmp;
+ }
+ /**
+ * Decrypts a message.<br/>
+ * See P1363.1 section 9.2.3.
+ *
+ * @param data The message to decrypt
+ * @param privKey the corresponding private key
+ * @return the decrypted message
+ * @throws InvalidCipherTextException if the encrypted data is invalid, or <code>maxLenBytes</code> is greater than 255
+ */
+ private byte[] decrypt(byte[] data, NTRUEncryptionPrivateKeyParameters privKey)
+ throws InvalidCipherTextException
+ {
+ Polynomial priv_t = privKey.t;
+ IntegerPolynomial priv_fp = privKey.fp;
+ IntegerPolynomial pub = privKey.h;
+ int N = params.N;
+ int q = params.q;
+ int db = params.db;
+ int maxMsgLenBytes = params.maxMsgLenBytes;
+ int dm0 = params.dm0;
+ int pkLen = params.pkLen;
+ int minCallsMask = params.minCallsMask;
+ boolean hashSeed = params.hashSeed;
+ byte[] oid = params.oid;
+
+ if (maxMsgLenBytes > 255)
+ {
+ throw new DataLengthException("maxMsgLenBytes values bigger than 255 are not supported");
+ }
+
+ int bLen = db / 8;
+
+ IntegerPolynomial e = IntegerPolynomial.fromBinary(data, N, q);
+ IntegerPolynomial ci = decrypt(e, priv_t, priv_fp);
+
+ if (ci.count(-1) < dm0)
+ {
+ throw new InvalidCipherTextException("Less than dm0 coefficients equal -1");
+ }
+ if (ci.count(0) < dm0)
+ {
+ throw new InvalidCipherTextException("Less than dm0 coefficients equal 0");
+ }
+ if (ci.count(1) < dm0)
+ {
+ throw new InvalidCipherTextException("Less than dm0 coefficients equal 1");
+ }
+
+ IntegerPolynomial cR = (IntegerPolynomial)e.clone();
+ cR.sub(ci);
+ cR.modPositive(q);
+ IntegerPolynomial cR4 = (IntegerPolynomial)cR.clone();
+ cR4.modPositive(4);
+ byte[] coR4 = cR4.toBinary(4);
+ IntegerPolynomial mask = MGF(coR4, N, minCallsMask, hashSeed);
+ IntegerPolynomial cMTrin = ci;
+ cMTrin.sub(mask);
+ cMTrin.mod3();
+ byte[] cM = cMTrin.toBinary3Sves();
+
+ byte[] cb = new byte[bLen];
+ System.arraycopy(cM, 0, cb, 0, bLen);
+ int cl = cM[bLen] & 0xFF; // llen=1, so read one byte
+ if (cl > maxMsgLenBytes)
+ {
+ throw new InvalidCipherTextException("Message too long: " + cl + ">" + maxMsgLenBytes);
+ }
+ byte[] cm = new byte[cl];
+ System.arraycopy(cM, bLen + 1, cm, 0, cl);
+ byte[] p0 = new byte[cM.length - (bLen + 1 + cl)];
+ System.arraycopy(cM, bLen + 1 + cl, p0, 0, p0.length);
+ if (!Arrays.areEqual(p0, new byte[p0.length]))
+ {
+ throw new InvalidCipherTextException("The message is not followed by zeroes");
+ }
+
+ // sData = OID|m|b|hTrunc
+ byte[] bh = pub.toBinary(q);
+ byte[] hTrunc = copyOf(bh, pkLen / 8);
+ byte[] sData = buildSData(oid, cm, cl, cb, hTrunc);
+
+ Polynomial cr = generateBlindingPoly(sData, cm);
+ IntegerPolynomial cRPrime = cr.mult(pub);
+ cRPrime.modPositive(q);
+ if (!cRPrime.equals(cR))
+ {
+ throw new InvalidCipherTextException("Invalid message encoding");
+ }
+
+ return cm;
+ }
+
+ /**
+ * @param e
+ * @param priv_t a polynomial such that if <code>fastFp=true</code>, <code>f=1+3*priv_t</code>; otherwise, <code>f=priv_t</code>
+ * @param priv_fp
+ * @return
+ */
+ protected IntegerPolynomial decrypt(IntegerPolynomial e, Polynomial priv_t, IntegerPolynomial priv_fp)
+ {
+ IntegerPolynomial a;
+ if (params.fastFp)
+ {
+ a = priv_t.mult(e, params.q);
+ a.mult(3);
+ a.add(e);
+ }
+ else
+ {
+ a = priv_t.mult(e, params.q);
+ }
+ a.center0(params.q);
+ a.mod3();
+
+ IntegerPolynomial c = params.fastFp ? a : new DenseTernaryPolynomial(a).mult(priv_fp, 3);
+ c.center0(3);
+ return c;
+ }
+
+ private byte[] copyOf(byte[] src, int len)
+ {
+ byte[] tmp = new byte[len];
+
+ System.arraycopy(src, 0, tmp, 0, len < src.length ? len : src.length);
+
+ return tmp;
+ }
+
+ private int log2(int value)
+ {
+ if (value == 2048)
+ {
+ return 11;
+ }
+
+ throw new IllegalStateException("log2 not fully implemented");
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUParameters.java
new file mode 100644
index 00000000..3ce2154d
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUParameters.java
@@ -0,0 +1,7 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+public class NTRUParameters
+{
+ public static final int TERNARY_POLYNOMIAL_TYPE_SIMPLE = 0;
+ public static final int TERNARY_POLYNOMIAL_TYPE_PRODUCT = 1;
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigner.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigner.java
new file mode 100644
index 00000000..0886cff1
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigner.java
@@ -0,0 +1,263 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.nio.ByteBuffer;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+
+/**
+* Signs, verifies data and generates key pairs.
+* @deprecated the NTRUSigner algorithm was broken in 2012 by Ducas and Nguyen. See
+* <a href="http://www.di.ens.fr/~ducas/NTRUSign_Cryptanalysis/DucasNguyen_Learning.pdf">
+* http://www.di.ens.fr/~ducas/NTRUSign_Cryptanalysis/DucasNguyen_Learning.pdf</a>
+* for details.
+*/
+public class NTRUSigner
+{
+ private NTRUSigningParameters params;
+ private Digest hashAlg;
+ private NTRUSigningPrivateKeyParameters signingKeyPair;
+ private NTRUSigningPublicKeyParameters verificationKey;
+
+ /**
+ * Constructs a new instance with a set of signature parameters.
+ *
+ * @param params signature parameters
+ */
+ public NTRUSigner(NTRUSigningParameters params)
+ {
+ this.params = params;
+ }
+
+ /**
+ * Resets the engine for signing a message.
+ *
+ * @param forSigning
+ * @param params
+ */
+ public void init(boolean forSigning, CipherParameters params)
+ {
+ if (forSigning)
+ {
+ this.signingKeyPair = (NTRUSigningPrivateKeyParameters)params;
+ }
+ else
+ {
+ this.verificationKey = (NTRUSigningPublicKeyParameters)params;
+ }
+ hashAlg = this.params.hashAlg;
+ hashAlg.reset();
+ }
+
+ /**
+ * Adds data to sign or verify.
+ *
+ * @param b data
+ */
+ public void update(byte b)
+ {
+ if (hashAlg == null)
+ {
+ throw new IllegalStateException("Call initSign or initVerify first!");
+ }
+
+ hashAlg.update(b);
+ }
+
+ /**
+ * Adds data to sign or verify.
+ *
+ * @param m data
+ * @param off offset
+ * @param length number of bytes
+ */
+ public void update(byte[] m, int off, int length)
+ {
+ if (hashAlg == null)
+ {
+ throw new IllegalStateException("Call initSign or initVerify first!");
+ }
+
+ hashAlg.update(m, off, length);
+ }
+
+ /**
+ * Adds data to sign and computes a signature over this data and any data previously added via {@link #update(byte[], int, int)}.
+ *
+ * @return a signature
+ * @throws IllegalStateException if <code>initSign</code> was not called
+ */
+ public byte[] generateSignature()
+ {
+ if (hashAlg == null || signingKeyPair == null)
+ {
+ throw new IllegalStateException("Call initSign first!");
+ }
+
+ byte[] msgHash = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.doFinal(msgHash, 0);
+ return signHash(msgHash, signingKeyPair);
+ }
+
+ private byte[] signHash(byte[] msgHash, NTRUSigningPrivateKeyParameters kp)
+ {
+ int r = 0;
+ IntegerPolynomial s;
+ IntegerPolynomial i;
+
+ NTRUSigningPublicKeyParameters kPub = kp.getPublicKey();
+ do
+ {
+ r++;
+ if (r > params.signFailTolerance)
+ {
+ throw new IllegalStateException("Signing failed: too many retries (max=" + params.signFailTolerance + ")");
+ }
+ i = createMsgRep(msgHash, r);
+ s = sign(i, kp);
+ }
+ while (!verify(i, s, kPub.h));
+
+ byte[] rawSig = s.toBinary(params.q);
+ ByteBuffer sbuf = ByteBuffer.allocate(rawSig.length + 4);
+ sbuf.put(rawSig);
+ sbuf.putInt(r);
+ return sbuf.array();
+ }
+
+ private IntegerPolynomial sign(IntegerPolynomial i, NTRUSigningPrivateKeyParameters kp)
+ {
+ int N = params.N;
+ int q = params.q;
+ int perturbationBases = params.B;
+
+ NTRUSigningPrivateKeyParameters kPriv = kp;
+ NTRUSigningPublicKeyParameters kPub = kp.getPublicKey();
+
+ IntegerPolynomial s = new IntegerPolynomial(N);
+ int iLoop = perturbationBases;
+ while (iLoop >= 1)
+ {
+ Polynomial f = kPriv.getBasis(iLoop).f;
+ Polynomial fPrime = kPriv.getBasis(iLoop).fPrime;
+
+ IntegerPolynomial y = f.mult(i);
+ y.div(q);
+ y = fPrime.mult(y);
+
+ IntegerPolynomial x = fPrime.mult(i);
+ x.div(q);
+ x = f.mult(x);
+
+ IntegerPolynomial si = y;
+ si.sub(x);
+ s.add(si);
+
+ IntegerPolynomial hi = (IntegerPolynomial)kPriv.getBasis(iLoop).h.clone();
+ if (iLoop > 1)
+ {
+ hi.sub(kPriv.getBasis(iLoop - 1).h);
+ }
+ else
+ {
+ hi.sub(kPub.h);
+ }
+ i = si.mult(hi, q);
+
+ iLoop--;
+ }
+
+ Polynomial f = kPriv.getBasis(0).f;
+ Polynomial fPrime = kPriv.getBasis(0).fPrime;
+
+ IntegerPolynomial y = f.mult(i);
+ y.div(q);
+ y = fPrime.mult(y);
+
+ IntegerPolynomial x = fPrime.mult(i);
+ x.div(q);
+ x = f.mult(x);
+
+ y.sub(x);
+ s.add(y);
+ s.modPositive(q);
+ return s;
+ }
+
+ /**
+ * Verifies a signature for any data previously added via {@link #update(byte[], int, int)}.
+ *
+ * @param sig a signature
+ * @return whether the signature is valid
+ * @throws IllegalStateException if <code>initVerify</code> was not called
+ */
+ public boolean verifySignature(byte[] sig)
+ {
+ if (hashAlg == null || verificationKey == null)
+ {
+ throw new IllegalStateException("Call initVerify first!");
+ }
+
+ byte[] msgHash = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.doFinal(msgHash, 0);
+
+ return verifyHash(msgHash, sig, verificationKey);
+ }
+
+ private boolean verifyHash(byte[] msgHash, byte[] sig, NTRUSigningPublicKeyParameters pub)
+ {
+ ByteBuffer sbuf = ByteBuffer.wrap(sig);
+ byte[] rawSig = new byte[sig.length - 4];
+ sbuf.get(rawSig);
+ IntegerPolynomial s = IntegerPolynomial.fromBinary(rawSig, params.N, params.q);
+ int r = sbuf.getInt();
+ return verify(createMsgRep(msgHash, r), s, pub.h);
+ }
+
+ private boolean verify(IntegerPolynomial i, IntegerPolynomial s, IntegerPolynomial h)
+ {
+ int q = params.q;
+ double normBoundSq = params.normBoundSq;
+ double betaSq = params.betaSq;
+
+ IntegerPolynomial t = h.mult(s, q);
+ t.sub(i);
+ long centeredNormSq = (long)(s.centeredNormSq(q) + betaSq * t.centeredNormSq(q));
+ return centeredNormSq <= normBoundSq;
+ }
+
+ protected IntegerPolynomial createMsgRep(byte[] msgHash, int r)
+ {
+ int N = params.N;
+ int q = params.q;
+
+ int c = 31 - Integer.numberOfLeadingZeros(q);
+ int B = (c + 7) / 8;
+ IntegerPolynomial i = new IntegerPolynomial(N);
+
+ ByteBuffer cbuf = ByteBuffer.allocate(msgHash.length + 4);
+ cbuf.put(msgHash);
+ cbuf.putInt(r);
+ NTRUSignerPrng prng = new NTRUSignerPrng(cbuf.array(), params.hashAlg);
+
+ for (int t = 0; t < N; t++)
+ {
+ byte[] o = prng.nextBytes(B);
+ int hi = o[o.length - 1];
+ hi >>= 8 * B - c;
+ hi <<= 8 * B - c;
+ o[o.length - 1] = (byte)hi;
+
+ ByteBuffer obuf = ByteBuffer.allocate(4);
+ obuf.put(o);
+ obuf.rewind();
+ // reverse byte order so it matches the endianness of java ints
+ i.coeffs[t] = Integer.reverseBytes(obuf.getInt());
+ }
+ return i;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSignerPrng.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSignerPrng.java
new file mode 100644
index 00000000..c9278dd5
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSignerPrng.java
@@ -0,0 +1,64 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.nio.ByteBuffer;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * An implementation of the deterministic pseudo-random generator in EESS section 3.7.3.1
+ */
+public class NTRUSignerPrng
+{
+ private int counter;
+ private byte[] seed;
+ private Digest hashAlg;
+
+ /**
+ * Constructs a new PRNG and seeds it with a byte array.
+ *
+ * @param seed a seed
+ * @param hashAlg the hash algorithm to use
+ */
+ NTRUSignerPrng(byte[] seed, Digest hashAlg)
+ {
+ counter = 0;
+ this.seed = seed;
+ this.hashAlg = hashAlg;
+ }
+
+ /**
+ * Returns <code>n</code> random bytes
+ *
+ * @param n number of bytes to return
+ * @return the next <code>n</code> random bytes
+ */
+ byte[] nextBytes(int n)
+ {
+ ByteBuffer buf = ByteBuffer.allocate(n);
+
+ while (buf.hasRemaining())
+ {
+ ByteBuffer cbuf = ByteBuffer.allocate(seed.length + 4);
+ cbuf.put(seed);
+ cbuf.putInt(counter);
+ byte[] array = cbuf.array();
+ byte[] hash = new byte[hashAlg.getDigestSize()];
+
+ hashAlg.update(array, 0, array.length);
+
+ hashAlg.doFinal(hash, 0);
+
+ if (buf.remaining() < hash.length)
+ {
+ buf.put(hash, 0, buf.remaining());
+ }
+ else
+ {
+ buf.put(hash);
+ }
+ counter++;
+ }
+
+ return buf.array();
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java
new file mode 100644
index 00000000..f9c17f1c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java
@@ -0,0 +1,407 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.text.DecimalFormat;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruSign. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUSigningKeyGenerationParameters
+ extends KeyGenerationParameters
+ implements Cloneable
+{
+ public static final int BASIS_TYPE_STANDARD = 0;
+ public static final int BASIS_TYPE_TRANSPOSE = 1;
+
+ public static final int KEY_GEN_ALG_RESULTANT = 0;
+ public static final int KEY_GEN_ALG_FLOAT = 1;
+
+ /**
+ * Gives 128 bits of security
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_439 = new NTRUSigningKeyGenerationParameters(439, 2048, 146, 1, BASIS_TYPE_TRANSPOSE, 0.165, 490, 280, false, true, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+
+ /**
+ * Like <code>APR2011_439</code>, this parameter set gives 128 bits of security but uses product-form polynomials
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_439_PROD = new NTRUSigningKeyGenerationParameters(439, 2048, 9, 8, 5, 1, BASIS_TYPE_TRANSPOSE, 0.165, 490, 280, false, true, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+
+ /**
+ * Gives 256 bits of security
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_743 = new NTRUSigningKeyGenerationParameters(743, 2048, 248, 1, BASIS_TYPE_TRANSPOSE, 0.127, 560, 360, true, false, KEY_GEN_ALG_RESULTANT, new SHA512Digest());
+
+ /**
+ * Like <code>APR2011_439</code>, this parameter set gives 256 bits of security but uses product-form polynomials
+ */
+ public static final NTRUSigningKeyGenerationParameters APR2011_743_PROD = new NTRUSigningKeyGenerationParameters(743, 2048, 11, 11, 15, 1, BASIS_TYPE_TRANSPOSE, 0.127, 560, 360, true, false, KEY_GEN_ALG_RESULTANT, new SHA512Digest());
+
+ /**
+ * Generates key pairs quickly. Use for testing only.
+ */
+ public static final NTRUSigningKeyGenerationParameters TEST157 = new NTRUSigningKeyGenerationParameters(157, 256, 29, 1, BASIS_TYPE_TRANSPOSE, 0.38, 200, 80, false, false, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+ /**
+ * Generates key pairs quickly. Use for testing only.
+ */
+ public static final NTRUSigningKeyGenerationParameters TEST157_PROD = new NTRUSigningKeyGenerationParameters(157, 256, 5, 5, 8, 1, BASIS_TYPE_TRANSPOSE, 0.38, 200, 80, false, false, KEY_GEN_ALG_RESULTANT, new SHA256Digest());
+
+
+ public int N;
+ public int q;
+ public int d, d1, d2, d3, B;
+ double beta;
+ public double betaSq;
+ double normBound;
+ public double normBoundSq;
+ public int signFailTolerance = 100;
+ double keyNormBound;
+ public double keyNormBoundSq;
+ public boolean primeCheck; // true if N and 2N+1 are prime
+ public int basisType;
+ int bitsF = 6; // max #bits needed to encode one coefficient of the polynomial F
+ public boolean sparse; // whether to treat ternary polynomials as sparsely populated
+ public int keyGenAlg;
+ public Digest hashAlg;
+ public int polyType;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. <code>polyType=SIMPLE</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param B number of perturbations
+ * @param basisType whether to use the standard or transpose lattice
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param keyNormBound maximum norm for the ploynomials <code>F</code> and <code>G</code>
+ * @param primeCheck whether <code>2N+1</code> is prime
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param keyGenAlg <code>RESULTANT</code> produces better bases, <code>FLOAT</code> is slightly faster. <code>RESULTANT</code> follows the EESS standard while <code>FLOAT</code> is described in Hoffstein et al: An Introduction to Mathematical Cryptography.
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUSigningKeyGenerationParameters(int N, int q, int d, int B, int basisType, double beta, double normBound, double keyNormBound, boolean primeCheck, boolean sparse, int keyGenAlg, Digest hashAlg)
+ {
+ super(new SecureRandom(), N);
+ this.N = N;
+ this.q = q;
+ this.d = d;
+ this.B = B;
+ this.basisType = basisType;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.keyNormBound = keyNormBound;
+ this.primeCheck = primeCheck;
+ this.sparse = sparse;
+ this.keyGenAlg = keyGenAlg;
+ this.hashAlg = hashAlg;
+ polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. <code>polyType=PRODUCT</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d1 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param d2 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param d3 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param B number of perturbations
+ * @param basisType whether to use the standard or transpose lattice
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param keyNormBound maximum norm for the ploynomials <code>F</code> and <code>G</code>
+ * @param primeCheck whether <code>2N+1</code> is prime
+ * @param sparse whether to treat ternary polynomials as sparsely populated ({@link org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial} vs {@link org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial})
+ * @param keyGenAlg <code>RESULTANT</code> produces better bases, <code>FLOAT</code> is slightly faster. <code>RESULTANT</code> follows the EESS standard while <code>FLOAT</code> is described in Hoffstein et al: An Introduction to Mathematical Cryptography.
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUSigningKeyGenerationParameters(int N, int q, int d1, int d2, int d3, int B, int basisType, double beta, double normBound, double keyNormBound, boolean primeCheck, boolean sparse, int keyGenAlg, Digest hashAlg)
+ {
+ super(new SecureRandom(), N);
+ this.N = N;
+ this.q = q;
+ this.d1 = d1;
+ this.d2 = d2;
+ this.d3 = d3;
+ this.B = B;
+ this.basisType = basisType;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.keyNormBound = keyNormBound;
+ this.primeCheck = primeCheck;
+ this.sparse = sparse;
+ this.keyGenAlg = keyGenAlg;
+ this.hashAlg = hashAlg;
+ polyType = NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT;
+ init();
+ }
+
+ private void init()
+ {
+ betaSq = beta * beta;
+ normBoundSq = normBound * normBound;
+ keyNormBoundSq = keyNormBound * keyNormBound;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws java.io.IOException
+ */
+ public NTRUSigningKeyGenerationParameters(InputStream is)
+ throws IOException
+ {
+ super(new SecureRandom(), 0); // TODO:
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ d = dis.readInt();
+ d1 = dis.readInt();
+ d2 = dis.readInt();
+ d3 = dis.readInt();
+ B = dis.readInt();
+ basisType = dis.readInt();
+ beta = dis.readDouble();
+ normBound = dis.readDouble();
+ keyNormBound = dis.readDouble();
+ signFailTolerance = dis.readInt();
+ primeCheck = dis.readBoolean();
+ sparse = dis.readBoolean();
+ bitsF = dis.readInt();
+ keyGenAlg = dis.read();
+ String alg = dis.readUTF();
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+ polyType = dis.read();
+ init();
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws java.io.IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(d);
+ dos.writeInt(d1);
+ dos.writeInt(d2);
+ dos.writeInt(d3);
+ dos.writeInt(B);
+ dos.writeInt(basisType);
+ dos.writeDouble(beta);
+ dos.writeDouble(normBound);
+ dos.writeDouble(keyNormBound);
+ dos.writeInt(signFailTolerance);
+ dos.writeBoolean(primeCheck);
+ dos.writeBoolean(sparse);
+ dos.writeInt(bitsF);
+ dos.write(keyGenAlg);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ dos.write(polyType);
+ }
+
+ public NTRUSigningParameters getSigningParameters()
+ {
+ return new NTRUSigningParameters(N, q, d, B, beta, normBound, hashAlg);
+ }
+
+ public NTRUSigningKeyGenerationParameters clone()
+ {
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ return new NTRUSigningKeyGenerationParameters(N, q, d, B, basisType, beta, normBound, keyNormBound, primeCheck, sparse, keyGenAlg, hashAlg);
+ }
+ else
+ {
+ return new NTRUSigningKeyGenerationParameters(N, q, d1, d2, d3, B, basisType, beta, normBound, keyNormBound, primeCheck, sparse, keyGenAlg, hashAlg);
+ }
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + B;
+ result = prime * result + N;
+ result = prime * result + basisType;
+ long temp;
+ temp = Double.doubleToLongBits(beta);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(betaSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + bitsF;
+ result = prime * result + d;
+ result = prime * result + d1;
+ result = prime * result + d2;
+ result = prime * result + d3;
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ result = prime * result + keyGenAlg;
+ temp = Double.doubleToLongBits(keyNormBound);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(keyNormBoundSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(normBound);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(normBoundSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + polyType;
+ result = prime * result + (primeCheck ? 1231 : 1237);
+ result = prime * result + q;
+ result = prime * result + signFailTolerance;
+ result = prime * result + (sparse ? 1231 : 1237);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUSigningKeyGenerationParameters))
+ {
+ return false;
+ }
+ NTRUSigningKeyGenerationParameters other = (NTRUSigningKeyGenerationParameters)obj;
+ if (B != other.B)
+ {
+ return false;
+ }
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (basisType != other.basisType)
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(beta) != Double.doubleToLongBits(other.beta))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(betaSq) != Double.doubleToLongBits(other.betaSq))
+ {
+ return false;
+ }
+ if (bitsF != other.bitsF)
+ {
+ return false;
+ }
+ if (d != other.d)
+ {
+ return false;
+ }
+ if (d1 != other.d1)
+ {
+ return false;
+ }
+ if (d2 != other.d2)
+ {
+ return false;
+ }
+ if (d3 != other.d3)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (keyGenAlg != other.keyGenAlg)
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(keyNormBound) != Double.doubleToLongBits(other.keyNormBound))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(keyNormBoundSq) != Double.doubleToLongBits(other.keyNormBoundSq))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBound) != Double.doubleToLongBits(other.normBound))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBoundSq) != Double.doubleToLongBits(other.normBoundSq))
+ {
+ return false;
+ }
+ if (polyType != other.polyType)
+ {
+ return false;
+ }
+ if (primeCheck != other.primeCheck)
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (signFailTolerance != other.signFailTolerance)
+ {
+ return false;
+ }
+ if (sparse != other.sparse)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString()
+ {
+ DecimalFormat format = new DecimalFormat("0.00");
+
+ StringBuilder output = new StringBuilder("SignatureParameters(N=" + N + " q=" + q);
+ if (polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE)
+ {
+ output.append(" polyType=SIMPLE d=" + d);
+ }
+ else
+ {
+ output.append(" polyType=PRODUCT d1=" + d1 + " d2=" + d2 + " d3=" + d3);
+ }
+ output.append(" B=" + B + " basisType=" + basisType + " beta=" + format.format(beta) +
+ " normBound=" + format.format(normBound) + " keyNormBound=" + format.format(keyNormBound) +
+ " prime=" + primeCheck + " sparse=" + sparse + " keyGenAlg=" + keyGenAlg + " hashAlg=" + hashAlg + ")");
+ return output.toString();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java
new file mode 100644
index 00000000..eac283aa
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java
@@ -0,0 +1,357 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.pqc.math.ntru.euclid.BigIntEuclidean;
+import org.spongycastle.pqc.math.ntru.polynomial.BigDecimalPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.BigIntPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Resultant;
+
+import static java.math.BigInteger.ONE;
+import static java.math.BigInteger.ZERO;
+
+public class NTRUSigningKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private NTRUSigningKeyGenerationParameters params;
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.params = (NTRUSigningKeyGenerationParameters)param;
+ }
+
+ /**
+ * Generates a new signature key pair. Starts <code>B+1</code> threads.
+ *
+ * @return a key pair
+ */
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ NTRUSigningPublicKeyParameters pub = null;
+ ExecutorService executor = Executors.newCachedThreadPool();
+ List<Future<NTRUSigningPrivateKeyParameters.Basis>> bases = new ArrayList<Future<NTRUSigningPrivateKeyParameters.Basis>>();
+ for (int k = params.B; k >= 0; k--)
+ {
+ bases.add(executor.submit(new BasisGenerationTask()));
+ }
+ executor.shutdown();
+
+ List<NTRUSigningPrivateKeyParameters.Basis> basises = new ArrayList<NTRUSigningPrivateKeyParameters.Basis>();
+
+ for (int k = params.B; k >= 0; k--)
+ {
+ Future<NTRUSigningPrivateKeyParameters.Basis> basis = bases.get(k);
+ try
+ {
+ basises.add(basis.get());
+ if (k == params.B)
+ {
+ pub = new NTRUSigningPublicKeyParameters(basis.get().h, params.getSigningParameters());
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+ NTRUSigningPrivateKeyParameters priv = new NTRUSigningPrivateKeyParameters(basises, pub);
+ AsymmetricCipherKeyPair kp = new AsymmetricCipherKeyPair(pub, priv);
+ return kp;
+ }
+
+ /**
+ * Generates a new signature key pair. Runs in a single thread.
+ *
+ * @return a key pair
+ */
+ public AsymmetricCipherKeyPair generateKeyPairSingleThread()
+ {
+ List<NTRUSigningPrivateKeyParameters.Basis> basises = new ArrayList<NTRUSigningPrivateKeyParameters.Basis>();
+ NTRUSigningPublicKeyParameters pub = null;
+ for (int k = params.B; k >= 0; k--)
+ {
+ NTRUSigningPrivateKeyParameters.Basis basis = generateBoundedBasis();
+ basises.add(basis);
+ if (k == 0)
+ {
+ pub = new NTRUSigningPublicKeyParameters(basis.h, params.getSigningParameters());
+ }
+ }
+ NTRUSigningPrivateKeyParameters priv = new NTRUSigningPrivateKeyParameters(basises, pub);
+ return new AsymmetricCipherKeyPair(pub, priv);
+ }
+
+
+ /**
+ * Implementation of the optional steps 20 through 26 in EESS1v2.pdf, section 3.5.1.1.
+ * This doesn't seem to have much of an effect and sometimes actually increases the
+ * norm of F, but on average it slightly reduces the norm.<br/>
+ * This method changes <code>F</code> and <code>g</code> but leaves <code>f</code> and
+ * <code>g</code> unchanged.
+ *
+ * @param f
+ * @param g
+ * @param F
+ * @param G
+ * @param N
+ */
+ private void minimizeFG(IntegerPolynomial f, IntegerPolynomial g, IntegerPolynomial F, IntegerPolynomial G, int N)
+ {
+ int E = 0;
+ for (int j = 0; j < N; j++)
+ {
+ E += 2 * N * (f.coeffs[j] * f.coeffs[j] + g.coeffs[j] * g.coeffs[j]);
+ }
+
+ // [f(1)+g(1)]^2 = 4
+ E -= 4;
+
+ IntegerPolynomial u = (IntegerPolynomial)f.clone();
+ IntegerPolynomial v = (IntegerPolynomial)g.clone();
+ int j = 0;
+ int k = 0;
+ int maxAdjustment = N;
+ while (k < maxAdjustment && j < N)
+ {
+ int D = 0;
+ int i = 0;
+ while (i < N)
+ {
+ int D1 = F.coeffs[i] * f.coeffs[i];
+ int D2 = G.coeffs[i] * g.coeffs[i];
+ int D3 = 4 * N * (D1 + D2);
+ D += D3;
+ i++;
+ }
+ // f(1)+g(1) = 2
+ int D1 = 4 * (F.sumCoeffs() + G.sumCoeffs());
+ D -= D1;
+
+ if (D > E)
+ {
+ F.sub(u);
+ G.sub(v);
+ k++;
+ j = 0;
+ }
+ else if (D < -E)
+ {
+ F.add(u);
+ G.add(v);
+ k++;
+ j = 0;
+ }
+ j++;
+ u.rotate1();
+ v.rotate1();
+ }
+ }
+
+ /**
+ * Creates a NTRUSigner basis consisting of polynomials <code>f, g, F, G, h</code>.<br/>
+ * If <code>KeyGenAlg=FLOAT</code>, the basis may not be valid and this method must be rerun if that is the case.<br/>
+ *
+ * @see #generateBoundedBasis()
+ */
+ private FGBasis generateBasis()
+ {
+ int N = params.N;
+ int q = params.q;
+ int d = params.d;
+ int d1 = params.d1;
+ int d2 = params.d2;
+ int d3 = params.d3;
+ int basisType = params.basisType;
+
+ Polynomial f;
+ IntegerPolynomial fInt;
+ Polynomial g;
+ IntegerPolynomial gInt;
+ IntegerPolynomial fq;
+ Resultant rf;
+ Resultant rg;
+ BigIntEuclidean r;
+
+ int _2n1 = 2 * N + 1;
+ boolean primeCheck = params.primeCheck;
+
+ do
+ {
+ do
+ {
+ f = params.polyType== NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? DenseTernaryPolynomial.generateRandom(N, d + 1, d, new SecureRandom()) : ProductFormPolynomial.generateRandom(N, d1, d2, d3 + 1, d3, new SecureRandom());
+ fInt = f.toIntegerPolynomial();
+ }
+ while (primeCheck && fInt.resultant(_2n1).res.equals(ZERO));
+ fq = fInt.invertFq(q);
+ }
+ while (fq == null);
+ rf = fInt.resultant();
+
+ do
+ {
+ do
+ {
+ do
+ {
+ g = params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? DenseTernaryPolynomial.generateRandom(N, d + 1, d, new SecureRandom()) : ProductFormPolynomial.generateRandom(N, d1, d2, d3 + 1, d3, new SecureRandom());
+ gInt = g.toIntegerPolynomial();
+ }
+ while (primeCheck && gInt.resultant(_2n1).res.equals(ZERO));
+ }
+ while (gInt.invertFq(q) == null);
+ rg = gInt.resultant();
+ r = BigIntEuclidean.calculate(rf.res, rg.res);
+ }
+ while (!r.gcd.equals(ONE));
+
+ BigIntPolynomial A = (BigIntPolynomial)rf.rho.clone();
+ A.mult(r.x.multiply(BigInteger.valueOf(q)));
+ BigIntPolynomial B = (BigIntPolynomial)rg.rho.clone();
+ B.mult(r.y.multiply(BigInteger.valueOf(-q)));
+
+ BigIntPolynomial C;
+ if (params.keyGenAlg == NTRUSigningKeyGenerationParameters.KEY_GEN_ALG_RESULTANT)
+ {
+ int[] fRevCoeffs = new int[N];
+ int[] gRevCoeffs = new int[N];
+ fRevCoeffs[0] = fInt.coeffs[0];
+ gRevCoeffs[0] = gInt.coeffs[0];
+ for (int i = 1; i < N; i++)
+ {
+ fRevCoeffs[i] = fInt.coeffs[N - i];
+ gRevCoeffs[i] = gInt.coeffs[N - i];
+ }
+ IntegerPolynomial fRev = new IntegerPolynomial(fRevCoeffs);
+ IntegerPolynomial gRev = new IntegerPolynomial(gRevCoeffs);
+
+ IntegerPolynomial t = f.mult(fRev);
+ t.add(g.mult(gRev));
+ Resultant rt = t.resultant();
+ C = fRev.mult(B); // fRev.mult(B) is actually faster than new SparseTernaryPolynomial(fRev).mult(B), possibly due to cache locality?
+ C.add(gRev.mult(A));
+ C = C.mult(rt.rho);
+ C.div(rt.res);
+ }
+ else
+ { // KeyGenAlg.FLOAT
+ // calculate ceil(log10(N))
+ int log10N = 0;
+ for (int i = 1; i < N; i *= 10)
+ {
+ log10N++;
+ }
+
+ // * Cdec needs to be accurate to 1 decimal place so it can be correctly rounded;
+ // * fInv loses up to (#digits of longest coeff of B) places in fInv.mult(B);
+ // * multiplying fInv by B also multiplies the rounding error by a factor of N;
+ // so make #decimal places of fInv the sum of the above.
+ BigDecimalPolynomial fInv = rf.rho.div(new BigDecimal(rf.res), B.getMaxCoeffLength() + 1 + log10N);
+ BigDecimalPolynomial gInv = rg.rho.div(new BigDecimal(rg.res), A.getMaxCoeffLength() + 1 + log10N);
+
+ BigDecimalPolynomial Cdec = fInv.mult(B);
+ Cdec.add(gInv.mult(A));
+ Cdec.halve();
+ C = Cdec.round();
+ }
+
+ BigIntPolynomial F = (BigIntPolynomial)B.clone();
+ F.sub(f.mult(C));
+ BigIntPolynomial G = (BigIntPolynomial)A.clone();
+ G.sub(g.mult(C));
+
+ IntegerPolynomial FInt = new IntegerPolynomial(F);
+ IntegerPolynomial GInt = new IntegerPolynomial(G);
+ minimizeFG(fInt, gInt, FInt, GInt, N);
+
+ Polynomial fPrime;
+ IntegerPolynomial h;
+ if (basisType == NTRUSigningKeyGenerationParameters.BASIS_TYPE_STANDARD)
+ {
+ fPrime = FInt;
+ h = g.mult(fq, q);
+ }
+ else
+ {
+ fPrime = g;
+ h = FInt.mult(fq, q);
+ }
+ h.modPositive(q);
+
+ return new FGBasis(f, fPrime, h, FInt, GInt, params);
+ }
+
+ /**
+ * Creates a basis such that <code>|F| &lt; keyNormBound</code> and <code>|G| &lt; keyNormBound</code>
+ *
+ * @return a NTRUSigner basis
+ */
+ public NTRUSigningPrivateKeyParameters.Basis generateBoundedBasis()
+ {
+ while (true)
+ {
+ FGBasis basis = generateBasis();
+ if (basis.isNormOk())
+ {
+ return basis;
+ }
+ }
+ }
+
+ private class BasisGenerationTask
+ implements Callable<NTRUSigningPrivateKeyParameters.Basis>
+ {
+
+
+ public NTRUSigningPrivateKeyParameters.Basis call()
+ throws Exception
+ {
+ return generateBoundedBasis();
+ }
+ }
+
+ /**
+ * A subclass of Basis that additionally contains the polynomials <code>F</code> and <code>G</code>.
+ */
+ public class FGBasis
+ extends NTRUSigningPrivateKeyParameters.Basis
+ {
+ public IntegerPolynomial F;
+ public IntegerPolynomial G;
+
+ FGBasis(Polynomial f, Polynomial fPrime, IntegerPolynomial h, IntegerPolynomial F, IntegerPolynomial G, NTRUSigningKeyGenerationParameters params)
+ {
+ super(f, fPrime, h, params);
+ this.F = F;
+ this.G = G;
+ }
+
+ /**
+ * Returns <code>true</code> if the norms of the polynomials <code>F</code> and <code>G</code>
+ * are within {@link NTRUSigningKeyGenerationParameters#keyNormBound}.
+ *
+ * @return
+ */
+ boolean isNormOk()
+ {
+ double keyNormBoundSq = params.keyNormBoundSq;
+ int q = params.q;
+ return (F.centeredNormSq(q) < keyNormBoundSq && G.centeredNormSq(q) < keyNormBoundSq);
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningParameters.java
new file mode 100644
index 00000000..bf9ba2cb
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningParameters.java
@@ -0,0 +1,269 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.DecimalFormat;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA256Digest;
+import org.spongycastle.crypto.digests.SHA512Digest;
+
+/**
+ * A set of parameters for NtruSign. Several predefined parameter sets are available and new ones can be created as well.
+ */
+public class NTRUSigningParameters
+ implements Cloneable
+{
+ public int N;
+ public int q;
+ public int d, d1, d2, d3, B;
+ double beta;
+ public double betaSq;
+ double normBound;
+ public double normBoundSq;
+ public int signFailTolerance = 100;
+ int bitsF = 6; // max #bits needed to encode one coefficient of the polynomial F
+ public Digest hashAlg;
+
+ /**
+ * Constructs a parameter set that uses ternary private keys (i.e. <code>polyType=SIMPLE</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param B number of perturbations
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUSigningParameters(int N, int q, int d, int B, double beta, double normBound, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.d = d;
+ this.B = B;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ /**
+ * Constructs a parameter set that uses product-form private keys (i.e. <code>polyType=PRODUCT</code>).
+ *
+ * @param N number of polynomial coefficients
+ * @param q modulus
+ * @param d1 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param d2 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param d3 number of -1's in the private polynomials <code>f</code> and <code>g</code>
+ * @param B number of perturbations
+ * @param beta balancing factor for the transpose lattice
+ * @param normBound maximum norm for valid signatures
+ * @param keyNormBound maximum norm for the ploynomials <code>F</code> and <code>G</code>
+ * @param hashAlg a valid identifier for a <code>java.security.MessageDigest</code> instance such as <code>SHA-256</code>. The <code>MessageDigest</code> must support the <code>getDigestLength()</code> method.
+ */
+ public NTRUSigningParameters(int N, int q, int d1, int d2, int d3, int B, double beta, double normBound, double keyNormBound, Digest hashAlg)
+ {
+ this.N = N;
+ this.q = q;
+ this.d1 = d1;
+ this.d2 = d2;
+ this.d3 = d3;
+ this.B = B;
+ this.beta = beta;
+ this.normBound = normBound;
+ this.hashAlg = hashAlg;
+ init();
+ }
+
+ private void init()
+ {
+ betaSq = beta * beta;
+ normBoundSq = normBound * normBound;
+ }
+
+ /**
+ * Reads a parameter set from an input stream.
+ *
+ * @param is an input stream
+ * @throws IOException
+ */
+ public NTRUSigningParameters(InputStream is)
+ throws IOException
+ {
+ DataInputStream dis = new DataInputStream(is);
+ N = dis.readInt();
+ q = dis.readInt();
+ d = dis.readInt();
+ d1 = dis.readInt();
+ d2 = dis.readInt();
+ d3 = dis.readInt();
+ B = dis.readInt();
+ beta = dis.readDouble();
+ normBound = dis.readDouble();
+ signFailTolerance = dis.readInt();
+ bitsF = dis.readInt();
+ String alg = dis.readUTF();
+ if ("SHA-512".equals(alg))
+ {
+ hashAlg = new SHA512Digest();
+ }
+ else if ("SHA-256".equals(alg))
+ {
+ hashAlg = new SHA256Digest();
+ }
+ init();
+ }
+
+ /**
+ * Writes the parameter set to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(N);
+ dos.writeInt(q);
+ dos.writeInt(d);
+ dos.writeInt(d1);
+ dos.writeInt(d2);
+ dos.writeInt(d3);
+ dos.writeInt(B);
+ dos.writeDouble(beta);
+ dos.writeDouble(normBound);
+ dos.writeInt(signFailTolerance);
+ dos.writeInt(bitsF);
+ dos.writeUTF(hashAlg.getAlgorithmName());
+ }
+
+ public NTRUSigningParameters clone()
+ {
+ return new NTRUSigningParameters(N, q, d, B, beta, normBound, hashAlg);
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + B;
+ result = prime * result + N;
+ long temp;
+ temp = Double.doubleToLongBits(beta);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(betaSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + bitsF;
+ result = prime * result + d;
+ result = prime * result + d1;
+ result = prime * result + d2;
+ result = prime * result + d3;
+ result = prime * result + ((hashAlg == null) ? 0 : hashAlg.getAlgorithmName().hashCode());
+ temp = Double.doubleToLongBits(normBound);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(normBoundSq);
+ result = prime * result + (int)(temp ^ (temp >>> 32));
+ result = prime * result + q;
+ result = prime * result + signFailTolerance;
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof NTRUSigningParameters))
+ {
+ return false;
+ }
+ NTRUSigningParameters other = (NTRUSigningParameters)obj;
+ if (B != other.B)
+ {
+ return false;
+ }
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(beta) != Double.doubleToLongBits(other.beta))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(betaSq) != Double.doubleToLongBits(other.betaSq))
+ {
+ return false;
+ }
+ if (bitsF != other.bitsF)
+ {
+ return false;
+ }
+ if (d != other.d)
+ {
+ return false;
+ }
+ if (d1 != other.d1)
+ {
+ return false;
+ }
+ if (d2 != other.d2)
+ {
+ return false;
+ }
+ if (d3 != other.d3)
+ {
+ return false;
+ }
+ if (hashAlg == null)
+ {
+ if (other.hashAlg != null)
+ {
+ return false;
+ }
+ }
+ else if (!hashAlg.getAlgorithmName().equals(other.hashAlg.getAlgorithmName()))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBound) != Double.doubleToLongBits(other.normBound))
+ {
+ return false;
+ }
+ if (Double.doubleToLongBits(normBoundSq) != Double.doubleToLongBits(other.normBoundSq))
+ {
+ return false;
+ }
+ if (q != other.q)
+ {
+ return false;
+ }
+ if (signFailTolerance != other.signFailTolerance)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public String toString()
+ {
+ DecimalFormat format = new DecimalFormat("0.00");
+
+ StringBuilder output = new StringBuilder("SignatureParameters(N=" + N + " q=" + q);
+
+ output.append(" B=" + B + " beta=" + format.format(beta) +
+ " normBound=" + format.format(normBound) +
+ " hashAlg=" + hashAlg + ")");
+ return output.toString();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPrivateKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPrivateKeyParameters.java
new file mode 100644
index 00000000..bbbaaeef
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPrivateKeyParameters.java
@@ -0,0 +1,385 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.Polynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+
+/**
+ * A NtruSign private key comprises one or more {@link NTRUSigningPrivateKeyParameters.Basis} of three polynomials each,
+ * except the zeroth basis for which <code>h</code> is undefined.
+ */
+public class NTRUSigningPrivateKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private List<Basis> bases;
+ private NTRUSigningPublicKeyParameters publicKey;
+
+ /**
+ * Constructs a new private key from a byte array
+ *
+ * @param b an encoded private key
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPrivateKeyParameters(byte[] b, NTRUSigningKeyGenerationParameters params)
+ throws IOException
+ {
+ this(new ByteArrayInputStream(b), params);
+ }
+
+ /**
+ * Constructs a new private key from an input stream
+ *
+ * @param is an input stream
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPrivateKeyParameters(InputStream is, NTRUSigningKeyGenerationParameters params)
+ throws IOException
+ {
+ super(true);
+ bases = new ArrayList<Basis>();
+ for (int i = 0; i <= params.B; i++)
+ // include a public key h[i] in all bases except for the first one
+ {
+ add(new Basis(is, params, i != 0));
+ }
+ publicKey = new NTRUSigningPublicKeyParameters(is, params.getSigningParameters());
+ }
+
+ public NTRUSigningPrivateKeyParameters(List<Basis> bases, NTRUSigningPublicKeyParameters publicKey)
+ {
+ super(true);
+ this.bases = new ArrayList<Basis>(bases);
+ this.publicKey = publicKey;
+ }
+
+ /**
+ * Adds a basis to the key.
+ *
+ * @param b a NtruSign basis
+ */
+ private void add(Basis b)
+ {
+ bases.add(b);
+ }
+
+ /**
+ * Returns the <code>i</code>-th basis
+ *
+ * @param i the index
+ * @return the basis at index <code>i</code>
+ */
+ public Basis getBasis(int i)
+ {
+ return bases.get(i);
+ }
+
+ public NTRUSigningPublicKeyParameters getPublicKey()
+ {
+ return publicKey;
+ }
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ for (int i = 0; i < bases.size(); i++)
+ {
+ // all bases except for the first one contain a public key
+ bases.get(i).encode(os, i != 0);
+ }
+
+ os.write(publicKey.getEncoded());
+
+ return os.toByteArray();
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((bases == null) ? 0 : bases.hashCode());
+ for (Basis basis : bases)
+ {
+ result += basis.hashCode();
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUSigningPrivateKeyParameters other = (NTRUSigningPrivateKeyParameters)obj;
+ if (bases == null)
+ {
+ if (other.bases != null)
+ {
+ return false;
+ }
+ }
+ if (bases.size() != other.bases.size())
+ {
+ return false;
+ }
+ for (int i = 0; i < bases.size(); i++)
+ {
+ Basis basis1 = bases.get(i);
+ Basis basis2 = other.bases.get(i);
+ if (!basis1.f.equals(basis2.f))
+ {
+ return false;
+ }
+ if (!basis1.fPrime.equals(basis2.fPrime))
+ {
+ return false;
+ }
+ if (i != 0 && !basis1.h.equals(basis2.h)) // don't compare h for the 0th basis
+ {
+ return false;
+ }
+ if (!basis1.params.equals(basis2.params))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * A NtruSign basis. Contains three polynomials <code>f, f', h</code>.
+ */
+ public static class Basis
+ {
+ public Polynomial f;
+ public Polynomial fPrime;
+ public IntegerPolynomial h;
+ NTRUSigningKeyGenerationParameters params;
+
+ /**
+ * Constructs a new basis from polynomials <code>f, f', h</code>.
+ *
+ * @param f
+ * @param fPrime
+ * @param h
+ * @param params NtruSign parameters
+ */
+ protected Basis(Polynomial f, Polynomial fPrime, IntegerPolynomial h, NTRUSigningKeyGenerationParameters params)
+ {
+ this.f = f;
+ this.fPrime = fPrime;
+ this.h = h;
+ this.params = params;
+ }
+
+ /**
+ * Reads a basis from an input stream and constructs a new basis.
+ *
+ * @param is an input stream
+ * @param params NtruSign parameters
+ * @param include_h whether to read the polynomial <code>h</code> (<code>true</code>) or only <code>f</code> and <code>f'</code> (<code>false</code>)
+ */
+ Basis(InputStream is, NTRUSigningKeyGenerationParameters params, boolean include_h)
+ throws IOException
+ {
+ int N = params.N;
+ int q = params.q;
+ int d1 = params.d1;
+ int d2 = params.d2;
+ int d3 = params.d3;
+ boolean sparse = params.sparse;
+ this.params = params;
+
+ if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ f = ProductFormPolynomial.fromBinary(is, N, d1, d2, d3 + 1, d3);
+ }
+ else
+ {
+ IntegerPolynomial fInt = IntegerPolynomial.fromBinary3Tight(is, N);
+ f = sparse ? new SparseTernaryPolynomial(fInt) : new DenseTernaryPolynomial(fInt);
+ }
+
+ if (params.basisType == NTRUSigningKeyGenerationParameters.BASIS_TYPE_STANDARD)
+ {
+ IntegerPolynomial fPrimeInt = IntegerPolynomial.fromBinary(is, N, q);
+ for (int i = 0; i < fPrimeInt.coeffs.length; i++)
+ {
+ fPrimeInt.coeffs[i] -= q / 2;
+ }
+ fPrime = fPrimeInt;
+ }
+ else if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
+ {
+ fPrime = ProductFormPolynomial.fromBinary(is, N, d1, d2, d3 + 1, d3);
+ }
+ else
+ {
+ fPrime = IntegerPolynomial.fromBinary3Tight(is, N);
+ }
+
+ if (include_h)
+ {
+ h = IntegerPolynomial.fromBinary(is, N, q);
+ }
+ }
+
+ /**
+ * Writes the basis to an output stream
+ *
+ * @param os an output stream
+ * @param include_h whether to write the polynomial <code>h</code> (<code>true</code>) or only <code>f</code> and <code>f'</code> (<code>false</code>)
+ * @throws IOException
+ */
+ void encode(OutputStream os, boolean include_h)
+ throws IOException
+ {
+ int q = params.q;
+
+ os.write(getEncoded(f));
+ if (params.basisType == NTRUSigningKeyGenerationParameters.BASIS_TYPE_STANDARD)
+ {
+ IntegerPolynomial fPrimeInt = fPrime.toIntegerPolynomial();
+ for (int i = 0; i < fPrimeInt.coeffs.length; i++)
+ {
+ fPrimeInt.coeffs[i] += q / 2;
+ }
+ os.write(fPrimeInt.toBinary(q));
+ }
+ else
+ {
+ os.write(getEncoded(fPrime));
+ }
+ if (include_h)
+ {
+ os.write(h.toBinary(q));
+ }
+ }
+
+ private byte[] getEncoded(Polynomial p)
+ {
+ if (p instanceof ProductFormPolynomial)
+ {
+ return ((ProductFormPolynomial)p).toBinary();
+ }
+ else
+ {
+ return p.toIntegerPolynomial().toBinary3Tight();
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((f == null) ? 0 : f.hashCode());
+ result = prime * result + ((fPrime == null) ? 0 : fPrime.hashCode());
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (!(obj instanceof Basis))
+ {
+ return false;
+ }
+ Basis other = (Basis)obj;
+ if (f == null)
+ {
+ if (other.f != null)
+ {
+ return false;
+ }
+ }
+ else if (!f.equals(other.f))
+ {
+ return false;
+ }
+ if (fPrime == null)
+ {
+ if (other.fPrime != null)
+ {
+ return false;
+ }
+ }
+ else if (!fPrime.equals(other.fPrime))
+ {
+ return false;
+ }
+ if (h == null)
+ {
+ if (other.h != null)
+ {
+ return false;
+ }
+ }
+ else if (!h.equals(other.h))
+ {
+ return false;
+ }
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPublicKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPublicKeyParameters.java
new file mode 100644
index 00000000..d7431b8b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/ntru/NTRUSigningPublicKeyParameters.java
@@ -0,0 +1,132 @@
+package org.spongycastle.pqc.crypto.ntru;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
+
+/**
+ * A NtruSign public key is essentially a polynomial named <code>h</code>.
+ */
+public class NTRUSigningPublicKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private NTRUSigningParameters params;
+ public IntegerPolynomial h;
+
+ /**
+ * Constructs a new public key from a polynomial
+ *
+ * @param h the polynomial <code>h</code> which determines the key
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPublicKeyParameters(IntegerPolynomial h, NTRUSigningParameters params)
+ {
+ super(false);
+ this.h = h;
+ this.params = params;
+ }
+
+ /**
+ * Converts a byte array to a polynomial <code>h</code> and constructs a new public key
+ *
+ * @param b an encoded polynomial
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPublicKeyParameters(byte[] b, NTRUSigningParameters params)
+ {
+ super(false);
+ h = IntegerPolynomial.fromBinary(b, params.N, params.q);
+ this.params = params;
+ }
+
+ /**
+ * Reads a polynomial <code>h</code> from an input stream and constructs a new public key
+ *
+ * @param is an input stream
+ * @param params the NtruSign parameters to use
+ */
+ public NTRUSigningPublicKeyParameters(InputStream is, NTRUSigningParameters params)
+ throws IOException
+ {
+ super(false);
+ h = IntegerPolynomial.fromBinary(is, params.N, params.q);
+ this.params = params;
+ }
+
+
+ /**
+ * Converts the key to a byte array
+ *
+ * @return the encoded key
+ */
+ public byte[] getEncoded()
+ {
+ return h.toBinary(params.q);
+ }
+
+ /**
+ * Writes the key to an output stream
+ *
+ * @param os an output stream
+ * @throws IOException
+ */
+ public void writeTo(OutputStream os)
+ throws IOException
+ {
+ os.write(getEncoded());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((h == null) ? 0 : h.hashCode());
+ result = prime * result + ((params == null) ? 0 : params.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ NTRUSigningPublicKeyParameters other = (NTRUSigningPublicKeyParameters)obj;
+ if (h == null)
+ {
+ if (other.h != null)
+ {
+ return false;
+ }
+ }
+ else if (!h.equals(other.h))
+ {
+ return false;
+ }
+ if (params == null)
+ {
+ if (other.params != null)
+ {
+ return false;
+ }
+ }
+ else if (!params.equals(other.params))
+ {
+ return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/Layer.java b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/Layer.java
new file mode 100644
index 00000000..0556531e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/Layer.java
@@ -0,0 +1,322 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.pqc.crypto.rainbow.util.GF2Field;
+import org.spongycastle.pqc.crypto.rainbow.util.RainbowUtil;
+import org.spongycastle.util.Arrays;
+
+
+/**
+ * This class represents a layer of the Rainbow Oil- and Vinegar Map. Each Layer
+ * consists of oi polynomials with their coefficients, generated at random.
+ * <p>
+ * To sign a document, we solve a LES (linear equation system) for each layer in
+ * order to find the oil variables of that layer and to be able to use the
+ * variables to compute the signature. This functionality is implemented in the
+ * RainbowSignature-class, by the aid of the private key.
+ * <p>
+ * Each layer is a part of the private key.
+ * <p>
+ * More information about the layer can be found in the paper of Jintai Ding,
+ * Dieter Schmidt: Rainbow, a New Multivariable Polynomial Signature Scheme.
+ * ACNS 2005: 164-175 (http://dx.doi.org/10.1007/11496137_12)
+ */
+public class Layer
+{
+ private int vi; // number of vinegars in this layer
+ private int viNext; // number of vinegars in next layer
+ private int oi; // number of oils in this layer
+
+ /*
+ * k : index of polynomial
+ *
+ * i,j : indices of oil and vinegar variables
+ */
+ private short[/* k */][/* i */][/* j */] coeff_alpha;
+ private short[/* k */][/* i */][/* j */] coeff_beta;
+ private short[/* k */][/* i */] coeff_gamma;
+ private short[/* k */] coeff_eta;
+
+ /**
+ * Constructor
+ *
+ * @param vi number of vinegar variables of this layer
+ * @param viNext number of vinegar variables of next layer. It's the same as
+ * (num of oils) + (num of vinegars) of this layer.
+ * @param coeffAlpha alpha-coefficients in the polynomials of this layer
+ * @param coeffBeta beta-coefficients in the polynomials of this layer
+ * @param coeffGamma gamma-coefficients in the polynomials of this layer
+ * @param coeffEta eta-coefficients in the polynomials of this layer
+ */
+ public Layer(byte vi, byte viNext, short[][][] coeffAlpha,
+ short[][][] coeffBeta, short[][] coeffGamma, short[] coeffEta)
+ {
+ this.vi = vi & 0xff;
+ this.viNext = viNext & 0xff;
+ this.oi = this.viNext - this.vi;
+
+ // the secret coefficients of all polynomials in this layer
+ this.coeff_alpha = coeffAlpha;
+ this.coeff_beta = coeffBeta;
+ this.coeff_gamma = coeffGamma;
+ this.coeff_eta = coeffEta;
+ }
+
+ /**
+ * This function generates the coefficients of all polynomials in this layer
+ * at random using random generator.
+ *
+ * @param sr the random generator which is to be used
+ */
+ public Layer(int vi, int viNext, SecureRandom sr)
+ {
+ this.vi = vi;
+ this.viNext = viNext;
+ this.oi = viNext - vi;
+
+ // the coefficients of all polynomials in this layer
+ this.coeff_alpha = new short[this.oi][this.oi][this.vi];
+ this.coeff_beta = new short[this.oi][this.vi][this.vi];
+ this.coeff_gamma = new short[this.oi][this.viNext];
+ this.coeff_eta = new short[this.oi];
+
+ int numOfPoly = this.oi; // number of polynomials per layer
+
+ // Alpha coeffs
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ for (int i = 0; i < this.oi; i++)
+ {
+ for (int j = 0; j < this.vi; j++)
+ {
+ coeff_alpha[k][i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ }
+ // Beta coeffs
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ for (int i = 0; i < this.vi; i++)
+ {
+ for (int j = 0; j < this.vi; j++)
+ {
+ coeff_beta[k][i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ }
+ // Gamma coeffs
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ for (int i = 0; i < this.viNext; i++)
+ {
+ coeff_gamma[k][i] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ // Eta
+ for (int k = 0; k < numOfPoly; k++)
+ {
+ coeff_eta[k] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+
+ /**
+ * This method plugs in the vinegar variables into the polynomials of this
+ * layer and computes the coefficients of the Oil-variables as well as the
+ * free coefficient in each polynomial.
+ * <p>
+ * It is needed for computing the Oil variables while signing.
+ *
+ * @param x vinegar variables of this layer that should be plugged into
+ * the polynomials.
+ * @return coeff the coefficients of Oil variables and the free coeff in the
+ * polynomials of this layer.
+ */
+ public short[][] plugInVinegars(short[] x)
+ {
+ // temporary variable needed for the multiplication
+ short tmpMult = 0;
+ // coeff: 1st index = which polynomial, 2nd index=which variable
+ short[][] coeff = new short[oi][oi + 1]; // gets returned
+ // free coefficient per polynomial
+ short[] sum = new short[oi];
+
+ /*
+ * evaluate the beta-part of the polynomials (it contains no oil
+ * variables)
+ */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = 0; i < vi; i++)
+ {
+ for (int j = 0; j < vi; j++)
+ {
+ // tmp = beta * xi (plug in)
+ tmpMult = GF2Field.multElem(coeff_beta[k][i][j], x[i]);
+ // tmp = tmp * xj
+ tmpMult = GF2Field.multElem(tmpMult, x[j]);
+ // accumulate into the array for the free coefficients.
+ sum[k] = GF2Field.addElem(sum[k], tmpMult);
+ }
+ }
+ }
+
+ /* evaluate the alpha-part (it contains oils) */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = 0; i < oi; i++)
+ {
+ for (int j = 0; j < vi; j++)
+ {
+ // alpha * xj (plug in)
+ tmpMult = GF2Field.multElem(coeff_alpha[k][i][j], x[j]);
+ // accumulate
+ coeff[k][i] = GF2Field.addElem(coeff[k][i], tmpMult);
+ }
+ }
+ }
+ /* evaluate the gama-part of the polynomial (containing no oils) */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = 0; i < vi; i++)
+ {
+ // gamma * xi (plug in)
+ tmpMult = GF2Field.multElem(coeff_gamma[k][i], x[i]);
+ // accumulate in the array for the free coefficients (per
+ // polynomial).
+ sum[k] = GF2Field.addElem(sum[k], tmpMult);
+ }
+ }
+ /* evaluate the gama-part of the polynomial (but containing oils) */
+ for (int k = 0; k < oi; k++)
+ {
+ for (int i = vi; i < viNext; i++)
+ { // oils
+ // accumulate the coefficients of the oil variables (per
+ // polynomial).
+ coeff[k][i - vi] = GF2Field.addElem(coeff_gamma[k][i],
+ coeff[k][i - vi]);
+ }
+ }
+ /* evaluate the eta-part of the polynomial */
+ for (int k = 0; k < oi; k++)
+ {
+ // accumulate in the array for the free coefficients per polynomial.
+ sum[k] = GF2Field.addElem(sum[k], coeff_eta[k]);
+ }
+
+ /* put the free coefficients (sum) into the coeff-array as last column */
+ for (int k = 0; k < oi; k++)
+ {
+ coeff[k][oi] = sum[k];
+ }
+ return coeff;
+ }
+
+ /**
+ * Getter for the number of vinegar variables of this layer.
+ *
+ * @return the number of vinegar variables of this layer.
+ */
+ public int getVi()
+ {
+ return vi;
+ }
+
+ /**
+ * Getter for the number of vinegar variables of the next layer.
+ *
+ * @return the number of vinegar variables of the next layer.
+ */
+ public int getViNext()
+ {
+ return viNext;
+ }
+
+ /**
+ * Getter for the number of Oil variables of this layer.
+ *
+ * @return the number of oil variables of this layer.
+ */
+ public int getOi()
+ {
+ return oi;
+ }
+
+ /**
+ * Getter for the alpha-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients of alpha-terms of this layer.
+ */
+ public short[][][] getCoeffAlpha()
+ {
+ return coeff_alpha;
+ }
+
+ /**
+ * Getter for the beta-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients of beta-terms of this layer.
+ */
+
+ public short[][][] getCoeffBeta()
+ {
+ return coeff_beta;
+ }
+
+ /**
+ * Getter for the gamma-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients of gamma-terms of this layer
+ */
+ public short[][] getCoeffGamma()
+ {
+ return coeff_gamma;
+ }
+
+ /**
+ * Getter for the eta-coefficients of the polynomials in this layer.
+ *
+ * @return the coefficients eta of this layer
+ */
+ public short[] getCoeffEta()
+ {
+ return coeff_eta;
+ }
+
+ /**
+ * This function compares this Layer with another object.
+ *
+ * @param other the other object
+ * @return the result of the comparison
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof Layer))
+ {
+ return false;
+ }
+ Layer otherLayer = (Layer)other;
+
+ return vi == otherLayer.getVi()
+ && viNext == otherLayer.getViNext()
+ && oi == otherLayer.getOi()
+ && RainbowUtil.equals(coeff_alpha, otherLayer.getCoeffAlpha())
+ && RainbowUtil.equals(coeff_beta, otherLayer.getCoeffBeta())
+ && RainbowUtil.equals(coeff_gamma, otherLayer.getCoeffGamma())
+ && RainbowUtil.equals(coeff_eta, otherLayer.getCoeffEta());
+ }
+
+ public int hashCode()
+ {
+ int hash = vi;
+ hash = hash * 37 + viNext;
+ hash = hash * 37 + oi;
+ hash = hash * 37 + Arrays.hashCode(coeff_alpha);
+ hash = hash * 37 + Arrays.hashCode(coeff_beta);
+ hash = hash * 37 + Arrays.hashCode(coeff_gamma);
+ hash = hash * 37 + Arrays.hashCode(coeff_eta);
+
+ return hash;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyGenerationParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyGenerationParameters.java
new file mode 100644
index 00000000..2cbc5b62
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyGenerationParameters.java
@@ -0,0 +1,26 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.KeyGenerationParameters;
+
+public class RainbowKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+ private RainbowParameters params;
+
+ public RainbowKeyGenerationParameters(
+ SecureRandom random,
+ RainbowParameters params)
+ {
+ // TODO: key size?
+ super(random, params.getVi()[params.getVi().length - 1] - params.getVi()[0]);
+ this.params = params;
+ }
+
+ public RainbowParameters getParameters()
+ {
+ return params;
+ }
+}
+
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java
new file mode 100644
index 00000000..1115d649
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java
@@ -0,0 +1,413 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.spongycastle.crypto.KeyGenerationParameters;
+import org.spongycastle.pqc.crypto.rainbow.util.ComputeInField;
+import org.spongycastle.pqc.crypto.rainbow.util.GF2Field;
+
+/**
+ * This class implements AsymmetricCipherKeyPairGenerator. It is used
+ * as a generator for the private and public key of the Rainbow Signature
+ * Scheme.
+ * <p>
+ * Detailed information about the key generation is to be found in the paper of
+ * Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable Polynomial
+ * Signature Scheme. ACNS 2005: 164-175 (http://dx.doi.org/10.1007/11496137_12)
+ */
+public class RainbowKeyPairGenerator
+ implements AsymmetricCipherKeyPairGenerator
+{
+ private boolean initialized = false;
+ private SecureRandom sr;
+ private RainbowKeyGenerationParameters rainbowParams;
+
+ /* linear affine map L1: */
+ private short[][] A1; // matrix of the lin. affine map L1(n-v1 x n-v1 matrix)
+ private short[][] A1inv; // inverted A1
+ private short[] b1; // translation element of the lin.affine map L1
+
+ /* linear affine map L2: */
+ private short[][] A2; // matrix of the lin. affine map (n x n matrix)
+ private short[][] A2inv; // inverted A2
+ private short[] b2; // translation elemt of the lin.affine map L2
+
+ /* components of F: */
+ private int numOfLayers; // u (number of sets S)
+ private Layer layers[]; // layers of polynomials of F
+ private int[] vi; // set of vinegar vars per layer.
+
+ /* components of Public Key */
+ private short[][] pub_quadratic; // quadratic(mixed) coefficients
+ private short[][] pub_singular; // singular coefficients
+ private short[] pub_scalar; // scalars
+
+ // TODO
+
+ /**
+ * The standard constructor tries to generate the Rainbow algorithm identifier
+ * with the corresponding OID.
+ */
+ public RainbowKeyPairGenerator()
+ {
+ }
+
+
+ /**
+ * This function generates a Rainbow key pair.
+ *
+ * @return the generated key pair
+ */
+ public AsymmetricCipherKeyPair genKeyPair()
+ {
+ RainbowPrivateKeyParameters privKey;
+ RainbowPublicKeyParameters pubKey;
+
+ if (!initialized)
+ {
+ initializeDefault();
+ }
+
+ /* choose all coefficients at random */
+ keygen();
+
+ /* now marshall them to PrivateKey */
+ privKey = new RainbowPrivateKeyParameters(A1inv, b1, A2inv, b2, vi, layers);
+
+
+ /* marshall to PublicKey */
+ pubKey = new RainbowPublicKeyParameters(vi[vi.length - 1] - vi[0], pub_quadratic, pub_singular, pub_scalar);
+
+ return new AsymmetricCipherKeyPair(pubKey, privKey);
+ }
+
+ // TODO
+ public void initialize(
+ KeyGenerationParameters param)
+ {
+ this.rainbowParams = (RainbowKeyGenerationParameters)param;
+
+ // set source of randomness
+ this.sr = new SecureRandom();
+
+ // unmarshalling:
+ this.vi = this.rainbowParams.getParameters().getVi();
+ this.numOfLayers = this.rainbowParams.getParameters().getNumOfLayers();
+
+ this.initialized = true;
+ }
+
+ private void initializeDefault()
+ {
+ RainbowKeyGenerationParameters rbKGParams = new RainbowKeyGenerationParameters(new SecureRandom(), new RainbowParameters());
+ initialize(rbKGParams);
+ }
+
+ /**
+ * This function calls the functions for the random generation of the coefficients
+ * and the matrices needed for the private key and the method for computing the public key.
+ */
+ private void keygen()
+ {
+ generateL1();
+ generateL2();
+ generateF();
+ computePublicKey();
+ }
+
+ /**
+ * This function generates the invertible affine linear map L1 = A1*x + b1
+ * <p/>
+ * The translation part b1, is stored in a separate array. The inverse of
+ * the matrix-part of L1 A1inv is also computed here.
+ * <p/>
+ * This linear map hides the output of the map F. It is on k^(n-v1).
+ */
+ private void generateL1()
+ {
+
+ // dimension = n-v1 = vi[last] - vi[first]
+ int dim = vi[vi.length - 1] - vi[0];
+ this.A1 = new short[dim][dim];
+ this.A1inv = null;
+ ComputeInField c = new ComputeInField();
+
+ /* generation of A1 at random */
+ while (A1inv == null)
+ {
+ for (int i = 0; i < dim; i++)
+ {
+ for (int j = 0; j < dim; j++)
+ {
+ A1[i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ A1inv = c.inverse(A1);
+ }
+
+ /* generation of the translation vector at random */
+ b1 = new short[dim];
+ for (int i = 0; i < dim; i++)
+ {
+ b1[i] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+
+ /**
+ * This function generates the invertible affine linear map L2 = A2*x + b2
+ * <p/>
+ * The translation part b2, is stored in a separate array. The inverse of
+ * the matrix-part of L2 A2inv is also computed here.
+ * <p/>
+ * This linear map hides the output of the map F. It is on k^(n).
+ */
+ private void generateL2()
+ {
+
+ // dimension = n = vi[last]
+ int dim = vi[vi.length - 1];
+ this.A2 = new short[dim][dim];
+ this.A2inv = null;
+ ComputeInField c = new ComputeInField();
+
+ /* generation of A2 at random */
+ while (this.A2inv == null)
+ {
+ for (int i = 0; i < dim; i++)
+ {
+ for (int j = 0; j < dim; j++)
+ { // one col extra for b
+ A2[i][j] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+ }
+ this.A2inv = c.inverse(A2);
+ }
+ /* generation of the translation vector at random */
+ b2 = new short[dim];
+ for (int i = 0; i < dim; i++)
+ {
+ b2[i] = (short)(sr.nextInt() & GF2Field.MASK);
+ }
+
+ }
+
+ /**
+ * This function generates the private map F, which consists of u-1 layers.
+ * Each layer consists of oi polynomials where oi = vi[i+1]-vi[i].
+ * <p/>
+ * The methods for the generation of the coefficients of these polynomials
+ * are called here.
+ */
+ private void generateF()
+ {
+
+ this.layers = new Layer[this.numOfLayers];
+ for (int i = 0; i < this.numOfLayers; i++)
+ {
+ layers[i] = new Layer(this.vi[i], this.vi[i + 1], sr);
+ }
+ }
+
+ /**
+ * This function computes the public key from the private key.
+ * <p/>
+ * The composition of F with L2 is computed, followed by applying L1 to the
+ * composition's result. The singular and scalar values constitute to the
+ * public key as is, the quadratic terms are compacted in
+ * <tt>compactPublicKey()</tt>
+ */
+ private void computePublicKey()
+ {
+
+ ComputeInField c = new ComputeInField();
+ int rows = this.vi[this.vi.length - 1] - this.vi[0];
+ int vars = this.vi[this.vi.length - 1];
+ // Fpub
+ short[][][] coeff_quadratic_3dim = new short[rows][vars][vars];
+ this.pub_singular = new short[rows][vars];
+ this.pub_scalar = new short[rows];
+
+ // Coefficients of layers of Private Key F
+ short[][][] coeff_alpha;
+ short[][][] coeff_beta;
+ short[][] coeff_gamma;
+ short[] coeff_eta;
+
+ // Needed for counters;
+ int oils = 0;
+ int vins = 0;
+ int crnt_row = 0; // current row (polynomial)
+
+ short vect_tmp[] = new short[vars]; // vector tmp;
+ short sclr_tmp = 0;
+
+ // Composition of F and L2: Insert L2 = A2*x+b2 in F
+ for (int l = 0; l < this.layers.length; l++)
+ {
+ // get coefficients of current layer
+ coeff_alpha = this.layers[l].getCoeffAlpha();
+ coeff_beta = this.layers[l].getCoeffBeta();
+ coeff_gamma = this.layers[l].getCoeffGamma();
+ coeff_eta = this.layers[l].getCoeffEta();
+ oils = coeff_alpha[0].length;// this.layers[l].getOi();
+ vins = coeff_beta[0].length;// this.layers[l].getVi();
+ // compute polynomials of layer
+ for (int p = 0; p < oils; p++)
+ {
+ // multiply alphas
+ for (int x1 = 0; x1 < oils; x1++)
+ {
+ for (int x2 = 0; x2 < vins; x2++)
+ {
+ // multiply polynomial1 with polynomial2
+ vect_tmp = c.multVect(coeff_alpha[p][x1][x2],
+ this.A2[x1 + vins]);
+ coeff_quadratic_3dim[crnt_row + p] = c.addSquareMatrix(
+ coeff_quadratic_3dim[crnt_row + p], c
+ .multVects(vect_tmp, this.A2[x2]));
+ // mul poly1 with scalar2
+ vect_tmp = c.multVect(this.b2[x2], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with poly2
+ vect_tmp = c.multVect(coeff_alpha[p][x1][x2],
+ this.A2[x2]);
+ vect_tmp = c.multVect(b2[x1 + vins], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with scalar2
+ sclr_tmp = GF2Field.multElem(coeff_alpha[p][x1][x2],
+ this.b2[x1 + vins]);
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], GF2Field
+ .multElem(sclr_tmp, this.b2[x2]));
+ }
+ }
+ // multiply betas
+ for (int x1 = 0; x1 < vins; x1++)
+ {
+ for (int x2 = 0; x2 < vins; x2++)
+ {
+ // multiply polynomial1 with polynomial2
+ vect_tmp = c.multVect(coeff_beta[p][x1][x2],
+ this.A2[x1]);
+ coeff_quadratic_3dim[crnt_row + p] = c.addSquareMatrix(
+ coeff_quadratic_3dim[crnt_row + p], c
+ .multVects(vect_tmp, this.A2[x2]));
+ // mul poly1 with scalar2
+ vect_tmp = c.multVect(this.b2[x2], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with poly2
+ vect_tmp = c.multVect(coeff_beta[p][x1][x2],
+ this.A2[x2]);
+ vect_tmp = c.multVect(this.b2[x1], vect_tmp);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar1 with scalar2
+ sclr_tmp = GF2Field.multElem(coeff_beta[p][x1][x2],
+ this.b2[x1]);
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], GF2Field
+ .multElem(sclr_tmp, this.b2[x2]));
+ }
+ }
+ // multiply gammas
+ for (int n = 0; n < vins + oils; n++)
+ {
+ // mul poly with scalar
+ vect_tmp = c.multVect(coeff_gamma[p][n], this.A2[n]);
+ this.pub_singular[crnt_row + p] = c.addVect(vect_tmp,
+ this.pub_singular[crnt_row + p]);
+ // mul scalar with scalar
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], GF2Field.multElem(
+ coeff_gamma[p][n], this.b2[n]));
+ }
+ // add eta
+ this.pub_scalar[crnt_row + p] = GF2Field.addElem(
+ this.pub_scalar[crnt_row + p], coeff_eta[p]);
+ }
+ crnt_row = crnt_row + oils;
+ }
+
+ // Apply L1 = A1*x+b1 to composition of F and L2
+ {
+ // temporary coefficient arrays
+ short[][][] tmp_c_quad = new short[rows][vars][vars];
+ short[][] tmp_c_sing = new short[rows][vars];
+ short[] tmp_c_scal = new short[rows];
+ for (int r = 0; r < rows; r++)
+ {
+ for (int q = 0; q < A1.length; q++)
+ {
+ tmp_c_quad[r] = c.addSquareMatrix(tmp_c_quad[r], c
+ .multMatrix(A1[r][q], coeff_quadratic_3dim[q]));
+ tmp_c_sing[r] = c.addVect(tmp_c_sing[r], c.multVect(
+ A1[r][q], this.pub_singular[q]));
+ tmp_c_scal[r] = GF2Field.addElem(tmp_c_scal[r], GF2Field
+ .multElem(A1[r][q], this.pub_scalar[q]));
+ }
+ tmp_c_scal[r] = GF2Field.addElem(tmp_c_scal[r], b1[r]);
+ }
+ // set public key
+ coeff_quadratic_3dim = tmp_c_quad;
+ this.pub_singular = tmp_c_sing;
+ this.pub_scalar = tmp_c_scal;
+ }
+ compactPublicKey(coeff_quadratic_3dim);
+ }
+
+ /**
+ * The quadratic (or mixed) terms of the public key are compacted from a n x
+ * n matrix per polynomial to an upper diagonal matrix stored in one integer
+ * array of n (n + 1) / 2 elements per polynomial. The ordering of elements
+ * is lexicographic and the result is updating <tt>this.pub_quadratic</tt>,
+ * which stores the quadratic elements of the public key.
+ *
+ * @param coeff_quadratic_to_compact 3-dimensional array containing a n x n Matrix for each of the
+ * n - v1 polynomials
+ */
+ private void compactPublicKey(short[][][] coeff_quadratic_to_compact)
+ {
+ int polynomials = coeff_quadratic_to_compact.length;
+ int n = coeff_quadratic_to_compact[0].length;
+ int entries = n * (n + 1) / 2;// the small gauss
+ this.pub_quadratic = new short[polynomials][entries];
+ int offset = 0;
+
+ for (int p = 0; p < polynomials; p++)
+ {
+ offset = 0;
+ for (int x = 0; x < n; x++)
+ {
+ for (int y = x; y < n; y++)
+ {
+ if (y == x)
+ {
+ this.pub_quadratic[p][offset] = coeff_quadratic_to_compact[p][x][y];
+ }
+ else
+ {
+ this.pub_quadratic[p][offset] = GF2Field.addElem(
+ coeff_quadratic_to_compact[p][x][y],
+ coeff_quadratic_to_compact[p][y][x]);
+ }
+ offset++;
+ }
+ }
+ }
+ }
+
+ public void init(KeyGenerationParameters param)
+ {
+ this.initialize(param);
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair()
+ {
+ return genKeyPair();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyParameters.java
new file mode 100644
index 00000000..075afda4
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowKeyParameters.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+
+public class RainbowKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private int docLength;
+
+ public RainbowKeyParameters(
+ boolean isPrivate,
+ int docLength)
+ {
+ super(isPrivate);
+ this.docLength = docLength;
+ }
+
+ /**
+ * @return the docLength
+ */
+ public int getDocLength()
+ {
+ return this.docLength;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowParameters.java
new file mode 100644
index 00000000..edc0d023
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowParameters.java
@@ -0,0 +1,111 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import org.spongycastle.crypto.CipherParameters;
+
+public class RainbowParameters
+ implements CipherParameters
+{
+
+ /**
+ * DEFAULT PARAMS
+ */
+ /*
+ * Vi = vinegars per layer whereas n is vu (vu = 33 = n) such that
+ *
+ * v1 = 6; o1 = 12-6 = 6
+ *
+ * v2 = 12; o2 = 17-12 = 5
+ *
+ * v3 = 17; o3 = 22-17 = 5
+ *
+ * v4 = 22; o4 = 33-22 = 11
+ *
+ * v5 = 33; (o5 = 0)
+ */
+ private final int[] DEFAULT_VI = {6, 12, 17, 22, 33};
+
+ private int[] vi;// set of vinegar vars per layer.
+
+ /**
+ * Default Constructor The elements of the array containing the number of
+ * Vinegar variables in each layer are set to the default values here.
+ */
+ public RainbowParameters()
+ {
+ this.vi = this.DEFAULT_VI;
+ }
+
+ /**
+ * Constructor with parameters
+ *
+ * @param vi The elements of the array containing the number of Vinegar
+ * variables per layer are set to the values of the input array.
+ */
+ public RainbowParameters(int[] vi)
+ {
+ this.vi = vi;
+ try
+ {
+ checkParams();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void checkParams()
+ throws Exception
+ {
+ if (vi == null)
+ {
+ throw new Exception("no layers defined.");
+ }
+ if (vi.length > 1)
+ {
+ for (int i = 0; i < vi.length - 1; i++)
+ {
+ if (vi[i] >= vi[i + 1])
+ {
+ throw new Exception(
+ "v[i] has to be smaller than v[i+1]");
+ }
+ }
+ }
+ else
+ {
+ throw new Exception(
+ "Rainbow needs at least 1 layer, such that v1 < v2.");
+ }
+ }
+
+ /**
+ * Getter for the number of layers
+ *
+ * @return the number of layers
+ */
+ public int getNumOfLayers()
+ {
+ return this.vi.length - 1;
+ }
+
+ /**
+ * Getter for the number of all the polynomials in Rainbow
+ *
+ * @return the number of the polynomials
+ */
+ public int getDocLength()
+ {
+ return vi[vi.length - 1] - vi[0];
+ }
+
+ /**
+ * Getter for the array containing the number of Vinegar-variables per layer
+ *
+ * @return the numbers of vinegars per layer
+ */
+ public int[] getVi()
+ {
+ return this.vi;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPrivateKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPrivateKeyParameters.java
new file mode 100644
index 00000000..5d501a53
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPrivateKeyParameters.java
@@ -0,0 +1,117 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+public class RainbowPrivateKeyParameters
+ extends RainbowKeyParameters
+{
+ /**
+ * Constructor
+ *
+ * @param A1inv the inverse of A1(the matrix part of the affine linear map L1)
+ * (n-v1 x n-v1 matrix)
+ * @param b1 translation vector, part of the linear affine map L1
+ * @param A2inv the inverse of A2(the matrix part of the affine linear map L2)
+ * (n x n matrix)
+ * @param b2 translation vector, part of the linear affine map L2
+ * @param vi the number of Vinegar-variables per layer
+ * @param layers the polynomials with their coefficients of private map F
+ */
+ public RainbowPrivateKeyParameters(short[][] A1inv, short[] b1,
+ short[][] A2inv, short[] b2, int[] vi, Layer[] layers)
+ {
+ super(true, vi[vi.length - 1] - vi[0]);
+
+ this.A1inv = A1inv;
+ this.b1 = b1;
+ this.A2inv = A2inv;
+ this.b2 = b2;
+ this.vi = vi;
+ this.layers = layers;
+ }
+
+ /*
+ * invertible affine linear map L1
+ */
+ // the inverse of A1, (n-v1 x n-v1 matrix)
+ private short[][] A1inv;
+
+ // translation vector of L1
+ private short[] b1;
+
+ /*
+ * invertible affine linear map L2
+ */
+ // the inverse of A2, (n x n matrix)
+ private short[][] A2inv;
+
+ // translation vector of L2
+ private short[] b2;
+
+ /*
+ * components of F
+ */
+ // the number of Vinegar-variables per layer.
+ private int[] vi;
+
+ // contains the polynomials with their coefficients of private map F
+ private Layer[] layers;
+
+ /**
+ * Getter for the translation part of the private quadratic map L1.
+ *
+ * @return b1 the translation part of L1
+ */
+ public short[] getB1()
+ {
+ return this.b1;
+ }
+
+ /**
+ * Getter for the inverse matrix of A1.
+ *
+ * @return the A1inv inverse
+ */
+ public short[][] getInvA1()
+ {
+ return this.A1inv;
+ }
+
+ /**
+ * Getter for the translation part of the private quadratic map L2.
+ *
+ * @return b2 the translation part of L2
+ */
+ public short[] getB2()
+ {
+ return this.b2;
+ }
+
+ /**
+ * Getter for the inverse matrix of A2
+ *
+ * @return the A2inv
+ */
+ public short[][] getInvA2()
+ {
+ return this.A2inv;
+ }
+
+ /**
+ * Returns the layers contained in the private key
+ *
+ * @return layers
+ */
+ public Layer[] getLayers()
+ {
+ return this.layers;
+ }
+
+ /**
+ * /** Returns the array of vi-s
+ *
+ * @return the vi
+ */
+ public int[] getVi()
+ {
+ return vi;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPublicKeyParameters.java b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPublicKeyParameters.java
new file mode 100644
index 00000000..3a5314e1
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowPublicKeyParameters.java
@@ -0,0 +1,53 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+public class RainbowPublicKeyParameters
+ extends RainbowKeyParameters
+{
+ private short[][] coeffquadratic;
+ private short[][] coeffsingular;
+ private short[] coeffscalar;
+
+ /**
+ * Constructor
+ *
+ * @param docLength
+ * @param coeffQuadratic
+ * @param coeffSingular
+ * @param coeffScalar
+ */
+ public RainbowPublicKeyParameters(int docLength,
+ short[][] coeffQuadratic, short[][] coeffSingular,
+ short[] coeffScalar)
+ {
+ super(false, docLength);
+
+ this.coeffquadratic = coeffQuadratic;
+ this.coeffsingular = coeffSingular;
+ this.coeffscalar = coeffScalar;
+
+ }
+
+ /**
+ * @return the coeffquadratic
+ */
+ public short[][] getCoeffQuadratic()
+ {
+ return coeffquadratic;
+ }
+
+ /**
+ * @return the coeffsingular
+ */
+ public short[][] getCoeffSingular()
+ {
+ return coeffsingular;
+ }
+
+ /**
+ * @return the coeffscalar
+ */
+ public short[] getCoeffScalar()
+ {
+ return coeffscalar;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowSigner.java b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowSigner.java
new file mode 100644
index 00000000..9033b37e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/RainbowSigner.java
@@ -0,0 +1,301 @@
+package org.spongycastle.pqc.crypto.rainbow;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.pqc.crypto.MessageSigner;
+import org.spongycastle.pqc.crypto.rainbow.util.ComputeInField;
+import org.spongycastle.pqc.crypto.rainbow.util.GF2Field;
+
+/**
+ * It implements the sign and verify functions for the Rainbow Signature Scheme.
+ * Here the message, which has to be signed, is updated. The use of
+ * different hash functions is possible.
+ * <p>
+ * Detailed information about the signature and the verify-method is to be found
+ * in the paper of Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable
+ * Polynomial Signature Scheme. ACNS 2005: 164-175
+ * (http://dx.doi.org/10.1007/11496137_12)
+ */
+public class RainbowSigner
+ implements MessageSigner
+{
+ // Source of randomness
+ private SecureRandom random;
+
+ // The length of a document that can be signed with the privKey
+ int signableDocumentLength;
+
+ // Container for the oil and vinegar variables of all the layers
+ private short[] x;
+
+ private ComputeInField cf = new ComputeInField();
+
+ RainbowKeyParameters key;
+
+ public void init(boolean forSigning,
+ CipherParameters param)
+ {
+ if (forSigning)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ this.key = (RainbowPrivateKeyParameters)rParam.getParameters();
+
+ }
+ else
+ {
+
+ this.random = new SecureRandom();
+ this.key = (RainbowPrivateKeyParameters)param;
+ }
+ }
+ else
+ {
+ this.key = (RainbowPublicKeyParameters)param;
+ }
+
+ this.signableDocumentLength = this.key.getDocLength();
+ }
+
+
+ /**
+ * initial operations before solving the Linear equation system.
+ *
+ * @param layer the current layer for which a LES is to be solved.
+ * @param msg the message that should be signed.
+ * @return Y_ the modified document needed for solving LES, (Y_ =
+ * A1^{-1}*(Y-b1)) linear map L1 = A1 x + b1.
+ */
+ private short[] initSign(Layer[] layer, short[] msg)
+ {
+
+ /* preparation: Modifies the document with the inverse of L1 */
+ // tmp = Y - b1:
+ short[] tmpVec = new short[msg.length];
+
+ tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB1(), msg);
+
+ // Y_ = A1^{-1} * (Y - b1) :
+ short[] Y_ = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA1(), tmpVec);
+
+ /* generates the vinegar vars of the first layer at random */
+ for (int i = 0; i < layer[0].getVi(); i++)
+ {
+ x[i] = (short)random.nextInt();
+ x[i] = (short)(x[i] & GF2Field.MASK);
+ }
+
+ return Y_;
+ }
+
+ /**
+ * This function signs the message that has been updated, making use of the
+ * private key.
+ * <p>
+ * For computing the signature, L1 and L2 are needed, as well as LES should
+ * be solved for each layer in order to find the Oil-variables in the layer.
+ * <p>
+ * The Vinegar-variables of the first layer are random generated.
+ *
+ * @param message the message
+ * @return the signature of the message.
+ */
+ public byte[] generateSignature(byte[] message)
+ {
+ Layer[] layer = ((RainbowPrivateKeyParameters)this.key).getLayers();
+ int numberOfLayers = layer.length;
+
+ x = new short[((RainbowPrivateKeyParameters)this.key).getInvA2().length]; // all variables
+
+ short[] Y_; // modified document
+ short[] y_i; // part of Y_ each polynomial
+ int counter; // index of the current part of the doc
+
+ short[] solVec; // the solution of LES pro layer
+ short[] tmpVec;
+
+ // the signature as an array of shorts:
+ short[] signature;
+ // the signature as a byte-array:
+ byte[] S = new byte[layer[numberOfLayers - 1].getViNext()];
+
+ short[] msgHashVals = makeMessageRepresentative(message);
+
+ // shows if an exception is caught
+ boolean ok;
+ do
+ {
+ ok = true;
+ counter = 0;
+ try
+ {
+ Y_ = initSign(layer, msgHashVals);
+
+ for (int i = 0; i < numberOfLayers; i++)
+ {
+
+ y_i = new short[layer[i].getOi()];
+ solVec = new short[layer[i].getOi()]; // solution of LES
+
+ /* copy oi elements of Y_ into y_i */
+ for (int k = 0; k < layer[i].getOi(); k++)
+ {
+ y_i[k] = Y_[counter];
+ counter++; // current index of Y_
+ }
+
+ /*
+ * plug in the vars of the previous layer in order to get
+ * the vars of the current layer
+ */
+ solVec = cf.solveEquation(layer[i].plugInVinegars(x), y_i);
+
+ if (solVec == null)
+ { // LES is not solveable
+ throw new Exception("LES is not solveable!");
+ }
+
+ /* copy the new vars into the x-array */
+ for (int j = 0; j < solVec.length; j++)
+ {
+ x[layer[i].getVi() + j] = solVec[j];
+ }
+ }
+
+ /* apply the inverse of L2: (signature = A2^{-1}*(b2+x)) */
+ tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB2(), x);
+ signature = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA2(), tmpVec);
+
+ /* cast signature from short[] to byte[] */
+ for (int i = 0; i < S.length; i++)
+ {
+ S[i] = ((byte)signature[i]);
+ }
+ }
+ catch (Exception se)
+ {
+ // if one of the LESs was not solveable - sign again
+ ok = false;
+ }
+ }
+ while (!ok);
+ /* return the signature in bytes */
+ return S;
+ }
+
+ /**
+ * This function verifies the signature of the message that has been
+ * updated, with the aid of the public key.
+ *
+ * @param message the message
+ * @param signature the signature of the message
+ * @return true if the signature has been verified, false otherwise.
+ */
+ public boolean verifySignature(byte[] message, byte[] signature)
+ {
+ short[] sigInt = new short[signature.length];
+ short tmp;
+
+ for (int i = 0; i < signature.length; i++)
+ {
+ tmp = (short)signature[i];
+ tmp &= (short)0xff;
+ sigInt[i] = tmp;
+ }
+
+ short[] msgHashVal = makeMessageRepresentative(message);
+
+ // verify
+ short[] verificationResult = verifySignatureIntern(sigInt);
+
+ // compare
+ boolean verified = true;
+ if (msgHashVal.length != verificationResult.length)
+ {
+ return false;
+ }
+ for (int i = 0; i < msgHashVal.length; i++)
+ {
+ verified = verified && msgHashVal[i] == verificationResult[i];
+ }
+
+ return verified;
+ }
+
+ /**
+ * Signature verification using public key
+ *
+ * @param signature vector of dimension n
+ * @return document hash of length n - v1
+ */
+ private short[] verifySignatureIntern(short[] signature)
+ {
+
+ short[][] coeff_quadratic = ((RainbowPublicKeyParameters)this.key).getCoeffQuadratic();
+ short[][] coeff_singular = ((RainbowPublicKeyParameters)this.key).getCoeffSingular();
+ short[] coeff_scalar = ((RainbowPublicKeyParameters)this.key).getCoeffScalar();
+
+ short[] rslt = new short[coeff_quadratic.length];// n - v1
+ int n = coeff_singular[0].length;
+ int offset = 0; // array position
+ short tmp = 0; // for scalar
+
+ for (int p = 0; p < coeff_quadratic.length; p++)
+ { // no of polynomials
+ offset = 0;
+ for (int x = 0; x < n; x++)
+ {
+ // calculate quadratic terms
+ for (int y = x; y < n; y++)
+ {
+ tmp = GF2Field.multElem(coeff_quadratic[p][offset],
+ GF2Field.multElem(signature[x], signature[y]));
+ rslt[p] = GF2Field.addElem(rslt[p], tmp);
+ offset++;
+ }
+ // calculate singular terms
+ tmp = GF2Field.multElem(coeff_singular[p][x], signature[x]);
+ rslt[p] = GF2Field.addElem(rslt[p], tmp);
+ }
+ // add scalar
+ rslt[p] = GF2Field.addElem(rslt[p], coeff_scalar[p]);
+ }
+
+ return rslt;
+ }
+
+ /**
+ * This function creates the representative of the message which gets signed
+ * or verified.
+ *
+ * @param message the message
+ * @return message representative
+ */
+ private short[] makeMessageRepresentative(byte[] message)
+ {
+ // the message representative
+ short[] output = new short[this.signableDocumentLength];
+
+ int h = 0;
+ int i = 0;
+ do
+ {
+ if (i >= message.length)
+ {
+ break;
+ }
+ output[i] = (short)message[h];
+ output[i] &= (short)0xff;
+ h++;
+ i++;
+ }
+ while (i < output.length);
+
+ return output;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/ComputeInField.java b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/ComputeInField.java
new file mode 100644
index 00000000..1e4b6c0f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/ComputeInField.java
@@ -0,0 +1,490 @@
+package org.spongycastle.pqc.crypto.rainbow.util;
+
+/**
+ * This class offers different operations on matrices in field GF2^8.
+ * <p>
+ * Implemented are functions:
+ * - finding inverse of a matrix
+ * - solving linear equation systems using the Gauss-Elimination method
+ * - basic operations like matrix multiplication, addition and so on.
+ */
+
+public class ComputeInField
+{
+
+ private short[][] A; // used by solveEquation and inverse
+ short[] x;
+
+ /**
+ * Constructor with no parameters
+ */
+ public ComputeInField()
+ {
+ }
+
+
+ /**
+ * This function finds a solution of the equation Bx = b.
+ * Exception is thrown if the linear equation system has no solution
+ *
+ * @param B this matrix is the left part of the
+ * equation (B in the equation above)
+ * @param b the right part of the equation
+ * (b in the equation above)
+ * @return x the solution of the equation if it is solvable
+ * null otherwise
+ * @throws RuntimeException if LES is not solvable
+ */
+ public short[] solveEquation(short[][] B, short[] b)
+ {
+ try
+ {
+
+ if (B.length != b.length)
+ {
+ throw new RuntimeException(
+ "The equation system is not solvable");
+ }
+
+ /** initialize **/
+ // this matrix stores B and b from the equation B*x = b
+ // b is stored as the last column.
+ // B contains one column more than rows.
+ // In this column we store a free coefficient that should be later subtracted from b
+ A = new short[B.length][B.length + 1];
+ // stores the solution of the LES
+ x = new short[B.length];
+
+ /** copy B into the global matrix A **/
+ for (int i = 0; i < B.length; i++)
+ { // rows
+ for (int j = 0; j < B[0].length; j++)
+ { // cols
+ A[i][j] = B[i][j];
+ }
+ }
+
+ /** copy the vector b into the global A **/
+ //the free coefficient, stored in the last column of A( A[i][b.length]
+ // is to be subtracted from b
+ for (int i = 0; i < b.length; i++)
+ {
+ A[i][b.length] = GF2Field.addElem(b[i], A[i][b.length]);
+ }
+
+ /** call the methods for gauss elimination and backward substitution **/
+ computeZerosUnder(false); // obtain zeros under the diagonal
+ substitute();
+
+ return x;
+
+ }
+ catch (RuntimeException rte)
+ {
+ return null; // the LES is not solvable!
+ }
+ }
+
+ /**
+ * This function computes the inverse of a given matrix using the Gauss-
+ * Elimination method.
+ * <p>
+ * An exception is thrown if the matrix has no inverse
+ *
+ * @param coef the matrix which inverse matrix is needed
+ * @return inverse matrix of the input matrix.
+ * If the matrix is singular, null is returned.
+ * @throws RuntimeException if the given matrix is not invertible
+ */
+ public short[][] inverse(short[][] coef)
+ {
+ try
+ {
+ /** Initialization: **/
+ short factor;
+ short[][] inverse;
+ A = new short[coef.length][2 * coef.length];
+ if (coef.length != coef[0].length)
+ {
+ throw new RuntimeException(
+ "The matrix is not invertible. Please choose another one!");
+ }
+
+ /** prepare: Copy coef and the identity matrix into the global A. **/
+ for (int i = 0; i < coef.length; i++)
+ {
+ for (int j = 0; j < coef.length; j++)
+ {
+ //copy the input matrix coef into A
+ A[i][j] = coef[i][j];
+ }
+ // copy the identity matrix into A.
+ for (int j = coef.length; j < 2 * coef.length; j++)
+ {
+ A[i][j] = 0;
+ }
+ A[i][i + A.length] = 1;
+ }
+
+ /** Elimination operations to get the identity matrix from the left side of A. **/
+ // modify A to get 0s under the diagonal.
+ computeZerosUnder(true);
+
+ // modify A to get only 1s on the diagonal: A[i][j] =A[i][j]/A[i][i].
+ for (int i = 0; i < A.length; i++)
+ {
+ factor = GF2Field.invElem(A[i][i]);
+ for (int j = i; j < 2 * A.length; j++)
+ {
+ A[i][j] = GF2Field.multElem(A[i][j], factor);
+ }
+ }
+
+ //modify A to get only 0s above the diagonal.
+ computeZerosAbove();
+
+ // copy the result (the second half of A) in the matrix inverse.
+ inverse = new short[A.length][A.length];
+ for (int i = 0; i < A.length; i++)
+ {
+ for (int j = A.length; j < 2 * A.length; j++)
+ {
+ inverse[i][j - A.length] = A[i][j];
+ }
+ }
+ return inverse;
+
+ }
+ catch (RuntimeException rte)
+ {
+ // The matrix is not invertible! A new one should be generated!
+ return null;
+ }
+ }
+
+ /**
+ * Elimination under the diagonal.
+ * This function changes a matrix so that it contains only zeros under the
+ * diagonal(Ai,i) using only Gauss-Elimination operations.
+ * <p/>
+ * It is used in solveEquaton as well as in the function for
+ * finding an inverse of a matrix: {@link}inverse. Both of them use the
+ * Gauss-Elimination Method.
+ * <p/>
+ * The result is stored in the global matrix A
+ *
+ * @param usedForInverse This parameter shows if the function is used by the
+ * solveEquation-function or by the inverse-function and according
+ * to this creates matrices of different sizes.
+ * @throws RuntimeException in case a multiplicative inverse of 0 is needed
+ */
+ private void computeZerosUnder(boolean usedForInverse)
+ throws RuntimeException
+ {
+
+ //the number of columns in the global A where the tmp results are stored
+ int length;
+ short tmp = 0;
+
+ //the function is used in inverse() - A should have 2 times more columns than rows
+ if (usedForInverse)
+ {
+ length = 2 * A.length;
+ }
+ //the function is used in solveEquation - A has 1 column more than rows
+ else
+ {
+ length = A.length + 1;
+ }
+
+ //elimination operations to modify A so that that it contains only 0s under the diagonal
+ for (int k = 0; k < A.length - 1; k++)
+ { // the fixed row
+ for (int i = k + 1; i < A.length; i++)
+ { // rows
+ short factor1 = A[i][k];
+ short factor2 = GF2Field.invElem(A[k][k]);
+
+ //The element which multiplicative inverse is needed, is 0
+ //in this case is the input matrix not invertible
+ if (factor2 == 0)
+ {
+ throw new RuntimeException("Matrix not invertible! We have to choose another one!");
+ }
+
+ for (int j = k; j < length; j++)
+ {// columns
+ // tmp=A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(A[k][j], factor2);
+ // tmp = A[i,k] * A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(factor1, tmp);
+ // A[i,j]=A[i,j]-A[i,k]/A[k,k]*A[k,j];
+ A[i][j] = GF2Field.addElem(A[i][j], tmp);
+ }
+ }
+ }
+ }
+
+ /**
+ * Elimination above the diagonal.
+ * This function changes a matrix so that it contains only zeros above the
+ * diagonal(Ai,i) using only Gauss-Elimination operations.
+ * <p/>
+ * It is used in the inverse-function
+ * The result is stored in the global matrix A
+ *
+ * @throws RuntimeException in case a multiplicative inverse of 0 is needed
+ */
+ private void computeZerosAbove()
+ throws RuntimeException
+ {
+ short tmp = 0;
+ for (int k = A.length - 1; k > 0; k--)
+ { // the fixed row
+ for (int i = k - 1; i >= 0; i--)
+ { // rows
+ short factor1 = A[i][k];
+ short factor2 = GF2Field.invElem(A[k][k]);
+ if (factor2 == 0)
+ {
+ throw new RuntimeException("The matrix is not invertible");
+ }
+ for (int j = k; j < 2 * A.length; j++)
+ { // columns
+ // tmp = A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(A[k][j], factor2);
+ // tmp = A[i,k] * A[k,j] / A[k,k]
+ tmp = GF2Field.multElem(factor1, tmp);
+ // A[i,j] = A[i,j] - A[i,k] / A[k,k] * A[k,j];
+ A[i][j] = GF2Field.addElem(A[i][j], tmp);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * This function uses backward substitution to find x
+ * of the linear equation system (LES) B*x = b,
+ * where A a triangle-matrix is (contains only zeros under the diagonal)
+ * and b is a vector
+ * <p/>
+ * If the multiplicative inverse of 0 is needed, an exception is thrown.
+ * In this case is the LES not solvable
+ *
+ * @throws RuntimeException in case a multiplicative inverse of 0 is needed
+ */
+ private void substitute()
+ throws RuntimeException
+ {
+
+ // for the temporary results of the operations in field
+ short tmp, temp;
+
+ temp = GF2Field.invElem(A[A.length - 1][A.length - 1]);
+ if (temp == 0)
+ {
+ throw new RuntimeException("The equation system is not solvable");
+ }
+
+ /** backward substitution **/
+ x[A.length - 1] = GF2Field.multElem(A[A.length - 1][A.length], temp);
+ for (int i = A.length - 2; i >= 0; i--)
+ {
+ tmp = A[i][A.length];
+ for (int j = A.length - 1; j > i; j--)
+ {
+ temp = GF2Field.multElem(A[i][j], x[j]);
+ tmp = GF2Field.addElem(tmp, temp);
+ }
+
+ temp = GF2Field.invElem(A[i][i]);
+ if (temp == 0)
+ {
+ throw new RuntimeException("Not solvable equation system");
+ }
+ x[i] = GF2Field.multElem(tmp, temp);
+ }
+ }
+
+
+ /**
+ * This function multiplies two given matrices.
+ * If the given matrices cannot be multiplied due
+ * to different sizes, an exception is thrown.
+ *
+ * @param M1 -the 1st matrix
+ * @param M2 -the 2nd matrix
+ * @return A = M1*M2
+ * @throws RuntimeException in case the given matrices cannot be multiplied
+ * due to different dimensions.
+ */
+ public short[][] multiplyMatrix(short[][] M1, short[][] M2)
+ throws RuntimeException
+ {
+
+ if (M1[0].length != M2.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short tmp = 0;
+ A = new short[M1.length][M2[0].length];
+ for (int i = 0; i < M1.length; i++)
+ {
+ for (int j = 0; j < M2.length; j++)
+ {
+ for (int k = 0; k < M2[0].length; k++)
+ {
+ tmp = GF2Field.multElem(M1[i][j], M2[j][k]);
+ A[i][k] = GF2Field.addElem(A[i][k], tmp);
+ }
+ }
+ }
+ return A;
+ }
+
+ /**
+ * This function multiplies a given matrix with a one-dimensional array.
+ * <p>
+ * An exception is thrown, if the number of columns in the matrix and
+ * the number of rows in the one-dim. array differ.
+ *
+ * @param M1 the matrix to be multiplied
+ * @param m the one-dimensional array to be multiplied
+ * @return M1*m
+ * @throws RuntimeException in case of dimension inconsistency
+ */
+ public short[] multiplyMatrix(short[][] M1, short[] m)
+ throws RuntimeException
+ {
+ if (M1[0].length != m.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short tmp = 0;
+ short[] B = new short[M1.length];
+ for (int i = 0; i < M1.length; i++)
+ {
+ for (int j = 0; j < m.length; j++)
+ {
+ tmp = GF2Field.multElem(M1[i][j], m[j]);
+ B[i] = GF2Field.addElem(B[i], tmp);
+ }
+ }
+ return B;
+ }
+
+ /**
+ * Addition of two vectors
+ *
+ * @param vector1 first summand, always of dim n
+ * @param vector2 second summand, always of dim n
+ * @return addition of vector1 and vector2
+ * @throws RuntimeException in case the addition is impossible
+ * due to inconsistency in the dimensions
+ */
+ public short[] addVect(short[] vector1, short[] vector2)
+ {
+ if (vector1.length != vector2.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short rslt[] = new short[vector1.length];
+ for (int n = 0; n < rslt.length; n++)
+ {
+ rslt[n] = GF2Field.addElem(vector1[n], vector2[n]);
+ }
+ return rslt;
+ }
+
+ /**
+ * Multiplication of column vector with row vector
+ *
+ * @param vector1 column vector, always n x 1
+ * @param vector2 row vector, always 1 x n
+ * @return resulting n x n matrix of multiplication
+ * @throws RuntimeException in case the multiplication is impossible due to
+ * inconsistency in the dimensions
+ */
+ public short[][] multVects(short[] vector1, short[] vector2)
+ {
+ if (vector1.length != vector2.length)
+ {
+ throw new RuntimeException("Multiplication is not possible!");
+ }
+ short rslt[][] = new short[vector1.length][vector2.length];
+ for (int i = 0; i < vector1.length; i++)
+ {
+ for (int j = 0; j < vector2.length; j++)
+ {
+ rslt[i][j] = GF2Field.multElem(vector1[i], vector2[j]);
+ }
+ }
+ return rslt;
+ }
+
+ /**
+ * Multiplies vector with scalar
+ *
+ * @param scalar galois element to multiply vector with
+ * @param vector vector to be multiplied
+ * @return vector multiplied with scalar
+ */
+ public short[] multVect(short scalar, short[] vector)
+ {
+ short rslt[] = new short[vector.length];
+ for (int n = 0; n < rslt.length; n++)
+ {
+ rslt[n] = GF2Field.multElem(scalar, vector[n]);
+ }
+ return rslt;
+ }
+
+ /**
+ * Multiplies matrix with scalar
+ *
+ * @param scalar galois element to multiply matrix with
+ * @param matrix 2-dim n x n matrix to be multiplied
+ * @return matrix multiplied with scalar
+ */
+ public short[][] multMatrix(short scalar, short[][] matrix)
+ {
+ short[][] rslt = new short[matrix.length][matrix[0].length];
+ for (int i = 0; i < matrix.length; i++)
+ {
+ for (int j = 0; j < matrix[0].length; j++)
+ {
+ rslt[i][j] = GF2Field.multElem(scalar, matrix[i][j]);
+ }
+ }
+ return rslt;
+ }
+
+ /**
+ * Adds the n x n matrices matrix1 and matrix2
+ *
+ * @param matrix1 first summand
+ * @param matrix2 second summand
+ * @return addition of matrix1 and matrix2; both having the dimensions n x n
+ * @throws RuntimeException in case the addition is not possible because of
+ * different dimensions of the matrices
+ */
+ public short[][] addSquareMatrix(short[][] matrix1, short[][] matrix2)
+ {
+ if (matrix1.length != matrix2.length || matrix1[0].length != matrix2[0].length)
+ {
+ throw new RuntimeException("Addition is not possible!");
+ }
+
+ short[][] rslt = new short[matrix1.length][matrix1.length];//
+ for (int i = 0; i < matrix1.length; i++)
+ {
+ for (int j = 0; j < matrix2.length; j++)
+ {
+ rslt[i][j] = GF2Field.addElem(matrix1[i][j], matrix2[i][j]);
+ }
+ }
+ return rslt;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/GF2Field.java b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/GF2Field.java
new file mode 100644
index 00000000..675f0ec5
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/GF2Field.java
@@ -0,0 +1,139 @@
+package org.spongycastle.pqc.crypto.rainbow.util;
+
+/**
+ * This class provides the basic operations like addition, multiplication and
+ * finding the multiplicative inverse of an element in GF2^8.
+ * <p>
+ * The operations are implemented using the irreducible polynomial
+ * 1+x^2+x^3+x^6+x^8 ( 1 0100 1101 = 0x14d )
+ * <p>
+ * This class makes use of lookup tables(exps and logs) for implementing the
+ * operations in order to increase the efficiency of Rainbow.
+ */
+public class GF2Field
+{
+
+ public static final int MASK = 0xff;
+
+ /*
+ * this lookup table is needed for multiplication and computing the
+ * multiplicative inverse
+ */
+ static final short exps[] = {1, 2, 4, 8, 16, 32, 64, 128, 77, 154, 121, 242,
+ 169, 31, 62, 124, 248, 189, 55, 110, 220, 245, 167, 3, 6, 12, 24,
+ 48, 96, 192, 205, 215, 227, 139, 91, 182, 33, 66, 132, 69, 138, 89,
+ 178, 41, 82, 164, 5, 10, 20, 40, 80, 160, 13, 26, 52, 104, 208,
+ 237, 151, 99, 198, 193, 207, 211, 235, 155, 123, 246, 161, 15, 30,
+ 60, 120, 240, 173, 23, 46, 92, 184, 61, 122, 244, 165, 7, 14, 28,
+ 56, 112, 224, 141, 87, 174, 17, 34, 68, 136, 93, 186, 57, 114, 228,
+ 133, 71, 142, 81, 162, 9, 18, 36, 72, 144, 109, 218, 249, 191, 51,
+ 102, 204, 213, 231, 131, 75, 150, 97, 194, 201, 223, 243, 171, 27,
+ 54, 108, 216, 253, 183, 35, 70, 140, 85, 170, 25, 50, 100, 200,
+ 221, 247, 163, 11, 22, 44, 88, 176, 45, 90, 180, 37, 74, 148, 101,
+ 202, 217, 255, 179, 43, 86, 172, 21, 42, 84, 168, 29, 58, 116, 232,
+ 157, 119, 238, 145, 111, 222, 241, 175, 19, 38, 76, 152, 125, 250,
+ 185, 63, 126, 252, 181, 39, 78, 156, 117, 234, 153, 127, 254, 177,
+ 47, 94, 188, 53, 106, 212, 229, 135, 67, 134, 65, 130, 73, 146,
+ 105, 210, 233, 159, 115, 230, 129, 79, 158, 113, 226, 137, 95, 190,
+ 49, 98, 196, 197, 199, 195, 203, 219, 251, 187, 59, 118, 236, 149,
+ 103, 206, 209, 239, 147, 107, 214, 225, 143, 83, 166, 1};
+
+ /*
+ * this lookup table is needed for multiplication and computing the
+ * multiplicative inverse
+ */
+ static final short logs[] = {0, 0, 1, 23, 2, 46, 24, 83, 3, 106, 47, 147,
+ 25, 52, 84, 69, 4, 92, 107, 182, 48, 166, 148, 75, 26, 140, 53,
+ 129, 85, 170, 70, 13, 5, 36, 93, 135, 108, 155, 183, 193, 49, 43,
+ 167, 163, 149, 152, 76, 202, 27, 230, 141, 115, 54, 205, 130, 18,
+ 86, 98, 171, 240, 71, 79, 14, 189, 6, 212, 37, 210, 94, 39, 136,
+ 102, 109, 214, 156, 121, 184, 8, 194, 223, 50, 104, 44, 253, 168,
+ 138, 164, 90, 150, 41, 153, 34, 77, 96, 203, 228, 28, 123, 231, 59,
+ 142, 158, 116, 244, 55, 216, 206, 249, 131, 111, 19, 178, 87, 225,
+ 99, 220, 172, 196, 241, 175, 72, 10, 80, 66, 15, 186, 190, 199, 7,
+ 222, 213, 120, 38, 101, 211, 209, 95, 227, 40, 33, 137, 89, 103,
+ 252, 110, 177, 215, 248, 157, 243, 122, 58, 185, 198, 9, 65, 195,
+ 174, 224, 219, 51, 68, 105, 146, 45, 82, 254, 22, 169, 12, 139,
+ 128, 165, 74, 91, 181, 151, 201, 42, 162, 154, 192, 35, 134, 78,
+ 188, 97, 239, 204, 17, 229, 114, 29, 61, 124, 235, 232, 233, 60,
+ 234, 143, 125, 159, 236, 117, 30, 245, 62, 56, 246, 217, 63, 207,
+ 118, 250, 31, 132, 160, 112, 237, 20, 144, 179, 126, 88, 251, 226,
+ 32, 100, 208, 221, 119, 173, 218, 197, 64, 242, 57, 176, 247, 73,
+ 180, 11, 127, 81, 21, 67, 145, 16, 113, 187, 238, 191, 133, 200,
+ 161};
+
+ /**
+ * This function calculates the sum of two elements as an operation in GF2^8
+ *
+ * @param x the first element that is to be added
+ * @param y the second element that should be add
+ * @return the sum of the two elements x and y in GF2^8
+ */
+ public static short addElem(short x, short y)
+ {
+ return (short)(x ^ y);
+ }
+
+ /**
+ * This function computes the multiplicative inverse of a given element in
+ * GF2^8 The 0 has no multiplicative inverse and in this case 0 is returned.
+ *
+ * @param x the element which multiplicative inverse is to be computed
+ * @return the multiplicative inverse of the given element, in case it
+ * exists or 0, otherwise
+ */
+ public static short invElem(short x)
+ {
+ if (x == 0)
+ {
+ return 0;
+ }
+ return (exps[255 - logs[x]]);
+ }
+
+ /**
+ * This function multiplies two elements in GF2^8. If one of the two
+ * elements is 0, 0 is returned.
+ *
+ * @param x the first element to be multiplied.
+ * @param y the second element to be multiplied.
+ * @return the product of the two input elements in GF2^8.
+ */
+ public static short multElem(short x, short y)
+ {
+ if (x == 0 || y == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return (exps[(logs[x] + logs[y]) % 255]);
+ }
+ }
+
+ /**
+ * This function returns the values of exps-lookup table which correspond to
+ * the input
+ *
+ * @param x the index in the lookup table exps
+ * @return exps-value, corresponding to the input
+ */
+ public static short getExp(short x)
+ {
+ return exps[x];
+ }
+
+ /**
+ * This function returns the values of logs-lookup table which correspond to
+ * the input
+ *
+ * @param x the index in the lookup table logs
+ * @return logs-value, corresponding to the input
+ */
+ public static short getLog(short x)
+ {
+ return logs[x];
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/RainbowUtil.java b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/RainbowUtil.java
new file mode 100644
index 00000000..1e4f40f4
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/crypto/rainbow/util/RainbowUtil.java
@@ -0,0 +1,230 @@
+package org.spongycastle.pqc.crypto.rainbow.util;
+
+/**
+ * This class is needed for the conversions while encoding and decoding, as well as for
+ * comparison between arrays of some dimensions
+ */
+public class RainbowUtil
+{
+
+ /**
+ * This function converts an one-dimensional array of bytes into a
+ * one-dimensional array of int
+ *
+ * @param in the array to be converted
+ * @return out
+ * the one-dimensional int-array that corresponds the input
+ */
+ public static int[] convertArraytoInt(byte[] in)
+ {
+ int[] out = new int[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = in[i] & GF2Field.MASK;
+ }
+ return out;
+ }
+
+ /**
+ * This function converts an one-dimensional array of bytes into a
+ * one-dimensional array of type short
+ *
+ * @param in the array to be converted
+ * @return out
+ * one-dimensional short-array that corresponds the input
+ */
+ public static short[] convertArray(byte[] in)
+ {
+ short[] out = new short[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = (short)(in[i] & GF2Field.MASK);
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a matrix of bytes into a matrix of type short
+ *
+ * @param in the matrix to be converted
+ * @return out
+ * short-matrix that corresponds the input
+ */
+ public static short[][] convertArray(byte[][] in)
+ {
+ short[][] out = new short[in.length][in[0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ out[i][j] = (short)(in[i][j] & GF2Field.MASK);
+ }
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a 3-dimensional array of bytes into a 3-dimensional array of type short
+ *
+ * @param in the array to be converted
+ * @return out
+ * short-array that corresponds the input
+ */
+ public static short[][][] convertArray(byte[][][] in)
+ {
+ short[][][] out = new short[in.length][in[0].length][in[0][0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ for (int k = 0; k < in[0][0].length; k++)
+ {
+ out[i][j][k] = (short)(in[i][j][k] & GF2Field.MASK);
+ }
+ }
+ }
+ return out;
+ }
+
+ /**
+ * This function converts an array of type int into an array of type byte
+ *
+ * @param in the array to be converted
+ * @return out
+ * the byte-array that corresponds the input
+ */
+ public static byte[] convertIntArray(int[] in)
+ {
+ byte[] out = new byte[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = (byte)in[i];
+ }
+ return out;
+ }
+
+
+ /**
+ * This function converts an array of type short into an array of type byte
+ *
+ * @param in the array to be converted
+ * @return out
+ * the byte-array that corresponds the input
+ */
+ public static byte[] convertArray(short[] in)
+ {
+ byte[] out = new byte[in.length];
+ for (int i = 0; i < in.length; i++)
+ {
+ out[i] = (byte)in[i];
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a matrix of type short into a matrix of type byte
+ *
+ * @param in the matrix to be converted
+ * @return out
+ * the byte-matrix that corresponds the input
+ */
+ public static byte[][] convertArray(short[][] in)
+ {
+ byte[][] out = new byte[in.length][in[0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ out[i][j] = (byte)in[i][j];
+ }
+ }
+ return out;
+ }
+
+ /**
+ * This function converts a 3-dimensional array of type short into a 3-dimensional array of type byte
+ *
+ * @param in the array to be converted
+ * @return out
+ * the byte-array that corresponds the input
+ */
+ public static byte[][][] convertArray(short[][][] in)
+ {
+ byte[][][] out = new byte[in.length][in[0].length][in[0][0].length];
+ for (int i = 0; i < in.length; i++)
+ {
+ for (int j = 0; j < in[0].length; j++)
+ {
+ for (int k = 0; k < in[0][0].length; k++)
+ {
+ out[i][j][k] = (byte)in[i][j][k];
+ }
+ }
+ }
+ return out;
+ }
+
+ /**
+ * Compare two short arrays. No null checks are performed.
+ *
+ * @param left the first short array
+ * @param right the second short array
+ * @return the result of the comparison
+ */
+ public static boolean equals(short[] left, short[] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+ /**
+ * Compare two two-dimensional short arrays. No null checks are performed.
+ *
+ * @param left the first short array
+ * @param right the second short array
+ * @return the result of the comparison
+ */
+ public static boolean equals(short[][] left, short[][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= equals(left[i], right[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Compare two three-dimensional short arrays. No null checks are performed.
+ *
+ * @param left the first short array
+ * @param right the second short array
+ * @return the result of the comparison
+ */
+ public static boolean equals(short[][][] left, short[][][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= equals(left[i], right[i]);
+ }
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigEndianConversions.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigEndianConversions.java
new file mode 100644
index 00000000..4ed7f1c9
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigEndianConversions.java
@@ -0,0 +1,306 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+/**
+ * This is a utility class containing data type conversions using big-endian
+ * byte order.
+ *
+ * @see LittleEndianConversions
+ */
+public final class BigEndianConversions
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private BigEndianConversions()
+ {
+ // empty
+ }
+
+ /**
+ * Convert an integer to an octet string of length 4 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @param x the integer to convert
+ * @return the converted integer
+ */
+ public static byte[] I2OSP(int x)
+ {
+ byte[] result = new byte[4];
+ result[0] = (byte)(x >>> 24);
+ result[1] = (byte)(x >>> 16);
+ result[2] = (byte)(x >>> 8);
+ result[3] = (byte)x;
+ return result;
+ }
+
+ /**
+ * Convert an integer to an octet string according to IEEE 1363, Section
+ * 5.5.3. Length checking is performed.
+ *
+ * @param x the integer to convert
+ * @param oLen the desired length of the octet string
+ * @return an octet string of length <tt>oLen</tt> representing the
+ * integer <tt>x</tt>, or <tt>null</tt> if the integer is
+ * negative
+ * @throws ArithmeticException if <tt>x</tt> can't be encoded into <tt>oLen</tt>
+ * octets.
+ */
+ public static byte[] I2OSP(int x, int oLen)
+ throws ArithmeticException
+ {
+ if (x < 0)
+ {
+ return null;
+ }
+ int octL = IntegerFunctions.ceilLog256(x);
+ if (octL > oLen)
+ {
+ throw new ArithmeticException(
+ "Cannot encode given integer into specified number of octets.");
+ }
+ byte[] result = new byte[oLen];
+ for (int i = oLen - 1; i >= oLen - octL; i--)
+ {
+ result[i] = (byte)(x >>> (8 * (oLen - 1 - i)));
+ }
+ return result;
+ }
+
+ /**
+ * Convert an integer to an octet string of length 4 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @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(int input, byte[] output, int outOff)
+ {
+ output[outOff++] = (byte)(input >>> 24);
+ output[outOff++] = (byte)(input >>> 16);
+ output[outOff++] = (byte)(input >>> 8);
+ output[outOff] = (byte)input;
+ }
+
+ /**
+ * Convert an integer to an octet string of length 8 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @param input the integer to convert
+ * @return the converted integer
+ */
+ public static byte[] I2OSP(long input)
+ {
+ byte[] output = new byte[8];
+ output[0] = (byte)(input >>> 56);
+ output[1] = (byte)(input >>> 48);
+ output[2] = (byte)(input >>> 40);
+ output[3] = (byte)(input >>> 32);
+ output[4] = (byte)(input >>> 24);
+ output[5] = (byte)(input >>> 16);
+ output[6] = (byte)(input >>> 8);
+ output[7] = (byte)input;
+ return output;
+ }
+
+ /**
+ * Convert an integer to an octet string of length 8 according to IEEE 1363,
+ * Section 5.5.3.
+ *
+ * @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)
+ {
+ output[outOff++] = (byte)(input >>> 56);
+ output[outOff++] = (byte)(input >>> 48);
+ output[outOff++] = (byte)(input >>> 40);
+ output[outOff++] = (byte)(input >>> 32);
+ output[outOff++] = (byte)(input >>> 24);
+ output[outOff++] = (byte)(input >>> 16);
+ output[outOff++] = (byte)(input >>> 8);
+ output[outOff] = (byte)input;
+ }
+
+ /**
+ * Convert an integer to an octet string of the specified length according
+ * to IEEE 1363, Section 5.5.3. No length checking is performed (i.e., if
+ * the integer cannot be encoded into <tt>length</tt> octets, it is
+ * truncated).
+ *
+ * @param input the integer to convert
+ * @param output byte array holding the output
+ * @param outOff offset in output array where the result is stored
+ * @param length the length of the encoding
+ */
+ public static void I2OSP(int input, byte[] output, int outOff, int length)
+ {
+ for (int i = length - 1; i >= 0; i--)
+ {
+ output[outOff + i] = (byte)(input >>> (8 * (length - 1 - i)));
+ }
+ }
+
+ /**
+ * Convert an octet string to an integer according to IEEE 1363, Section
+ * 5.5.3.
+ *
+ * @param input the byte array holding the octet string
+ * @return an integer representing the octet string <tt>input</tt>, or
+ * <tt>0</tt> if the represented integer is negative or too large
+ * or the byte array is empty
+ * @throws ArithmeticException if the length of the given octet string is larger than 4.
+ */
+ public static int OS2IP(byte[] input)
+ {
+ if (input.length > 4)
+ {
+ throw new ArithmeticException("invalid input length");
+ }
+ if (input.length == 0)
+ {
+ return 0;
+ }
+ int result = 0;
+ for (int j = 0; j < input.length; j++)
+ {
+ result |= (input[j] & 0xff) << (8 * (input.length - 1 - j));
+ }
+ return result;
+ }
+
+ /**
+ * Convert a 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)
+ {
+ int result = (input[inOff++] & 0xff) << 24;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= input[inOff] & 0xff;
+ return result;
+ }
+
+ /**
+ * Convert an octet string to an integer according to IEEE 1363, Section
+ * 5.5.3.
+ *
+ * @param input the byte array holding the octet string
+ * @param inOff the offset in the input byte array where the octet string
+ * starts
+ * @param inLen the length of the encoded integer
+ * @return an integer representing the octet string <tt>bytes</tt>, or
+ * <tt>0</tt> if the represented integer is negative or too large
+ * or the byte array is empty
+ */
+ public static int OS2IP(byte[] input, int inOff, int inLen)
+ {
+ if ((input.length == 0) || input.length < inOff + inLen - 1)
+ {
+ return 0;
+ }
+ int result = 0;
+ for (int j = 0; j < inLen; j++)
+ {
+ result |= (input[inOff + j] & 0xff) << (8 * (inLen - j - 1));
+ }
+ 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)
+ {
+ long result = ((long)input[inOff++] & 0xff) << 56;
+ result |= ((long)input[inOff++] & 0xff) << 48;
+ result |= ((long)input[inOff++] & 0xff) << 40;
+ result |= ((long)input[inOff++] & 0xff) << 32;
+ result |= ((long)input[inOff++] & 0xff) << 24;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= input[inOff] & 0xff;
+ return result;
+ }
+
+ /**
+ * Convert an int array into a byte array.
+ *
+ * @param input the int array
+ * @return the converted array
+ */
+ public static byte[] toByteArray(final int[] input)
+ {
+ byte[] result = new byte[input.length << 2];
+ for (int i = 0; i < input.length; i++)
+ {
+ I2OSP(input[i], result, i << 2);
+ }
+ return result;
+ }
+
+ /**
+ * Convert an int array into a byte array of the specified length. No length
+ * checking is performed (i.e., if the last integer cannot be encoded into
+ * <tt>length % 4</tt> octets, it is truncated).
+ *
+ * @param input the int array
+ * @param length the length of the converted array
+ * @return the converted array
+ */
+ public static byte[] toByteArray(final int[] input, int length)
+ {
+ final int intLen = input.length;
+ byte[] result = new byte[length];
+ int index = 0;
+ for (int i = 0; i <= intLen - 2; i++, index += 4)
+ {
+ I2OSP(input[i], result, index);
+ }
+ I2OSP(input[intLen - 1], result, index, length - index);
+ return result;
+ }
+
+ /**
+ * Convert a byte array into an int array.
+ *
+ * @param input the byte array
+ * @return the converted array
+ */
+ public static int[] toIntArray(byte[] input)
+ {
+ final int intLen = (input.length + 3) / 4;
+ final 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/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigIntUtils.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigIntUtils.java
new file mode 100644
index 00000000..19734977
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/BigIntUtils.java
@@ -0,0 +1,138 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+
+/**
+ * FIXME: is this really necessary?!
+ */
+public final class BigIntUtils
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private BigIntUtils()
+ {
+ // empty
+ }
+
+ /**
+ * Checks if two BigInteger arrays contain the same entries
+ *
+ * @param a first BigInteger array
+ * @param b second BigInteger array
+ * @return true or false
+ */
+ public static boolean equals(BigInteger[] a, BigInteger[] b)
+ {
+ int flag = 0;
+
+ if (a.length != b.length)
+ {
+ return false;
+ }
+ for (int i = 0; i < a.length; i++)
+ {
+ // avoid branches here!
+ // problem: compareTo on BigIntegers is not
+ // guaranteed constant-time!
+ flag |= a[i].compareTo(b[i]);
+ }
+ return flag == 0;
+ }
+
+ /**
+ * Fill the given BigInteger array with the given value.
+ *
+ * @param array the array
+ * @param value the value
+ */
+ public static void fill(BigInteger[] array, BigInteger value)
+ {
+ for (int i = array.length - 1; i >= 0; i--)
+ {
+ array[i] = value;
+ }
+ }
+
+ /**
+ * Generates a subarray of a given BigInteger array.
+ *
+ * @param input -
+ * the input BigInteger 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 BigInteger[] subArray(BigInteger[] input, int start, int end)
+ {
+ BigInteger[] result = new BigInteger[end - start];
+ System.arraycopy(input, start, result, 0, end - start);
+ return result;
+ }
+
+ /**
+ * Converts a BigInteger array into an integer array
+ *
+ * @param input -
+ * the BigInteger array
+ * @return the integer array
+ */
+ public static int[] toIntArray(BigInteger[] input)
+ {
+ int[] result = new int[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = input[i].intValue();
+ }
+ return result;
+ }
+
+ /**
+ * Converts a BigInteger array into an integer array, reducing all
+ * BigIntegers mod q.
+ *
+ * @param q -
+ * the modulus
+ * @param input -
+ * the BigInteger array
+ * @return the integer array
+ */
+ public static int[] toIntArrayModQ(int q, BigInteger[] input)
+ {
+ BigInteger bq = BigInteger.valueOf(q);
+ int[] result = new int[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = input[i].mod(bq).intValue();
+ }
+ return result;
+ }
+
+ /**
+ * Return the value of <tt>big</tt> as a byte array. Although BigInteger
+ * has such a method, it uses an extra bit to indicate the sign of the
+ * number. For elliptic curve cryptography, the numbers usually are
+ * positive. Thus, this helper method returns a byte array of minimal
+ * length, ignoring the sign of the number.
+ *
+ * @param value the <tt>BigInteger</tt> value to be converted to a byte
+ * array
+ * @return the value <tt>big</tt> as byte array
+ */
+ public static byte[] toMinimalByteArray(BigInteger value)
+ {
+ byte[] valBytes = value.toByteArray();
+ if ((valBytes.length == 1) || (value.bitLength() & 0x07) != 0)
+ {
+ return valBytes;
+ }
+ byte[] result = new byte[value.bitLength() >> 3];
+ System.arraycopy(valBytes, 1, result, 0, result.length);
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/ByteUtils.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/ByteUtils.java
new file mode 100644
index 00000000..0e234ae9
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/ByteUtils.java
@@ -0,0 +1,414 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This class is a utility class for manipulating byte arrays.
+ */
+public final class ByteUtils
+{
+
+ private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ /**
+ * Default constructor (private)
+ */
+ private ByteUtils()
+ {
+ // empty
+ }
+
+ /**
+ * Compare two byte arrays (perform null checks beforehand).
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[] left, byte[] right)
+ {
+ if (left == null)
+ {
+ return right == null;
+ }
+ if (right == null)
+ {
+ return false;
+ }
+
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+ /**
+ * Compare two two-dimensional byte arrays. No null checks are performed.
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[][] left, byte[][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= ByteUtils.equals(left[i], right[i]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compare two three-dimensional byte arrays. No null checks are performed.
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[][][] left, byte[][][] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ if (left[i].length != right[i].length)
+ {
+ return false;
+ }
+ for (int j = left[i].length - 1; j >= 0; j--)
+ {
+ result &= ByteUtils.equals(left[i][j], right[i][j]);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a one-dimensional byte array
+ * rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[] array)
+ {
+ int result = 1;
+ for (int i = 0; i < array.length; i++)
+ {
+ result = 31 * result + array[i];
+ }
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a two-dimensional byte array
+ * rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[][] array)
+ {
+ int result = 1;
+ for (int i = 0; i < array.length; i++)
+ {
+ result = 31 * result + deepHashCode(array[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a three-dimensional byte
+ * array rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[][][] array)
+ {
+ int result = 1;
+ for (int i = 0; i < array.length; i++)
+ {
+ result = 31 * result + deepHashCode(array[i]);
+ }
+ return result;
+ }
+
+
+ /**
+ * Return a clone of the given byte array (performs null check beforehand).
+ *
+ * @param array the array to clone
+ * @return the clone of the given array, or <tt>null</tt> if the array is
+ * <tt>null</tt>
+ */
+ public static byte[] clone(byte[] array)
+ {
+ if (array == null)
+ {
+ return null;
+ }
+ byte[] result = new byte[array.length];
+ System.arraycopy(array, 0, result, 0, array.length);
+ return result;
+ }
+
+ /**
+ * Convert a string containing hexadecimal characters to a byte-array.
+ *
+ * @param s a hex string
+ * @return a byte array with the corresponding value
+ */
+ public static byte[] fromHexString(String s)
+ {
+ char[] rawChars = s.toUpperCase().toCharArray();
+
+ int hexChars = 0;
+ for (int i = 0; i < rawChars.length; i++)
+ {
+ if ((rawChars[i] >= '0' && rawChars[i] <= '9')
+ || (rawChars[i] >= 'A' && rawChars[i] <= 'F'))
+ {
+ hexChars++;
+ }
+ }
+
+ byte[] byteString = new byte[(hexChars + 1) >> 1];
+
+ int pos = hexChars & 1;
+
+ for (int i = 0; i < rawChars.length; i++)
+ {
+ if (rawChars[i] >= '0' && rawChars[i] <= '9')
+ {
+ byteString[pos >> 1] <<= 4;
+ byteString[pos >> 1] |= rawChars[i] - '0';
+ }
+ else if (rawChars[i] >= 'A' && rawChars[i] <= 'F')
+ {
+ byteString[pos >> 1] <<= 4;
+ byteString[pos >> 1] |= rawChars[i] - 'A' + 10;
+ }
+ else
+ {
+ continue;
+ }
+ pos++;
+ }
+
+ return byteString;
+ }
+
+ /**
+ * Convert a byte array to the corresponding hexstring.
+ *
+ * @param input the byte array to be converted
+ * @return the corresponding hexstring
+ */
+ public static String toHexString(byte[] input)
+ {
+ String result = "";
+ for (int i = 0; i < input.length; i++)
+ {
+ result += HEX_CHARS[(input[i] >>> 4) & 0x0f];
+ result += HEX_CHARS[(input[i]) & 0x0f];
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array to the corresponding hex string.
+ *
+ * @param input the byte array to be converted
+ * @param prefix the prefix to put at the beginning of the hex string
+ * @param seperator a separator string
+ * @return the corresponding hex string
+ */
+ public static String toHexString(byte[] input, String prefix,
+ String seperator)
+ {
+ String result = new String(prefix);
+ for (int i = 0; i < input.length; i++)
+ {
+ result += HEX_CHARS[(input[i] >>> 4) & 0x0f];
+ result += HEX_CHARS[(input[i]) & 0x0f];
+ if (i < input.length - 1)
+ {
+ result += seperator;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array to the corresponding bit string.
+ *
+ * @param input the byte array to be converted
+ * @return the corresponding bit string
+ */
+ public static String toBinaryString(byte[] input)
+ {
+ String result = "";
+ int i;
+ for (i = 0; i < input.length; i++)
+ {
+ int e = input[i];
+ for (int ii = 0; ii < 8; ii++)
+ {
+ int b = (e >>> ii) & 1;
+ result += b;
+ }
+ if (i != input.length - 1)
+ {
+ result += " ";
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Compute the bitwise XOR of two arrays of bytes. The arrays have to be of
+ * same length. No length checking is performed.
+ *
+ * @param x1 the first array
+ * @param x2 the second array
+ * @return x1 XOR x2
+ */
+ public static byte[] xor(byte[] x1, byte[] x2)
+ {
+ byte[] out = new byte[x1.length];
+
+ for (int i = x1.length - 1; i >= 0; i--)
+ {
+ out[i] = (byte)(x1[i] ^ x2[i]);
+ }
+ return out;
+ }
+
+ /**
+ * Concatenate two byte arrays. No null checks are performed.
+ *
+ * @param x1 the first array
+ * @param x2 the second array
+ * @return (x2||x1) (little-endian order, i.e. x1 is at lower memory
+ * addresses)
+ */
+ public static byte[] concatenate(byte[] x1, byte[] x2)
+ {
+ byte[] result = new byte[x1.length + x2.length];
+
+ System.arraycopy(x1, 0, result, 0, x1.length);
+ System.arraycopy(x2, 0, result, x1.length, x2.length);
+
+ return result;
+ }
+
+ /**
+ * Convert a 2-dimensional byte array into a 1-dimensional byte array by
+ * concatenating all entries.
+ *
+ * @param array a 2-dimensional byte array
+ * @return the concatenated input array
+ */
+ public static byte[] concatenate(byte[][] array)
+ {
+ int rowLength = array[0].length;
+ byte[] result = new byte[array.length * rowLength];
+ int index = 0;
+ for (int i = 0; i < array.length; i++)
+ {
+ System.arraycopy(array[i], 0, result, index, rowLength);
+ index += rowLength;
+ }
+ return result;
+ }
+
+ /**
+ * Split a byte array <tt>input</tt> into two arrays at <tt>index</tt>,
+ * i.e. the first array will have the lower <tt>index</tt> bytes, the
+ * second one the higher <tt>input.length - index</tt> bytes.
+ *
+ * @param input the byte array to be split
+ * @param index the index where the byte array is split
+ * @return the splitted input array as an array of two byte arrays
+ * @throws ArrayIndexOutOfBoundsException if <tt>index</tt> is out of bounds
+ */
+ public static byte[][] split(byte[] input, int index)
+ throws ArrayIndexOutOfBoundsException
+ {
+ if (index > input.length)
+ {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ byte[][] result = new byte[2][];
+ result[0] = new byte[index];
+ result[1] = new byte[input.length - index];
+ System.arraycopy(input, 0, result[0], 0, index);
+ System.arraycopy(input, index, result[1], 0, input.length - index);
+ return result;
+ }
+
+ /**
+ * Generate a subarray of a given byte array.
+ *
+ * @param input the input byte array
+ * @param start the start index
+ * @param end the end index
+ * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt>
+ * (inclusively) to <tt>end</tt> (exclusively)
+ */
+ public static byte[] subArray(byte[] input, int start, int end)
+ {
+ byte[] result = new byte[end - start];
+ System.arraycopy(input, start, result, 0, end - start);
+ return result;
+ }
+
+ /**
+ * Generate a subarray of a given byte array.
+ *
+ * @param input the input byte array
+ * @param start the start index
+ * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt> to
+ * the end of the array
+ */
+ public static byte[] subArray(byte[] input, int start)
+ {
+ return subArray(input, start, input.length);
+ }
+
+ /**
+ * Rewrite a byte array as a char array
+ *
+ * @param input -
+ * the byte array
+ * @return char array
+ */
+ public static char[] toCharArray(byte[] input)
+ {
+ char[] result = new char[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = (char)input[i];
+ }
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/CharUtils.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/CharUtils.java
new file mode 100644
index 00000000..44f97716
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/CharUtils.java
@@ -0,0 +1,98 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+public final class CharUtils
+{
+
+ /**
+ * Default constructor (private)
+ */
+ private CharUtils()
+ {
+ // empty
+ }
+
+ /**
+ * Return a clone of the given char array. No null checks are performed.
+ *
+ * @param array the array to clone
+ * @return the clone of the given array
+ */
+ public static char[] clone(char[] array)
+ {
+ char[] result = new char[array.length];
+ System.arraycopy(array, 0, result, 0, array.length);
+ return result;
+ }
+
+ /**
+ * Convert the given char array into a byte array.
+ *
+ * @param chars the char array
+ * @return the converted array
+ */
+ public static byte[] toByteArray(char[] chars)
+ {
+ byte[] result = new byte[chars.length];
+ for (int i = chars.length - 1; i >= 0; i--)
+ {
+ result[i] = (byte)chars[i];
+ }
+ return result;
+ }
+
+ /**
+ * Convert the given char array into a
+ * byte array for use with PBE encryption.
+ *
+ * @param chars the char array
+ * @return the converted array
+ */
+ public static byte[] toByteArrayForPBE(char[] chars)
+ {
+
+ byte[] out = new byte[chars.length];
+
+ for (int i = 0; i < chars.length; i++)
+ {
+ out[i] = (byte)chars[i];
+ }
+
+ int length = out.length * 2;
+ byte[] ret = new byte[length + 2];
+
+ int j = 0;
+ for (int i = 0; i < out.length; i++)
+ {
+ j = i * 2;
+ ret[j] = 0;
+ ret[j + 1] = out[i];
+ }
+
+ ret[length] = 0;
+ ret[length + 1] = 0;
+
+ return ret;
+ }
+
+ /**
+ * Compare two char arrays. No null checks are performed.
+ *
+ * @param left the char byte array
+ * @param right the second char array
+ * @return the result of the comparison
+ */
+ public static boolean equals(char[] left, char[] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Matrix.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Matrix.java
new file mode 100644
index 00000000..7e2de19e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Matrix.java
@@ -0,0 +1,1323 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes some operations with matrices over finite field GF(2)
+ * and is used in ecc and MQ-PKC (also has some specific methods and
+ * implementation)
+ */
+public class GF2Matrix
+ extends Matrix
+{
+
+ /**
+ * For the matrix representation the array of type int[][] is used, thus one
+ * element of the array keeps 32 elements of the matrix (from one row and 32
+ * columns)
+ */
+ private int[][] matrix;
+
+ /**
+ * the length of each array representing a row of this matrix, computed as
+ * <tt>(numColumns + 31) / 32</tt>
+ */
+ private int length;
+
+ /**
+ * Create the matrix from encoded form.
+ *
+ * @param enc the encoded matrix
+ */
+ public GF2Matrix(byte[] enc)
+ {
+ if (enc.length < 9)
+ {
+ throw new ArithmeticException(
+ "given array is not an encoded matrix over GF(2)");
+ }
+
+ numRows = LittleEndianConversions.OS2IP(enc, 0);
+ numColumns = LittleEndianConversions.OS2IP(enc, 4);
+
+ int n = ((numColumns + 7) >>> 3) * numRows;
+
+ if ((numRows <= 0) || (n != (enc.length - 8)))
+ {
+ throw new ArithmeticException(
+ "given array is not an encoded matrix over GF(2)");
+ }
+
+ length = (numColumns + 31) >>> 5;
+ matrix = new int[numRows][length];
+
+ // number of "full" integer
+ int q = numColumns >> 5;
+ // number of bits in non-full integer
+ int r = numColumns & 0x1f;
+
+ int count = 8;
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < q; j++, count += 4)
+ {
+ matrix[i][j] = LittleEndianConversions.OS2IP(enc, count);
+ }
+ for (int j = 0; j < r; j += 8)
+ {
+ matrix[i][q] ^= (enc[count++] & 0xff) << j;
+ }
+ }
+ }
+
+ /**
+ * Create the matrix with the contents of the given array. The matrix is not
+ * copied. Unused coefficients are masked out.
+ *
+ * @param numColumns the number of columns
+ * @param matrix the element array
+ */
+ public GF2Matrix(int numColumns, int[][] matrix)
+ {
+ if (matrix[0].length != (numColumns + 31) >> 5)
+ {
+ throw new ArithmeticException(
+ "Int array does not match given number of columns.");
+ }
+ this.numColumns = numColumns;
+ numRows = matrix.length;
+ length = matrix[0].length;
+ int rest = numColumns & 0x1f;
+ int bitMask;
+ if (rest == 0)
+ {
+ bitMask = 0xffffffff;
+ }
+ else
+ {
+ bitMask = (1 << rest) - 1;
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ matrix[i][length - 1] &= bitMask;
+ }
+ this.matrix = matrix;
+ }
+
+ /**
+ * Create an nxn matrix of the given type.
+ *
+ * @param n the number of rows (and columns)
+ * @param typeOfMatrix the martix type (see {@link Matrix} for predefined
+ * constants)
+ */
+ public GF2Matrix(int n, char typeOfMatrix)
+ {
+ this(n, typeOfMatrix, new java.security.SecureRandom());
+ }
+
+ /**
+ * Create an nxn matrix of the given type.
+ *
+ * @param n the matrix size
+ * @param typeOfMatrix the matrix type
+ * @param sr the source of randomness
+ */
+ public GF2Matrix(int n, char typeOfMatrix, SecureRandom sr)
+ {
+ if (n <= 0)
+ {
+ throw new ArithmeticException("Size of matrix is non-positive.");
+ }
+
+ switch (typeOfMatrix)
+ {
+
+ case Matrix.MATRIX_TYPE_ZERO:
+ assignZeroMatrix(n, n);
+ break;
+
+ case Matrix.MATRIX_TYPE_UNIT:
+ assignUnitMatrix(n);
+ break;
+
+ case Matrix.MATRIX_TYPE_RANDOM_LT:
+ assignRandomLowerTriangularMatrix(n, sr);
+ break;
+
+ case Matrix.MATRIX_TYPE_RANDOM_UT:
+ assignRandomUpperTriangularMatrix(n, sr);
+ break;
+
+ case Matrix.MATRIX_TYPE_RANDOM_REGULAR:
+ assignRandomRegularMatrix(n, sr);
+ break;
+
+ default:
+ throw new ArithmeticException("Unknown matrix type.");
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param a another {@link GF2Matrix}
+ */
+ public GF2Matrix(GF2Matrix a)
+ {
+ numColumns = a.getNumColumns();
+ numRows = a.getNumRows();
+ length = a.length;
+ matrix = new int[a.matrix.length][];
+ for (int i = 0; i < matrix.length; i++)
+ {
+ matrix[i] = IntUtils.clone(a.matrix[i]);
+ }
+
+ }
+
+ /**
+ * create the mxn zero matrix
+ */
+ private GF2Matrix(int m, int n)
+ {
+ if ((n <= 0) || (m <= 0))
+ {
+ throw new ArithmeticException("size of matrix is non-positive");
+ }
+
+ assignZeroMatrix(m, n);
+ }
+
+ /**
+ * Create the mxn zero matrix.
+ *
+ * @param m number of rows
+ * @param n number of columns
+ */
+ private void assignZeroMatrix(int m, int n)
+ {
+ numRows = m;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ matrix[i][j] = 0;
+ }
+ }
+ }
+
+ /**
+ * Create the mxn unit matrix.
+ *
+ * @param n number of rows (and columns)
+ */
+ private void assignUnitMatrix(int n)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ matrix[i][j] = 0;
+ }
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ int rest = i & 0x1f;
+ matrix[i][i >>> 5] = 1 << rest;
+ }
+ }
+
+ /**
+ * Create a nxn random lower triangular matrix.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ */
+ private void assignRandomLowerTriangularMatrix(int n, SecureRandom sr)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ for (int i = 0; i < numRows; i++)
+ {
+ int q = i >>> 5;
+ int r = i & 0x1f;
+ int s = 31 - r;
+ r = 1 << r;
+ for (int j = 0; j < q; j++)
+ {
+ matrix[i][j] = sr.nextInt();
+ }
+ matrix[i][q] = (sr.nextInt() >>> s) | r;
+ for (int j = q + 1; j < length; j++)
+ {
+ matrix[i][j] = 0;
+ }
+
+ }
+
+ }
+
+ /**
+ * Create a nxn random upper triangular matrix.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ */
+ private void assignRandomUpperTriangularMatrix(int n, SecureRandom sr)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ int rest = n & 0x1f;
+ int help;
+ if (rest == 0)
+ {
+ help = 0xffffffff;
+ }
+ else
+ {
+ help = (1 << rest) - 1;
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ int q = i >>> 5;
+ int r = i & 0x1f;
+ int s = r;
+ r = 1 << r;
+ for (int j = 0; j < q; j++)
+ {
+ matrix[i][j] = 0;
+ }
+ matrix[i][q] = (sr.nextInt() << s) | r;
+ for (int j = q + 1; j < length; j++)
+ {
+ matrix[i][j] = sr.nextInt();
+ }
+ matrix[i][length - 1] &= help;
+ }
+
+ }
+
+ /**
+ * Create an nxn random regular matrix.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ */
+ private void assignRandomRegularMatrix(int n, SecureRandom sr)
+ {
+ numRows = n;
+ numColumns = n;
+ length = (n + 31) >>> 5;
+ matrix = new int[numRows][length];
+ GF2Matrix lm = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_LT, sr);
+ GF2Matrix um = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_UT, sr);
+ GF2Matrix rm = (GF2Matrix)lm.rightMultiply(um);
+ Permutation perm = new Permutation(n, sr);
+ int[] p = perm.getVector();
+ for (int i = 0; i < n; i++)
+ {
+ System.arraycopy(rm.matrix[i], 0, matrix[p[i]], 0, length);
+ }
+ }
+
+ /**
+ * Create a nxn random regular matrix and its inverse.
+ *
+ * @param n number of rows (and columns)
+ * @param sr source of randomness
+ * @return the created random regular matrix and its inverse
+ */
+ public static GF2Matrix[] createRandomRegularMatrixAndItsInverse(int n,
+ SecureRandom sr)
+ {
+
+ GF2Matrix[] result = new GF2Matrix[2];
+
+ // ------------------------------------
+ // First part: create regular matrix
+ // ------------------------------------
+
+ // ------
+ int length = (n + 31) >> 5;
+ GF2Matrix lm = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_LT, sr);
+ GF2Matrix um = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_UT, sr);
+ GF2Matrix rm = (GF2Matrix)lm.rightMultiply(um);
+ Permutation p = new Permutation(n, sr);
+ int[] pVec = p.getVector();
+
+ int[][] matrix = new int[n][length];
+ for (int i = 0; i < n; i++)
+ {
+ System.arraycopy(rm.matrix[pVec[i]], 0, matrix[i], 0, length);
+ }
+
+ result[0] = new GF2Matrix(n, matrix);
+
+ // ------------------------------------
+ // Second part: create inverse matrix
+ // ------------------------------------
+
+ // inverse to lm
+ GF2Matrix invLm = new GF2Matrix(n, Matrix.MATRIX_TYPE_UNIT);
+ for (int i = 0; i < n; i++)
+ {
+ int rest = i & 0x1f;
+ int q = i >>> 5;
+ int r = 1 << rest;
+ for (int j = i + 1; j < n; j++)
+ {
+ int b = (lm.matrix[j][q]) & r;
+ if (b != 0)
+ {
+ for (int k = 0; k <= q; k++)
+ {
+ invLm.matrix[j][k] ^= invLm.matrix[i][k];
+ }
+ }
+ }
+ }
+ // inverse to um
+ GF2Matrix invUm = new GF2Matrix(n, Matrix.MATRIX_TYPE_UNIT);
+ for (int i = n - 1; i >= 0; i--)
+ {
+ int rest = i & 0x1f;
+ int q = i >>> 5;
+ int r = 1 << rest;
+ for (int j = i - 1; j >= 0; j--)
+ {
+ int b = (um.matrix[j][q]) & r;
+ if (b != 0)
+ {
+ for (int k = q; k < length; k++)
+ {
+ invUm.matrix[j][k] ^= invUm.matrix[i][k];
+ }
+ }
+ }
+ }
+
+ // inverse matrix
+ result[1] = (GF2Matrix)invUm.rightMultiply(invLm.rightMultiply(p));
+
+ return result;
+ }
+
+ /**
+ * @return the array keeping the matrix elements
+ */
+ public int[][] getIntArray()
+ {
+ return matrix;
+ }
+
+ /**
+ * @return the length of each array representing a row of this matrix
+ */
+ public int getLength()
+ {
+ return length;
+ }
+
+ /**
+ * Return the row of this matrix with the given index.
+ *
+ * @param index the index
+ * @return the row of this matrix with the given index
+ */
+ public int[] getRow(int index)
+ {
+ return matrix[index];
+ }
+
+ /**
+ * Returns encoded matrix, i.e., this matrix in byte array form
+ *
+ * @return the encoded matrix
+ */
+ public byte[] getEncoded()
+ {
+ int n = (numColumns + 7) >>> 3;
+ n *= numRows;
+ n += 8;
+ byte[] enc = new byte[n];
+
+ LittleEndianConversions.I2OSP(numRows, enc, 0);
+ LittleEndianConversions.I2OSP(numColumns, enc, 4);
+
+ // number of "full" integer
+ int q = numColumns >>> 5;
+ // number of bits in non-full integer
+ int r = numColumns & 0x1f;
+
+ int count = 8;
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < q; j++, count += 4)
+ {
+ LittleEndianConversions.I2OSP(matrix[i][j], enc, count);
+ }
+ for (int j = 0; j < r; j += 8)
+ {
+ enc[count++] = (byte)((matrix[i][q] >>> j) & 0xff);
+ }
+
+ }
+ return enc;
+ }
+
+
+ /**
+ * Returns the percentage of the number of "ones" in this matrix.
+ *
+ * @return the Hamming weight of this matrix (as a ratio).
+ */
+ public double getHammingWeight()
+ {
+ double counter = 0.0;
+ double elementCounter = 0.0;
+ int rest = numColumns & 0x1f;
+ int d;
+ if (rest == 0)
+ {
+ d = length;
+ }
+ else
+ {
+ d = length - 1;
+ }
+
+ for (int i = 0; i < numRows; i++)
+ {
+
+ for (int j = 0; j < d; j++)
+ {
+ int a = matrix[i][j];
+ for (int k = 0; k < 32; k++)
+ {
+ int b = (a >>> k) & 1;
+ counter = counter + b;
+ elementCounter = elementCounter + 1;
+ }
+ }
+ int a = matrix[i][length - 1];
+ for (int k = 0; k < rest; k++)
+ {
+ int b = (a >>> k) & 1;
+ counter = counter + b;
+ elementCounter = elementCounter + 1;
+ }
+ }
+
+ return counter / elementCounter;
+ }
+
+ /**
+ * Check if this is the zero matrix (i.e., all entries are zero).
+ *
+ * @return <tt>true</tt> if this is the zero matrix
+ */
+ public boolean isZero()
+ {
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ if (matrix[i][j] != 0)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get the quadratic submatrix of this matrix consisting of the leftmost
+ * <tt>numRows</tt> columns.
+ *
+ * @return the <tt>(numRows x numRows)</tt> submatrix
+ */
+ public GF2Matrix getLeftSubMatrix()
+ {
+ if (numColumns <= numRows)
+ {
+ throw new ArithmeticException("empty submatrix");
+ }
+ int length = (numRows + 31) >> 5;
+ int[][] result = new int[numRows][length];
+ int bitMask = (1 << (numRows & 0x1f)) - 1;
+ if (bitMask == 0)
+ {
+ bitMask = -1;
+ }
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ System.arraycopy(matrix[i], 0, result[i], 0, length);
+ result[i][length - 1] &= bitMask;
+ }
+ return new GF2Matrix(numRows, result);
+ }
+
+ /**
+ * Compute the full form matrix <tt>(this | Id)</tt> from this matrix in
+ * left compact form, where <tt>Id</tt> is the <tt>k x k</tt> identity
+ * matrix and <tt>k</tt> is the number of rows of this matrix.
+ *
+ * @return <tt>(this | Id)</tt>
+ */
+ public GF2Matrix extendLeftCompactForm()
+ {
+ int newNumColumns = numColumns + numRows;
+ GF2Matrix result = new GF2Matrix(numRows, newNumColumns);
+
+ int ind = numRows - 1 + numColumns;
+ for (int i = numRows - 1; i >= 0; i--, ind--)
+ {
+ // copy this matrix to first columns
+ System.arraycopy(matrix[i], 0, result.matrix[i], 0, length);
+ // store the identity in last columns
+ result.matrix[i][ind >> 5] |= 1 << (ind & 0x1f);
+ }
+
+ return result;
+ }
+
+ /**
+ * Get the submatrix of this matrix consisting of the rightmost
+ * <tt>numColumns-numRows</tt> columns.
+ *
+ * @return the <tt>(numRows x (numColumns-numRows))</tt> submatrix
+ */
+ public GF2Matrix getRightSubMatrix()
+ {
+ if (numColumns <= numRows)
+ {
+ throw new ArithmeticException("empty submatrix");
+ }
+
+ int q = numRows >> 5;
+ int r = numRows & 0x1f;
+
+ GF2Matrix result = new GF2Matrix(numRows, numColumns - numRows);
+
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ // if words have to be shifted
+ if (r != 0)
+ {
+ int ind = q;
+ // process all but last word
+ for (int j = 0; j < result.length - 1; j++)
+ {
+ // shift to correct position
+ result.matrix[i][j] = (matrix[i][ind++] >>> r)
+ | (matrix[i][ind] << (32 - r));
+ }
+ // process last word
+ result.matrix[i][result.length - 1] = matrix[i][ind++] >>> r;
+ if (ind < length)
+ {
+ result.matrix[i][result.length - 1] |= matrix[i][ind] << (32 - r);
+ }
+ }
+ else
+ {
+ // no shifting necessary
+ System.arraycopy(matrix[i], q, result.matrix[i], 0,
+ result.length);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Compute the full form matrix <tt>(Id | this)</tt> from this matrix in
+ * right compact form, where <tt>Id</tt> is the <tt>k x k</tt> identity
+ * matrix and <tt>k</tt> is the number of rows of this matrix.
+ *
+ * @return <tt>(Id | this)</tt>
+ */
+ public GF2Matrix extendRightCompactForm()
+ {
+ GF2Matrix result = new GF2Matrix(numRows, numRows + numColumns);
+
+ int q = numRows >> 5;
+ int r = numRows & 0x1f;
+
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ // store the identity in first columns
+ result.matrix[i][i >> 5] |= 1 << (i & 0x1f);
+
+ // copy this matrix to last columns
+
+ // if words have to be shifted
+ if (r != 0)
+ {
+ int ind = q;
+ // process all but last word
+ for (int j = 0; j < length - 1; j++)
+ {
+ // obtain matrix word
+ int mw = matrix[i][j];
+ // shift to correct position
+ result.matrix[i][ind++] |= mw << r;
+ result.matrix[i][ind] |= mw >>> (32 - r);
+ }
+ // process last word
+ int mw = matrix[i][length - 1];
+ result.matrix[i][ind++] |= mw << r;
+ if (ind < result.length)
+ {
+ result.matrix[i][ind] |= mw >>> (32 - r);
+ }
+ }
+ else
+ {
+ // no shifting necessary
+ System.arraycopy(matrix[i], 0, result.matrix[i], q, length);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the transpose of this matrix.
+ *
+ * @return <tt>(this)<sup>T</sup></tt>
+ */
+ public Matrix computeTranspose()
+ {
+ int[][] result = new int[numColumns][(numRows + 31) >>> 5];
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < numColumns; j++)
+ {
+ int qs = j >>> 5;
+ int rs = j & 0x1f;
+ int b = (matrix[i][qs] >>> rs) & 1;
+ int qt = i >>> 5;
+ int rt = i & 0x1f;
+ if (b == 1)
+ {
+ result[j][qt] |= 1 << rt;
+ }
+ }
+ }
+
+ return new GF2Matrix(numRows, result);
+ }
+
+ /**
+ * Compute the inverse of this matrix.
+ *
+ * @return the inverse of this matrix (newly created).
+ * @throws ArithmeticException if this matrix is not invertible.
+ */
+ public Matrix computeInverse()
+ {
+ if (numRows != numColumns)
+ {
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+
+ // clone this matrix
+ int[][] tmpMatrix = new int[numRows][length];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ tmpMatrix[i] = IntUtils.clone(matrix[i]);
+ }
+
+ // initialize inverse matrix as unit matrix
+ int[][] invMatrix = new int[numRows][length];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ int q = i >> 5;
+ int r = i & 0x1f;
+ invMatrix[i][q] = 1 << r;
+ }
+
+ // simultaneously compute Gaussian reduction of tmpMatrix and unit
+ // matrix
+ for (int i = 0; i < numRows; i++)
+ {
+ // i = q * 32 + (i mod 32)
+ int q = i >> 5;
+ int bitMask = 1 << (i & 0x1f);
+ // if diagonal element is zero
+ if ((tmpMatrix[i][q] & bitMask) == 0)
+ {
+ boolean foundNonZero = false;
+ // find a non-zero element in the same column
+ for (int j = i + 1; j < numRows; j++)
+ {
+ if ((tmpMatrix[j][q] & bitMask) != 0)
+ {
+ // found it, swap rows ...
+ foundNonZero = true;
+ swapRows(tmpMatrix, i, j);
+ swapRows(invMatrix, i, j);
+ // ... and quit searching
+ j = numRows;
+ continue;
+ }
+ }
+ // if no non-zero element was found ...
+ if (!foundNonZero)
+ {
+ // ... the matrix is not invertible
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+ }
+
+ // normalize all but i-th row
+ for (int j = numRows - 1; j >= 0; j--)
+ {
+ if ((j != i) && ((tmpMatrix[j][q] & bitMask) != 0))
+ {
+ addToRow(tmpMatrix[i], tmpMatrix[j], q);
+ addToRow(invMatrix[i], invMatrix[j], 0);
+ }
+ }
+ }
+
+ return new GF2Matrix(numColumns, invMatrix);
+ }
+
+ /**
+ * Compute the product of a permutation matrix (which is generated from an
+ * n-permutation) and this matrix.
+ *
+ * @param p the permutation
+ * @return {@link GF2Matrix} <tt>P*this</tt>
+ */
+ public Matrix leftMultiply(Permutation p)
+ {
+ int[] pVec = p.getVector();
+ if (pVec.length != numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[][] result = new int[numRows][];
+
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ result[i] = IntUtils.clone(matrix[pVec[i]]);
+ }
+
+ return new GF2Matrix(numRows, result);
+ }
+
+ /**
+ * compute product a row vector and this matrix
+ *
+ * @param vec a vector over GF(2)
+ * @return Vector product a*matrix
+ */
+ public Vector leftMultiply(Vector vec)
+ {
+
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[length];
+
+ int q = numRows >> 5;
+ int r = 1 << (numRows & 0x1f);
+
+ // compute scalar products with full words of vector
+ int row = 0;
+ for (int i = 0; i < q; i++)
+ {
+ int bitMask = 1;
+ do
+ {
+ int b = v[i] & bitMask;
+ if (b != 0)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ }
+ row++;
+ bitMask <<= 1;
+ }
+ while (bitMask != 0);
+ }
+
+ // compute scalar products with last word of vector
+ int bitMask = 1;
+ while (bitMask != r)
+ {
+ int b = v[q] & bitMask;
+ if (b != 0)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ }
+ row++;
+ bitMask <<= 1;
+ }
+
+ return new GF2Vector(res, numColumns);
+ }
+
+ /**
+ * Compute the product of the matrix <tt>(this | Id)</tt> and a column
+ * vector, where <tt>Id</tt> is a <tt>(numRows x numRows)</tt> unit
+ * matrix.
+ *
+ * @param vec the vector over GF(2)
+ * @return <tt>(this | Id)*vector</tt>
+ */
+ public Vector leftMultiplyLeftCompactForm(Vector vec)
+ {
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[(numRows + numColumns + 31) >>> 5];
+
+ // process full words of vector
+ int words = numRows >>> 5;
+ int row = 0;
+ for (int i = 0; i < words; i++)
+ {
+ int bitMask = 1;
+ do
+ {
+ int b = v[i] & bitMask;
+ if (b != 0)
+ {
+ // compute scalar product part
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ // set last bit
+ int q = (numColumns + row) >>> 5;
+ int r = (numColumns + row) & 0x1f;
+ res[q] |= 1 << r;
+ }
+ row++;
+ bitMask <<= 1;
+ }
+ while (bitMask != 0);
+ }
+
+ // process last word of vector
+ int rem = 1 << (numRows & 0x1f);
+ int bitMask = 1;
+ while (bitMask != rem)
+ {
+ int b = v[words] & bitMask;
+ if (b != 0)
+ {
+ // compute scalar product part
+ for (int j = 0; j < length; j++)
+ {
+ res[j] ^= matrix[row][j];
+ }
+ // set last bit
+ int q = (numColumns + row) >>> 5;
+ int r = (numColumns + row) & 0x1f;
+ res[q] |= 1 << r;
+ }
+ row++;
+ bitMask <<= 1;
+ }
+
+ return new GF2Vector(res, numRows + numColumns);
+ }
+
+ /**
+ * Compute the product of this matrix and a matrix A over GF(2).
+ *
+ * @param mat a matrix A over GF(2)
+ * @return matrix product <tt>this*matrixA</tt>
+ */
+ public Matrix rightMultiply(Matrix mat)
+ {
+ if (!(mat instanceof GF2Matrix))
+ {
+ throw new ArithmeticException("matrix is not defined over GF(2)");
+ }
+
+ if (mat.numRows != numColumns)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ GF2Matrix a = (GF2Matrix)mat;
+ GF2Matrix result = new GF2Matrix(numRows, mat.numColumns);
+
+ int d;
+ int rest = numColumns & 0x1f;
+ if (rest == 0)
+ {
+ d = length;
+ }
+ else
+ {
+ d = length - 1;
+ }
+ for (int i = 0; i < numRows; i++)
+ {
+ int count = 0;
+ for (int j = 0; j < d; j++)
+ {
+ int e = matrix[i][j];
+ for (int h = 0; h < 32; h++)
+ {
+ int b = e & (1 << h);
+ if (b != 0)
+ {
+ for (int g = 0; g < a.length; g++)
+ {
+ result.matrix[i][g] ^= a.matrix[count][g];
+ }
+ }
+ count++;
+ }
+ }
+ int e = matrix[i][length - 1];
+ for (int h = 0; h < rest; h++)
+ {
+ int b = e & (1 << h);
+ if (b != 0)
+ {
+ for (int g = 0; g < a.length; g++)
+ {
+ result.matrix[i][g] ^= a.matrix[count][g];
+ }
+ }
+ count++;
+ }
+
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the product of this matrix and a permutation matrix which is
+ * generated from an n-permutation.
+ *
+ * @param p the permutation
+ * @return {@link GF2Matrix} <tt>this*P</tt>
+ */
+ public Matrix rightMultiply(Permutation p)
+ {
+
+ int[] pVec = p.getVector();
+ if (pVec.length != numColumns)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ GF2Matrix result = new GF2Matrix(numRows, numColumns);
+
+ for (int i = numColumns - 1; i >= 0; i--)
+ {
+ int q = i >>> 5;
+ int r = i & 0x1f;
+ int pq = pVec[i] >>> 5;
+ int pr = pVec[i] & 0x1f;
+ for (int j = numRows - 1; j >= 0; j--)
+ {
+ result.matrix[j][q] |= ((matrix[j][pq] >>> pr) & 1) << r;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the product of this matrix and the given column vector.
+ *
+ * @param vec the vector over GF(2)
+ * @return <tt>this*vector</tt>
+ */
+ public Vector rightMultiply(Vector vec)
+ {
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numColumns)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[(numRows + 31) >>> 5];
+
+ for (int i = 0; i < numRows; i++)
+ {
+ // compute full word scalar products
+ int help = 0;
+ for (int j = 0; j < length; j++)
+ {
+ help ^= matrix[i][j] & v[j];
+ }
+ // compute single word scalar product
+ int bitValue = 0;
+ for (int j = 0; j < 32; j++)
+ {
+ bitValue ^= (help >>> j) & 1;
+ }
+ // set result bit
+ if (bitValue == 1)
+ {
+ res[i >>> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return new GF2Vector(res, numRows);
+ }
+
+ /**
+ * Compute the product of the matrix <tt>(Id | this)</tt> and a column
+ * vector, where <tt>Id</tt> is a <tt>(numRows x numRows)</tt> unit
+ * matrix.
+ *
+ * @param vec the vector over GF(2)
+ * @return <tt>(Id | this)*vector</tt>
+ */
+ public Vector rightMultiplyRightCompactForm(Vector vec)
+ {
+ if (!(vec instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ if (vec.length != numColumns + numRows)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] v = ((GF2Vector)vec).getVecArray();
+ int[] res = new int[(numRows + 31) >>> 5];
+
+ int q = numRows >> 5;
+ int r = numRows & 0x1f;
+
+ // for all rows
+ for (int i = 0; i < numRows; i++)
+ {
+ // get vector bit
+ int help = (v[i >> 5] >>> (i & 0x1f)) & 1;
+
+ // compute full word scalar products
+ int vInd = q;
+ // if words have to be shifted
+ if (r != 0)
+ {
+ int vw = 0;
+ // process all but last word
+ for (int j = 0; j < length - 1; j++)
+ {
+ // shift to correct position
+ vw = (v[vInd++] >>> r) | (v[vInd] << (32 - r));
+ help ^= matrix[i][j] & vw;
+ }
+ // process last word
+ vw = v[vInd++] >>> r;
+ if (vInd < v.length)
+ {
+ vw |= v[vInd] << (32 - r);
+ }
+ help ^= matrix[i][length - 1] & vw;
+ }
+ else
+ {
+ // no shifting necessary
+ for (int j = 0; j < length; j++)
+ {
+ help ^= matrix[i][j] & v[vInd++];
+ }
+ }
+
+ // compute single word scalar product
+ int bitValue = 0;
+ for (int j = 0; j < 32; j++)
+ {
+ bitValue ^= help & 1;
+ help >>>= 1;
+ }
+
+ // set result bit
+ if (bitValue == 1)
+ {
+ res[i >> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return new GF2Vector(res, numRows);
+ }
+
+ /**
+ * Compare this matrix with another object.
+ *
+ * @param other another object
+ * @return the result of the comparison
+ */
+ public boolean equals(Object other)
+ {
+
+ if (!(other instanceof GF2Matrix))
+ {
+ return false;
+ }
+ GF2Matrix otherMatrix = (GF2Matrix)other;
+
+ if ((numRows != otherMatrix.numRows)
+ || (numColumns != otherMatrix.numColumns)
+ || (length != otherMatrix.length))
+ {
+ return false;
+ }
+
+ for (int i = 0; i < numRows; i++)
+ {
+ if (!IntUtils.equals(matrix[i], otherMatrix.matrix[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return the hash code of this matrix
+ */
+ public int hashCode()
+ {
+ int hash = (numRows * 31 + numColumns) * 31 + length;
+ for (int i = 0; i < numRows; i++)
+ {
+ hash = hash * 31 + matrix[i].hashCode();
+ }
+ return hash;
+ }
+
+ /**
+ * @return a human readable form of the matrix
+ */
+ public String toString()
+ {
+ int rest = numColumns & 0x1f;
+ int d;
+ if (rest == 0)
+ {
+ d = length;
+ }
+ else
+ {
+ d = length - 1;
+ }
+
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < numRows; i++)
+ {
+ buf.append(i + ": ");
+ for (int j = 0; j < d; j++)
+ {
+ int a = matrix[i][j];
+ for (int k = 0; k < 32; k++)
+ {
+ int b = (a >>> k) & 1;
+ if (b == 0)
+ {
+ buf.append('0');
+ }
+ else
+ {
+ buf.append('1');
+ }
+ }
+ buf.append(' ');
+ }
+ int a = matrix[i][length - 1];
+ for (int k = 0; k < rest; k++)
+ {
+ int b = (a >>> k) & 1;
+ if (b == 0)
+ {
+ buf.append('0');
+ }
+ else
+ {
+ buf.append('1');
+ }
+ }
+ buf.append('\n');
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Swap two rows of the given matrix.
+ *
+ * @param matrix the matrix
+ * @param first the index of the first row
+ * @param second the index of the second row
+ */
+ private static void swapRows(int[][] matrix, int first, int second)
+ {
+ int[] tmp = matrix[first];
+ matrix[first] = matrix[second];
+ matrix[second] = tmp;
+ }
+
+ /**
+ * Partially add one row to another.
+ *
+ * @param fromRow the addend
+ * @param toRow the row to add to
+ * @param startIndex the array index to start from
+ */
+ private static void addToRow(int[] fromRow, int[] toRow, int startIndex)
+ {
+ for (int i = toRow.length - 1; i >= startIndex; i--)
+ {
+ toRow[i] = fromRow[i] ^ toRow[i];
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Polynomial.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Polynomial.java
new file mode 100644
index 00000000..25bf099a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Polynomial.java
@@ -0,0 +1,2039 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.math.BigInteger;
+import java.util.Random;
+
+
+/**
+ * This class stores very long strings of bits and does some basic arithmetics.
+ * It is used by <tt>GF2nField</tt>, <tt>GF2nPolynomialField</tt> and
+ * <tt>GFnPolynomialElement</tt>.
+ *
+ * @see GF2nPolynomialElement
+ * @see GF2nField
+ */
+public class GF2Polynomial
+{
+
+ // number of bits stored in this GF2Polynomial
+ private int len;
+
+ // number of int used in value
+ private int blocks;
+
+ // storage
+ private int[] value;
+
+ // Random source
+ private static Random rand = new Random();
+
+ // Lookup-Table for vectorMult: parity[a]= #1(a) mod 2 == 1
+ private static final boolean[] parity = {false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, true, false, false, true, false, true, true, false, false,
+ true, true, false, true, false, false, true, true, false, false,
+ true, false, true, true, false, false, true, true, false, true,
+ false, false, true, false, true, true, false, true, false, false,
+ true, true, false, false, true, false, true, true, false, true,
+ false, false, true, false, true, true, false, false, true, true,
+ false, true, false, false, true, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, false, true, true, false, true, false, false, true, true,
+ false, false, true, false, true, true, false, true, false, false,
+ true, false, true, true, false, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, false, true, true, false, true, false, false, true, false,
+ true, true, false, true, false, false, true, true, false, false,
+ true, false, true, true, false, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false, true, false, false, true, false, true, true, false, false,
+ true, true, false, true, false, false, true, false, true, true,
+ false, true, false, false, true, true, false, false, true, false,
+ true, true, false, true, false, false, true, false, true, true,
+ false, false, true, true, false, true, false, false, true, true,
+ false, false, true, false, true, true, false, false, true, true,
+ false, true, false, false, true, false, true, true, false, true,
+ false, false, true, true, false, false, true, false, true, true,
+ false};
+
+ // Lookup-Table for Squaring: squaringTable[a]=a^2
+ private static final short[] squaringTable = {0x0000, 0x0001, 0x0004,
+ 0x0005, 0x0010, 0x0011, 0x0014, 0x0015, 0x0040, 0x0041, 0x0044,
+ 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101, 0x0104,
+ 0x0105, 0x0110, 0x0111, 0x0114, 0x0115, 0x0140, 0x0141, 0x0144,
+ 0x0145, 0x0150, 0x0151, 0x0154, 0x0155, 0x0400, 0x0401, 0x0404,
+ 0x0405, 0x0410, 0x0411, 0x0414, 0x0415, 0x0440, 0x0441, 0x0444,
+ 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, 0x0500, 0x0501, 0x0504,
+ 0x0505, 0x0510, 0x0511, 0x0514, 0x0515, 0x0540, 0x0541, 0x0544,
+ 0x0545, 0x0550, 0x0551, 0x0554, 0x0555, 0x1000, 0x1001, 0x1004,
+ 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, 0x1040, 0x1041, 0x1044,
+ 0x1045, 0x1050, 0x1051, 0x1054, 0x1055, 0x1100, 0x1101, 0x1104,
+ 0x1105, 0x1110, 0x1111, 0x1114, 0x1115, 0x1140, 0x1141, 0x1144,
+ 0x1145, 0x1150, 0x1151, 0x1154, 0x1155, 0x1400, 0x1401, 0x1404,
+ 0x1405, 0x1410, 0x1411, 0x1414, 0x1415, 0x1440, 0x1441, 0x1444,
+ 0x1445, 0x1450, 0x1451, 0x1454, 0x1455, 0x1500, 0x1501, 0x1504,
+ 0x1505, 0x1510, 0x1511, 0x1514, 0x1515, 0x1540, 0x1541, 0x1544,
+ 0x1545, 0x1550, 0x1551, 0x1554, 0x1555, 0x4000, 0x4001, 0x4004,
+ 0x4005, 0x4010, 0x4011, 0x4014, 0x4015, 0x4040, 0x4041, 0x4044,
+ 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, 0x4100, 0x4101, 0x4104,
+ 0x4105, 0x4110, 0x4111, 0x4114, 0x4115, 0x4140, 0x4141, 0x4144,
+ 0x4145, 0x4150, 0x4151, 0x4154, 0x4155, 0x4400, 0x4401, 0x4404,
+ 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, 0x4440, 0x4441, 0x4444,
+ 0x4445, 0x4450, 0x4451, 0x4454, 0x4455, 0x4500, 0x4501, 0x4504,
+ 0x4505, 0x4510, 0x4511, 0x4514, 0x4515, 0x4540, 0x4541, 0x4544,
+ 0x4545, 0x4550, 0x4551, 0x4554, 0x4555, 0x5000, 0x5001, 0x5004,
+ 0x5005, 0x5010, 0x5011, 0x5014, 0x5015, 0x5040, 0x5041, 0x5044,
+ 0x5045, 0x5050, 0x5051, 0x5054, 0x5055, 0x5100, 0x5101, 0x5104,
+ 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, 0x5140, 0x5141, 0x5144,
+ 0x5145, 0x5150, 0x5151, 0x5154, 0x5155, 0x5400, 0x5401, 0x5404,
+ 0x5405, 0x5410, 0x5411, 0x5414, 0x5415, 0x5440, 0x5441, 0x5444,
+ 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, 0x5500, 0x5501, 0x5504,
+ 0x5505, 0x5510, 0x5511, 0x5514, 0x5515, 0x5540, 0x5541, 0x5544,
+ 0x5545, 0x5550, 0x5551, 0x5554, 0x5555};
+
+ // pre-computed Bitmask for fast masking, bitMask[a]=0x1 << a
+ private static final int[] bitMask = {0x00000001, 0x00000002, 0x00000004,
+ 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080,
+ 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000,
+ 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000,
+ 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000,
+ 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x00000000};
+
+ // pre-computed Bitmask for fast masking, rightMask[a]=0xffffffff >>> (32-a)
+ private static final int[] reverseRightMask = {0x00000000, 0x00000001,
+ 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f,
+ 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
+ 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
+ 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff,
+ 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff,
+ 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
+ 0xffffffff};
+
+ /**
+ * Creates a new GF2Polynomial of the given <i>length</i> and value zero.
+ *
+ * @param length the desired number of bits to store
+ */
+ public GF2Polynomial(int length)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ }
+
+ /**
+ * Creates a new GF2Polynomial of the given <i>length</i> and random value.
+ *
+ * @param length the desired number of bits to store
+ * @param rand SecureRandom to use for randomization
+ */
+ public GF2Polynomial(int length, Random rand)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ randomize(rand);
+ }
+
+ /**
+ * Creates a new GF2Polynomial of the given <i>length</i> and value
+ * selected by <i>value</i>:
+ * <UL>
+ * <LI>ZERO</LI>
+ * <LI>ONE</LI>
+ * <LI>RANDOM</LI>
+ * <LI>X</LI>
+ * <LI>ALL</LI>
+ * </UL>
+ *
+ * @param length the desired number of bits to store
+ * @param value the value described by a String
+ */
+ public GF2Polynomial(int length, String value)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ this.value = new int[blocks];
+ len = l;
+ if (value.equalsIgnoreCase("ZERO"))
+ {
+ assignZero();
+ }
+ else if (value.equalsIgnoreCase("ONE"))
+ {
+ assignOne();
+ }
+ else if (value.equalsIgnoreCase("RANDOM"))
+ {
+ randomize();
+ }
+ else if (value.equalsIgnoreCase("X"))
+ {
+ assignX();
+ }
+ else if (value.equalsIgnoreCase("ALL"))
+ {
+ assignAll();
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "Error: GF2Polynomial was called using " + value
+ + " as value!");
+ }
+
+ }
+
+ /**
+ * Creates a new GF2Polynomial of the given <i>length</i> using the given
+ * int[]. LSB is contained in bs[0].
+ *
+ * @param length the desired number of bits to store
+ * @param bs contains the desired value, LSB in bs[0]
+ */
+ public GF2Polynomial(int length, int[] bs)
+ {
+ int leng = length;
+ if (leng < 1)
+ {
+ leng = 1;
+ }
+ blocks = ((leng - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = leng;
+ int l = Math.min(blocks, bs.length);
+ System.arraycopy(bs, 0, value, 0, l);
+ zeroUnusedBits();
+ }
+
+ /**
+ * Creates a new GF2Polynomial by converting the given byte[] <i>os</i>
+ * according to 1363 and using the given <i>length</i>.
+ *
+ * @param length the intended length of this polynomial
+ * @param os the octet string to assign to this polynomial
+ * @see "P1363 5.5.2 p22f, OS2BSP"
+ */
+ public GF2Polynomial(int length, byte[] os)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ int i, m;
+ int k = Math.min(((os.length - 1) >> 2) + 1, blocks);
+ for (i = 0; i < k - 1; i++)
+ {
+ m = os.length - (i << 2) - 1;
+ value[i] = (os[m]) & 0x000000ff;
+ value[i] |= (os[m - 1] << 8) & 0x0000ff00;
+ value[i] |= (os[m - 2] << 16) & 0x00ff0000;
+ value[i] |= (os[m - 3] << 24) & 0xff000000;
+ }
+ i = k - 1;
+ m = os.length - (i << 2) - 1;
+ value[i] = os[m] & 0x000000ff;
+ if (m > 0)
+ {
+ value[i] |= (os[m - 1] << 8) & 0x0000ff00;
+ }
+ if (m > 1)
+ {
+ value[i] |= (os[m - 2] << 16) & 0x00ff0000;
+ }
+ if (m > 2)
+ {
+ value[i] |= (os[m - 3] << 24) & 0xff000000;
+ }
+ zeroUnusedBits();
+ reduceN();
+ }
+
+ /**
+ * Creates a new GF2Polynomial by converting the given FlexiBigInt <i>bi</i>
+ * according to 1363 and using the given <i>length</i>.
+ *
+ * @param length the intended length of this polynomial
+ * @param bi the FlexiBigInt to assign to this polynomial
+ * @see "P1363 5.5.1 p22, I2BSP"
+ */
+ public GF2Polynomial(int length, BigInteger bi)
+ {
+ int l = length;
+ if (l < 1)
+ {
+ l = 1;
+ }
+ blocks = ((l - 1) >> 5) + 1;
+ value = new int[blocks];
+ len = l;
+ int i;
+ byte[] val = bi.toByteArray();
+ if (val[0] == 0)
+ {
+ byte[] dummy = new byte[val.length - 1];
+ System.arraycopy(val, 1, dummy, 0, dummy.length);
+ val = dummy;
+ }
+ int ov = val.length & 0x03;
+ int k = ((val.length - 1) >> 2) + 1;
+ for (i = 0; i < ov; i++)
+ {
+ value[k - 1] |= (val[i] & 0x000000ff) << ((ov - 1 - i) << 3);
+ }
+ int m = 0;
+ for (i = 0; i <= (val.length - 4) >> 2; i++)
+ {
+ m = val.length - 1 - (i << 2);
+ value[i] = (val[m]) & 0x000000ff;
+ value[i] |= ((val[m - 1]) << 8) & 0x0000ff00;
+ value[i] |= ((val[m - 2]) << 16) & 0x00ff0000;
+ value[i] |= ((val[m - 3]) << 24) & 0xff000000;
+ }
+ if ((len & 0x1f) != 0)
+ {
+ value[blocks - 1] &= reverseRightMask[len & 0x1f];
+ }
+ reduceN();
+ }
+
+ /**
+ * Creates a new GF2Polynomial by cloneing the given GF2Polynomial <i>b</i>.
+ *
+ * @param b the GF2Polynomial to clone
+ */
+ public GF2Polynomial(GF2Polynomial b)
+ {
+ len = b.len;
+ blocks = b.blocks;
+ value = IntUtils.clone(b.value);
+ }
+
+ /**
+ * @return a copy of this GF2Polynomial
+ */
+ public Object clone()
+ {
+ return new GF2Polynomial(this);
+ }
+
+ /**
+ * Returns the length of this GF2Polynomial. The length can be greater than
+ * the degree. To get the degree call reduceN() before calling getLength().
+ *
+ * @return the length of this GF2Polynomial
+ */
+ public int getLength()
+ {
+ return len;
+ }
+
+ /**
+ * Returns the value of this GF2Polynomial in an int[].
+ *
+ * @return the value of this GF2Polynomial in a new int[], LSB in int[0]
+ */
+ public int[] toIntegerArray()
+ {
+ int[] result;
+ result = new int[blocks];
+ System.arraycopy(value, 0, result, 0, blocks);
+ return result;
+ }
+
+ /**
+ * Returns a string representing this GF2Polynomials value using hexadecimal
+ * or binary radix in MSB-first order.
+ *
+ * @param radix the radix to use (2 or 16, otherwise 2 is used)
+ * @return a String representing this GF2Polynomials value.
+ */
+ public String toString(int radix)
+ {
+ final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
+ '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ final String[] BIN_CHARS = {"0000", "0001", "0010", "0011", "0100",
+ "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100",
+ "1101", "1110", "1111"};
+ String res;
+ int i;
+ res = new String();
+ if (radix == 16)
+ {
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ res += HEX_CHARS[(value[i] >>> 28) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 24) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 20) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 16) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 12) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 8) & 0x0f];
+ res += HEX_CHARS[(value[i] >>> 4) & 0x0f];
+ res += HEX_CHARS[(value[i]) & 0x0f];
+ res += " ";
+ }
+ }
+ else
+ {
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ res += BIN_CHARS[(value[i] >>> 28) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 24) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 20) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 16) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 12) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 8) & 0x0f];
+ res += BIN_CHARS[(value[i] >>> 4) & 0x0f];
+ res += BIN_CHARS[(value[i]) & 0x0f];
+ res += " ";
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Converts this polynomial to a byte[] (octet string) according to 1363.
+ *
+ * @return a byte[] representing the value of this polynomial
+ * @see "P1363 5.5.2 p22f, BS2OSP"
+ */
+ public byte[] toByteArray()
+ {
+ int k = ((len - 1) >> 3) + 1;
+ int ov = k & 0x03;
+ int m;
+ byte[] res = new byte[k];
+ int i;
+ for (i = 0; i < (k >> 2); i++)
+ {
+ m = k - (i << 2) - 1;
+ res[m] = (byte)((value[i] & 0x000000ff));
+ res[m - 1] = (byte)((value[i] & 0x0000ff00) >>> 8);
+ res[m - 2] = (byte)((value[i] & 0x00ff0000) >>> 16);
+ res[m - 3] = (byte)((value[i] & 0xff000000) >>> 24);
+ }
+ for (i = 0; i < ov; i++)
+ {
+ m = (ov - i - 1) << 3;
+ res[i] = (byte)((value[blocks - 1] & (0x000000ff << m)) >>> m);
+ }
+ return res;
+ }
+
+ /**
+ * Converts this polynomial to an integer according to 1363.
+ *
+ * @return a FlexiBigInt representing the value of this polynomial
+ * @see "P1363 5.5.1 p22, BS2IP"
+ */
+ public BigInteger toFlexiBigInt()
+ {
+ if (len == 0 || isZero())
+ {
+ return new BigInteger(0, new byte[0]);
+ }
+ return new BigInteger(1, toByteArray());
+ }
+
+ /**
+ * Sets the LSB to 1 and all other to 0, assigning 'one' to this
+ * GF2Polynomial.
+ */
+ public void assignOne()
+ {
+ int i;
+ for (i = 1; i < blocks; i++)
+ {
+ value[i] = 0x00;
+ }
+ value[0] = 0x01;
+ }
+
+ /**
+ * Sets Bit 1 to 1 and all other to 0, assigning 'x' to this GF2Polynomial.
+ */
+ public void assignX()
+ {
+ int i;
+ for (i = 1; i < blocks; i++)
+ {
+ value[i] = 0x00;
+ }
+ value[0] = 0x02;
+ }
+
+ /**
+ * Sets all Bits to 1.
+ */
+ public void assignAll()
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = 0xffffffff;
+ }
+ zeroUnusedBits();
+ }
+
+ /**
+ * Resets all bits to zero.
+ */
+ public void assignZero()
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = 0x00;
+ }
+ }
+
+ /**
+ * Fills all len bits of this GF2Polynomial with random values.
+ */
+ public void randomize()
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = rand.nextInt();
+ }
+ zeroUnusedBits();
+ }
+
+ /**
+ * Fills all len bits of this GF2Polynomial with random values using the
+ * specified source of randomness.
+ *
+ * @param rand the source of randomness
+ */
+ public void randomize(Random rand)
+ {
+ int i;
+ for (i = 0; i < blocks; i++)
+ {
+ value[i] = rand.nextInt();
+ }
+ zeroUnusedBits();
+ }
+
+ /**
+ * Returns true if two GF2Polynomials have the same size and value and thus
+ * are equal.
+ *
+ * @param other the other GF2Polynomial
+ * @return true if this GF2Polynomial equals <i>b</i> (<i>this</i> ==
+ * <i>b</i>)
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2Polynomial))
+ {
+ return false;
+ }
+
+ GF2Polynomial otherPol = (GF2Polynomial)other;
+
+ if (len != otherPol.len)
+ {
+ return false;
+ }
+ for (int i = 0; i < blocks; i++)
+ {
+ if (value[i] != otherPol.value[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return the hash code of this polynomial
+ */
+ public int hashCode()
+ {
+ return len + value.hashCode();
+ }
+
+ /**
+ * Tests if all bits equal zero.
+ *
+ * @return true if this GF2Polynomial equals 'zero' (<i>this</i> == 0)
+ */
+ public boolean isZero()
+ {
+ int i;
+ if (len == 0)
+ {
+ return true;
+ }
+ for (i = 0; i < blocks; i++)
+ {
+ if (value[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tests if all bits are reset to 0 and LSB is set to 1.
+ *
+ * @return true if this GF2Polynomial equals 'one' (<i>this</i> == 1)
+ */
+ public boolean isOne()
+ {
+ int i;
+ for (i = 1; i < blocks; i++)
+ {
+ if (value[i] != 0)
+ {
+ return false;
+ }
+ }
+ if (value[0] != 0x01)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Adds <i>b</i> to this GF2Polynomial and assigns the result to this
+ * GF2Polynomial. <i>b</i> can be of different size.
+ *
+ * @param b GF2Polynomial to add to this GF2Polynomial
+ */
+ public void addToThis(GF2Polynomial b)
+ {
+ expandN(b.len);
+ xorThisBy(b);
+ }
+
+ /**
+ * Adds two GF2Polynomials, <i>this</i> and <i>b</i>, and returns the
+ * result. <i>this</i> and <i>b</i> can be of different size.
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (<i>this</i> + <i>b</i>)
+ */
+ public GF2Polynomial add(GF2Polynomial b)
+ {
+ return xor(b);
+ }
+
+ /**
+ * Subtracts <i>b</i> from this GF2Polynomial and assigns the result to
+ * this GF2Polynomial. <i>b</i> can be of different size.
+ *
+ * @param b a GF2Polynomial
+ */
+ public void subtractFromThis(GF2Polynomial b)
+ {
+ expandN(b.len);
+ xorThisBy(b);
+ }
+
+ /**
+ * Subtracts two GF2Polynomials, <i>this</i> and <i>b</i>, and returns the
+ * result in a new GF2Polynomial. <i>this</i> and <i>b</i> can be of
+ * different size.
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (<i>this</i> - <i>b</i>)
+ */
+ public GF2Polynomial subtract(GF2Polynomial b)
+ {
+ return xor(b);
+ }
+
+ /**
+ * Toggles the LSB of this GF2Polynomial, increasing its value by 'one'.
+ */
+ public void increaseThis()
+ {
+ xorBit(0);
+ }
+
+ /**
+ * Toggles the LSB of this GF2Polynomial, increasing the value by 'one' and
+ * returns the result in a new GF2Polynomial.
+ *
+ * @return <tt>this + 1</tt>
+ */
+ public GF2Polynomial increase()
+ {
+ GF2Polynomial result = new GF2Polynomial(this);
+ result.increaseThis();
+ return result;
+ }
+
+ /**
+ * Multiplies this GF2Polynomial with <i>b</i> and returns the result in a
+ * new GF2Polynomial. This method does not reduce the result in GF(2^N).
+ * This method uses classic multiplication (schoolbook).
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (<i>this</i> * <i>b</i>)
+ */
+ public GF2Polynomial multiplyClassic(GF2Polynomial b)
+ {
+ GF2Polynomial result = new GF2Polynomial(Math.max(len, b.len) << 1);
+ GF2Polynomial[] m = new GF2Polynomial[32];
+ int i, j;
+ m[0] = new GF2Polynomial(this);
+ for (i = 1; i <= 31; i++)
+ {
+ m[i] = m[i - 1].shiftLeft();
+ }
+ for (i = 0; i < b.blocks; i++)
+ {
+ for (j = 0; j <= 31; j++)
+ {
+ if ((b.value[i] & bitMask[j]) != 0)
+ {
+ result.xorThisBy(m[j]);
+ }
+ }
+ for (j = 0; j <= 31; j++)
+ {
+ m[j].shiftBlocksLeft();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies this GF2Polynomial with <i>b</i> and returns the result in a
+ * new GF2Polynomial. This method does not reduce the result in GF(2^N).
+ * This method uses Karatzuba multiplication.
+ *
+ * @param b a GF2Polynomial
+ * @return a new GF2Polynomial (<i>this</i> * <i>b</i>)
+ */
+ public GF2Polynomial multiply(GF2Polynomial b)
+ {
+ int n = Math.max(len, b.len);
+ expandN(n);
+ b.expandN(n);
+ return karaMult(b);
+ }
+
+ /**
+ * Does the recursion for Karatzuba multiplication.
+ */
+ private GF2Polynomial karaMult(GF2Polynomial b)
+ {
+ GF2Polynomial result = new GF2Polynomial(len << 1);
+ if (len <= 32)
+ {
+ result.value = mult32(value[0], b.value[0]);
+ return result;
+ }
+ if (len <= 64)
+ {
+ result.value = mult64(value, b.value);
+ return result;
+ }
+ if (len <= 128)
+ {
+ result.value = mult128(value, b.value);
+ return result;
+ }
+ if (len <= 256)
+ {
+ result.value = mult256(value, b.value);
+ return result;
+ }
+ if (len <= 512)
+ {
+ result.value = mult512(value, b.value);
+ return result;
+ }
+
+ int n = IntegerFunctions.floorLog(len - 1);
+ n = bitMask[n];
+
+ GF2Polynomial a0 = lower(((n - 1) >> 5) + 1);
+ GF2Polynomial a1 = upper(((n - 1) >> 5) + 1);
+ GF2Polynomial b0 = b.lower(((n - 1) >> 5) + 1);
+ GF2Polynomial b1 = b.upper(((n - 1) >> 5) + 1);
+
+ GF2Polynomial c = a1.karaMult(b1); // c = a1*b1
+ GF2Polynomial e = a0.karaMult(b0); // e = a0*b0
+ a0.addToThis(a1); // a0 = a0 + a1
+ b0.addToThis(b1); // b0 = b0 + b1
+ GF2Polynomial d = a0.karaMult(b0); // d = (a0+a1)*(b0+b1)
+
+ result.shiftLeftAddThis(c, n << 1);
+ result.shiftLeftAddThis(c, n);
+ result.shiftLeftAddThis(d, n);
+ result.shiftLeftAddThis(e, n);
+ result.addToThis(e);
+ return result;
+ }
+
+ /**
+ * 16-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult512(int[] a, int[] b)
+ {
+ int[] result = new int[32];
+ int[] a0 = new int[8];
+ System.arraycopy(a, 0, a0, 0, Math.min(8, a.length));
+ int[] a1 = new int[8];
+ if (a.length > 8)
+ {
+ System.arraycopy(a, 8, a1, 0, Math.min(8, a.length - 8));
+ }
+ int[] b0 = new int[8];
+ System.arraycopy(b, 0, b0, 0, Math.min(8, b.length));
+ int[] b1 = new int[8];
+ if (b.length > 8)
+ {
+ System.arraycopy(b, 8, b1, 0, Math.min(8, b.length - 8));
+ }
+ int[] c = mult256(a1, b1);
+ result[31] ^= c[15];
+ result[30] ^= c[14];
+ result[29] ^= c[13];
+ result[28] ^= c[12];
+ result[27] ^= c[11];
+ result[26] ^= c[10];
+ result[25] ^= c[9];
+ result[24] ^= c[8];
+ result[23] ^= c[7] ^ c[15];
+ result[22] ^= c[6] ^ c[14];
+ result[21] ^= c[5] ^ c[13];
+ result[20] ^= c[4] ^ c[12];
+ result[19] ^= c[3] ^ c[11];
+ result[18] ^= c[2] ^ c[10];
+ result[17] ^= c[1] ^ c[9];
+ result[16] ^= c[0] ^ c[8];
+ result[15] ^= c[7];
+ result[14] ^= c[6];
+ result[13] ^= c[5];
+ result[12] ^= c[4];
+ result[11] ^= c[3];
+ result[10] ^= c[2];
+ result[9] ^= c[1];
+ result[8] ^= c[0];
+ a1[0] ^= a0[0];
+ a1[1] ^= a0[1];
+ a1[2] ^= a0[2];
+ a1[3] ^= a0[3];
+ a1[4] ^= a0[4];
+ a1[5] ^= a0[5];
+ a1[6] ^= a0[6];
+ a1[7] ^= a0[7];
+ b1[0] ^= b0[0];
+ b1[1] ^= b0[1];
+ b1[2] ^= b0[2];
+ b1[3] ^= b0[3];
+ b1[4] ^= b0[4];
+ b1[5] ^= b0[5];
+ b1[6] ^= b0[6];
+ b1[7] ^= b0[7];
+ int[] d = mult256(a1, b1);
+ result[23] ^= d[15];
+ result[22] ^= d[14];
+ result[21] ^= d[13];
+ result[20] ^= d[12];
+ result[19] ^= d[11];
+ result[18] ^= d[10];
+ result[17] ^= d[9];
+ result[16] ^= d[8];
+ result[15] ^= d[7];
+ result[14] ^= d[6];
+ result[13] ^= d[5];
+ result[12] ^= d[4];
+ result[11] ^= d[3];
+ result[10] ^= d[2];
+ result[9] ^= d[1];
+ result[8] ^= d[0];
+ int[] e = mult256(a0, b0);
+ result[23] ^= e[15];
+ result[22] ^= e[14];
+ result[21] ^= e[13];
+ result[20] ^= e[12];
+ result[19] ^= e[11];
+ result[18] ^= e[10];
+ result[17] ^= e[9];
+ result[16] ^= e[8];
+ result[15] ^= e[7] ^ e[15];
+ result[14] ^= e[6] ^ e[14];
+ result[13] ^= e[5] ^ e[13];
+ result[12] ^= e[4] ^ e[12];
+ result[11] ^= e[3] ^ e[11];
+ result[10] ^= e[2] ^ e[10];
+ result[9] ^= e[1] ^ e[9];
+ result[8] ^= e[0] ^ e[8];
+ result[7] ^= e[7];
+ result[6] ^= e[6];
+ result[5] ^= e[5];
+ result[4] ^= e[4];
+ result[3] ^= e[3];
+ result[2] ^= e[2];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ return result;
+ }
+
+ /**
+ * 8-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult256(int[] a, int[] b)
+ {
+ int[] result = new int[16];
+ int[] a0 = new int[4];
+ System.arraycopy(a, 0, a0, 0, Math.min(4, a.length));
+ int[] a1 = new int[4];
+ if (a.length > 4)
+ {
+ System.arraycopy(a, 4, a1, 0, Math.min(4, a.length - 4));
+ }
+ int[] b0 = new int[4];
+ System.arraycopy(b, 0, b0, 0, Math.min(4, b.length));
+ int[] b1 = new int[4];
+ if (b.length > 4)
+ {
+ System.arraycopy(b, 4, b1, 0, Math.min(4, b.length - 4));
+ }
+ if (a1[3] == 0 && a1[2] == 0 && b1[3] == 0 && b1[2] == 0)
+ {
+ if (a1[1] == 0 && b1[1] == 0)
+ {
+ if (a1[0] != 0 || b1[0] != 0)
+ { // [3]=[2]=[1]=0, [0]!=0
+ int[] c = mult32(a1[0], b1[0]);
+ result[9] ^= c[1];
+ result[8] ^= c[0];
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ }
+ }
+ else
+ { // [3]=[2]=0 [1]!=0, [0]!=0
+ int[] c = mult64(a1, b1);
+ result[11] ^= c[3];
+ result[10] ^= c[2];
+ result[9] ^= c[1];
+ result[8] ^= c[0];
+ result[7] ^= c[3];
+ result[6] ^= c[2];
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ }
+ }
+ else
+ { // [3]!=0 [2]!=0 [1]!=0, [0]!=0
+ int[] c = mult128(a1, b1);
+ result[15] ^= c[7];
+ result[14] ^= c[6];
+ result[13] ^= c[5];
+ result[12] ^= c[4];
+ result[11] ^= c[3] ^ c[7];
+ result[10] ^= c[2] ^ c[6];
+ result[9] ^= c[1] ^ c[5];
+ result[8] ^= c[0] ^ c[4];
+ result[7] ^= c[3];
+ result[6] ^= c[2];
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ }
+ a1[0] ^= a0[0];
+ a1[1] ^= a0[1];
+ a1[2] ^= a0[2];
+ a1[3] ^= a0[3];
+ b1[0] ^= b0[0];
+ b1[1] ^= b0[1];
+ b1[2] ^= b0[2];
+ b1[3] ^= b0[3];
+ int[] d = mult128(a1, b1);
+ result[11] ^= d[7];
+ result[10] ^= d[6];
+ result[9] ^= d[5];
+ result[8] ^= d[4];
+ result[7] ^= d[3];
+ result[6] ^= d[2];
+ result[5] ^= d[1];
+ result[4] ^= d[0];
+ int[] e = mult128(a0, b0);
+ result[11] ^= e[7];
+ result[10] ^= e[6];
+ result[9] ^= e[5];
+ result[8] ^= e[4];
+ result[7] ^= e[3] ^ e[7];
+ result[6] ^= e[2] ^ e[6];
+ result[5] ^= e[1] ^ e[5];
+ result[4] ^= e[0] ^ e[4];
+ result[3] ^= e[3];
+ result[2] ^= e[2];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ return result;
+ }
+
+ /**
+ * 4-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult128(int[] a, int[] b)
+ {
+ int[] result = new int[8];
+ int[] a0 = new int[2];
+ System.arraycopy(a, 0, a0, 0, Math.min(2, a.length));
+ int[] a1 = new int[2];
+ if (a.length > 2)
+ {
+ System.arraycopy(a, 2, a1, 0, Math.min(2, a.length - 2));
+ }
+ int[] b0 = new int[2];
+ System.arraycopy(b, 0, b0, 0, Math.min(2, b.length));
+ int[] b1 = new int[2];
+ if (b.length > 2)
+ {
+ System.arraycopy(b, 2, b1, 0, Math.min(2, b.length - 2));
+ }
+ if (a1[1] == 0 && b1[1] == 0)
+ {
+ if (a1[0] != 0 || b1[0] != 0)
+ {
+ int[] c = mult32(a1[0], b1[0]);
+ result[5] ^= c[1];
+ result[4] ^= c[0];
+ result[3] ^= c[1];
+ result[2] ^= c[0];
+ }
+ }
+ else
+ {
+ int[] c = mult64(a1, b1);
+ result[7] ^= c[3];
+ result[6] ^= c[2];
+ result[5] ^= c[1] ^ c[3];
+ result[4] ^= c[0] ^ c[2];
+ result[3] ^= c[1];
+ result[2] ^= c[0];
+ }
+ a1[0] ^= a0[0];
+ a1[1] ^= a0[1];
+ b1[0] ^= b0[0];
+ b1[1] ^= b0[1];
+ if (a1[1] == 0 && b1[1] == 0)
+ {
+ int[] d = mult32(a1[0], b1[0]);
+ result[3] ^= d[1];
+ result[2] ^= d[0];
+ }
+ else
+ {
+ int[] d = mult64(a1, b1);
+ result[5] ^= d[3];
+ result[4] ^= d[2];
+ result[3] ^= d[1];
+ result[2] ^= d[0];
+ }
+ if (a0[1] == 0 && b0[1] == 0)
+ {
+ int[] e = mult32(a0[0], b0[0]);
+ result[3] ^= e[1];
+ result[2] ^= e[0];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ }
+ else
+ {
+ int[] e = mult64(a0, b0);
+ result[5] ^= e[3];
+ result[4] ^= e[2];
+ result[3] ^= e[1] ^ e[3];
+ result[2] ^= e[0] ^ e[2];
+ result[1] ^= e[1];
+ result[0] ^= e[0];
+ }
+ return result;
+ }
+
+ /**
+ * 2-Integer Version of Karatzuba multiplication.
+ */
+ private static int[] mult64(int[] a, int[] b)
+ {
+ int[] result = new int[4];
+ int a0 = a[0];
+ int a1 = 0;
+ if (a.length > 1)
+ {
+ a1 = a[1];
+ }
+ int b0 = b[0];
+ int b1 = 0;
+ if (b.length > 1)
+ {
+ b1 = b[1];
+ }
+ if (a1 != 0 || b1 != 0)
+ {
+ int[] c = mult32(a1, b1);
+ result[3] ^= c[1];
+ result[2] ^= c[0] ^ c[1];
+ result[1] ^= c[0];
+ }
+ int[] d = mult32(a0 ^ a1, b0 ^ b1);
+ result[2] ^= d[1];
+ result[1] ^= d[0];
+ int[] e = mult32(a0, b0);
+ result[2] ^= e[1];
+ result[1] ^= e[0] ^ e[1];
+ result[0] ^= e[0];
+ return result;
+ }
+
+ /**
+ * 4-Byte Version of Karatzuba multiplication. Here the actual work is done.
+ */
+ private static int[] mult32(int a, int b)
+ {
+ int[] result = new int[2];
+ if (a == 0 || b == 0)
+ {
+ return result;
+ }
+ long b2 = b;
+ b2 &= 0x00000000ffffffffL;
+ int i;
+ long h = 0;
+ for (i = 1; i <= 32; i++)
+ {
+ if ((a & bitMask[i - 1]) != 0)
+ {
+ h ^= b2;
+ }
+ b2 <<= 1;
+ }
+ result[1] = (int)(h >>> 32);
+ result[0] = (int)(h & 0x00000000ffffffffL);
+ return result;
+ }
+
+ /**
+ * Returns a new GF2Polynomial containing the upper <i>k</i> bytes of this
+ * GF2Polynomial.
+ *
+ * @param k
+ * @return a new GF2Polynomial containing the upper <i>k</i> bytes of this
+ * GF2Polynomial
+ * @see GF2Polynomial#karaMult
+ */
+ private GF2Polynomial upper(int k)
+ {
+ int j = Math.min(k, blocks - k);
+ GF2Polynomial result = new GF2Polynomial(j << 5);
+ if (blocks >= k)
+ {
+ System.arraycopy(value, k, result.value, 0, j);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a new GF2Polynomial containing the lower <i>k</i> bytes of this
+ * GF2Polynomial.
+ *
+ * @param k
+ * @return a new GF2Polynomial containing the lower <i>k</i> bytes of this
+ * GF2Polynomial
+ * @see GF2Polynomial#karaMult
+ */
+ private GF2Polynomial lower(int k)
+ {
+ GF2Polynomial result = new GF2Polynomial(k << 5);
+ System.arraycopy(value, 0, result.value, 0, Math.min(k, blocks));
+ return result;
+ }
+
+ /**
+ * Returns the remainder of <i>this</i> divided by <i>g</i> in a new
+ * GF2Polynomial.
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial (<i>this</i> % <i>g</i>)
+ * @throws PolynomialIsZeroException if <i>g</i> equals zero
+ */
+ public GF2Polynomial remainder(GF2Polynomial g)
+ throws RuntimeException
+ {
+ /* a div b = q / r */
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial j;
+ int i;
+ if (b.isZero())
+ {
+ throw new RuntimeException();
+ }
+ a.reduceN();
+ b.reduceN();
+ if (a.len < b.len)
+ {
+ return a;
+ }
+ i = a.len - b.len;
+ while (i >= 0)
+ {
+ j = b.shiftLeft(i);
+ a.subtractFromThis(j);
+ a.reduceN();
+ i = a.len - b.len;
+ }
+ return a;
+ }
+
+ /**
+ * Returns the absolute quotient of <i>this</i> divided by <i>g</i> in a
+ * new GF2Polynomial.
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial |_ <i>this</i> / <i>g</i> _|
+ * @throws PolynomialIsZeroException if <i>g</i> equals zero
+ */
+ public GF2Polynomial quotient(GF2Polynomial g)
+ throws RuntimeException
+ {
+ /* a div b = q / r */
+ GF2Polynomial q = new GF2Polynomial(len);
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial j;
+ int i;
+ if (b.isZero())
+ {
+ throw new RuntimeException();
+ }
+ a.reduceN();
+ b.reduceN();
+ if (a.len < b.len)
+ {
+ return new GF2Polynomial(0);
+ }
+ i = a.len - b.len;
+ q.expandN(i + 1);
+
+ while (i >= 0)
+ {
+ j = b.shiftLeft(i);
+ a.subtractFromThis(j);
+ a.reduceN();
+ q.xorBit(i);
+ i = a.len - b.len;
+ }
+
+ return q;
+ }
+
+ /**
+ * Divides <i>this</i> by <i>g</i> and returns the quotient and remainder
+ * in a new GF2Polynomial[2], quotient in [0], remainder in [1].
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial[2] containing quotient and remainder
+ * @throws PolynomialIsZeroException if <i>g</i> equals zero
+ */
+ public GF2Polynomial[] divide(GF2Polynomial g)
+ throws RuntimeException
+ {
+ /* a div b = q / r */
+ GF2Polynomial[] result = new GF2Polynomial[2];
+ GF2Polynomial q = new GF2Polynomial(len);
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial j;
+ int i;
+ if (b.isZero())
+ {
+ throw new RuntimeException();
+ }
+ a.reduceN();
+ b.reduceN();
+ if (a.len < b.len)
+ {
+ result[0] = new GF2Polynomial(0);
+ result[1] = a;
+ return result;
+ }
+ i = a.len - b.len;
+ q.expandN(i + 1);
+
+ while (i >= 0)
+ {
+ j = b.shiftLeft(i);
+ a.subtractFromThis(j);
+ a.reduceN();
+ q.xorBit(i);
+ i = a.len - b.len;
+ }
+
+ result[0] = q;
+ result[1] = a;
+ return result;
+ }
+
+ /**
+ * Returns the greatest common divisor of <i>this</i> and <i>g</i> in a
+ * new GF2Polynomial.
+ *
+ * @param g GF2Polynomial != 0
+ * @return a new GF2Polynomial gcd(<i>this</i>,<i>g</i>)
+ * @throws ArithmeticException if <i>this</i> and <i>g</i> both are equal to zero
+ * @throws PolynomialIsZeroException to be API-compliant (should never be thrown).
+ */
+ public GF2Polynomial gcd(GF2Polynomial g)
+ throws RuntimeException
+ {
+ if (isZero() && g.isZero())
+ {
+ throw new ArithmeticException("Both operands of gcd equal zero.");
+ }
+ if (isZero())
+ {
+ return new GF2Polynomial(g);
+ }
+ if (g.isZero())
+ {
+ return new GF2Polynomial(this);
+ }
+ GF2Polynomial a = new GF2Polynomial(this);
+ GF2Polynomial b = new GF2Polynomial(g);
+ GF2Polynomial c;
+
+ while (!b.isZero())
+ {
+ c = a.remainder(b);
+ a = b;
+ b = c;
+ }
+
+ return a;
+ }
+
+ /**
+ * Checks if <i>this</i> is irreducible, according to IEEE P1363, A.5.5,
+ * p103.<br>
+ * Note: The algorithm from IEEE P1363, A5.5 can be used to check a
+ * polynomial with coefficients in GF(2^r) for irreducibility. As this class
+ * only represents polynomials with coefficients in GF(2), the algorithm is
+ * adapted to the case r=1.
+ *
+ * @return true if <i>this</i> is irreducible
+ * @see "P1363, A.5.5, p103"
+ */
+ public boolean isIrreducible()
+ {
+ if (isZero())
+ {
+ return false;
+ }
+ GF2Polynomial f = new GF2Polynomial(this);
+ int d, i;
+ GF2Polynomial u, g;
+ GF2Polynomial dummy;
+ f.reduceN();
+ d = f.len - 1;
+ u = new GF2Polynomial(f.len, "X");
+
+ for (i = 1; i <= (d >> 1); i++)
+ {
+ u.squareThisPreCalc();
+ u = u.remainder(f);
+ dummy = u.add(new GF2Polynomial(32, "X"));
+ if (!dummy.isZero())
+ {
+ g = f.gcd(dummy);
+ if (!g.isOne())
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Reduces this GF2Polynomial using the trinomial x^<i>m</i> + x^<i>tc</i> +
+ * 1.
+ *
+ * @param m the degree of the used field
+ * @param tc degree of the middle x in the trinomial
+ */
+ void reduceTrinomial(int m, int tc)
+ {
+ int i;
+ int p0, p1;
+ int q0, q1;
+ long t;
+ p0 = m >>> 5; // block which contains 2^m
+ q0 = 32 - (m & 0x1f); // (32-index) of 2^m within block p0
+ p1 = (m - tc) >>> 5; // block which contains 2^tc
+ q1 = 32 - ((m - tc) & 0x1f); // (32-index) of 2^tc within block q1
+ int max = ((m << 1) - 2) >>> 5; // block which contains 2^(2m-2)
+ int min = p0; // block which contains 2^m
+ for (i = max; i > min; i--)
+ { // for i = maxBlock to minBlock
+ // reduce coefficients contained in t
+ // t = block[i]
+ t = value[i] & 0x00000000ffffffffL;
+ // block[i-p0-1] ^= t << q0
+ value[i - p0 - 1] ^= (int)(t << q0);
+ // block[i-p0] ^= t >>> (32-q0)
+ value[i - p0] ^= t >>> (32 - q0);
+ // block[i-p1-1] ^= << q1
+ value[i - p1 - 1] ^= (int)(t << q1);
+ // block[i-p1] ^= t >>> (32-q1)
+ value[i - p1] ^= t >>> (32 - q1);
+ value[i] = 0x00;
+ }
+ // reduce last coefficients in block containing 2^m
+ t = value[min] & 0x00000000ffffffffL & (0xffffffffL << (m & 0x1f)); // t
+ // contains the last coefficients > m
+ value[0] ^= t >>> (32 - q0);
+ if (min - p1 - 1 >= 0)
+ {
+ value[min - p1 - 1] ^= (int)(t << q1);
+ }
+ value[min - p1] ^= t >>> (32 - q1);
+
+ value[min] &= reverseRightMask[m & 0x1f];
+ blocks = ((m - 1) >>> 5) + 1;
+ len = m;
+ }
+
+ /**
+ * Reduces this GF2Polynomial using the pentanomial x^<i>m</i> + x^<i>pc[2]</i> +
+ * x^<i>pc[1]</i> + x^<i>pc[0]</i> + 1.
+ *
+ * @param m the degree of the used field
+ * @param pc degrees of the middle x's in the pentanomial
+ */
+ void reducePentanomial(int m, int[] pc)
+ {
+ int i;
+ int p0, p1, p2, p3;
+ int q0, q1, q2, q3;
+ long t;
+ p0 = m >>> 5;
+ q0 = 32 - (m & 0x1f);
+ p1 = (m - pc[0]) >>> 5;
+ q1 = 32 - ((m - pc[0]) & 0x1f);
+ p2 = (m - pc[1]) >>> 5;
+ q2 = 32 - ((m - pc[1]) & 0x1f);
+ p3 = (m - pc[2]) >>> 5;
+ q3 = 32 - ((m - pc[2]) & 0x1f);
+ int max = ((m << 1) - 2) >>> 5;
+ int min = p0;
+ for (i = max; i > min; i--)
+ {
+ t = value[i] & 0x00000000ffffffffL;
+ value[i - p0 - 1] ^= (int)(t << q0);
+ value[i - p0] ^= t >>> (32 - q0);
+ value[i - p1 - 1] ^= (int)(t << q1);
+ value[i - p1] ^= t >>> (32 - q1);
+ value[i - p2 - 1] ^= (int)(t << q2);
+ value[i - p2] ^= t >>> (32 - q2);
+ value[i - p3 - 1] ^= (int)(t << q3);
+ value[i - p3] ^= t >>> (32 - q3);
+ value[i] = 0;
+ }
+ t = value[min] & 0x00000000ffffffffL & (0xffffffffL << (m & 0x1f));
+ value[0] ^= t >>> (32 - q0);
+ if (min - p1 - 1 >= 0)
+ {
+ value[min - p1 - 1] ^= (int)(t << q1);
+ }
+ value[min - p1] ^= t >>> (32 - q1);
+ if (min - p2 - 1 >= 0)
+ {
+ value[min - p2 - 1] ^= (int)(t << q2);
+ }
+ value[min - p2] ^= t >>> (32 - q2);
+ if (min - p3 - 1 >= 0)
+ {
+ value[min - p3 - 1] ^= (int)(t << q3);
+ }
+ value[min - p3] ^= t >>> (32 - q3);
+ value[min] &= reverseRightMask[m & 0x1f];
+
+ blocks = ((m - 1) >>> 5) + 1;
+ len = m;
+ }
+
+ /**
+ * Reduces len by finding the most significant bit set to one and reducing
+ * len and blocks.
+ */
+ public void reduceN()
+ {
+ int i, j, h;
+ i = blocks - 1;
+ while ((value[i] == 0) && (i > 0))
+ {
+ i--;
+ }
+ h = value[i];
+ j = 0;
+ while (h != 0)
+ {
+ h >>>= 1;
+ j++;
+ }
+ len = (i << 5) + j;
+ blocks = i + 1;
+ }
+
+ /**
+ * Expands len and int[] value to <i>i</i>. This is useful before adding
+ * two GF2Polynomials of different size.
+ *
+ * @param i the intended length
+ */
+ public void expandN(int i)
+ {
+ int k;
+ int[] bs;
+ if (len >= i)
+ {
+ return;
+ }
+ len = i;
+ k = ((i - 1) >>> 5) + 1;
+ if (blocks >= k)
+ {
+ return;
+ }
+ if (value.length >= k)
+ {
+ int j;
+ for (j = blocks; j < k; j++)
+ {
+ value[j] = 0;
+ }
+ blocks = k;
+ return;
+ }
+ bs = new int[k];
+ System.arraycopy(value, 0, bs, 0, blocks);
+ blocks = k;
+ value = null;
+ value = bs;
+ }
+
+ /**
+ * Squares this GF2Polynomial and expands it accordingly. This method does
+ * not reduce the result in GF(2^N). There exists a faster method for
+ * squaring in GF(2^N).
+ *
+ * @see GF2nPolynomialElement#square
+ */
+ public void squareThisBitwise()
+ {
+ int i, h, j, k;
+ if (isZero())
+ {
+ return;
+ }
+ int[] result = new int[blocks << 1];
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ h = value[i];
+ j = 0x00000001;
+ for (k = 0; k < 16; k++)
+ {
+ if ((h & 0x01) != 0)
+ {
+ result[i << 1] |= j;
+ }
+ if ((h & 0x00010000) != 0)
+ {
+ result[(i << 1) + 1] |= j;
+ }
+ j <<= 2;
+ h >>>= 1;
+ }
+ }
+ value = null;
+ value = result;
+ blocks = result.length;
+ len = (len << 1) - 1;
+ }
+
+ /**
+ * Squares this GF2Polynomial by using precomputed values of squaringTable.
+ * This method does not reduce the result in GF(2^N).
+ */
+ public void squareThisPreCalc()
+ {
+ int i;
+ if (isZero())
+ {
+ return;
+ }
+ if (value.length >= (blocks << 1))
+ {
+ for (i = blocks - 1; i >= 0; i--)
+ {
+ value[(i << 1) + 1] = GF2Polynomial.squaringTable[(value[i] & 0x00ff0000) >>> 16]
+ | (GF2Polynomial.squaringTable[(value[i] & 0xff000000) >>> 24] << 16);
+ value[i << 1] = GF2Polynomial.squaringTable[value[i] & 0x000000ff]
+ | (GF2Polynomial.squaringTable[(value[i] & 0x0000ff00) >>> 8] << 16);
+ }
+ blocks <<= 1;
+ len = (len << 1) - 1;
+ }
+ else
+ {
+ int[] result = new int[blocks << 1];
+ for (i = 0; i < blocks; i++)
+ {
+ result[i << 1] = GF2Polynomial.squaringTable[value[i] & 0x000000ff]
+ | (GF2Polynomial.squaringTable[(value[i] & 0x0000ff00) >>> 8] << 16);
+ result[(i << 1) + 1] = GF2Polynomial.squaringTable[(value[i] & 0x00ff0000) >>> 16]
+ | (GF2Polynomial.squaringTable[(value[i] & 0xff000000) >>> 24] << 16);
+ }
+ value = null;
+ value = result;
+ blocks <<= 1;
+ len = (len << 1) - 1;
+ }
+ }
+
+ /**
+ * Does a vector-multiplication modulo 2 and returns the result as boolean.
+ *
+ * @param b GF2Polynomial
+ * @return this x <i>b</i> as boolean (1-&gt;true, 0-&gt;false)
+ * @throws PolynomialsHaveDifferentLengthException if <i>this</i> and <i>b</i> have a different length and
+ * thus cannot be vector-multiplied
+ */
+ public boolean vectorMult(GF2Polynomial b)
+ throws RuntimeException
+ {
+ int i;
+ int h;
+ boolean result = false;
+ if (len != b.len)
+ {
+ throw new RuntimeException();
+ }
+ for (i = 0; i < blocks; i++)
+ {
+ h = value[i] & b.value[i];
+ result ^= parity[h & 0x000000ff];
+ result ^= parity[(h >>> 8) & 0x000000ff];
+ result ^= parity[(h >>> 16) & 0x000000ff];
+ result ^= parity[(h >>> 24) & 0x000000ff];
+ }
+ return result;
+ }
+
+ /**
+ * Returns the bitwise exclusive-or of <i>this</i> and <i>b</i> in a new
+ * GF2Polynomial. <i>this</i> and <i>b</i> can be of different size.
+ *
+ * @param b GF2Polynomial
+ * @return a new GF2Polynomial (<i>this</i> ^ <i>b</i>)
+ */
+ public GF2Polynomial xor(GF2Polynomial b)
+ {
+ int i;
+ GF2Polynomial result;
+ int k = Math.min(blocks, b.blocks);
+ if (len >= b.len)
+ {
+ result = new GF2Polynomial(this);
+ for (i = 0; i < k; i++)
+ {
+ result.value[i] ^= b.value[i];
+ }
+ }
+ else
+ {
+ result = new GF2Polynomial(b);
+ for (i = 0; i < k; i++)
+ {
+ result.value[i] ^= value[i];
+ }
+ }
+ // If we xor'ed some bits too many by proceeding blockwise,
+ // restore them to zero:
+ result.zeroUnusedBits();
+ return result;
+ }
+
+ /**
+ * Computes the bitwise exclusive-or of this GF2Polynomial and <i>b</i> and
+ * stores the result in this GF2Polynomial. <i>b</i> can be of different
+ * size.
+ *
+ * @param b GF2Polynomial
+ */
+ public void xorThisBy(GF2Polynomial b)
+ {
+ int i;
+ for (i = 0; i < Math.min(blocks, b.blocks); i++)
+ {
+ value[i] ^= b.value[i];
+ }
+ // If we xor'ed some bits too many by proceeding blockwise,
+ // restore them to zero:
+ zeroUnusedBits();
+ }
+
+ /**
+ * If {@link #len} is not a multiple of the block size (32), some extra bits
+ * of the last block might have been modified during a blockwise operation.
+ * This method compensates for that by restoring these "extra" bits to zero.
+ */
+ private void zeroUnusedBits()
+ {
+ if ((len & 0x1f) != 0)
+ {
+ value[blocks - 1] &= reverseRightMask[len & 0x1f];
+ }
+ }
+
+ /**
+ * Sets the bit at position <i>i</i>.
+ *
+ * @param i int
+ * @throws BitDoesNotExistException if (<i>i</i> &lt; 0) || (<i>i</i> &gt; (len - 1))
+ */
+ public void setBit(int i)
+ throws RuntimeException
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ throw new RuntimeException();
+ }
+ if (i > (len - 1))
+ {
+ return;
+ }
+ value[i >>> 5] |= bitMask[i & 0x1f];
+ return;
+ }
+
+ /**
+ * Returns the bit at position <i>i</i>.
+ *
+ * @param i int
+ * @return the bit at position <i>i</i> if <i>i</i> is a valid position, 0
+ * otherwise.
+ */
+ public int getBit(int i)
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ return 0;
+ }
+ return ((value[i >>> 5] & bitMask[i & 0x1f]) != 0) ? 1 : 0;
+ }
+
+ /**
+ * Resets the bit at position <i>i</i>.
+ *
+ * @param i int
+ * @throws BitDoesNotExistException if (<i>i</i> &lt; 0) || (<i>i</i> &gt; (len - 1))
+ */
+ public void resetBit(int i)
+ throws RuntimeException
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ throw new RuntimeException();
+ }
+ if (i > (len - 1))
+ {
+ return;
+ }
+ value[i >>> 5] &= ~bitMask[i & 0x1f];
+ }
+
+ /**
+ * Xors the bit at position <i>i</i>.
+ *
+ * @param i int
+ * @throws BitDoesNotExistException if (<i>i</i> &lt; 0) || (<i>i</i> &gt; (len - 1))
+ */
+ public void xorBit(int i)
+ throws RuntimeException
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ throw new RuntimeException();
+ }
+ if (i > (len - 1))
+ {
+ return;
+ }
+ value[i >>> 5] ^= bitMask[i & 0x1f];
+ }
+
+ /**
+ * Tests the bit at position <i>i</i>.
+ *
+ * @param i the position of the bit to be tested
+ * @return true if the bit at position <i>i</i> is set (a(<i>i</i>) ==
+ * 1). False if (<i>i</i> &lt; 0) || (<i>i</i> &gt; (len - 1))
+ */
+ public boolean testBit(int i)
+ {
+ if (i < 0 || i > (len - 1))
+ {
+ return false;
+ }
+ return (value[i >>> 5] & bitMask[i & 0x1f]) != 0;
+ }
+
+ /**
+ * Returns this GF2Polynomial shift-left by 1 in a new GF2Polynomial.
+ *
+ * @return a new GF2Polynomial (this &lt;&lt; 1)
+ */
+ public GF2Polynomial shiftLeft()
+ {
+ GF2Polynomial result = new GF2Polynomial(len + 1, value);
+ int i;
+ for (i = result.blocks - 1; i >= 1; i--)
+ {
+ result.value[i] <<= 1;
+ result.value[i] |= result.value[i - 1] >>> 31;
+ }
+ result.value[0] <<= 1;
+ return result;
+ }
+
+ /**
+ * Shifts-left this by one and enlarges the size of value if necesary.
+ */
+ public void shiftLeftThis()
+ {
+ /** @todo This is untested. */
+ int i;
+ if ((len & 0x1f) == 0)
+ { // check if blocks increases
+ len += 1;
+ blocks += 1;
+ if (blocks > value.length)
+ { // enlarge value
+ int[] bs = new int[blocks];
+ System.arraycopy(value, 0, bs, 0, value.length);
+ value = null;
+ value = bs;
+ }
+ for (i = blocks - 1; i >= 1; i--)
+ {
+ value[i] |= value[i - 1] >>> 31;
+ value[i - 1] <<= 1;
+ }
+ }
+ else
+ {
+ len += 1;
+ for (i = blocks - 1; i >= 1; i--)
+ {
+ value[i] <<= 1;
+ value[i] |= value[i - 1] >>> 31;
+ }
+ value[0] <<= 1;
+ }
+ }
+
+ /**
+ * Returns this GF2Polynomial shift-left by <i>k</i> in a new
+ * GF2Polynomial.
+ *
+ * @param k int
+ * @return a new GF2Polynomial (this &lt;&lt; <i>k</i>)
+ */
+ public GF2Polynomial shiftLeft(int k)
+ {
+ // Variant 2, requiring a modified shiftBlocksLeft(k)
+ // In case of modification, consider a rename to doShiftBlocksLeft()
+ // with an explicit note that this method assumes that the polynomial
+ // has already been resized. Or consider doing things inline.
+ // Construct the resulting polynomial of appropriate length:
+ GF2Polynomial result = new GF2Polynomial(len + k, value);
+ // Shift left as many multiples of the block size as possible:
+ if (k >= 32)
+ {
+ result.doShiftBlocksLeft(k >>> 5);
+ }
+ // Shift left by the remaining (<32) amount:
+ final int remaining = k & 0x1f;
+ if (remaining != 0)
+ {
+ for (int i = result.blocks - 1; i >= 1; i--)
+ {
+ result.value[i] <<= remaining;
+ result.value[i] |= result.value[i - 1] >>> (32 - remaining);
+ }
+ result.value[0] <<= remaining;
+ }
+ return result;
+ }
+
+ /**
+ * Shifts left b and adds the result to Its a fast version of
+ * <tt>this = add(b.shl(k));</tt>
+ *
+ * @param b GF2Polynomial to shift and add to this
+ * @param k the amount to shift
+ * @see GF2nPolynomialElement#invertEEA
+ */
+ public void shiftLeftAddThis(GF2Polynomial b, int k)
+ {
+ if (k == 0)
+ {
+ addToThis(b);
+ return;
+ }
+ int i;
+ expandN(b.len + k);
+ int d = k >>> 5;
+ for (i = b.blocks - 1; i >= 0; i--)
+ {
+ if ((i + d + 1 < blocks) && ((k & 0x1f) != 0))
+ {
+ value[i + d + 1] ^= b.value[i] >>> (32 - (k & 0x1f));
+ }
+ value[i + d] ^= b.value[i] << (k & 0x1f);
+ }
+ }
+
+ /**
+ * Shifts-left this GF2Polynomial's value blockwise 1 block resulting in a
+ * shift-left by 32.
+ *
+ * @see GF2Polynomial#multiply
+ */
+ void shiftBlocksLeft()
+ {
+ blocks += 1;
+ len += 32;
+ if (blocks <= value.length)
+ {
+ int i;
+ for (i = blocks - 1; i >= 1; i--)
+ {
+ value[i] = value[i - 1];
+ }
+ value[0] = 0x00;
+ }
+ else
+ {
+ int[] result = new int[blocks];
+ System.arraycopy(value, 0, result, 1, blocks - 1);
+ value = null;
+ value = result;
+ }
+ }
+
+ /**
+ * Shifts left this GF2Polynomial's value blockwise <i>b</i> blocks
+ * resulting in a shift-left by b*32. This method assumes that {@link #len}
+ * and {@link #blocks} have already been updated to reflect the final state.
+ *
+ * @param b shift amount (in blocks)
+ */
+ private void doShiftBlocksLeft(int b)
+ {
+ if (blocks <= value.length)
+ {
+ int i;
+ for (i = blocks - 1; i >= b; i--)
+ {
+ value[i] = value[i - b];
+ }
+ for (i = 0; i < b; i++)
+ {
+ value[i] = 0x00;
+ }
+ }
+ else
+ {
+ int[] result = new int[blocks];
+ System.arraycopy(value, 0, result, b, blocks - b);
+ value = null;
+ value = result;
+ }
+ }
+
+ /**
+ * Returns this GF2Polynomial shift-right by 1 in a new GF2Polynomial.
+ *
+ * @return a new GF2Polynomial (this &lt;&lt; 1)
+ */
+ public GF2Polynomial shiftRight()
+ {
+ GF2Polynomial result = new GF2Polynomial(len - 1);
+ int i;
+ System.arraycopy(value, 0, result.value, 0, result.blocks);
+ for (i = 0; i <= result.blocks - 2; i++)
+ {
+ result.value[i] >>>= 1;
+ result.value[i] |= result.value[i + 1] << 31;
+ }
+ result.value[result.blocks - 1] >>>= 1;
+ if (result.blocks < blocks)
+ {
+ result.value[result.blocks - 1] |= value[result.blocks] << 31;
+ }
+ return result;
+ }
+
+ /**
+ * Shifts-right this GF2Polynomial by 1.
+ */
+ public void shiftRightThis()
+ {
+ int i;
+ len -= 1;
+ blocks = ((len - 1) >>> 5) + 1;
+ for (i = 0; i <= blocks - 2; i++)
+ {
+ value[i] >>>= 1;
+ value[i] |= value[i + 1] << 31;
+ }
+ value[blocks - 1] >>>= 1;
+ if ((len & 0x1f) == 0)
+ {
+ value[blocks - 1] |= value[blocks] << 31;
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Vector.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Vector.java
new file mode 100644
index 00000000..560ad216
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2Vector.java
@@ -0,0 +1,539 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class implements the abstract class <tt>Vector</tt> for the case of
+ * vectors over the finite field GF(2). <br>
+ * For the vector representation the array of type int[] is used, thus one
+ * element of the array holds 32 elements of the vector.
+ *
+ * @see Vector
+ */
+public class GF2Vector
+ extends Vector
+{
+
+ /**
+ * holds the elements of this vector
+ */
+ private int[] v;
+
+ /**
+ * Construct the zero vector of the given length.
+ *
+ * @param length the length of the vector
+ */
+ public GF2Vector(int length)
+ {
+ if (length < 0)
+ {
+ throw new ArithmeticException("Negative length.");
+ }
+ this.length = length;
+ v = new int[(length + 31) >> 5];
+ }
+
+ /**
+ * Construct a random GF2Vector of the given length.
+ *
+ * @param length the length of the vector
+ * @param sr the source of randomness
+ */
+ public GF2Vector(int length, SecureRandom sr)
+ {
+ this.length = length;
+
+ int size = (length + 31) >> 5;
+ v = new int[size];
+
+ // generate random elements
+ for (int i = size - 1; i >= 0; i--)
+ {
+ v[i] = sr.nextInt();
+ }
+
+ // erase unused bits
+ int r = length & 0x1f;
+ if (r != 0)
+ {
+ // erase unused bits
+ v[size - 1] &= (1 << r) - 1;
+ }
+ }
+
+ /**
+ * Construct a random GF2Vector of the given length with the specified
+ * number of non-zero coefficients.
+ *
+ * @param length the length of the vector
+ * @param t the number of non-zero coefficients
+ * @param sr the source of randomness
+ */
+ public GF2Vector(int length, int t, SecureRandom sr)
+ {
+ if (t > length)
+ {
+ throw new ArithmeticException(
+ "The hamming weight is greater than the length of vector.");
+ }
+ this.length = length;
+
+ int size = (length + 31) >> 5;
+ v = new int[size];
+
+ int[] help = new int[length];
+ for (int i = 0; i < length; i++)
+ {
+ help[i] = i;
+ }
+
+ int m = length;
+ for (int i = 0; i < t; i++)
+ {
+ int j = RandUtils.nextInt(sr, m);
+ setBit(help[j]);
+ m--;
+ help[j] = help[m];
+ }
+ }
+
+ /**
+ * Construct a GF2Vector of the given length and with elements from the
+ * given array. The array is copied and unused bits are masked out.
+ *
+ * @param length the length of the vector
+ * @param v the element array
+ */
+ public GF2Vector(int length, int[] v)
+ {
+ if (length < 0)
+ {
+ throw new ArithmeticException("negative length");
+ }
+ this.length = length;
+
+ int size = (length + 31) >> 5;
+
+ if (v.length != size)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ this.v = IntUtils.clone(v);
+
+ int r = length & 0x1f;
+ if (r != 0)
+ {
+ // erase unused bits
+ this.v[size - 1] &= (1 << r) - 1;
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other another {@link GF2Vector}
+ */
+ public GF2Vector(GF2Vector other)
+ {
+ this.length = other.length;
+ this.v = IntUtils.clone(other.v);
+ }
+
+ /**
+ * Construct a new {@link GF2Vector} of the given length and with the given
+ * element array. The array is not changed and only a reference to the array
+ * is stored. No length checking is performed either.
+ *
+ * @param v the element array
+ * @param length the length of the vector
+ */
+ protected GF2Vector(int[] v, int length)
+ {
+ this.v = v;
+ this.length = length;
+ }
+
+ /**
+ * Construct a new GF2Vector with the given length out of the encoded
+ * vector.
+ *
+ * @param length the length of the vector
+ * @param encVec the encoded vector
+ * @return the decoded vector
+ */
+ public static GF2Vector OS2VP(int length, byte[] encVec)
+ {
+ if (length < 0)
+ {
+ throw new ArithmeticException("negative length");
+ }
+
+ int byteLen = (length + 7) >> 3;
+
+ if (encVec.length > byteLen)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ return new GF2Vector(length, LittleEndianConversions.toIntArray(encVec));
+ }
+
+ /**
+ * Encode this vector as byte array.
+ *
+ * @return the encoded vector
+ */
+ public byte[] getEncoded()
+ {
+ int byteLen = (length + 7) >> 3;
+ return LittleEndianConversions.toByteArray(v, byteLen);
+ }
+
+ /**
+ * @return the int array representation of this vector
+ */
+ public int[] getVecArray()
+ {
+ return v;
+ }
+
+ /**
+ * Return the Hamming weight of this vector, i.e., compute the number of
+ * units of this vector.
+ *
+ * @return the Hamming weight of this vector
+ */
+ public int getHammingWeight()
+ {
+ int weight = 0;
+ for (int i = 0; i < v.length; i++)
+ {
+ int e = v[i];
+ for (int j = 0; j < 32; j++)
+ {
+ int b = e & 1;
+ if (b != 0)
+ {
+ weight++;
+ }
+ e >>>= 1;
+ }
+ }
+ return weight;
+ }
+
+ /**
+ * @return whether this is the zero vector (i.e., all elements are zero)
+ */
+ public boolean isZero()
+ {
+ for (int i = v.length - 1; i >= 0; i--)
+ {
+ if (v[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the value of the bit of this vector at the specified index.
+ *
+ * @param index the index
+ * @return the value of the bit (0 or 1)
+ */
+ public int getBit(int index)
+ {
+ if (index >= length)
+ {
+ throw new IndexOutOfBoundsException();
+ }
+ int q = index >> 5;
+ int r = index & 0x1f;
+ return (v[q] & (1 << r)) >>> r;
+ }
+
+ /**
+ * Set the coefficient at the given index to 1. If the index is out of
+ * bounds, do nothing.
+ *
+ * @param index the index of the coefficient to set
+ */
+ public void setBit(int index)
+ {
+ if (index >= length)
+ {
+ throw new IndexOutOfBoundsException();
+ }
+ v[index >> 5] |= 1 << (index & 0x1f);
+ }
+
+ /**
+ * Adds another GF2Vector to this vector.
+ *
+ * @param other another GF2Vector
+ * @return <tt>this + other</tt>
+ * @throws ArithmeticException if the other vector is not a GF2Vector or has another
+ * length.
+ */
+ public Vector add(Vector other)
+ {
+ if (!(other instanceof GF2Vector))
+ {
+ throw new ArithmeticException("vector is not defined over GF(2)");
+ }
+
+ GF2Vector otherVec = (GF2Vector)other;
+ if (length != otherVec.length)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ int[] vec = IntUtils.clone(((GF2Vector)other).v);
+
+ for (int i = vec.length - 1; i >= 0; i--)
+ {
+ vec[i] ^= v[i];
+ }
+
+ return new GF2Vector(length, vec);
+ }
+
+ /**
+ * Multiply this vector with a permutation.
+ *
+ * @param p the permutation
+ * @return <tt>this*p = p*this</tt>
+ */
+ public Vector multiply(Permutation p)
+ {
+ int[] pVec = p.getVector();
+ if (length != pVec.length)
+ {
+ throw new ArithmeticException("length mismatch");
+ }
+
+ GF2Vector result = new GF2Vector(length);
+
+ for (int i = 0; i < pVec.length; i++)
+ {
+ int e = v[pVec[i] >> 5] & (1 << (pVec[i] & 0x1f));
+ if (e != 0)
+ {
+ result.v[i >> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Return a new vector consisting of the elements of this vector with the
+ * indices given by the set <tt>setJ</tt>.
+ *
+ * @param setJ the set of indices of elements to extract
+ * @return the new {@link GF2Vector}
+ * <tt>[this_setJ[0], this_setJ[1], ..., this_setJ[#setJ-1]]</tt>
+ */
+ public GF2Vector extractVector(int[] setJ)
+ {
+ int k = setJ.length;
+ if (setJ[k - 1] > length)
+ {
+ throw new ArithmeticException("invalid index set");
+ }
+
+ GF2Vector result = new GF2Vector(k);
+
+ for (int i = 0; i < k; i++)
+ {
+ int e = v[setJ[i] >> 5] & (1 << (setJ[i] & 0x1f));
+ if (e != 0)
+ {
+ result.v[i >> 5] |= 1 << (i & 0x1f);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Return a new vector consisting of the first <tt>k</tt> elements of this
+ * vector.
+ *
+ * @param k the number of elements to extract
+ * @return a new {@link GF2Vector} consisting of the first <tt>k</tt>
+ * elements of this vector
+ */
+ public GF2Vector extractLeftVector(int k)
+ {
+ if (k > length)
+ {
+ throw new ArithmeticException("invalid length");
+ }
+
+ if (k == length)
+ {
+ return new GF2Vector(this);
+ }
+
+ GF2Vector result = new GF2Vector(k);
+
+ int q = k >> 5;
+ int r = k & 0x1f;
+
+ System.arraycopy(v, 0, result.v, 0, q);
+ if (r != 0)
+ {
+ result.v[q] = v[q] & ((1 << r) - 1);
+ }
+
+ return result;
+ }
+
+ /**
+ * Return a new vector consisting of the last <tt>k</tt> elements of this
+ * vector.
+ *
+ * @param k the number of elements to extract
+ * @return a new {@link GF2Vector} consisting of the last <tt>k</tt>
+ * elements of this vector
+ */
+ public GF2Vector extractRightVector(int k)
+ {
+ if (k > length)
+ {
+ throw new ArithmeticException("invalid length");
+ }
+
+ if (k == length)
+ {
+ return new GF2Vector(this);
+ }
+
+ GF2Vector result = new GF2Vector(k);
+
+ int q = (length - k) >> 5;
+ int r = (length - k) & 0x1f;
+ int length = (k + 31) >> 5;
+
+ int ind = q;
+ // if words have to be shifted
+ if (r != 0)
+ {
+ // process all but last word
+ for (int i = 0; i < length - 1; i++)
+ {
+ result.v[i] = (v[ind++] >>> r) | (v[ind] << (32 - r));
+ }
+ // process last word
+ result.v[length - 1] = v[ind++] >>> r;
+ if (ind < v.length)
+ {
+ result.v[length - 1] |= v[ind] << (32 - r);
+ }
+ }
+ else
+ {
+ // no shift necessary
+ System.arraycopy(v, q, result.v, 0, length);
+ }
+
+ return result;
+ }
+
+ /**
+ * Rewrite this vector as a vector over <tt>GF(2<sup>m</sup>)</tt> with
+ * <tt>t</tt> elements.
+ *
+ * @param field the finite field <tt>GF(2<sup>m</sup>)</tt>
+ * @return the converted vector over <tt>GF(2<sup>m</sup>)</tt>
+ */
+ public GF2mVector toExtensionFieldVector(GF2mField field)
+ {
+ int m = field.getDegree();
+ if ((length % m) != 0)
+ {
+ throw new ArithmeticException("conversion is impossible");
+ }
+
+ int t = length / m;
+ int[] result = new int[t];
+ int count = 0;
+ for (int i = t - 1; i >= 0; i--)
+ {
+ for (int j = field.getDegree() - 1; j >= 0; j--)
+ {
+ int q = count >>> 5;
+ int r = count & 0x1f;
+
+ int e = (v[q] >>> r) & 1;
+ if (e == 1)
+ {
+ result[i] ^= 1 << j;
+ }
+ count++;
+ }
+ }
+ return new GF2mVector(field, result);
+ }
+
+ /**
+ * Check if the given object is equal to this vector.
+ *
+ * @param other vector
+ * @return the result of the comparison
+ */
+ public boolean equals(Object other)
+ {
+
+ if (!(other instanceof GF2Vector))
+ {
+ return false;
+ }
+ GF2Vector otherVec = (GF2Vector)other;
+
+ return (length == otherVec.length) && IntUtils.equals(v, otherVec.v);
+ }
+
+ /**
+ * @return the hash code of this vector
+ */
+ public int hashCode()
+ {
+ int hash = length;
+ hash = hash * 31 + v.hashCode();
+ return hash;
+ }
+
+ /**
+ * @return a human readable form of this vector
+ */
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < length; i++)
+ {
+ if ((i != 0) && ((i & 0x1f) == 0))
+ {
+ buf.append(' ');
+ }
+ int q = i >> 5;
+ int r = i & 0x1f;
+ int bit = v[q] & (1 << r);
+ if (bit == 0)
+ {
+ buf.append('0');
+ }
+ else
+ {
+ buf.append('1');
+ }
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mField.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mField.java
new file mode 100644
index 00000000..b60cd598
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mField.java
@@ -0,0 +1,365 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes operations with elements from the finite field F =
+ * GF(2^m). ( GF(2^m)= GF(2)[A] where A is a root of irreducible polynomial with
+ * degree m, each field element B has a polynomial basis representation, i.e. it
+ * is represented by a different binary polynomial of degree less than m, B =
+ * poly(A) ) All operations are defined only for field with 1&lt; m &lt;32. For the
+ * representation of field elements the map f: F-&gt;Z, poly(A)-&gt;poly(2) is used,
+ * where integers have the binary representation. For example: A^7+A^3+A+1 -&gt;
+ * (00...0010001011)=139 Also for elements type Integer is used.
+ *
+ * @see PolynomialRingGF2
+ */
+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 IllegalArgumentException(
+ " Error: the degree of field is too large ");
+ }
+ if (degree < 1)
+ {
+ throw new IllegalArgumentException(
+ " 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 IllegalArgumentException(
+ " Error: the degree is not correct");
+ }
+ if (!PolynomialRingGF2.isIrreducible(poly))
+ {
+ throw new IllegalArgumentException(
+ " Error: given polynomial is reducible");
+ }
+ this.degree = degree;
+ polynomial = poly;
+
+ }
+
+ public GF2mField(byte[] enc)
+ {
+ if (enc.length != 4)
+ {
+ throw new IllegalArgumentException(
+ "byte array is not an encoded finite field");
+ }
+ polynomial = LittleEndianConversions.OS2IP(enc);
+ if (!PolynomialRingGF2.isIrreducible(polynomial))
+ {
+ throw new IllegalArgumentException(
+ "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 (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;
+ }
+ 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(new SecureRandom());
+ }
+
+ /**
+ * 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 boolean 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;
+ }
+ 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 boolean equals(Object other)
+ {
+ if ((other == null) || !(other instanceof 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;
+ int i = 1;
+ while (p != 0)
+ {
+ b = (byte)(p & 0x01);
+ if (b == 1)
+ {
+ str = str + "+x^" + i;
+ }
+ p >>>= 1;
+ i++;
+ }
+ }
+ return str;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mMatrix.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mMatrix.java
new file mode 100644
index 00000000..d2989bcf
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mMatrix.java
@@ -0,0 +1,377 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This class describes some operations with matrices over finite field <i>GF(2<sup>m</sup>)</i>
+ * with small <i>m</i> (1&lt; m &lt;32).
+ *
+ * @see Matrix
+ */
+public class GF2mMatrix
+ extends Matrix
+{
+
+ /**
+ * finite field GF(2^m)
+ */
+ protected GF2mField field;
+
+ /**
+ * For the matrix representation the array of type int[][] is used, thus
+ * every element of the array keeps one element of the matrix (element from
+ * finite field GF(2^m))
+ */
+ protected int[][] matrix;
+
+ /**
+ * Constructor.
+ *
+ * @param field a finite field GF(2^m)
+ * @param enc byte[] matrix in byte array form
+ */
+ public GF2mMatrix(GF2mField field, byte[] enc)
+ {
+
+ this.field = field;
+
+ // decode matrix
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ if (enc.length < 5)
+ {
+ throw new IllegalArgumentException(
+ " Error: given array is not encoded matrix over GF(2^m)");
+ }
+
+ this.numRows = ((enc[3] & 0xff) << 24) ^ ((enc[2] & 0xff) << 16)
+ ^ ((enc[1] & 0xff) << 8) ^ (enc[0] & 0xff);
+
+ int n = count * this.numRows;
+
+ if ((this.numRows <= 0) || (((enc.length - 4) % n) != 0))
+ {
+ throw new IllegalArgumentException(
+ " Error: given array is not encoded matrix over GF(2^m)");
+ }
+
+ this.numColumns = (enc.length - 4) / n;
+
+ matrix = new int[this.numRows][this.numColumns];
+ count = 4;
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ for (int jj = 0; jj < d; jj += 8)
+ {
+ matrix[i][j] ^= (enc[count++] & 0x000000ff) << jj;
+ }
+ if (!this.field.isElementOfThisField(matrix[i][j]))
+ {
+ throw new IllegalArgumentException(
+ " Error: given array is not encoded matrix over GF(2^m)");
+ }
+ }
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other another {@link GF2mMatrix}
+ */
+ public GF2mMatrix(GF2mMatrix other)
+ {
+ numRows = other.numRows;
+ numColumns = other.numColumns;
+ field = other.field;
+ matrix = new int[numRows][];
+ for (int i = 0; i < numRows; i++)
+ {
+ matrix[i] = IntUtils.clone(other.matrix[i]);
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param field a finite field GF(2^m)
+ * @param matrix the matrix as int array. Only the reference is copied.
+ */
+ protected GF2mMatrix(GF2mField field, int[][] matrix)
+ {
+ this.field = field;
+ this.matrix = matrix;
+ numRows = matrix.length;
+ numColumns = matrix[0].length;
+ }
+
+ /**
+ * @return a byte array encoding of this matrix
+ */
+ public byte[] getEncoded()
+ {
+ int d = 8;
+ int count = 1;
+ while (field.getDegree() > d)
+ {
+ count++;
+ d += 8;
+ }
+
+ byte[] bf = new byte[this.numRows * this.numColumns * count + 4];
+ bf[0] = (byte)(this.numRows & 0xff);
+ bf[1] = (byte)((this.numRows >>> 8) & 0xff);
+ bf[2] = (byte)((this.numRows >>> 16) & 0xff);
+ bf[3] = (byte)((this.numRows >>> 24) & 0xff);
+
+ count = 4;
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ for (int jj = 0; jj < d; jj += 8)
+ {
+ bf[count++] = (byte)(matrix[i][j] >>> jj);
+ }
+ }
+ }
+
+ return bf;
+ }
+
+ /**
+ * Check if this is the zero matrix (i.e., all entries are zero).
+ *
+ * @return <tt>true</tt> if this is the zero matrix
+ */
+ public boolean isZero()
+ {
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < numColumns; j++)
+ {
+ if (matrix[i][j] != 0)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compute the inverse of this matrix.
+ *
+ * @return the inverse of this matrix (newly created).
+ */
+ public Matrix computeInverse()
+ {
+ if (numRows != numColumns)
+ {
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+
+ // clone this matrix
+ int[][] tmpMatrix = new int[numRows][numRows];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ tmpMatrix[i] = IntUtils.clone(matrix[i]);
+ }
+
+ // initialize inverse matrix as unit matrix
+ int[][] invMatrix = new int[numRows][numRows];
+ for (int i = numRows - 1; i >= 0; i--)
+ {
+ invMatrix[i][i] = 1;
+ }
+
+ // simultaneously compute Gaussian reduction of tmpMatrix and unit
+ // matrix
+ for (int i = 0; i < numRows; i++)
+ {
+ // if diagonal element is zero
+ if (tmpMatrix[i][i] == 0)
+ {
+ boolean foundNonZero = false;
+ // find a non-zero element in the same column
+ for (int j = i + 1; j < numRows; j++)
+ {
+ if (tmpMatrix[j][i] != 0)
+ {
+ // found it, swap rows ...
+ foundNonZero = true;
+ swapColumns(tmpMatrix, i, j);
+ swapColumns(invMatrix, i, j);
+ // ... and quit searching
+ j = numRows;
+ continue;
+ }
+ }
+ // if no non-zero element was found
+ if (!foundNonZero)
+ {
+ // the matrix is not invertible
+ throw new ArithmeticException("Matrix is not invertible.");
+ }
+ }
+
+ // normalize i-th row
+ int coef = tmpMatrix[i][i];
+ int invCoef = field.inverse(coef);
+ multRowWithElementThis(tmpMatrix[i], invCoef);
+ multRowWithElementThis(invMatrix[i], invCoef);
+
+ // normalize all other rows
+ for (int j = 0; j < numRows; j++)
+ {
+ if (j != i)
+ {
+ coef = tmpMatrix[j][i];
+ if (coef != 0)
+ {
+ int[] tmpRow = multRowWithElement(tmpMatrix[i], coef);
+ int[] tmpInvRow = multRowWithElement(invMatrix[i], coef);
+ addToRow(tmpRow, tmpMatrix[j]);
+ addToRow(tmpInvRow, invMatrix[j]);
+ }
+ }
+ }
+ }
+
+ return new GF2mMatrix(field, invMatrix);
+ }
+
+ private static void swapColumns(int[][] matrix, int first, int second)
+ {
+ int[] tmp = matrix[first];
+ matrix[first] = matrix[second];
+ matrix[second] = tmp;
+ }
+
+ private void multRowWithElementThis(int[] row, int element)
+ {
+ for (int i = row.length - 1; i >= 0; i--)
+ {
+ row[i] = field.mult(row[i], element);
+ }
+ }
+
+ private int[] multRowWithElement(int[] row, int element)
+ {
+ int[] result = new int[row.length];
+ for (int i = row.length - 1; i >= 0; i--)
+ {
+ result[i] = field.mult(row[i], element);
+ }
+ return result;
+ }
+
+ /**
+ * Add one row to another.
+ *
+ * @param fromRow the addend
+ * @param toRow the row to add to
+ */
+ private void addToRow(int[] fromRow, int[] toRow)
+ {
+ for (int i = toRow.length - 1; i >= 0; i--)
+ {
+ toRow[i] = field.add(fromRow[i], toRow[i]);
+ }
+ }
+
+ public Matrix rightMultiply(Matrix a)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ public Matrix rightMultiply(Permutation perm)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ public Vector leftMultiply(Vector vector)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ public Vector rightMultiply(Vector vector)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Checks if given object is equal to this matrix. The method returns false
+ * whenever the given object is not a matrix over GF(2^m).
+ *
+ * @param other object
+ * @return true or false
+ */
+ public boolean equals(Object other)
+ {
+
+ if (other == null || !(other instanceof GF2mMatrix))
+ {
+ return false;
+ }
+
+ GF2mMatrix otherMatrix = (GF2mMatrix)other;
+
+ if ((!this.field.equals(otherMatrix.field))
+ || (otherMatrix.numRows != this.numColumns)
+ || (otherMatrix.numColumns != this.numColumns))
+ {
+ return false;
+ }
+
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ if (this.matrix[i][j] != otherMatrix.matrix[i][j])
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int hash = (this.field.hashCode() * 31 + numRows) * 31 + numColumns;
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ hash = hash * 31 + matrix[i][j];
+ }
+ }
+ return hash;
+ }
+
+ public String toString()
+ {
+ String str = this.numRows + " x " + this.numColumns + " Matrix over "
+ + this.field.toString() + ": \n";
+
+ for (int i = 0; i < this.numRows; i++)
+ {
+ for (int j = 0; j < this.numColumns; j++)
+ {
+ str = str + this.field.elementToStr(matrix[i][j]) + " : ";
+ }
+ str = str + "\n";
+ }
+
+ return str;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mVector.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mVector.java
new file mode 100644
index 00000000..8e613e7b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2mVector.java
@@ -0,0 +1,256 @@
+package org.spongycastle.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&lt;m&lt;32</tt>). It extends the abstract class {@link Vector}.
+ */
+public class GF2mVector
+ extends 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 IllegalArgumentException(
+ "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 IllegalArgumentException(
+ "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 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)(vector[i] >>> j);
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * @return whether this is the zero vector (i.e., all elements are zero)
+ */
+ public boolean 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 Vector add(Vector addend)
+ {
+ throw new RuntimeException("not implemented");
+ }
+
+ /**
+ * Multiply this vector with a permutation.
+ *
+ * @param p the permutation
+ * @return <tt>this*p = p*this</tt>
+ */
+ public 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 boolean equals(Object other)
+ {
+
+ if (!(other instanceof GF2mVector))
+ {
+ return false;
+ }
+ GF2mVector otherVec = (GF2mVector)other;
+
+ if (!field.equals(otherVec.field))
+ {
+ return false;
+ }
+
+ return IntUtils.equals(vector, otherVec.vector);
+ }
+
+ /**
+ * @return the hash code of this vector
+ */
+ public int hashCode()
+ {
+ int hash = this.field.hashCode();
+ hash = hash * 31 + vector.hashCode();
+ return hash;
+ }
+
+ /**
+ * @return a human readable form of this vector
+ */
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < vector.length; i++)
+ {
+ for (int j = 0; j < field.getDegree(); j++)
+ {
+ int r = j & 0x1f;
+ int bitMask = 1 << r;
+ int coeff = vector[i] & bitMask;
+ if (coeff != 0)
+ {
+ buf.append('1');
+ }
+ else
+ {
+ buf.append('0');
+ }
+ }
+ buf.append(' ');
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nElement.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nElement.java
new file mode 100644
index 00000000..a95b25f8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nElement.java
@@ -0,0 +1,186 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+/**
+ * This abstract class implements an element of the finite field <i>GF(2)<sup>n
+ * </sup></i> in either <i>optimal normal basis</i> representation (<i>ONB</i>)
+ * or in <i>polynomial</i> representation. It is extended by the classes <a
+ * href = GF2nONBElement.html><tt> GF2nONBElement</tt></a> and <a href =
+ * GF2nPolynomialElement.html> <tt>GF2nPolynomialElement</tt> </a>.
+ *
+ * @see GF2nPolynomialElement
+ * @see GF2nONBElement
+ * @see GF2nONBField
+ */
+public abstract class GF2nElement
+ implements GFElement
+{
+
+ // /////////////////////////////////////////////////////////////////////
+ // member variables
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * holds a pointer to this element's corresponding field.
+ */
+ protected GF2nField mField;
+
+ /**
+ * holds the extension degree <i>n</i> of this element's corresponding
+ * field.
+ */
+ protected int mDegree;
+
+ // /////////////////////////////////////////////////////////////////////
+ // pseudo-constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * @return a copy of this GF2nElement
+ */
+ public abstract Object clone();
+
+ // /////////////////////////////////////////////////////////////////////
+ // assignments
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Assign the value 0 to this element.
+ */
+ abstract void assignZero();
+
+ /**
+ * Assigns the value 1 to this element.
+ */
+ abstract void assignOne();
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns whether the rightmost bit of the bit representation is set. This
+ * is needed for data conversion according to 1363.
+ *
+ * @return true if the rightmost bit of this element is set
+ */
+ public abstract boolean testRightmostBit();
+
+ /**
+ * Checks whether the indexed bit of the bit representation is set
+ *
+ * @param index the index of the bit to test
+ * @return <tt>true</tt> if the indexed bit is set
+ */
+ abstract boolean testBit(int index);
+
+ /**
+ * Returns the field of this element.
+ *
+ * @return the field of this element
+ */
+ public final GF2nField getField()
+ {
+ return mField;
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns <tt>this</tt> element + 1.
+ *
+ * @return <tt>this</tt> + 1
+ */
+ public abstract GF2nElement increase();
+
+ /**
+ * Increases this element by one.
+ */
+ public abstract void increaseThis();
+
+ /**
+ * Compute the difference of this element and <tt>minuend</tt>.
+ *
+ * @param minuend the minuend
+ * @return <tt>this - minuend</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public final GFElement subtract(GFElement minuend)
+ throws RuntimeException
+ {
+ return add(minuend);
+ }
+
+ /**
+ * Compute the difference of this element and <tt>minuend</tt>,
+ * overwriting this element.
+ *
+ * @param minuend the minuend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public final void subtractFromThis(GFElement minuend)
+ {
+ addToThis(minuend);
+ }
+
+ /**
+ * Returns <tt>this</tt> element to the power of 2.
+ *
+ * @return <tt>this</tt><sup>2</sup>
+ */
+ public abstract GF2nElement square();
+
+ /**
+ * Squares <tt>this</tt> element.
+ */
+ public abstract void squareThis();
+
+ /**
+ * Compute the square root of this element and return the result in a new
+ * {@link GF2nElement}.
+ *
+ * @return <tt>this<sup>1/2</sup></tt> (newly created)
+ */
+ public abstract GF2nElement squareRoot();
+
+ /**
+ * Compute the square root of this element.
+ */
+ public abstract void squareRootThis();
+
+ /**
+ * Performs a basis transformation of this element to the given GF2nField
+ * <tt>basis</tt>.
+ *
+ * @param basis the GF2nField representation to transform this element to
+ * @return this element in the representation of <tt>basis</tt>
+ * @throws DifferentFieldsException if <tt>this</tt> cannot be converted according to
+ * <tt>basis</tt>.
+ */
+ public final GF2nElement convert(GF2nField basis)
+ throws RuntimeException
+ {
+ return mField.convert(this, basis);
+ }
+
+ /**
+ * Returns the trace of this element.
+ *
+ * @return the trace of this element
+ */
+ public abstract int trace();
+
+ /**
+ * Solves a quadratic equation.<br>
+ * Let z<sup>2</sup> + z = <tt>this</tt>. Then this method returns z.
+ *
+ * @return z with z<sup>2</sup> + z = <tt>this</tt>
+ * @throws NoSolutionException if z<sup>2</sup> + z = <tt>this</tt> does not have a
+ * solution
+ */
+ public abstract GF2nElement solveQuadraticEquation()
+ throws RuntimeException;
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nField.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nField.java
new file mode 100644
index 00000000..d1aa1363
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nField.java
@@ -0,0 +1,292 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.util.Vector;
+
+
+/**
+ * This abstract class defines the finite field <i>GF(2<sup>n</sup>)</i>. It
+ * holds the extension degree <i>n</i>, the characteristic, the irreducible
+ * fieldpolynomial and conversion matrices. GF2nField is implemented by the
+ * classes GF2nPolynomialField and GF2nONBField.
+ *
+ * @see GF2nONBField
+ * @see GF2nPolynomialField
+ */
+public abstract class GF2nField
+{
+
+ /**
+ * the degree of this field
+ */
+ protected int mDegree;
+
+ /**
+ * the irreducible fieldPolynomial stored in normal order (also for ONB)
+ */
+ protected GF2Polynomial fieldPolynomial;
+
+ /**
+ * holds a list of GF2nFields to which elements have been converted and thus
+ * a COB-Matrix exists
+ */
+ protected Vector fields;
+
+ /**
+ * the COB matrices
+ */
+ protected Vector matrices;
+
+ /**
+ * Returns the degree <i>n</i> of this field.
+ *
+ * @return the degree <i>n</i> of this field
+ */
+ public final int getDegree()
+ {
+ return mDegree;
+ }
+
+ /**
+ * Returns the fieldpolynomial as a new Bitstring.
+ *
+ * @return a copy of the fieldpolynomial as a new Bitstring
+ */
+ public final GF2Polynomial getFieldPolynomial()
+ {
+ if (fieldPolynomial == null)
+ {
+ computeFieldPolynomial();
+ }
+ return new GF2Polynomial(fieldPolynomial);
+ }
+
+ /**
+ * Decides whether the given object <tt>other</tt> is the same as this
+ * field.
+ *
+ * @param other another object
+ * @return (this == other)
+ */
+ public final boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nField))
+ {
+ return false;
+ }
+
+ GF2nField otherField = (GF2nField)other;
+
+ if (otherField.mDegree != mDegree)
+ {
+ return false;
+ }
+ if (!fieldPolynomial.equals(otherField.fieldPolynomial))
+ {
+ return false;
+ }
+ if ((this instanceof GF2nPolynomialField)
+ && !(otherField instanceof GF2nPolynomialField))
+ {
+ return false;
+ }
+ if ((this instanceof GF2nONBField)
+ && !(otherField instanceof GF2nONBField))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return the hash code of this field
+ */
+ public int hashCode()
+ {
+ return mDegree + fieldPolynomial.hashCode();
+ }
+
+ /**
+ * Computes a random root from the given irreducible fieldpolynomial
+ * according to IEEE 1363 algorithm A.5.6. This cal take very long for big
+ * degrees.
+ *
+ * @param B0FieldPolynomial the fieldpolynomial if the other basis as a Bitstring
+ * @return a random root of BOFieldPolynomial in representation according to
+ * this field
+ * @see "P1363 A.5.6, p103f"
+ */
+ protected abstract GF2nElement getRandomRoot(GF2Polynomial B0FieldPolynomial);
+
+ /**
+ * Computes the change-of-basis matrix for basis conversion according to
+ * 1363. The result is stored in the lists fields and matrices.
+ *
+ * @param B1 the GF2nField to convert to
+ * @see "P1363 A.7.3, p111ff"
+ */
+ protected abstract void computeCOBMatrix(GF2nField B1);
+
+ /**
+ * Computes the fieldpolynomial. This can take a long time for big degrees.
+ */
+ protected abstract void computeFieldPolynomial();
+
+ /**
+ * Inverts the given matrix represented as bitstrings.
+ *
+ * @param matrix the matrix to invert as a Bitstring[]
+ * @return matrix^(-1)
+ */
+ protected final GF2Polynomial[] invertMatrix(GF2Polynomial[] matrix)
+ {
+ GF2Polynomial[] a = new GF2Polynomial[matrix.length];
+ GF2Polynomial[] inv = new GF2Polynomial[matrix.length];
+ GF2Polynomial dummy;
+ int i, j;
+ // initialize a as a copy of matrix and inv as E(inheitsmatrix)
+ for (i = 0; i < mDegree; i++)
+ {
+ try
+ {
+ a[i] = new GF2Polynomial(matrix[i]);
+ inv[i] = new GF2Polynomial(mDegree);
+ inv[i].setBit(mDegree - 1 - i);
+ }
+ catch (RuntimeException BDNEExc)
+ {
+ BDNEExc.printStackTrace();
+ }
+ }
+ // construct triangle matrix so that for each a[i] the first i bits are
+ // zero
+ for (i = 0; i < mDegree - 1; i++)
+ {
+ // find column where bit i is set
+ j = i;
+ while ((j < mDegree) && !a[j].testBit(mDegree - 1 - i))
+ {
+ j++;
+ }
+ if (j >= mDegree)
+ {
+ throw new RuntimeException(
+ "GF2nField.invertMatrix: Matrix cannot be inverted!");
+ }
+ if (i != j)
+ { // swap a[i]/a[j] and inv[i]/inv[j]
+ dummy = a[i];
+ a[i] = a[j];
+ a[j] = dummy;
+ dummy = inv[i];
+ inv[i] = inv[j];
+ inv[j] = dummy;
+ }
+ for (j = i + 1; j < mDegree; j++)
+ { // add column i to all columns>i
+ // having their i-th bit set
+ if (a[j].testBit(mDegree - 1 - i))
+ {
+ a[j].addToThis(a[i]);
+ inv[j].addToThis(inv[i]);
+ }
+ }
+ }
+ // construct Einheitsmatrix from a
+ for (i = mDegree - 1; i > 0; i--)
+ {
+ for (j = i - 1; j >= 0; j--)
+ { // eliminate the i-th bit in all
+ // columns < i
+ if (a[j].testBit(mDegree - 1 - i))
+ {
+ a[j].addToThis(a[i]);
+ inv[j].addToThis(inv[i]);
+ }
+ }
+ }
+ return inv;
+ }
+
+ /**
+ * Converts the given element in representation according to this field to a
+ * new element in representation according to B1 using the change-of-basis
+ * matrix calculated by computeCOBMatrix.
+ *
+ * @param elem the GF2nElement to convert
+ * @param basis the basis to convert <tt>elem</tt> to
+ * @return <tt>elem</tt> converted to a new element representation
+ * according to <tt>basis</tt>
+ * @throws DifferentFieldsException if <tt>elem</tt> cannot be converted according to
+ * <tt>basis</tt>.
+ * @see GF2nField#computeCOBMatrix
+ * @see GF2nField#getRandomRoot
+ * @see GF2nPolynomial
+ * @see "P1363 A.7 p109ff"
+ */
+ public final GF2nElement convert(GF2nElement elem, GF2nField basis)
+ throws RuntimeException
+ {
+ if (basis == this)
+ {
+ return (GF2nElement)elem.clone();
+ }
+ if (fieldPolynomial.equals(basis.fieldPolynomial))
+ {
+ return (GF2nElement)elem.clone();
+ }
+ if (mDegree != basis.mDegree)
+ {
+ throw new RuntimeException("GF2nField.convert: B1 has a"
+ + " different degree and thus cannot be coverted to!");
+ }
+
+ int i;
+ GF2Polynomial[] COBMatrix;
+ i = fields.indexOf(basis);
+ if (i == -1)
+ {
+ computeCOBMatrix(basis);
+ i = fields.indexOf(basis);
+ }
+ COBMatrix = (GF2Polynomial[])matrices.elementAt(i);
+
+ GF2nElement elemCopy = (GF2nElement)elem.clone();
+ if (elemCopy instanceof GF2nONBElement)
+ {
+ // remember: ONB treats its bits in reverse order
+ ((GF2nONBElement)elemCopy).reverseOrder();
+ }
+ GF2Polynomial bs = new GF2Polynomial(mDegree, elemCopy.toFlexiBigInt());
+ bs.expandN(mDegree);
+ GF2Polynomial result = new GF2Polynomial(mDegree);
+ for (i = 0; i < mDegree; i++)
+ {
+ if (bs.vectorMult(COBMatrix[i]))
+ {
+ result.setBit(mDegree - 1 - i);
+ }
+ }
+ if (basis instanceof GF2nPolynomialField)
+ {
+ return new GF2nPolynomialElement((GF2nPolynomialField)basis,
+ result);
+ }
+ else if (basis instanceof GF2nONBField)
+ {
+ GF2nONBElement res = new GF2nONBElement((GF2nONBField)basis,
+ result.toFlexiBigInt());
+ // TODO Remember: ONB treats its Bits in reverse order !!!
+ res.reverseOrder();
+ return res;
+ }
+ else
+ {
+ throw new RuntimeException(
+ "GF2nField.convert: B1 must be an instance of "
+ + "GF2nPolynomialField or GF2nONBField!");
+ }
+
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBElement.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBElement.java
new file mode 100644
index 00000000..b14fdd1a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBElement.java
@@ -0,0 +1,1154 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.math.BigInteger;
+import java.util.Random;
+
+/**
+ * This class implements an element of the finite field <i>GF(2<sup>n </sup>)</i>.
+ * It is represented in an optimal normal basis representation and holds the
+ * pointer <tt>mField</tt> to its corresponding field.
+ *
+ * @see GF2nField
+ * @see GF2nElement
+ */
+public class GF2nONBElement
+ extends GF2nElement
+{
+
+ // /////////////////////////////////////////////////////////////////////
+ // member variables
+ // /////////////////////////////////////////////////////////////////////
+
+ private static final long[] mBitmask = new long[]{0x0000000000000001L,
+ 0x0000000000000002L, 0x0000000000000004L, 0x0000000000000008L,
+ 0x0000000000000010L, 0x0000000000000020L, 0x0000000000000040L,
+ 0x0000000000000080L, 0x0000000000000100L, 0x0000000000000200L,
+ 0x0000000000000400L, 0x0000000000000800L, 0x0000000000001000L,
+ 0x0000000000002000L, 0x0000000000004000L, 0x0000000000008000L,
+ 0x0000000000010000L, 0x0000000000020000L, 0x0000000000040000L,
+ 0x0000000000080000L, 0x0000000000100000L, 0x0000000000200000L,
+ 0x0000000000400000L, 0x0000000000800000L, 0x0000000001000000L,
+ 0x0000000002000000L, 0x0000000004000000L, 0x0000000008000000L,
+ 0x0000000010000000L, 0x0000000020000000L, 0x0000000040000000L,
+ 0x0000000080000000L, 0x0000000100000000L, 0x0000000200000000L,
+ 0x0000000400000000L, 0x0000000800000000L, 0x0000001000000000L,
+ 0x0000002000000000L, 0x0000004000000000L, 0x0000008000000000L,
+ 0x0000010000000000L, 0x0000020000000000L, 0x0000040000000000L,
+ 0x0000080000000000L, 0x0000100000000000L, 0x0000200000000000L,
+ 0x0000400000000000L, 0x0000800000000000L, 0x0001000000000000L,
+ 0x0002000000000000L, 0x0004000000000000L, 0x0008000000000000L,
+ 0x0010000000000000L, 0x0020000000000000L, 0x0040000000000000L,
+ 0x0080000000000000L, 0x0100000000000000L, 0x0200000000000000L,
+ 0x0400000000000000L, 0x0800000000000000L, 0x1000000000000000L,
+ 0x2000000000000000L, 0x4000000000000000L, 0x8000000000000000L};
+
+ private static final long[] mMaxmask = new long[]{0x0000000000000001L,
+ 0x0000000000000003L, 0x0000000000000007L, 0x000000000000000FL,
+ 0x000000000000001FL, 0x000000000000003FL, 0x000000000000007FL,
+ 0x00000000000000FFL, 0x00000000000001FFL, 0x00000000000003FFL,
+ 0x00000000000007FFL, 0x0000000000000FFFL, 0x0000000000001FFFL,
+ 0x0000000000003FFFL, 0x0000000000007FFFL, 0x000000000000FFFFL,
+ 0x000000000001FFFFL, 0x000000000003FFFFL, 0x000000000007FFFFL,
+ 0x00000000000FFFFFL, 0x00000000001FFFFFL, 0x00000000003FFFFFL,
+ 0x00000000007FFFFFL, 0x0000000000FFFFFFL, 0x0000000001FFFFFFL,
+ 0x0000000003FFFFFFL, 0x0000000007FFFFFFL, 0x000000000FFFFFFFL,
+ 0x000000001FFFFFFFL, 0x000000003FFFFFFFL, 0x000000007FFFFFFFL,
+ 0x00000000FFFFFFFFL, 0x00000001FFFFFFFFL, 0x00000003FFFFFFFFL,
+ 0x00000007FFFFFFFFL, 0x0000000FFFFFFFFFL, 0x0000001FFFFFFFFFL,
+ 0x0000003FFFFFFFFFL, 0x0000007FFFFFFFFFL, 0x000000FFFFFFFFFFL,
+ 0x000001FFFFFFFFFFL, 0x000003FFFFFFFFFFL, 0x000007FFFFFFFFFFL,
+ 0x00000FFFFFFFFFFFL, 0x00001FFFFFFFFFFFL, 0x00003FFFFFFFFFFFL,
+ 0x00007FFFFFFFFFFFL, 0x0000FFFFFFFFFFFFL, 0x0001FFFFFFFFFFFFL,
+ 0x0003FFFFFFFFFFFFL, 0x0007FFFFFFFFFFFFL, 0x000FFFFFFFFFFFFFL,
+ 0x001FFFFFFFFFFFFFL, 0x003FFFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL,
+ 0x00FFFFFFFFFFFFFFL, 0x01FFFFFFFFFFFFFFL, 0x03FFFFFFFFFFFFFFL,
+ 0x07FFFFFFFFFFFFFFL, 0x0FFFFFFFFFFFFFFFL, 0x1FFFFFFFFFFFFFFFL,
+ 0x3FFFFFFFFFFFFFFFL, 0x7FFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL};
+
+ // mIBy64[j * 16 + i] = (j * 16 + i)/64
+ // i =
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ //
+ private static final int[] mIBY64 = new int[]{
+ // j =
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 8
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 9
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 10
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 11
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 12
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 13
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 14
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 15
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 16
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 17
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 18
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 19
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 20
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 21
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 22
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 // 23
+ };
+
+ private static final int MAXLONG = 64;
+
+ /**
+ * holds the lenght of the polynomial with 64 bit sized fields.
+ */
+ private int mLength;
+
+ /**
+ * holds the value of mDeg % MAXLONG.
+ */
+ private int mBit;
+
+ /**
+ * holds this element in ONB representation.
+ */
+ private long[] mPol;
+
+ // /////////////////////////////////////////////////////////////////////
+ // constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Construct a random element over the field <tt>gf2n</tt>, using the
+ * specified source of randomness.
+ *
+ * @param gf2n the field
+ * @param rand the source of randomness
+ */
+ public GF2nONBElement(GF2nONBField gf2n, Random rand)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = new long[mLength];
+ if (mLength > 1)
+ {
+ for (int j = 0; j < mLength - 1; j++)
+ {
+ mPol[j] = rand.nextLong();
+ }
+ long last = rand.nextLong();
+ mPol[mLength - 1] = last >>> (MAXLONG - mBit);
+ }
+ else
+ {
+ mPol[0] = rand.nextLong();
+ mPol[0] = mPol[0] >>> (MAXLONG - mBit);
+ }
+ }
+
+ /**
+ * Construct a new GF2nONBElement from its encoding.
+ *
+ * @param gf2n the field
+ * @param e the encoded element
+ */
+ public GF2nONBElement(GF2nONBField gf2n, byte[] e)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = new long[mLength];
+ assign(e);
+ }
+
+ /**
+ * Construct the element of the field <tt>gf2n</tt> with the specified
+ * value <tt>val</tt>.
+ *
+ * @param gf2n the field
+ * @param val the value represented by a BigInteger
+ */
+ public GF2nONBElement(GF2nONBField gf2n, BigInteger val)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = new long[mLength];
+ assign(val);
+ }
+
+ /**
+ * Construct the element of the field <tt>gf2n</tt> with the specified
+ * value <tt>val</tt>.
+ *
+ * @param gf2n the field
+ * @param val the value in ONB representation
+ */
+ private GF2nONBElement(GF2nONBField gf2n, long[] val)
+ {
+ mField = gf2n;
+ mDegree = mField.getDegree();
+ mLength = gf2n.getONBLength();
+ mBit = gf2n.getONBBit();
+ mPol = val;
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // pseudo-constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Copy constructor.
+ *
+ * @param gf2n the field
+ */
+ public GF2nONBElement(GF2nONBElement gf2n)
+ {
+
+ mField = gf2n.mField;
+ mDegree = mField.getDegree();
+ mLength = ((GF2nONBField)mField).getONBLength();
+ mBit = ((GF2nONBField)mField).getONBBit();
+ mPol = new long[mLength];
+ assign(gf2n.getElement());
+ }
+
+ /**
+ * Create a new GF2nONBElement by cloning this GF2nPolynomialElement.
+ *
+ * @return a copy of this element
+ */
+ public Object clone()
+ {
+ return new GF2nONBElement(this);
+ }
+
+ /**
+ * Create the zero element.
+ *
+ * @param gf2n the finite field
+ * @return the zero element in the given finite field
+ */
+ public static GF2nONBElement ZERO(GF2nONBField gf2n)
+ {
+ long[] polynomial = new long[gf2n.getONBLength()];
+ return new GF2nONBElement(gf2n, polynomial);
+ }
+
+ /**
+ * Create the one element.
+ *
+ * @param gf2n the finite field
+ * @return the one element in the given finite field
+ */
+ public static GF2nONBElement ONE(GF2nONBField gf2n)
+ {
+ int mLength = gf2n.getONBLength();
+ long[] polynomial = new long[mLength];
+
+ // fill mDegree coefficients with one's
+ for (int i = 0; i < mLength - 1; i++)
+ {
+ polynomial[i] = 0xffffffffffffffffL;
+ }
+ polynomial[mLength - 1] = mMaxmask[gf2n.getONBBit() - 1];
+
+ return new GF2nONBElement(gf2n, polynomial);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // assignments
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * assigns to this element the zero element
+ */
+ void assignZero()
+ {
+ mPol = new long[mLength];
+ }
+
+ /**
+ * assigns to this element the one element
+ */
+ void assignOne()
+ {
+ // fill mDegree coefficients with one's
+ for (int i = 0; i < mLength - 1; i++)
+ {
+ mPol[i] = 0xffffffffffffffffL;
+ }
+ mPol[mLength - 1] = mMaxmask[mBit - 1];
+ }
+
+ /**
+ * assigns to this element the value <tt>val</tt>.
+ *
+ * @param val the value represented by a BigInteger
+ */
+ private void assign(BigInteger val)
+ {
+ assign(val.toByteArray());
+ }
+
+ /**
+ * assigns to this element the value <tt>val</tt>.
+ *
+ * @param val the value in ONB representation
+ */
+ private void assign(long[] val)
+ {
+ System.arraycopy(val, 0, mPol, 0, mLength);
+ }
+
+ /**
+ * assigns to this element the value <tt>val</tt>. First: inverting the
+ * order of val into reversed[]. That means: reversed[0] = val[length - 1],
+ * ..., reversed[reversed.length - 1] = val[0]. Second: mPol[0] = sum{i = 0,
+ * ... 7} (val[i]<<(i*8)) .... mPol[1] = sum{i = 8, ... 15} (val[i]<<(i*8))
+ *
+ * @param val the value in ONB representation
+ */
+ private void assign(byte[] val)
+ {
+ int j;
+ mPol = new long[mLength];
+ for (j = 0; j < val.length; j++)
+ {
+ mPol[j >>> 3] |= (val[val.length - 1 - j] & 0x00000000000000ffL) << ((j & 0x07) << 3);
+ }
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // comparison
+ // /////////////////////////////////////////////////////////////////
+
+ /**
+ * Checks whether this element is zero.
+ *
+ * @return <tt>true</tt> if <tt>this</tt> is the zero element
+ */
+ public boolean isZero()
+ {
+
+ boolean result = true;
+
+ for (int i = 0; i < mLength && result; i++)
+ {
+ result = result && ((mPol[i] & 0xFFFFFFFFFFFFFFFFL) == 0);
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks whether this element is one.
+ *
+ * @return <tt>true</tt> if <tt>this</tt> is the one element
+ */
+ public boolean isOne()
+ {
+
+ boolean result = true;
+
+ for (int i = 0; i < mLength - 1 && result; i++)
+ {
+ result = result
+ && ((mPol[i] & 0xFFFFFFFFFFFFFFFFL) == 0xFFFFFFFFFFFFFFFFL);
+ }
+
+ if (result)
+ {
+ result = result
+ && ((mPol[mLength - 1] & mMaxmask[mBit - 1]) == mMaxmask[mBit - 1]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compare this element with another object.
+ *
+ * @param other the other object
+ * @return <tt>true</tt> if the two objects are equal, <tt>false</tt>
+ * otherwise
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nONBElement))
+ {
+ return false;
+ }
+
+ GF2nONBElement otherElem = (GF2nONBElement)other;
+
+ for (int i = 0; i < mLength; i++)
+ {
+ if (mPol[i] != otherElem.mPol[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return the hash code of this element
+ */
+ public int hashCode()
+ {
+ return mPol.hashCode();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns whether the highest bit of the bit representation is set
+ *
+ * @return true, if the highest bit of mPol is set, false, otherwise
+ */
+ public boolean testRightmostBit()
+ {
+ // due to the reverse bit order (compared to 1363) this method returns
+ // the value of the leftmost bit
+ return (mPol[mLength - 1] & mBitmask[mBit - 1]) != 0L;
+ }
+
+ /**
+ * Checks whether the indexed bit of the bit representation is set. Warning:
+ * GF2nONBElement currently stores its bits in reverse order (compared to
+ * 1363) !!!
+ *
+ * @param index the index of the bit to test
+ * @return <tt>true</tt> if the indexed bit of mPol is set, <tt>false</tt>
+ * otherwise.
+ */
+ boolean testBit(int index)
+ {
+ if (index < 0 || index > mDegree)
+ {
+ return false;
+ }
+ long test = mPol[index >>> 6] & mBitmask[index & 0x3f];
+ return test != 0x0L;
+ }
+
+ /**
+ * @return this element in its ONB representation
+ */
+ private long[] getElement()
+ {
+
+ long[] result = new long[mPol.length];
+ System.arraycopy(mPol, 0, result, 0, mPol.length);
+
+ return result;
+ }
+
+ /**
+ * Returns the ONB representation of this element. The Bit-Order is
+ * exchanged (according to 1363)!
+ *
+ * @return this element in its representation and reverse bit-order
+ */
+ private long[] getElementReverseOrder()
+ {
+ long[] result = new long[mPol.length];
+ for (int i = 0; i < mDegree; i++)
+ {
+ if (testBit(mDegree - i - 1))
+ {
+ result[i >>> 6] |= mBitmask[i & 0x3f];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Reverses the bit-order in this element(according to 1363). This is a
+ * hack!
+ */
+ void reverseOrder()
+ {
+ mPol = getElementReverseOrder();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Compute the sum of this element and <tt>addend</tt>.
+ *
+ * @param addend the addend
+ * @return <tt>this + other</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement add(GFElement addend)
+ throws RuntimeException
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.addToThis(addend);
+ return result;
+ }
+
+ /**
+ * Compute <tt>this + addend</tt> (overwrite <tt>this</tt>).
+ *
+ * @param addend the addend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void addToThis(GFElement addend)
+ throws RuntimeException
+ {
+ if (!(addend instanceof GF2nONBElement))
+ {
+ throw new RuntimeException();
+ }
+ if (!mField.equals(((GF2nONBElement)addend).mField))
+ {
+ throw new RuntimeException();
+ }
+
+ for (int i = 0; i < mLength; i++)
+ {
+ mPol[i] ^= ((GF2nONBElement)addend).mPol[i];
+ }
+ }
+
+ /**
+ * returns <tt>this</tt> element + 1.
+ *
+ * @return <tt>this</tt> + 1
+ */
+ public GF2nElement increase()
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.increaseThis();
+ return result;
+ }
+
+ /**
+ * increases <tt>this</tt> element.
+ */
+ public void increaseThis()
+ {
+ addToThis(ONE((GF2nONBField)mField));
+ }
+
+ /**
+ * Compute the product of this element and <tt>factor</tt>.
+ *
+ * @param factor the factor
+ * @return <tt>this * factor</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement multiply(GFElement factor)
+ throws RuntimeException
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.multiplyThisBy(factor);
+ return result;
+ }
+
+ /**
+ * Compute <tt>this * factor</tt> (overwrite <tt>this</tt>).
+ *
+ * @param factor the factor
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void multiplyThisBy(GFElement factor)
+ throws RuntimeException
+ {
+
+ if (!(factor instanceof GF2nONBElement))
+ {
+ throw new RuntimeException("The elements have different"
+ + " representation: not yet" + " implemented");
+ }
+ if (!mField.equals(((GF2nONBElement)factor).mField))
+ {
+ throw new RuntimeException();
+ }
+
+ if (equals(factor))
+ {
+ squareThis();
+ }
+ else
+ {
+
+ long[] a = mPol;
+ long[] b = ((GF2nONBElement)factor).mPol;
+ long[] c = new long[mLength];
+
+ int[][] m = ((GF2nONBField)mField).mMult;
+
+ int degf, degb, s, fielda, fieldb, bita, bitb;
+ degf = mLength - 1;
+ degb = mBit - 1;
+ s = 0;
+
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ long TWOTODEGB = mBitmask[degb];
+
+ boolean old, now;
+
+ // the product c of a and b (a*b = c) is calculated in mDegree
+ // cicles
+ // in every cicle one coefficient of c is calculated and stored
+ // k indicates the coefficient
+ //
+ for (int k = 0; k < mDegree; k++)
+ {
+
+ s = 0;
+
+ for (int i = 0; i < mDegree; i++)
+ {
+
+ // fielda = i / MAXLONG
+ //
+ fielda = mIBY64[i];
+
+ // bita = i % MAXLONG
+ //
+ bita = i & (MAXLONG - 1);
+
+ // fieldb = m[i][0] / MAXLONG
+ //
+ fieldb = mIBY64[m[i][0]];
+
+ // bitb = m[i][0] % MAXLONG
+ //
+ bitb = m[i][0] & (MAXLONG - 1);
+
+ if ((a[fielda] & mBitmask[bita]) != 0)
+ {
+
+ if ((b[fieldb] & mBitmask[bitb]) != 0)
+ {
+ s ^= 1;
+ }
+
+ if (m[i][1] != -1)
+ {
+
+ // fieldb = m[i][1] / MAXLONG
+ //
+ fieldb = mIBY64[m[i][1]];
+
+ // bitb = m[i][1] % MAXLONG
+ //
+ bitb = m[i][1] & (MAXLONG - 1);
+
+ if ((b[fieldb] & mBitmask[bitb]) != 0)
+ {
+ s ^= 1;
+ }
+
+ }
+ }
+ }
+ fielda = mIBY64[k];
+ bita = k & (MAXLONG - 1);
+
+ if (s != 0)
+ {
+ c[fielda] ^= mBitmask[bita];
+ }
+
+ // Circular shift of x and y one bit to the right,
+ // respectively.
+
+ if (mLength > 1)
+ {
+
+ // Shift x.
+ //
+ old = (a[degf] & 1) == 1;
+
+ for (int i = degf - 1; i >= 0; i--)
+ {
+ now = (a[i] & 1) != 0;
+
+ a[i] = a[i] >>> 1;
+
+ if (old)
+ {
+ a[i] ^= TWOTOMAXLONGM1;
+ }
+
+ old = now;
+ }
+ a[degf] = a[degf] >>> 1;
+
+ if (old)
+ {
+ a[degf] ^= TWOTODEGB;
+ }
+
+ // Shift y.
+ //
+ old = (b[degf] & 1) == 1;
+
+ for (int i = degf - 1; i >= 0; i--)
+ {
+ now = (b[i] & 1) != 0;
+
+ b[i] = b[i] >>> 1;
+
+ if (old)
+ {
+ b[i] ^= TWOTOMAXLONGM1;
+ }
+
+ old = now;
+ }
+
+ b[degf] = b[degf] >>> 1;
+
+ if (old)
+ {
+ b[degf] ^= TWOTODEGB;
+ }
+ }
+ else
+ {
+ old = (a[0] & 1) == 1;
+ a[0] = a[0] >>> 1;
+
+ if (old)
+ {
+ a[0] ^= TWOTODEGB;
+ }
+
+ old = (b[0] & 1) == 1;
+ b[0] = b[0] >>> 1;
+
+ if (old)
+ {
+ b[0] ^= TWOTODEGB;
+ }
+ }
+ }
+ assign(c);
+ }
+ }
+
+ /**
+ * returns <tt>this</tt> element to the power of 2.
+ *
+ * @return <tt>this</tt><sup>2</sup>
+ */
+ public GF2nElement square()
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.squareThis();
+ return result;
+ }
+
+ /**
+ * squares <tt>this</tt> element.
+ */
+ public void squareThis()
+ {
+
+ long[] pol = getElement();
+
+ int f = mLength - 1;
+ int b = mBit - 1;
+
+ // Shift the coefficients one bit to the left.
+ //
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ boolean old, now;
+
+ old = (pol[f] & mBitmask[b]) != 0;
+
+ for (int i = 0; i < f; i++)
+ {
+
+ now = (pol[i] & TWOTOMAXLONGM1) != 0;
+
+ pol[i] = pol[i] << 1;
+
+ if (old)
+ {
+ pol[i] ^= 1;
+ }
+
+ old = now;
+ }
+ now = (pol[f] & mBitmask[b]) != 0;
+
+ pol[f] = pol[f] << 1;
+
+ if (old)
+ {
+ pol[f] ^= 1;
+ }
+
+ // Set the bit with index mDegree to zero.
+ //
+ if (now)
+ {
+ pol[f] ^= mBitmask[b + 1];
+ }
+
+ assign(pol);
+ }
+
+ /**
+ * Compute the multiplicative inverse of this element.
+ *
+ * @return <tt>this<sup>-1</sup></tt> (newly created)
+ * @throws ArithmeticException if <tt>this</tt> is the zero element.
+ */
+ public GFElement invert()
+ throws ArithmeticException
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.invertThis();
+ return result;
+ }
+
+ /**
+ * Multiplicatively invert of this element (overwrite <tt>this</tt>).
+ *
+ * @throws ArithmeticException if <tt>this</tt> is the zero element.
+ */
+ public void invertThis()
+ throws ArithmeticException
+ {
+
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ int r = 31; // mDegree kann nur 31 Bits lang sein!!!
+
+ // Bitlaenge von mDegree:
+ for (boolean found = false; !found && r >= 0; r--)
+ {
+
+ if (((mDegree - 1) & mBitmask[r]) != 0)
+ {
+ found = true;
+ }
+ }
+ r++;
+
+ GF2nElement m = ZERO((GF2nONBField)mField);
+ GF2nElement n = new GF2nONBElement(this);
+
+ int k = 1;
+
+ for (int i = r - 1; i >= 0; i--)
+ {
+ m = (GF2nElement)n.clone();
+ for (int j = 1; j <= k; j++)
+ {
+ m.squareThis();
+ }
+
+ n.multiplyThisBy(m);
+
+ k <<= 1;
+ if (((mDegree - 1) & mBitmask[i]) != 0)
+ {
+ n.squareThis();
+
+ n.multiplyThisBy(this);
+
+ k++;
+ }
+ }
+ n.squareThis();
+ }
+
+ /**
+ * returns the root of<tt>this</tt> element.
+ *
+ * @return <tt>this</tt><sup>1/2</sup>
+ */
+ public GF2nElement squareRoot()
+ {
+ GF2nONBElement result = new GF2nONBElement(this);
+ result.squareRootThis();
+ return result;
+ }
+
+ /**
+ * square roots <tt>this</tt> element.
+ */
+ public void squareRootThis()
+ {
+
+ long[] pol = getElement();
+
+ int f = mLength - 1;
+ int b = mBit - 1;
+
+ // Shift the coefficients one bit to the right.
+ //
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ boolean old, now;
+
+ old = (pol[0] & 1) != 0;
+
+ for (int i = f; i >= 0; i--)
+ {
+ now = (pol[i] & 1) != 0;
+ pol[i] = pol[i] >>> 1;
+
+ if (old)
+ {
+ if (i == f)
+ {
+ pol[i] ^= mBitmask[b];
+ }
+ else
+ {
+ pol[i] ^= TWOTOMAXLONGM1;
+ }
+ }
+ old = now;
+ }
+ assign(pol);
+ }
+
+ /**
+ * Returns the trace of this element.
+ *
+ * @return the trace of this element
+ */
+ public int trace()
+ {
+
+ // trace = sum of coefficients
+ //
+
+ int result = 0;
+
+ int max = mLength - 1;
+
+ for (int i = 0; i < max; i++)
+ {
+
+ for (int j = 0; j < MAXLONG; j++)
+ {
+
+ if ((mPol[i] & mBitmask[j]) != 0)
+ {
+ result ^= 1;
+ }
+ }
+ }
+
+ int b = mBit;
+
+ for (int j = 0; j < b; j++)
+ {
+
+ if ((mPol[max] & mBitmask[j]) != 0)
+ {
+ result ^= 1;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Solves a quadratic equation.<br>
+ * Let z<sup>2</sup> + z = <tt>this</tt>. Then this method returns z.
+ *
+ * @return z with z<sup>2</sup> + z = <tt>this</tt>
+ * @throws NoSolutionException if z<sup>2</sup> + z = <tt>this</tt> does not have a
+ * solution
+ */
+ public GF2nElement solveQuadraticEquation()
+ throws RuntimeException
+ {
+
+ if (trace() == 1)
+ {
+ throw new RuntimeException();
+ }
+
+ long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1];
+ long ZERO = 0L;
+ long ONE = 1L;
+
+ long[] p = new long[mLength];
+ long z = 0L;
+ int j = 1;
+ for (int i = 0; i < mLength - 1; i++)
+ {
+
+ for (j = 1; j < MAXLONG; j++)
+ {
+
+ //
+ if (!((((mBitmask[j] & mPol[i]) != ZERO) && ((z & mBitmask[j - 1]) != ZERO)) || (((mPol[i] & mBitmask[j]) == ZERO) && ((z & mBitmask[j - 1]) == ZERO))))
+ {
+ z ^= mBitmask[j];
+ }
+ }
+ p[i] = z;
+
+ if (((TWOTOMAXLONGM1 & z) != ZERO && (ONE & mPol[i + 1]) == ONE)
+ || ((TWOTOMAXLONGM1 & z) == ZERO && (ONE & mPol[i + 1]) == ZERO))
+ {
+ z = ZERO;
+ }
+ else
+ {
+ z = ONE;
+ }
+ }
+
+ int b = mDegree & (MAXLONG - 1);
+
+ long LASTLONG = mPol[mLength - 1];
+
+ for (j = 1; j < b; j++)
+ {
+ if (!((((mBitmask[j] & LASTLONG) != ZERO) && ((mBitmask[j - 1] & z) != ZERO)) || (((mBitmask[j] & LASTLONG) == ZERO) && ((mBitmask[j - 1] & z) == ZERO))))
+ {
+ z ^= mBitmask[j];
+ }
+ }
+ p[mLength - 1] = z;
+ return new GF2nONBElement((GF2nONBField)mField, p);
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // conversion
+ // /////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns a String representation of this element.
+ *
+ * @return String representation of this element with the specified radix
+ */
+ public String toString()
+ {
+ return toString(16);
+ }
+
+ /**
+ * Returns a String representation of this element. <tt>radix</tt>
+ * specifies the radix of the String representation.<br>
+ * NOTE: ONLY <tt>radix = 2</tt> or <tt>radix = 16</tt> IS IMPLEMENTED
+ *
+ * @param radix specifies the radix of the String representation
+ * @return String representation of this element with the specified radix
+ */
+ public String toString(int radix)
+ {
+ String s = "";
+
+ long[] a = getElement();
+ int b = mBit;
+
+ if (radix == 2)
+ {
+
+ for (int j = b - 1; j >= 0; j--)
+ {
+ if ((a[a.length - 1] & ((long)1 << j)) == 0)
+ {
+ s += "0";
+ }
+ else
+ {
+ s += "1";
+ }
+ }
+
+ for (int i = a.length - 2; i >= 0; i--)
+ {
+ for (int j = MAXLONG - 1; j >= 0; j--)
+ {
+ if ((a[i] & mBitmask[j]) == 0)
+ {
+ s += "0";
+ }
+ else
+ {
+ s += "1";
+ }
+ }
+ }
+ }
+ else if (radix == 16)
+ {
+ final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ for (int i = a.length - 1; i >= 0; i--)
+ {
+ s += HEX_CHARS[(int)(a[i] >>> 60) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 56) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 52) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 48) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 44) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 40) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 36) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 32) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 28) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 24) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 20) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 16) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 12) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 8) & 0x0f];
+ s += HEX_CHARS[(int)(a[i] >>> 4) & 0x0f];
+ s += HEX_CHARS[(int)(a[i]) & 0x0f];
+ s += " ";
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Returns this element as FlexiBigInt. The conversion is <a href =
+ * "http://grouper.ieee.org/groups/1363/">P1363</a>-conform.
+ *
+ * @return this element as BigInteger
+ */
+ public BigInteger toFlexiBigInt()
+ {
+ /** @todo this method does not reverse the bit-order as it should!!! */
+
+ return new BigInteger(1, toByteArray());
+ }
+
+ /**
+ * Returns this element as byte array. The conversion is <a href =
+ * "http://grouper.ieee.org/groups/1363/">P1363</a>-conform.
+ *
+ * @return this element as byte array
+ */
+ public byte[] toByteArray()
+ {
+ /** @todo this method does not reverse the bit-order as it should!!! */
+
+ int k = ((mDegree - 1) >> 3) + 1;
+ byte[] result = new byte[k];
+ int i;
+ for (i = 0; i < k; i++)
+ {
+ result[k - i - 1] = (byte)((mPol[i >>> 3] & (0x00000000000000ffL << ((i & 0x07) << 3))) >>> ((i & 0x07) << 3));
+ }
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBField.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBField.java
new file mode 100644
index 00000000..60e5be41
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nONBField.java
@@ -0,0 +1,546 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.util.Random;
+import java.util.Vector;
+
+
+/**
+ * This class implements the abstract class <tt>GF2nField</tt> for ONB
+ * representation. It computes the fieldpolynomial, multiplication matrix and
+ * one of its roots mONBRoot, (see for example <a
+ * href=http://www2.certicom.com/ecc/intro.htm>Certicoms Whitepapers</a>).
+ * GF2nField is used by GF2nONBElement which implements the elements of this
+ * field.
+ *
+ * @see GF2nField
+ * @see GF2nONBElement
+ */
+public class GF2nONBField
+ extends GF2nField
+{
+
+ // ///////////////////////////////////////////////////////////////////
+ // Hashtable for irreducible normal polynomials //
+ // ///////////////////////////////////////////////////////////////////
+
+ // i*5 + 0 i*5 + 1 i*5 + 2 i*5 + 3 i*5 + 4
+ /*
+ * private static int[][] mNB = {{0, 0, 0}, {0, 0, 0}, {1, 0, 0}, {1, 0, 0},
+ * {1, 0, 0}, // i = 0 {2, 0, 0}, {1, 0, 0}, {1, 0, 0}, {4, 3, 1}, {1, 0,
+ * 0}, // i = 1 {3, 0, 0}, {2, 0, 0}, {3, 0, 0}, {4, 3, 1}, {5, 0, 0}, // i =
+ * 2 {1, 0, 0}, {5, 3, 1}, {3, 0, 0}, {3, 0, 0}, {5, 2, 1}, // i = 3 {3, 0,
+ * 0}, {2, 0, 0}, {1, 0, 0}, {5, 0, 0}, {4, 3, 1}, // i = 4 {3, 0, 0}, {4,
+ * 3, 1}, {5, 2, 1}, {1, 0, 0}, {2, 0, 0}, // i = 5 {1, 0, 0}, {3, 0, 0},
+ * {7, 3, 2}, {10, 0, 0}, {7, 0, 0}, // i = 6 {2, 0, 0}, {9, 0, 0}, {6, 4,
+ * 1}, {6, 5, 1}, {4, 0, 0}, // i = 7 {5, 4, 3}, {3, 0, 0}, {7, 0, 0}, {6,
+ * 4, 3}, {5, 0, 0}, // i = 8 {4, 3, 1}, {1, 0, 0}, {5, 0, 0}, {5, 3, 2},
+ * {9, 0, 0}, // i = 9 {4, 3, 2}, {6, 3, 1}, {3, 0, 0}, {6, 2, 1}, {9, 0,
+ * 0}, // i = 10 {7, 0, 0}, {7, 4, 2}, {4, 0, 0}, {19, 0, 0}, {7, 4, 2}, //
+ * i = 11 {1, 0, 0}, {5, 2, 1}, {29, 0, 0}, {1, 0, 0}, {4, 3, 1}, // i = 12
+ * {18, 0, 0}, {3, 0, 0}, {5, 2, 1}, {9, 0, 0}, {6, 5, 2}, // i = 13 {5, 3,
+ * 1}, {6, 0, 0}, {10, 9, 3}, {25, 0, 0}, {35, 0, 0}, // i = 14 {6, 3, 1},
+ * {21, 0, 0}, {6, 5, 2}, {6, 5, 3}, {9, 0, 0}, // i = 15 {9, 4, 2}, {4, 0,
+ * 0}, {8, 3, 1}, {7, 4, 2}, {5, 0, 0}, // i = 16 {8, 2, 1}, {21, 0, 0},
+ * {13, 0, 0}, {7, 6, 2}, {38, 0, 0}, // i = 17 {27, 0, 0}, {8, 5, 1}, {21,
+ * 0, 0}, {2, 0, 0}, {21, 0, 0}, // i = 18 {11, 0, 0}, {10, 9, 6}, {6, 0,
+ * 0}, {11, 0, 0}, {6, 3, 1}, // i = 19 {15, 0, 0}, {7, 6, 1}, {29, 0, 0},
+ * {9, 0, 0}, {4, 3, 1}, // i = 20 {4, 0, 0}, {15, 0, 0}, {9, 7, 4}, {17, 0,
+ * 0}, {5, 4, 2}, // i = 21 {33, 0, 0}, {10, 0, 0}, {5, 4, 3}, {9, 0, 0},
+ * {5, 3, 2}, // i = 22 {8, 7, 5}, {4, 2, 1}, {5, 2, 1}, {33, 0, 0}, {8, 0,
+ * 0}, // i = 23 {4, 3, 1}, {18, 0, 0}, {6, 2, 1}, {2, 0, 0}, {19, 0, 0}, //
+ * i = 24 {7, 6, 5}, {21, 0, 0}, {1, 0, 0}, {7, 2, 1}, {5, 0, 0}, // i = 25
+ * {3, 0, 0}, {8, 3, 2}, {17, 0, 0}, {9, 8, 2}, {57, 0, 0}, // i = 26 {11,
+ * 0, 0}, {5, 3, 2}, {21, 0, 0}, {8, 7, 1}, {8, 5, 3}, // i = 27 {15, 0, 0},
+ * {10, 4, 1}, {21, 0, 0}, {5, 3, 2}, {7, 4, 2}, // i = 28 {52, 0, 0}, {71,
+ * 0, 0}, {14, 0, 0}, {27, 0, 0}, {10, 9, 7}, // i = 29 {53, 0, 0}, {3, 0,
+ * 0}, {6, 3, 2}, {1, 0, 0}, {15, 0, 0}, // i = 30 {62, 0, 0}, {9, 0, 0},
+ * {6, 5, 2}, {8, 6, 5}, {31, 0, 0}, // i = 31 {5, 3, 2}, {18, 0, 0 }, {27,
+ * 0, 0}, {7, 6, 3}, {10, 8, 7}, // i = 32 {9, 8, 3}, {37, 0, 0}, {6, 0, 0},
+ * {15, 3, 2}, {34, 0, 0}, // i = 33 {11, 0, 0}, {6, 5, 2}, {1, 0, 0}, {8,
+ * 5, 2}, {13, 0, 0}, // i = 34 {6, 0, 0}, {11, 3, 2}, {8, 0, 0}, {31, 0,
+ * 0}, {4, 2, 1}, // i = 35 {3, 0, 0}, {7, 6, 1}, {81, 0, 0}, {56, 0, 0},
+ * {9, 8, 7}, // i = 36 {24, 0, 0}, {11, 0, 0}, {7, 6, 5}, {6, 5, 2}, {6, 5,
+ * 2}, // i = 37 {8, 7, 6}, {9, 0, 0}, {7, 2, 1}, {15, 0, 0}, {87, 0, 0}, //
+ * i = 38 {8, 3, 2}, {3, 0, 0}, {9, 4, 2}, {9, 0, 0}, {34, 0, 0}, // i = 39
+ * {5, 3, 2}, {14, 0, 0}, {55, 0, 0}, {8, 7, 1}, {27, 0, 0}, // i = 40 {9,
+ * 5, 2}, {10, 9, 5}, {43, 0, 0}, {8, 6, 2}, {6, 0, 0}, // i = 41 {7, 0, 0},
+ * {11, 10, 8}, {105, 0, 0}, {6, 5, 2}, {73, 0, 0}}; // i = 42
+ */
+ // /////////////////////////////////////////////////////////////////////
+ // member variables
+ // /////////////////////////////////////////////////////////////////////
+ private static final int MAXLONG = 64;
+
+ /**
+ * holds the length of the array-representation of degree mDegree.
+ */
+ private int mLength;
+
+ /**
+ * holds the number of relevant bits in mONBPol[mLength-1].
+ */
+ private int mBit;
+
+ /**
+ * holds the type of mONB
+ */
+ private int mType;
+
+ /**
+ * holds the multiplication matrix
+ */
+ int[][] mMult;
+
+ // /////////////////////////////////////////////////////////////////////
+ // constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * constructs an instance of the finite field with 2<sup>deg</sup>
+ * elements and characteristic 2.
+ *
+ * @param deg -
+ * the extention degree of this field
+ * @throws NoSuchBasisException if an ONB-implementation other than type 1 or type 2 is
+ * requested.
+ */
+ public GF2nONBField(int deg)
+ throws RuntimeException
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("k must be at least 3");
+ }
+
+ mDegree = deg;
+ mLength = mDegree / MAXLONG;
+ mBit = mDegree & (MAXLONG - 1);
+ if (mBit == 0)
+ {
+ mBit = MAXLONG;
+ }
+ else
+ {
+ mLength++;
+ }
+
+ computeType();
+
+ // only ONB-implementations for type 1 and type 2
+ //
+ if (mType < 3)
+ {
+ mMult = new int[mDegree][2];
+ for (int i = 0; i < mDegree; i++)
+ {
+ mMult[i][0] = -1;
+ mMult[i][1] = -1;
+ }
+ computeMultMatrix();
+ }
+ else
+ {
+ throw new RuntimeException("\nThe type of this field is "
+ + mType);
+ }
+ computeFieldPolynomial();
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ int getONBLength()
+ {
+ return mLength;
+ }
+
+ int getONBBit()
+ {
+ return mBit;
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Computes a random root of the given polynomial.
+ *
+ * @param polynomial a polynomial
+ * @return a random root of the polynomial
+ * @see "P1363 A.5.6, p103f"
+ */
+ protected GF2nElement getRandomRoot(GF2Polynomial polynomial)
+ {
+ // We are in B1!!!
+ GF2nPolynomial c;
+ GF2nPolynomial ut;
+ GF2nElement u;
+ GF2nPolynomial h;
+ int hDegree;
+ // 1. Set g(t) <- f(t)
+ GF2nPolynomial g = new GF2nPolynomial(polynomial, this);
+ int gDegree = g.getDegree();
+ int i;
+
+ // 2. while deg(g) > 1
+ while (gDegree > 1)
+ {
+ do
+ {
+ // 2.1 choose random u (element of) GF(2^m)
+ u = new GF2nONBElement(this, new Random());
+ ut = new GF2nPolynomial(2, GF2nONBElement.ZERO(this));
+ // 2.2 Set c(t) <- ut
+ ut.set(1, u);
+ c = new GF2nPolynomial(ut);
+ // 2.3 For i from 1 to m-1 do
+ for (i = 1; i <= mDegree - 1; i++)
+ {
+ // 2.3.1 c(t) <- (c(t)^2 + ut) mod g(t)
+ c = c.multiplyAndReduce(c, g);
+ c = c.add(ut);
+ }
+ // 2.4 set h(t) <- GCD(c(t), g(t))
+ h = c.gcd(g);
+ // 2.5 if h(t) is constant or deg(g) = deg(h) then go to
+ // step 2.1
+ hDegree = h.getDegree();
+ gDegree = g.getDegree();
+ }
+ while ((hDegree == 0) || (hDegree == gDegree));
+ // 2.6 If 2deg(h) > deg(g) then set g(t) <- g(t)/h(t) ...
+ if ((hDegree << 1) > gDegree)
+ {
+ g = g.quotient(h);
+ }
+ else
+ {
+ // ... else g(t) <- h(t)
+ g = new GF2nPolynomial(h);
+ }
+ gDegree = g.getDegree();
+ }
+ // 3. Output g(0)
+ return g.at(0);
+
+ }
+
+ /**
+ * Computes the change-of-basis matrix for basis conversion according to
+ * 1363. The result is stored in the lists fields and matrices.
+ *
+ * @param B1 the GF2nField to convert to
+ * @see "P1363 A.7.3, p111ff"
+ */
+ protected void computeCOBMatrix(GF2nField B1)
+ {
+ // we are in B0 here!
+ if (mDegree != B1.mDegree)
+ {
+ throw new IllegalArgumentException(
+ "GF2nField.computeCOBMatrix: B1 has a "
+ + "different degree and thus cannot be coverted to!");
+ }
+ int i, j;
+ GF2nElement[] gamma;
+ GF2nElement u;
+ GF2Polynomial[] COBMatrix = new GF2Polynomial[mDegree];
+ for (i = 0; i < mDegree; i++)
+ {
+ COBMatrix[i] = new GF2Polynomial(mDegree);
+ }
+
+ // find Random Root
+ do
+ {
+ // u is in representation according to B1
+ u = B1.getRandomRoot(fieldPolynomial);
+ }
+ while (u.isZero());
+
+ gamma = new GF2nPolynomialElement[mDegree];
+ // build gamma matrix by squaring
+ gamma[0] = (GF2nElement)u.clone();
+ for (i = 1; i < mDegree; i++)
+ {
+ gamma[i] = gamma[i - 1].square();
+ }
+ // convert horizontal gamma matrix by vertical Bitstrings
+ for (i = 0; i < mDegree; i++)
+ {
+ for (j = 0; j < mDegree; j++)
+ {
+ if (gamma[i].testBit(j))
+ {
+ COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1);
+ }
+ }
+ }
+
+ fields.addElement(B1);
+ matrices.addElement(COBMatrix);
+ B1.fields.addElement(this);
+ B1.matrices.addElement(invertMatrix(COBMatrix));
+ }
+
+ /**
+ * Computes the field polynomial for a ONB according to IEEE 1363 A.7.2
+ * (p110f).
+ *
+ * @see "P1363 A.7.2, p110f"
+ */
+ protected void computeFieldPolynomial()
+ {
+ if (mType == 1)
+ {
+ fieldPolynomial = new GF2Polynomial(mDegree + 1, "ALL");
+ }
+ else if (mType == 2)
+ {
+ // 1. q = 1
+ GF2Polynomial q = new GF2Polynomial(mDegree + 1, "ONE");
+ // 2. p = t+1
+ GF2Polynomial p = new GF2Polynomial(mDegree + 1, "X");
+ p.addToThis(q);
+ GF2Polynomial r;
+ int i;
+ // 3. for i = 1 to (m-1) do
+ for (i = 1; i < mDegree; i++)
+ {
+ // r <- q
+ r = q;
+ // q <- p
+ q = p;
+ // p = tq+r
+ p = q.shiftLeft();
+ p.addToThis(r);
+ }
+ fieldPolynomial = p;
+ }
+ }
+
+ /**
+ * Compute the inverse of a matrix <tt>a</tt>.
+ *
+ * @param a the matrix
+ * @return <tt>a<sup>-1</sup></tt>
+ */
+ int[][] invMatrix(int[][] a)
+ {
+
+ int[][] A = new int[mDegree][mDegree];
+ A = a;
+ int[][] inv = new int[mDegree][mDegree];
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ inv[i][i] = 1;
+ }
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ for (int j = i; j < mDegree; j++)
+ {
+ A[mDegree - 1 - i][j] = A[i][i];
+ }
+ }
+ return null;
+ }
+
+ private void computeType()
+ throws RuntimeException
+ {
+ if ((mDegree & 7) == 0)
+ {
+ throw new RuntimeException(
+ "The extension degree is divisible by 8!");
+ }
+ // checking for the type
+ int s = 0;
+ int k = 0;
+ mType = 1;
+ for (int d = 0; d != 1; mType++)
+ {
+ s = mType * mDegree + 1;
+ if (IntegerFunctions.isPrime(s))
+ {
+ k = IntegerFunctions.order(2, s);
+ d = IntegerFunctions.gcd(mType * mDegree / k, mDegree);
+ }
+ }
+ mType--;
+ if (mType == 1)
+ {
+ s = (mDegree << 1) + 1;
+ if (IntegerFunctions.isPrime(s))
+ {
+ k = IntegerFunctions.order(2, s);
+ int d = IntegerFunctions.gcd((mDegree << 1) / k, mDegree);
+ if (d == 1)
+ {
+ mType++;
+ }
+ }
+ }
+ }
+
+ private void computeMultMatrix()
+ {
+
+ if ((mType & 7) != 0)
+ {
+ int p = mType * mDegree + 1;
+
+ // compute sequence F[1] ... F[p-1] via A.3.7. of 1363.
+ // F[0] will not be filled!
+ //
+ int[] F = new int[p];
+
+ int u;
+ if (mType == 1)
+ {
+ u = 1;
+ }
+ else if (mType == 2)
+ {
+ u = p - 1;
+ }
+ else
+ {
+ u = elementOfOrder(mType, p);
+ }
+
+ int w = 1;
+ int n;
+ for (int j = 0; j < mType; j++)
+ {
+ n = w;
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ F[n] = i;
+ n = (n << 1) % p;
+ if (n < 0)
+ {
+ n += p;
+ }
+ }
+ w = u * w % p;
+ if (w < 0)
+ {
+ w += p;
+ }
+ }
+
+ // building the matrix (mDegree * 2)
+ //
+ if (mType == 1)
+ {
+ for (int k = 1; k < p - 1; k++)
+ {
+ if (mMult[F[k + 1]][0] == -1)
+ {
+ mMult[F[k + 1]][0] = F[p - k];
+ }
+ else
+ {
+ mMult[F[k + 1]][1] = F[p - k];
+ }
+ }
+
+ int m_2 = mDegree >> 1;
+ for (int k = 1; k <= m_2; k++)
+ {
+
+ if (mMult[k - 1][0] == -1)
+ {
+ mMult[k - 1][0] = m_2 + k - 1;
+ }
+ else
+ {
+ mMult[k - 1][1] = m_2 + k - 1;
+ }
+
+ if (mMult[m_2 + k - 1][0] == -1)
+ {
+ mMult[m_2 + k - 1][0] = k - 1;
+ }
+ else
+ {
+ mMult[m_2 + k - 1][1] = k - 1;
+ }
+ }
+ }
+ else if (mType == 2)
+ {
+ for (int k = 1; k < p - 1; k++)
+ {
+ if (mMult[F[k + 1]][0] == -1)
+ {
+ mMult[F[k + 1]][0] = F[p - k];
+ }
+ else
+ {
+ mMult[F[k + 1]][1] = F[p - k];
+ }
+ }
+ }
+ else
+ {
+ throw new RuntimeException("only type 1 or type 2 implemented");
+ }
+ }
+ else
+ {
+ throw new RuntimeException("bisher nur fuer Gausssche Normalbasen"
+ + " implementiert");
+ }
+ }
+
+ private int elementOfOrder(int k, int p)
+ {
+ Random random = new Random();
+ int m = 0;
+ while (m == 0)
+ {
+ m = random.nextInt();
+ m %= p - 1;
+ if (m < 0)
+ {
+ m += p - 1;
+ }
+ }
+
+ int l = IntegerFunctions.order(m, p);
+
+ while (l % k != 0 || l == 0)
+ {
+ while (m == 0)
+ {
+ m = random.nextInt();
+ m %= p - 1;
+ if (m < 0)
+ {
+ m += p - 1;
+ }
+ }
+ l = IntegerFunctions.order(m, p);
+ }
+ int r = m;
+
+ l = k / l;
+
+ for (int i = 2; i <= l; i++)
+ {
+ r *= m;
+ }
+
+ return r;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomial.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomial.java
new file mode 100644
index 00000000..97ee022c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomial.java
@@ -0,0 +1,587 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+/**
+ * This class implements polynomials over GF2nElements.
+ *
+ * @see GF2nElement
+ */
+
+public class GF2nPolynomial
+{
+
+ private GF2nElement[] coeff; // keeps the coefficients of this polynomial
+
+ private int size; // the size of this polynomial
+
+ /**
+ * Creates a new PolynomialGF2n of size <i>deg</i> and elem as
+ * coefficients.
+ *
+ * @param deg -
+ * the maximum degree + 1
+ * @param elem -
+ * a GF2nElement
+ */
+ public GF2nPolynomial(int deg, GF2nElement elem)
+ {
+ size = deg;
+ coeff = new GF2nElement[size];
+ for (int i = 0; i < size; i++)
+ {
+ coeff[i] = (GF2nElement)elem.clone();
+ }
+ }
+
+ /**
+ * Creates a new PolynomialGF2n of size <i>deg</i>.
+ *
+ * @param deg the maximum degree + 1
+ */
+ private GF2nPolynomial(int deg)
+ {
+ size = deg;
+ coeff = new GF2nElement[size];
+ }
+
+ /**
+ * Creates a new PolynomialGF2n by cloning the given PolynomialGF2n <i>a</i>.
+ *
+ * @param a the PolynomialGF2n to clone
+ */
+ public GF2nPolynomial(GF2nPolynomial a)
+ {
+ int i;
+ coeff = new GF2nElement[a.size];
+ size = a.size;
+ for (i = 0; i < size; i++)
+ {
+ coeff[i] = (GF2nElement)a.coeff[i].clone();
+ }
+ }
+
+ /**
+ * Creates a new PolynomialGF2n from the given Bitstring <i>polynomial</i>
+ * over the GF2nField <i>B1</i>.
+ *
+ * @param polynomial the Bitstring to use
+ * @param B1 the field
+ */
+ public GF2nPolynomial(GF2Polynomial polynomial, GF2nField B1)
+ {
+ size = B1.getDegree() + 1;
+ coeff = new GF2nElement[size];
+ int i;
+ if (B1 instanceof GF2nONBField)
+ {
+ for (i = 0; i < size; i++)
+ {
+ if (polynomial.testBit(i))
+ {
+ coeff[i] = GF2nONBElement.ONE((GF2nONBField)B1);
+ }
+ else
+ {
+ coeff[i] = GF2nONBElement.ZERO((GF2nONBField)B1);
+ }
+ }
+ }
+ else if (B1 instanceof GF2nPolynomialField)
+ {
+ for (i = 0; i < size; i++)
+ {
+ if (polynomial.testBit(i))
+ {
+ coeff[i] = GF2nPolynomialElement
+ .ONE((GF2nPolynomialField)B1);
+ }
+ else
+ {
+ coeff[i] = GF2nPolynomialElement
+ .ZERO((GF2nPolynomialField)B1);
+ }
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "PolynomialGF2n(Bitstring, GF2nField): B1 must be "
+ + "an instance of GF2nONBField or GF2nPolynomialField!");
+ }
+ }
+
+ public final void assignZeroToElements()
+ {
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ coeff[i].assignZero();
+ }
+ }
+
+ /**
+ * Returns the size (=maximum degree + 1) of this PolynomialGF2n. This is
+ * not the degree, use getDegree instead.
+ *
+ * @return the size (=maximum degree + 1) of this PolynomialGF2n.
+ */
+ public final int size()
+ {
+ return size;
+ }
+
+ /**
+ * Returns the degree of this PolynomialGF2n.
+ *
+ * @return the degree of this PolynomialGF2n.
+ */
+ public final int getDegree()
+ {
+ int i;
+ for (i = size - 1; i >= 0; i--)
+ {
+ if (!coeff[i].isZero())
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Enlarges the size of this PolynomialGF2n to <i>k</i> + 1.
+ *
+ * @param k the new maximum degree
+ */
+ public final void enlarge(int k)
+ {
+ if (k <= size)
+ {
+ return;
+ }
+ int i;
+ GF2nElement[] res = new GF2nElement[k];
+ System.arraycopy(coeff, 0, res, 0, size);
+ GF2nField f = coeff[0].getField();
+ if (coeff[0] instanceof GF2nPolynomialElement)
+ {
+ for (i = size; i < k; i++)
+ {
+ res[i] = GF2nPolynomialElement.ZERO((GF2nPolynomialField)f);
+ }
+ }
+ else if (coeff[0] instanceof GF2nONBElement)
+ {
+ for (i = size; i < k; i++)
+ {
+ res[i] = GF2nONBElement.ZERO((GF2nONBField)f);
+ }
+ }
+ size = k;
+ coeff = res;
+ }
+
+ public final void shrink()
+ {
+ int i = size - 1;
+ while (coeff[i].isZero() && (i > 0))
+ {
+ i--;
+ }
+ i++;
+ if (i < size)
+ {
+ GF2nElement[] res = new GF2nElement[i];
+ System.arraycopy(coeff, 0, res, 0, i);
+ coeff = res;
+ size = i;
+ }
+ }
+
+ /**
+ * Sets the coefficient at <i>index</i> to <i>elem</i>.
+ *
+ * @param index the index
+ * @param elem the GF2nElement to store as coefficient <i>index</i>
+ */
+ public final void set(int index, GF2nElement elem)
+ {
+ if (!(elem instanceof GF2nPolynomialElement)
+ && !(elem instanceof GF2nONBElement))
+ {
+ throw new IllegalArgumentException(
+ "PolynomialGF2n.set f must be an "
+ + "instance of either GF2nPolynomialElement or GF2nONBElement!");
+ }
+ coeff[index] = (GF2nElement)elem.clone();
+ }
+
+ /**
+ * Returns the coefficient at <i>index</i>.
+ *
+ * @param index the index
+ * @return the GF2nElement stored as coefficient <i>index</i>
+ */
+ public final GF2nElement at(int index)
+ {
+ return coeff[index];
+ }
+
+ /**
+ * Returns true if all coefficients equal zero.
+ *
+ * @return true if all coefficients equal zero.
+ */
+ public final boolean isZero()
+ {
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (coeff[i] != null)
+ {
+ if (!coeff[i].isZero())
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public final boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nPolynomial))
+ {
+ return false;
+ }
+
+ GF2nPolynomial otherPol = (GF2nPolynomial)other;
+
+ if (getDegree() != otherPol.getDegree())
+ {
+ return false;
+ }
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (!coeff[i].equals(otherPol.coeff[i]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return the hash code of this polynomial
+ */
+ public int hashCode()
+ {
+ return getDegree() + coeff.hashCode();
+ }
+
+ /**
+ * Adds the PolynomialGF2n <tt>b</tt> to <tt>this</tt> and returns the
+ * result in a new <tt>PolynomialGF2n</tt>.
+ *
+ * @param b -
+ * the <tt>PolynomialGF2n</tt> to add
+ * @return <tt>this + b</tt>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial add(GF2nPolynomial b)
+ throws RuntimeException
+ {
+ GF2nPolynomial result;
+ if (size() >= b.size())
+ {
+ result = new GF2nPolynomial(size());
+ int i;
+ for (i = 0; i < b.size(); i++)
+ {
+ result.coeff[i] = (GF2nElement)coeff[i].add(b.coeff[i]);
+ }
+ for (; i < size(); i++)
+ {
+ result.coeff[i] = coeff[i];
+ }
+ }
+ else
+ {
+ result = new GF2nPolynomial(b.size());
+ int i;
+ for (i = 0; i < size(); i++)
+ {
+ result.coeff[i] = (GF2nElement)coeff[i].add(b.coeff[i]);
+ }
+ for (; i < b.size(); i++)
+ {
+ result.coeff[i] = b.coeff[i];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies the scalar <i>s</i> to each coefficient of this
+ * PolynomialGF2n and returns the result in a new PolynomialGF2n.
+ *
+ * @param s the scalar to multiply
+ * @return <i>this</i> x <i>s</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>s</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial scalarMultiply(GF2nElement s)
+ throws RuntimeException
+ {
+ GF2nPolynomial result = new GF2nPolynomial(size());
+ int i;
+ for (i = 0; i < size(); i++)
+ {
+ result.coeff[i] = (GF2nElement)coeff[i].multiply(s); // result[i]
+ // =
+ // a[i]*s
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies <i>this</i> by <i>b</i> and returns the result in a new
+ * PolynomialGF2n.
+ *
+ * @param b the PolynomialGF2n to multiply
+ * @return <i>this</i> * <i>b</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial multiply(GF2nPolynomial b)
+ throws RuntimeException
+ {
+ int i, j;
+ int aDegree = size();
+ int bDegree = b.size();
+ if (aDegree != bDegree)
+ {
+ throw new IllegalArgumentException(
+ "PolynomialGF2n.multiply: this and b must "
+ + "have the same size!");
+ }
+ GF2nPolynomial result = new GF2nPolynomial((aDegree << 1) - 1);
+ for (i = 0; i < size(); i++)
+ {
+ for (j = 0; j < b.size(); j++)
+ {
+ if (result.coeff[i + j] == null)
+ {
+ result.coeff[i + j] = (GF2nElement)coeff[i]
+ .multiply(b.coeff[j]);
+ }
+ else
+ {
+ result.coeff[i + j] = (GF2nElement)result.coeff[i + j]
+ .add(coeff[i].multiply(b.coeff[j]));
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Multiplies <i>this</i> by <i>b</i>, reduces the result by <i>g</i> and
+ * returns it in a new PolynomialGF2n.
+ *
+ * @param b the PolynomialGF2n to multiply
+ * @param g the modul
+ * @return <i>this</i> * <i>b</i> mod <i>g</i>
+ * @throws DifferentFieldsException if <tt>this</tt>, <tt>b</tt> and <tt>g</tt> are
+ * not all defined over the same field.
+ */
+ public final GF2nPolynomial multiplyAndReduce(GF2nPolynomial b,
+ GF2nPolynomial g)
+ throws RuntimeException,
+ ArithmeticException
+ {
+ return multiply(b).reduce(g);
+ }
+
+ /**
+ * Reduces <i>this</i> by <i>g</i> and returns the result in a new
+ * PolynomialGF2n.
+ *
+ * @param g -
+ * the modulus
+ * @return <i>this</i> % <i>g</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>g</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial reduce(GF2nPolynomial g)
+ throws RuntimeException, ArithmeticException
+ {
+ return remainder(g); // return this % g
+ }
+
+ /**
+ * Shifts left <i>this</i> by <i>amount</i> and stores the result in
+ * <i>this</i> PolynomialGF2n.
+ *
+ * @param amount the amount to shift the coefficients
+ */
+ public final void shiftThisLeft(int amount)
+ {
+ if (amount > 0)
+ {
+ int i;
+ int oldSize = size;
+ GF2nField f = coeff[0].getField();
+ enlarge(size + amount);
+ for (i = oldSize - 1; i >= 0; i--)
+ {
+ coeff[i + amount] = coeff[i];
+ }
+ if (coeff[0] instanceof GF2nPolynomialElement)
+ {
+ for (i = amount - 1; i >= 0; i--)
+ {
+ coeff[i] = GF2nPolynomialElement
+ .ZERO((GF2nPolynomialField)f);
+ }
+ }
+ else if (coeff[0] instanceof GF2nONBElement)
+ {
+ for (i = amount - 1; i >= 0; i--)
+ {
+ coeff[i] = GF2nONBElement.ZERO((GF2nONBField)f);
+ }
+ }
+ }
+ }
+
+ public final GF2nPolynomial shiftLeft(int amount)
+ {
+ if (amount <= 0)
+ {
+ return new GF2nPolynomial(this);
+ }
+ GF2nPolynomial result = new GF2nPolynomial(size + amount, coeff[0]);
+ result.assignZeroToElements();
+ for (int i = 0; i < size; i++)
+ {
+ result.coeff[i + amount] = coeff[i];
+ }
+ return result;
+ }
+
+ /**
+ * Divides <i>this</i> by <i>b</i> and stores the result in a new
+ * PolynomialGF2n[2], quotient in result[0] and remainder in result[1].
+ *
+ * @param b the divisor
+ * @return the quotient and remainder of <i>this</i> / <i>b</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial[] divide(GF2nPolynomial b)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial[] result = new GF2nPolynomial[2];
+ GF2nPolynomial a = new GF2nPolynomial(this);
+ a.shrink();
+ GF2nPolynomial shift;
+ GF2nElement factor;
+ int bDegree = b.getDegree();
+ GF2nElement inv = (GF2nElement)b.coeff[bDegree].invert();
+ if (a.getDegree() < bDegree)
+ {
+ result[0] = new GF2nPolynomial(this);
+ result[0].assignZeroToElements();
+ result[0].shrink();
+ result[1] = new GF2nPolynomial(this);
+ result[1].shrink();
+ return result;
+ }
+ result[0] = new GF2nPolynomial(this);
+ result[0].assignZeroToElements();
+ int i = a.getDegree() - bDegree;
+ while (i >= 0)
+ {
+ factor = (GF2nElement)a.coeff[a.getDegree()].multiply(inv);
+ shift = b.scalarMultiply(factor);
+ shift.shiftThisLeft(i);
+ a = a.add(shift);
+ a.shrink();
+ result[0].coeff[i] = (GF2nElement)factor.clone();
+ i = a.getDegree() - bDegree;
+ }
+ result[1] = a;
+ result[0].shrink();
+ return result;
+ }
+
+ /**
+ * Divides <i>this</i> by <i>b</i> and stores the remainder in a new
+ * PolynomialGF2n.
+ *
+ * @param b the divisor
+ * @return the remainder <i>this</i> % <i>b</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial remainder(GF2nPolynomial b)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial[] result = new GF2nPolynomial[2];
+ result = divide(b);
+ return result[1];
+ }
+
+ /**
+ * Divides <i>this</i> by <i>b</i> and stores the quotient in a new
+ * PolynomialGF2n.
+ *
+ * @param b the divisor
+ * @return the quotient <i>this</i> / <i>b</i>
+ * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
+ * the same field.
+ */
+ public final GF2nPolynomial quotient(GF2nPolynomial b)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial[] result = new GF2nPolynomial[2];
+ result = divide(b);
+ return result[0];
+ }
+
+ /**
+ * Computes the greatest common divisor of <i>this</i> and <i>g</i> and
+ * returns the result in a new PolynomialGF2n.
+ *
+ * @param g -
+ * a GF2nPolynomial
+ * @return gcd(<i>this</i>, <i>g</i>)
+ * @throws DifferentFieldsException if the coefficients of <i>this</i> and <i>g</i> use
+ * different fields
+ * @throws ArithmeticException if coefficients are zero.
+ */
+ public final GF2nPolynomial gcd(GF2nPolynomial g)
+ throws RuntimeException, ArithmeticException
+ {
+ GF2nPolynomial a = new GF2nPolynomial(this);
+ GF2nPolynomial b = new GF2nPolynomial(g);
+ a.shrink();
+ b.shrink();
+ GF2nPolynomial c;
+ GF2nPolynomial result;
+ GF2nElement alpha;
+ while (!b.isZero())
+ {
+ c = a.remainder(b);
+ a = b;
+ b = c;
+ }
+ alpha = a.coeff[a.getDegree()];
+ result = a.scalarMultiply((GF2nElement)alpha.invert());
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java
new file mode 100644
index 00000000..50e9d751
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java
@@ -0,0 +1,1021 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.math.BigInteger;
+import java.util.Random;
+
+
+/**
+ * This class implements elements of finite binary fields <i>GF(2<sup>n</sup>)</i>
+ * using polynomial representation. For more information on the arithmetic see
+ * for example IEEE Standard 1363 or <a
+ * href=http://www.certicom.com/research/online.html> Certicom online-tutorial</a>.
+ *
+ * @see "GF2nField"
+ * @see GF2nPolynomialField
+ * @see GF2nONBElement
+ * @see GF2Polynomial
+ */
+public class GF2nPolynomialElement
+ extends GF2nElement
+{
+
+ // pre-computed Bitmask for fast masking, bitMask[a]=0x1 << a
+ private static final int[] bitMask = {0x00000001, 0x00000002, 0x00000004,
+ 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080,
+ 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000,
+ 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000,
+ 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000,
+ 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x00000000};
+
+ // the used GF2Polynomial which stores the coefficients
+ private GF2Polynomial polynomial;
+
+ /**
+ * Create a new random GF2nPolynomialElement using the given field and
+ * source of randomness.
+ *
+ * @param f the GF2nField to use
+ * @param rand the source of randomness
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, Random rand)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(mDegree);
+ randomize(rand);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement using the given field and Bitstring.
+ *
+ * @param f the GF2nPolynomialField to use
+ * @param bs the desired value as Bitstring
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, GF2Polynomial bs)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(bs);
+ polynomial.expandN(mDegree);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement using the given field <i>f</i> and
+ * byte[] <i>os</i> as value. The conversion is done according to 1363.
+ *
+ * @param f the GF2nField to use
+ * @param os the octet string to assign to this GF2nPolynomialElement
+ * @see "P1363 5.5.5 p23, OS2FEP/OS2BSP"
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, byte[] os)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(mDegree, os);
+ polynomial.expandN(mDegree);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement using the given field <i>f</i> and
+ * int[] <i>is</i> as value.
+ *
+ * @param f the GF2nField to use
+ * @param is the integer string to assign to this GF2nPolynomialElement
+ */
+ public GF2nPolynomialElement(GF2nPolynomialField f, int[] is)
+ {
+ mField = f;
+ mDegree = mField.getDegree();
+ polynomial = new GF2Polynomial(mDegree, is);
+ polynomial.expandN(f.mDegree);
+ }
+
+ /**
+ * Creates a new GF2nPolynomialElement by cloning the given
+ * GF2nPolynomialElement <i>b</i>.
+ *
+ * @param other the GF2nPolynomialElement to clone
+ */
+ public GF2nPolynomialElement(GF2nPolynomialElement other)
+ {
+ mField = other.mField;
+ mDegree = other.mDegree;
+ polynomial = new GF2Polynomial(other.polynomial);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // pseudo-constructors
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Creates a new GF2nPolynomialElement by cloning this
+ * GF2nPolynomialElement.
+ *
+ * @return a copy of this element
+ */
+ public Object clone()
+ {
+ return new GF2nPolynomialElement(this);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // assignments
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Assigns the value 'zero' to this Polynomial.
+ */
+ void assignZero()
+ {
+ polynomial.assignZero();
+ }
+
+ /**
+ * Create the zero element.
+ *
+ * @param f the finite field
+ * @return the zero element in the given finite field
+ */
+ public static GF2nPolynomialElement ZERO(GF2nPolynomialField f)
+ {
+ GF2Polynomial polynomial = new GF2Polynomial(f.getDegree());
+ return new GF2nPolynomialElement(f, polynomial);
+ }
+
+ /**
+ * Create the one element.
+ *
+ * @param f the finite field
+ * @return the one element in the given finite field
+ */
+ public static GF2nPolynomialElement ONE(GF2nPolynomialField f)
+ {
+ GF2Polynomial polynomial = new GF2Polynomial(f.getDegree(),
+ new int[]{1});
+ return new GF2nPolynomialElement(f, polynomial);
+ }
+
+ /**
+ * Assigns the value 'one' to this Polynomial.
+ */
+ void assignOne()
+ {
+ polynomial.assignOne();
+ }
+
+ /**
+ * Assign a random value to this GF2nPolynomialElement using the specified
+ * source of randomness.
+ *
+ * @param rand the source of randomness
+ */
+ private void randomize(Random rand)
+ {
+ polynomial.expandN(mDegree);
+ polynomial.randomize(rand);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // comparison
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Checks whether this element is zero.
+ *
+ * @return <tt>true</tt> if <tt>this</tt> is the zero element
+ */
+ public boolean isZero()
+ {
+ return polynomial.isZero();
+ }
+
+ /**
+ * Tests if the GF2nPolynomialElement has 'one' as value.
+ *
+ * @return true if <i>this</i> equals one (this == 1)
+ */
+ public boolean isOne()
+ {
+ return polynomial.isOne();
+ }
+
+ /**
+ * Compare this element with another object.
+ *
+ * @param other the other object
+ * @return <tt>true</tt> if the two objects are equal, <tt>false</tt>
+ * otherwise
+ */
+ public boolean equals(Object other)
+ {
+ if (other == null || !(other instanceof GF2nPolynomialElement))
+ {
+ return false;
+ }
+ GF2nPolynomialElement otherElem = (GF2nPolynomialElement)other;
+
+ if (mField != otherElem.mField)
+ {
+ if (!mField.getFieldPolynomial().equals(
+ otherElem.mField.getFieldPolynomial()))
+ {
+ return false;
+ }
+ }
+
+ return polynomial.equals(otherElem.polynomial);
+ }
+
+ /**
+ * @return the hash code of this element
+ */
+ public int hashCode()
+ {
+ return mField.hashCode() + polynomial.hashCode();
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // access
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns the value of this GF2nPolynomialElement in a new Bitstring.
+ *
+ * @return the value of this GF2nPolynomialElement in a new Bitstring
+ */
+ private GF2Polynomial getGF2Polynomial()
+ {
+ return new GF2Polynomial(polynomial);
+ }
+
+ /**
+ * Checks whether the indexed bit of the bit representation is set.
+ *
+ * @param index the index of the bit to test
+ * @return <tt>true</tt> if the indexed bit is set
+ */
+ boolean testBit(int index)
+ {
+ return polynomial.testBit(index);
+ }
+
+ /**
+ * Returns whether the rightmost bit of the bit representation is set. This
+ * is needed for data conversion according to 1363.
+ *
+ * @return true if the rightmost bit of this element is set
+ */
+ public boolean testRightmostBit()
+ {
+ return polynomial.testBit(0);
+ }
+
+ /**
+ * Compute the sum of this element and <tt>addend</tt>.
+ *
+ * @param addend the addend
+ * @return <tt>this + other</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement add(GFElement addend)
+ throws RuntimeException
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.addToThis(addend);
+ return result;
+ }
+
+ /**
+ * Compute <tt>this + addend</tt> (overwrite <tt>this</tt>).
+ *
+ * @param addend the addend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void addToThis(GFElement addend)
+ throws RuntimeException
+ {
+ if (!(addend instanceof GF2nPolynomialElement))
+ {
+ throw new RuntimeException();
+ }
+ if (!mField.equals(((GF2nPolynomialElement)addend).mField))
+ {
+ throw new RuntimeException();
+ }
+ polynomial.addToThis(((GF2nPolynomialElement)addend).polynomial);
+ }
+
+ /**
+ * Returns <tt>this</tt> element + 'one".
+ *
+ * @return <tt>this</tt> + 'one'
+ */
+ public GF2nElement increase()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.increaseThis();
+ return result;
+ }
+
+ /**
+ * Increases this element by 'one'.
+ */
+ public void increaseThis()
+ {
+ polynomial.increaseThis();
+ }
+
+ /**
+ * Compute the product of this element and <tt>factor</tt>.
+ *
+ * @param factor the factor
+ * @return <tt>this * factor</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public GFElement multiply(GFElement factor)
+ throws RuntimeException
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.multiplyThisBy(factor);
+ return result;
+ }
+
+ /**
+ * Compute <tt>this * factor</tt> (overwrite <tt>this</tt>).
+ *
+ * @param factor the factor
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ public void multiplyThisBy(GFElement factor)
+ throws RuntimeException
+ {
+ if (!(factor instanceof GF2nPolynomialElement))
+ {
+ throw new RuntimeException();
+ }
+ if (!mField.equals(((GF2nPolynomialElement)factor).mField))
+ {
+ throw new RuntimeException();
+ }
+ if (equals(factor))
+ {
+ squareThis();
+ return;
+ }
+ polynomial = polynomial
+ .multiply(((GF2nPolynomialElement)factor).polynomial);
+ reduceThis();
+ }
+
+ /**
+ * Compute the multiplicative inverse of this element.
+ *
+ * @return <tt>this<sup>-1</sup></tt> (newly created)
+ * @throws ArithmeticException if <tt>this</tt> is the zero element.
+ * @see GF2nPolynomialElement#invertMAIA
+ * @see GF2nPolynomialElement#invertEEA
+ * @see GF2nPolynomialElement#invertSquare
+ */
+ public GFElement invert()
+ throws ArithmeticException
+ {
+ return invertMAIA();
+ }
+
+ /**
+ * Calculates the multiplicative inverse of <i>this</i> and returns the
+ * result in a new GF2nPolynomialElement.
+ *
+ * @return <i>this</i>^(-1)
+ * @throws ArithmeticException if <i>this</i> equals zero
+ */
+ public GF2nPolynomialElement invertEEA()
+ throws ArithmeticException
+ {
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ GF2Polynomial b = new GF2Polynomial(mDegree + 32, "ONE");
+ b.reduceN();
+ GF2Polynomial c = new GF2Polynomial(mDegree + 32);
+ c.reduceN();
+ GF2Polynomial u = getGF2Polynomial();
+ GF2Polynomial v = mField.getFieldPolynomial();
+ GF2Polynomial h;
+ int j;
+ u.reduceN();
+ while (!u.isOne())
+ {
+ u.reduceN();
+ v.reduceN();
+ j = u.getLength() - v.getLength();
+ if (j < 0)
+ {
+ h = u;
+ u = v;
+ v = h;
+ h = b;
+ b = c;
+ c = h;
+ j = -j;
+ c.reduceN(); // this increases the performance
+ }
+ u.shiftLeftAddThis(v, j);
+ b.shiftLeftAddThis(c, j);
+ }
+ b.reduceN();
+ return new GF2nPolynomialElement((GF2nPolynomialField)mField, b);
+ }
+
+ /**
+ * Calculates the multiplicative inverse of <i>this</i> and returns the
+ * result in a new GF2nPolynomialElement.
+ *
+ * @return <i>this</i>^(-1)
+ * @throws ArithmeticException if <i>this</i> equals zero
+ */
+ public GF2nPolynomialElement invertSquare()
+ throws ArithmeticException
+ {
+ GF2nPolynomialElement n;
+ GF2nPolynomialElement u;
+ int i, j, k, b;
+
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ // b = (n-1)
+ b = mField.getDegree() - 1;
+ // n = a
+ n = new GF2nPolynomialElement(this);
+ n.polynomial.expandN((mDegree << 1) + 32); // increase performance
+ n.polynomial.reduceN();
+ // k = 1
+ k = 1;
+
+ // for i = (r-1) downto 0 do, r=bitlength(b)
+ for (i = IntegerFunctions.floorLog(b) - 1; i >= 0; i--)
+ {
+ // u = n
+ u = new GF2nPolynomialElement(n);
+ // for j = 1 to k do
+ for (j = 1; j <= k; j++)
+ {
+ // u = u^2
+ u.squareThisPreCalc();
+ }
+ // n = nu
+ n.multiplyThisBy(u);
+ // k = 2k
+ k <<= 1;
+ // if b(i)==1
+ if ((b & bitMask[i]) != 0)
+ {
+ // n = n^2 * b
+ n.squareThisPreCalc();
+ n.multiplyThisBy(this);
+ // k = k+1
+ k += 1;
+ }
+ }
+
+ // outpur n^2
+ n.squareThisPreCalc();
+ return n;
+ }
+
+ /**
+ * Calculates the multiplicative inverse of <i>this</i> using the modified
+ * almost inverse algorithm and returns the result in a new
+ * GF2nPolynomialElement.
+ *
+ * @return <i>this</i>^(-1)
+ * @throws ArithmeticException if <i>this</i> equals zero
+ */
+ public GF2nPolynomialElement invertMAIA()
+ throws ArithmeticException
+ {
+ if (isZero())
+ {
+ throw new ArithmeticException();
+ }
+ GF2Polynomial b = new GF2Polynomial(mDegree, "ONE");
+ GF2Polynomial c = new GF2Polynomial(mDegree);
+ GF2Polynomial u = getGF2Polynomial();
+ GF2Polynomial v = mField.getFieldPolynomial();
+ GF2Polynomial h;
+ while (true)
+ {
+ while (!u.testBit(0))
+ { // x|u (x divides u)
+ u.shiftRightThis(); // u = u / x
+ if (!b.testBit(0))
+ {
+ b.shiftRightThis();
+ }
+ else
+ {
+ b.addToThis(mField.getFieldPolynomial());
+ b.shiftRightThis();
+ }
+ }
+ if (u.isOne())
+ {
+ return new GF2nPolynomialElement((GF2nPolynomialField)mField,
+ b);
+ }
+ u.reduceN();
+ v.reduceN();
+ if (u.getLength() < v.getLength())
+ {
+ h = u;
+ u = v;
+ v = h;
+ h = b;
+ b = c;
+ c = h;
+ }
+ u.addToThis(v);
+ b.addToThis(c);
+ }
+ }
+
+ /**
+ * This method is used internally to map the square()-calls within
+ * GF2nPolynomialElement to one of the possible squaring methods.
+ *
+ * @return <tt>this<sup>2</sup></tt> (newly created)
+ * @see GF2nPolynomialElement#squarePreCalc
+ */
+ public GF2nElement square()
+ {
+ return squarePreCalc();
+ }
+
+ /**
+ * This method is used internally to map the square()-calls within
+ * GF2nPolynomialElement to one of the possible squaring methods.
+ */
+ public void squareThis()
+ {
+ squareThisPreCalc();
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement using GF2nField's squaring matrix.
+ * This is supposed to be fast when using a polynomial (no tri- or
+ * pentanomial) as fieldpolynomial. Use squarePreCalc when using a tri- or
+ * pentanomial as fieldpolynomial instead.
+ *
+ * @return <tt>this<sup>2</sup></tt> (newly created)
+ * @see GF2Polynomial#vectorMult
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2nPolynomialElement#squareBitwise
+ */
+ public GF2nPolynomialElement squareMatrix()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareThisMatrix();
+ result.reduceThis();
+ return result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement using GF2nFields squaring matrix. This
+ * is supposed to be fast when using a polynomial (no tri- or pentanomial)
+ * as fieldpolynomial. Use squarePreCalc when using a tri- or pentanomial as
+ * fieldpolynomial instead.
+ *
+ * @see GF2Polynomial#vectorMult
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2nPolynomialElement#squareBitwise
+ */
+ public void squareThisMatrix()
+ {
+ GF2Polynomial result = new GF2Polynomial(mDegree);
+ for (int i = 0; i < mDegree; i++)
+ {
+ if (polynomial
+ .vectorMult(((GF2nPolynomialField)mField).squaringMatrix[mDegree
+ - i - 1]))
+ {
+ result.setBit(i);
+
+ }
+ }
+ polynomial = result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by shifting left its Bitstring and
+ * reducing. This is supposed to be the slowest method. Use squarePreCalc or
+ * squareMatrix instead.
+ *
+ * @return <tt>this<sup>2</sup></tt> (newly created)
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2Polynomial#squareThisBitwise
+ */
+ public GF2nPolynomialElement squareBitwise()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareThisBitwise();
+ result.reduceThis();
+ return result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by shifting left its Bitstring and
+ * reducing. This is supposed to be the slowest method. Use squarePreCalc or
+ * squareMatrix instead.
+ *
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2nPolynomialElement#squarePreCalc
+ * @see GF2Polynomial#squareThisBitwise
+ */
+ public void squareThisBitwise()
+ {
+ polynomial.squareThisBitwise();
+ reduceThis();
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by using precalculated values and
+ * reducing. This is supposed to de fastest when using a trinomial or
+ * pentanomial as field polynomial. Use squareMatrix when using a ordinary
+ * polynomial as field polynomial.
+ *
+ * @return <tt>this<sup>2</sup></tt> (newly created)
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2Polynomial#squareThisPreCalc
+ */
+ public GF2nPolynomialElement squarePreCalc()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareThisPreCalc();
+ result.reduceThis();
+ return result;
+ }
+
+ /**
+ * Squares this GF2nPolynomialElement by using precalculated values and
+ * reducing. This is supposed to de fastest when using a tri- or pentanomial
+ * as fieldpolynomial. Use squareMatrix when using a ordinary polynomial as
+ * fieldpolynomial.
+ *
+ * @see GF2nPolynomialElement#squareMatrix
+ * @see GF2Polynomial#squareThisPreCalc
+ */
+ public void squareThisPreCalc()
+ {
+ polynomial.squareThisPreCalc();
+ reduceThis();
+ }
+
+ /**
+ * Calculates <i>this</i> to the power of <i>k</i> and returns the result
+ * in a new GF2nPolynomialElement.
+ *
+ * @param k the power
+ * @return <i>this</i>^<i>k</i> in a new GF2nPolynomialElement
+ */
+ public GF2nPolynomialElement power(int k)
+ {
+ if (k == 1)
+ {
+ return new GF2nPolynomialElement(this);
+ }
+
+ GF2nPolynomialElement result = GF2nPolynomialElement
+ .ONE((GF2nPolynomialField)mField);
+ if (k == 0)
+ {
+ return result;
+ }
+
+ GF2nPolynomialElement x = new GF2nPolynomialElement(this);
+ x.polynomial.expandN((x.mDegree << 1) + 32); // increase performance
+ x.polynomial.reduceN();
+
+ for (int i = 0; i < mDegree; i++)
+ {
+ if ((k & (1 << i)) != 0)
+ {
+ result.multiplyThisBy(x);
+ }
+ x.square();
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the square root of this element and return the result in a new
+ * {@link GF2nPolynomialElement}.
+ *
+ * @return <tt>this<sup>1/2</sup></tt> (newly created)
+ */
+ public GF2nElement squareRoot()
+ {
+ GF2nPolynomialElement result = new GF2nPolynomialElement(this);
+ result.squareRootThis();
+ return result;
+ }
+
+ /**
+ * Compute the square root of this element.
+ */
+ public void squareRootThis()
+ {
+ // increase performance
+ polynomial.expandN((mDegree << 1) + 32);
+ polynomial.reduceN();
+ for (int i = 0; i < mField.getDegree() - 1; i++)
+ {
+ squareThis();
+ }
+ }
+
+ /**
+ * Solves the quadratic equation <tt>z<sup>2</sup> + z = this</tt> if
+ * such a solution exists. This method returns one of the two possible
+ * solutions. The other solution is <tt>z + 1</tt>. Use z.increase() to
+ * compute this solution.
+ *
+ * @return a GF2nPolynomialElement representing one z satisfying the
+ * equation <tt>z<sup>2</sup> + z = this</tt>
+ * @throws NoSolutionException if no solution exists
+ * @see "IEEE 1363, Annex A.4.7"
+ */
+ public GF2nElement solveQuadraticEquation()
+ throws RuntimeException
+ {
+ if (isZero())
+ {
+ return ZERO((GF2nPolynomialField)mField);
+ }
+
+ if ((mDegree & 1) == 1)
+ {
+ return halfTrace();
+ }
+
+ // TODO this can be sped-up by precomputation of p and w's
+ GF2nPolynomialElement z, w;
+ do
+ {
+ // step 1.
+ GF2nPolynomialElement p = new GF2nPolynomialElement(
+ (GF2nPolynomialField)mField, new Random());
+ // step 2.
+ z = ZERO((GF2nPolynomialField)mField);
+ w = (GF2nPolynomialElement)p.clone();
+ // step 3.
+ for (int i = 1; i < mDegree; i++)
+ {
+ // compute z = z^2 + w^2 * this
+ // and w = w^2 + p
+ z.squareThis();
+ w.squareThis();
+ z.addToThis(w.multiply(this));
+ w.addToThis(p);
+ }
+ }
+ while (w.isZero()); // step 4.
+
+ if (!equals(z.square().add(z)))
+ {
+ throw new RuntimeException();
+ }
+
+ // step 5.
+ return z;
+ }
+
+ /**
+ * Returns the trace of this GF2nPolynomialElement.
+ *
+ * @return the trace of this GF2nPolynomialElement
+ */
+ public int trace()
+ {
+ GF2nPolynomialElement t = new GF2nPolynomialElement(this);
+ int i;
+
+ for (i = 1; i < mDegree; i++)
+ {
+ t.squareThis();
+ t.addToThis(this);
+ }
+
+ if (t.isOne())
+ {
+ return 1;
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the half-trace of this GF2nPolynomialElement.
+ *
+ * @return a GF2nPolynomialElement representing the half-trace of this
+ * GF2nPolynomialElement.
+ * @throws DegreeIsEvenException if the degree of this GF2nPolynomialElement is even.
+ */
+ private GF2nPolynomialElement halfTrace()
+ throws RuntimeException
+ {
+ if ((mDegree & 0x01) == 0)
+ {
+ throw new RuntimeException();
+ }
+ int i;
+ GF2nPolynomialElement h = new GF2nPolynomialElement(this);
+
+ for (i = 1; i <= ((mDegree - 1) >> 1); i++)
+ {
+ h.squareThis();
+ h.squareThis();
+ h.addToThis(this);
+ }
+
+ return h;
+ }
+
+ /**
+ * Reduces this GF2nPolynomialElement modulo the field-polynomial.
+ *
+ * @see GF2Polynomial#reduceTrinomial
+ * @see GF2Polynomial#reducePentanomial
+ */
+ private void reduceThis()
+ {
+ if (polynomial.getLength() > mDegree)
+ { // really reduce ?
+ if (((GF2nPolynomialField)mField).isTrinomial())
+ { // fieldpolonomial
+ // is trinomial
+ int tc;
+ try
+ {
+ tc = ((GF2nPolynomialField)mField).getTc();
+ }
+ catch (RuntimeException NATExc)
+ {
+ throw new RuntimeException(
+ "GF2nPolynomialElement.reduce: the field"
+ + " polynomial is not a trinomial");
+ }
+ if (((mDegree - tc) <= 32) // do we have to use slow
+ // bitwise reduction ?
+ || (polynomial.getLength() > (mDegree << 1)))
+ {
+ reduceTrinomialBitwise(tc);
+ return;
+ }
+ polynomial.reduceTrinomial(mDegree, tc);
+ return;
+ }
+ else if (((GF2nPolynomialField)mField).isPentanomial())
+ { // fieldpolynomial
+ // is
+ // pentanomial
+ int[] pc;
+ try
+ {
+ pc = ((GF2nPolynomialField)mField).getPc();
+ }
+ catch (RuntimeException NATExc)
+ {
+ throw new RuntimeException(
+ "GF2nPolynomialElement.reduce: the field"
+ + " polynomial is not a pentanomial");
+ }
+ if (((mDegree - pc[2]) <= 32) // do we have to use slow
+ // bitwise reduction ?
+ || (polynomial.getLength() > (mDegree << 1)))
+ {
+ reducePentanomialBitwise(pc);
+ return;
+ }
+ polynomial.reducePentanomial(mDegree, pc);
+ return;
+ }
+ else
+ { // fieldpolynomial is something else
+ polynomial = polynomial.remainder(mField.getFieldPolynomial());
+ polynomial.expandN(mDegree);
+ return;
+ }
+ }
+ if (polynomial.getLength() < mDegree)
+ {
+ polynomial.expandN(mDegree);
+ }
+ }
+
+ /**
+ * Reduce this GF2nPolynomialElement using the trinomial x^n + x^tc + 1 as
+ * fieldpolynomial. The coefficients are reduced bit by bit.
+ */
+ private void reduceTrinomialBitwise(int tc)
+ {
+ int i;
+ int k = mDegree - tc;
+ for (i = polynomial.getLength() - 1; i >= mDegree; i--)
+ {
+ if (polynomial.testBit(i))
+ {
+
+ polynomial.xorBit(i);
+ polynomial.xorBit(i - k);
+ polynomial.xorBit(i - mDegree);
+
+ }
+ }
+ polynomial.reduceN();
+ polynomial.expandN(mDegree);
+ }
+
+ /**
+ * Reduce this GF2nPolynomialElement using the pentanomial x^n + x^pc[2] +
+ * x^pc[1] + x^pc[0] + 1 as fieldpolynomial. The coefficients are reduced
+ * bit by bit.
+ */
+ private void reducePentanomialBitwise(int[] pc)
+ {
+ int i;
+ int k = mDegree - pc[2];
+ int l = mDegree - pc[1];
+ int m = mDegree - pc[0];
+ for (i = polynomial.getLength() - 1; i >= mDegree; i--)
+ {
+ if (polynomial.testBit(i))
+ {
+ polynomial.xorBit(i);
+ polynomial.xorBit(i - k);
+ polynomial.xorBit(i - l);
+ polynomial.xorBit(i - m);
+ polynomial.xorBit(i - mDegree);
+
+ }
+ }
+ polynomial.reduceN();
+ polynomial.expandN(mDegree);
+ }
+
+ // /////////////////////////////////////////////////////////////////////
+ // conversion
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns a string representing this Bitstrings value using hexadecimal
+ * radix in MSB-first order.
+ *
+ * @return a String representing this Bitstrings value.
+ */
+ public String toString()
+ {
+ return polynomial.toString(16);
+ }
+
+ /**
+ * Returns a string representing this Bitstrings value using hexadecimal or
+ * binary radix in MSB-first order.
+ *
+ * @param radix the radix to use (2 or 16, otherwise 2 is used)
+ * @return a String representing this Bitstrings value.
+ */
+ public String toString(int radix)
+ {
+ return polynomial.toString(radix);
+ }
+
+ /**
+ * Converts this GF2nPolynomialElement to a byte[] according to 1363.
+ *
+ * @return a byte[] representing the value of this GF2nPolynomialElement
+ * @see "P1363 5.5.2 p22f BS2OSP, FE2OSP"
+ */
+ public byte[] toByteArray()
+ {
+ return polynomial.toByteArray();
+ }
+
+ /**
+ * Converts this GF2nPolynomialElement to an integer according to 1363.
+ *
+ * @return a BigInteger representing the value of this
+ * GF2nPolynomialElement
+ * @see "P1363 5.5.1 p22 BS2IP"
+ */
+ public BigInteger toFlexiBigInt()
+ {
+ return polynomial.toFlexiBigInt();
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialField.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialField.java
new file mode 100644
index 00000000..f9ec0bca
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GF2nPolynomialField.java
@@ -0,0 +1,553 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+
+import java.util.Random;
+import java.util.Vector;
+
+
+/**
+ * This class implements the abstract class <tt>GF2nField</tt> for polynomial
+ * representation. It computes the field polynomial and the squaring matrix.
+ * GF2nField is used by GF2nPolynomialElement which implements the elements of
+ * this field.
+ *
+ * @see GF2nField
+ * @see GF2nPolynomialElement
+ */
+public class GF2nPolynomialField
+ extends GF2nField
+{
+
+ /**
+ * Matrix used for fast squaring
+ */
+ GF2Polynomial[] squaringMatrix;
+
+ // field polynomial is a trinomial
+ private boolean isTrinomial = false;
+
+ // field polynomial is a pentanomial
+ private boolean isPentanomial = false;
+
+ // middle coefficient of the field polynomial in case it is a trinomial
+ private int tc;
+
+ // middle 3 coefficients of the field polynomial in case it is a pentanomial
+ private int[] pc = new int[3];
+
+ /**
+ * constructs an instance of the finite field with 2<sup>deg</sup>
+ * elements and characteristic 2.
+ *
+ * @param deg the extention degree of this field
+ */
+ public GF2nPolynomialField(int deg)
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("k must be at least 3");
+ }
+ mDegree = deg;
+ computeFieldPolynomial();
+ computeSquaringMatrix();
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ /**
+ * constructs an instance of the finite field with 2<sup>deg</sup>
+ * elements and characteristic 2.
+ *
+ * @param deg the degree of this field
+ * @param file true if you want to read the field polynomial from the
+ * file false if you want to use a random fielpolynomial
+ * (this can take very long for huge degrees)
+ */
+ public GF2nPolynomialField(int deg, boolean file)
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("k must be at least 3");
+ }
+ mDegree = deg;
+ if (file)
+ {
+ computeFieldPolynomial();
+ }
+ else
+ {
+ computeFieldPolynomial2();
+ }
+ computeSquaringMatrix();
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ /**
+ * Creates a new GF2nField of degree <i>i</i> and uses the given
+ * <i>polynomial</i> as field polynomial. The <i>polynomial</i> is checked
+ * whether it is irreducible. This can take some time if <i>i</i> is huge!
+ *
+ * @param deg degree of the GF2nField
+ * @param polynomial the field polynomial to use
+ * @throws PolynomialIsNotIrreducibleException if the given polynomial is not irreducible in GF(2^<i>i</i>)
+ */
+ public GF2nPolynomialField(int deg, GF2Polynomial polynomial)
+ throws RuntimeException
+ {
+ if (deg < 3)
+ {
+ throw new IllegalArgumentException("degree must be at least 3");
+ }
+ if (polynomial.getLength() != deg + 1)
+ {
+ throw new RuntimeException();
+ }
+ if (!polynomial.isIrreducible())
+ {
+ throw new RuntimeException();
+ }
+ mDegree = deg;
+ // fieldPolynomial = new Bitstring(polynomial);
+ fieldPolynomial = polynomial;
+ computeSquaringMatrix();
+ int k = 2; // check if the polynomial is a trinomial or pentanomial
+ for (int j = 1; j < fieldPolynomial.getLength() - 1; j++)
+ {
+ if (fieldPolynomial.testBit(j))
+ {
+ k++;
+ if (k == 3)
+ {
+ tc = j;
+ }
+ if (k <= 5)
+ {
+ pc[k - 3] = j;
+ }
+ }
+ }
+ if (k == 3)
+ {
+ isTrinomial = true;
+ }
+ if (k == 5)
+ {
+ isPentanomial = true;
+ }
+ fields = new Vector();
+ matrices = new Vector();
+ }
+
+ /**
+ * Returns true if the field polynomial is a trinomial. The coefficient can
+ * be retrieved using getTc().
+ *
+ * @return true if the field polynomial is a trinomial
+ */
+ public boolean isTrinomial()
+ {
+ return isTrinomial;
+ }
+
+ /**
+ * Returns true if the field polynomial is a pentanomial. The coefficients
+ * can be retrieved using getPc().
+ *
+ * @return true if the field polynomial is a pentanomial
+ */
+ public boolean isPentanomial()
+ {
+ return isPentanomial;
+ }
+
+ /**
+ * Returns the degree of the middle coefficient of the used field trinomial
+ * (x^n + x^(getTc()) + 1).
+ *
+ * @return the middle coefficient of the used field trinomial
+ * @throws GFException if the field polynomial is not a trinomial
+ */
+ public int getTc()
+ throws RuntimeException
+ {
+ if (!isTrinomial)
+ {
+ throw new RuntimeException();
+ }
+ return tc;
+ }
+
+ /**
+ * Returns the degree of the middle coefficients of the used field
+ * pentanomial (x^n + x^(getPc()[2]) + x^(getPc()[1]) + x^(getPc()[0]) + 1).
+ *
+ * @return the middle coefficients of the used field pentanomial
+ * @throws GFException if the field polynomial is not a pentanomial
+ */
+ public int[] getPc()
+ throws RuntimeException
+ {
+ if (!isPentanomial)
+ {
+ throw new RuntimeException();
+ }
+ int[] result = new int[3];
+ System.arraycopy(pc, 0, result, 0, 3);
+ return result;
+ }
+
+ /**
+ * Return row vector i of the squaring matrix.
+ *
+ * @param i the index of the row vector to return
+ * @return a copy of squaringMatrix[i]
+ * @see GF2nPolynomialElement#squareMatrix
+ */
+ public GF2Polynomial getSquaringVector(int i)
+ {
+ return new GF2Polynomial(squaringMatrix[i]);
+ }
+
+ /**
+ * Compute a random root of the given GF2Polynomial.
+ *
+ * @param polynomial the polynomial
+ * @return a random root of <tt>polynomial</tt>
+ */
+ protected GF2nElement getRandomRoot(GF2Polynomial polynomial)
+ {
+ // We are in B1!!!
+ GF2nPolynomial c;
+ GF2nPolynomial ut;
+ GF2nElement u;
+ GF2nPolynomial h;
+ int hDegree;
+ // 1. Set g(t) <- f(t)
+ GF2nPolynomial g = new GF2nPolynomial(polynomial, this);
+ int gDegree = g.getDegree();
+ int i;
+
+ // 2. while deg(g) > 1
+ while (gDegree > 1)
+ {
+ do
+ {
+ // 2.1 choose random u (element of) GF(2^m)
+ u = new GF2nPolynomialElement(this, new Random());
+ ut = new GF2nPolynomial(2, GF2nPolynomialElement.ZERO(this));
+ // 2.2 Set c(t) <- ut
+ ut.set(1, u);
+ c = new GF2nPolynomial(ut);
+ // 2.3 For i from 1 to m-1 do
+ for (i = 1; i <= mDegree - 1; i++)
+ {
+ // 2.3.1 c(t) <- (c(t)^2 + ut) mod g(t)
+ c = c.multiplyAndReduce(c, g);
+ c = c.add(ut);
+ }
+ // 2.4 set h(t) <- GCD(c(t), g(t))
+ h = c.gcd(g);
+ // 2.5 if h(t) is constant or deg(g) = deg(h) then go to
+ // step 2.1
+ hDegree = h.getDegree();
+ gDegree = g.getDegree();
+ }
+ while ((hDegree == 0) || (hDegree == gDegree));
+ // 2.6 If 2deg(h) > deg(g) then set g(t) <- g(t)/h(t) ...
+ if ((hDegree << 1) > gDegree)
+ {
+ g = g.quotient(h);
+ }
+ else
+ {
+ // ... else g(t) <- h(t)
+ g = new GF2nPolynomial(h);
+ }
+ gDegree = g.getDegree();
+ }
+ // 3. Output g(0)
+ return g.at(0);
+
+ }
+
+ /**
+ * Computes the change-of-basis matrix for basis conversion according to
+ * 1363. The result is stored in the lists fields and matrices.
+ *
+ * @param B1 the GF2nField to convert to
+ * @see "P1363 A.7.3, p111ff"
+ */
+ protected void computeCOBMatrix(GF2nField B1)
+ {
+ // we are in B0 here!
+ if (mDegree != B1.mDegree)
+ {
+ throw new IllegalArgumentException(
+ "GF2nPolynomialField.computeCOBMatrix: B1 has a different "
+ + "degree and thus cannot be coverted to!");
+ }
+ if (B1 instanceof GF2nONBField)
+ {
+ // speedup (calculation is done in PolynomialElements instead of
+ // ONB)
+ B1.computeCOBMatrix(this);
+ return;
+ }
+ int i, j;
+ GF2nElement[] gamma;
+ GF2nElement u;
+ GF2Polynomial[] COBMatrix = new GF2Polynomial[mDegree];
+ for (i = 0; i < mDegree; i++)
+ {
+ COBMatrix[i] = new GF2Polynomial(mDegree);
+ }
+
+ // find Random Root
+ do
+ {
+ // u is in representation according to B1
+ u = B1.getRandomRoot(fieldPolynomial);
+ }
+ while (u.isZero());
+
+ // build gamma matrix by multiplying by u
+ if (u instanceof GF2nONBElement)
+ {
+ gamma = new GF2nONBElement[mDegree];
+ gamma[mDegree - 1] = GF2nONBElement.ONE((GF2nONBField)B1);
+ }
+ else
+ {
+ gamma = new GF2nPolynomialElement[mDegree];
+ gamma[mDegree - 1] = GF2nPolynomialElement
+ .ONE((GF2nPolynomialField)B1);
+ }
+ gamma[mDegree - 2] = u;
+ for (i = mDegree - 3; i >= 0; i--)
+ {
+ gamma[i] = (GF2nElement)gamma[i + 1].multiply(u);
+ }
+ if (B1 instanceof GF2nONBField)
+ {
+ // convert horizontal gamma matrix by vertical Bitstrings
+ for (i = 0; i < mDegree; i++)
+ {
+ for (j = 0; j < mDegree; j++)
+ {
+ // TODO remember: ONB treats its Bits in reverse order !!!
+ if (gamma[i].testBit(mDegree - j - 1))
+ {
+ COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1);
+ }
+ }
+ }
+ }
+ else
+ {
+ // convert horizontal gamma matrix by vertical Bitstrings
+ for (i = 0; i < mDegree; i++)
+ {
+ for (j = 0; j < mDegree; j++)
+ {
+ if (gamma[i].testBit(j))
+ {
+ COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1);
+ }
+ }
+ }
+ }
+
+ // store field and matrix for further use
+ fields.addElement(B1);
+ matrices.addElement(COBMatrix);
+ // store field and inverse matrix for further use in B1
+ B1.fields.addElement(this);
+ B1.matrices.addElement(invertMatrix(COBMatrix));
+ }
+
+ /**
+ * Computes a new squaring matrix used for fast squaring.
+ *
+ * @see GF2nPolynomialElement#square
+ */
+ private void computeSquaringMatrix()
+ {
+ GF2Polynomial[] d = new GF2Polynomial[mDegree - 1];
+ int i, j;
+ squaringMatrix = new GF2Polynomial[mDegree];
+ for (i = 0; i < squaringMatrix.length; i++)
+ {
+ squaringMatrix[i] = new GF2Polynomial(mDegree, "ZERO");
+ }
+
+ for (i = 0; i < mDegree - 1; i++)
+ {
+ d[i] = new GF2Polynomial(1, "ONE").shiftLeft(mDegree + i)
+ .remainder(fieldPolynomial);
+ }
+ for (i = 1; i <= Math.abs(mDegree >> 1); i++)
+ {
+ for (j = 1; j <= mDegree; j++)
+ {
+ if (d[mDegree - (i << 1)].testBit(mDegree - j))
+ {
+ squaringMatrix[j - 1].setBit(mDegree - i);
+ }
+ }
+ }
+ for (i = Math.abs(mDegree >> 1) + 1; i <= mDegree; i++)
+ {
+ squaringMatrix[(i << 1) - mDegree - 1].setBit(mDegree - i);
+ }
+
+ }
+
+ /**
+ * Computes the field polynomial. This can take a long time for big degrees.
+ */
+ protected void computeFieldPolynomial()
+ {
+ if (testTrinomials())
+ {
+ return;
+ }
+ if (testPentanomials())
+ {
+ return;
+ }
+ testRandom();
+ }
+
+ /**
+ * Computes the field polynomial. This can take a long time for big degrees.
+ */
+ protected void computeFieldPolynomial2()
+ {
+ if (testTrinomials())
+ {
+ return;
+ }
+ if (testPentanomials())
+ {
+ return;
+ }
+ testRandom();
+ }
+
+ /**
+ * Tests all trinomials of degree (n+1) until a irreducible is found and
+ * stores the result in <i>field polynomial</i>. Returns false if no
+ * irreducible trinomial exists in GF(2^n). This can take very long for huge
+ * degrees.
+ *
+ * @return true if an irreducible trinomial is found
+ */
+ private boolean testTrinomials()
+ {
+ int i, l;
+ boolean done = false;
+ l = 0;
+
+ fieldPolynomial = new GF2Polynomial(mDegree + 1);
+ fieldPolynomial.setBit(0);
+ fieldPolynomial.setBit(mDegree);
+ for (i = 1; (i < mDegree) && !done; i++)
+ {
+ fieldPolynomial.setBit(i);
+ done = fieldPolynomial.isIrreducible();
+ l++;
+ if (done)
+ {
+ isTrinomial = true;
+ tc = i;
+ return done;
+ }
+ fieldPolynomial.resetBit(i);
+ done = fieldPolynomial.isIrreducible();
+ }
+
+ return done;
+ }
+
+ /**
+ * Tests all pentanomials of degree (n+1) until a irreducible is found and
+ * stores the result in <i>field polynomial</i>. Returns false if no
+ * irreducible pentanomial exists in GF(2^n). This can take very long for
+ * huge degrees.
+ *
+ * @return true if an irreducible pentanomial is found
+ */
+ private boolean testPentanomials()
+ {
+ int i, j, k, l;
+ boolean done = false;
+ l = 0;
+
+ fieldPolynomial = new GF2Polynomial(mDegree + 1);
+ fieldPolynomial.setBit(0);
+ fieldPolynomial.setBit(mDegree);
+ for (i = 1; (i <= (mDegree - 3)) && !done; i++)
+ {
+ fieldPolynomial.setBit(i);
+ for (j = i + 1; (j <= (mDegree - 2)) && !done; j++)
+ {
+ fieldPolynomial.setBit(j);
+ for (k = j + 1; (k <= (mDegree - 1)) && !done; k++)
+ {
+ fieldPolynomial.setBit(k);
+ if (((mDegree & 1) != 0) | ((i & 1) != 0) | ((j & 1) != 0)
+ | ((k & 1) != 0))
+ {
+ done = fieldPolynomial.isIrreducible();
+ l++;
+ if (done)
+ {
+ isPentanomial = true;
+ pc[0] = i;
+ pc[1] = j;
+ pc[2] = k;
+ return done;
+ }
+ }
+ fieldPolynomial.resetBit(k);
+ }
+ fieldPolynomial.resetBit(j);
+ }
+ fieldPolynomial.resetBit(i);
+ }
+
+ return done;
+ }
+
+ /**
+ * Tests random polynomials of degree (n+1) until an irreducible is found
+ * and stores the result in <i>field polynomial</i>. This can take very
+ * long for huge degrees.
+ *
+ * @return true
+ */
+ private boolean testRandom()
+ {
+ int l;
+ boolean done = false;
+
+ fieldPolynomial = new GF2Polynomial(mDegree + 1);
+ l = 0;
+ while (!done)
+ {
+ l++;
+ fieldPolynomial.randomize();
+ fieldPolynomial.setBit(mDegree);
+ fieldPolynomial.setBit(0);
+ if (fieldPolynomial.isIrreducible())
+ {
+ done = true;
+ return done;
+ }
+ }
+
+ return done;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GFElement.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GFElement.java
new file mode 100644
index 00000000..c33f1956
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GFElement.java
@@ -0,0 +1,158 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+
+
+/**
+ * This interface defines a finite field element. It is implemented by the
+ * classes {@link GFPElement} and {@link GF2nElement}.
+ *
+ * @see GFPElement
+ * @see GF2nElement
+ */
+public interface GFElement
+{
+
+ /**
+ * @return a copy of this GFElement
+ */
+ Object clone();
+
+ // /////////////////////////////////////////////////////////////////
+ // comparison
+ // /////////////////////////////////////////////////////////////////
+
+ /**
+ * Compare this curve with another object.
+ *
+ * @param other the other object
+ * @return the result of the comparison
+ */
+ boolean equals(Object other);
+
+ /**
+ * @return the hash code of this element
+ */
+ int hashCode();
+
+ /**
+ * Checks whether this element is zero.
+ *
+ * @return <tt>true</tt> if <tt>this</tt> is the zero element
+ */
+ boolean isZero();
+
+ /**
+ * Checks whether this element is one.
+ *
+ * @return <tt>true</tt> if <tt>this</tt> is the one element
+ */
+ boolean isOne();
+
+ // /////////////////////////////////////////////////////////////////////
+ // arithmetic
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Compute the sum of this element and the addend.
+ *
+ * @param addend the addend
+ * @return <tt>this + other</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ GFElement add(GFElement addend)
+ throws RuntimeException;
+
+ /**
+ * Compute the sum of this element and the addend, overwriting this element.
+ *
+ * @param addend the addend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ void addToThis(GFElement addend)
+ throws RuntimeException;
+
+ /**
+ * Compute the difference of this element and <tt>minuend</tt>.
+ *
+ * @param minuend the minuend
+ * @return <tt>this - minuend</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ GFElement subtract(GFElement minuend)
+ throws RuntimeException;
+
+ /**
+ * Compute the difference of this element and <tt>minuend</tt>,
+ * overwriting this element.
+ *
+ * @param minuend the minuend
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ void subtractFromThis(GFElement minuend);
+
+ /**
+ * Compute the product of this element and <tt>factor</tt>.
+ *
+ * @param factor the factor
+ * @return <tt>this * factor</tt> (newly created)
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ GFElement multiply(GFElement factor)
+ throws RuntimeException;
+
+ /**
+ * Compute <tt>this * factor</tt> (overwrite <tt>this</tt>).
+ *
+ * @param factor the factor
+ * @throws DifferentFieldsException if the elements are of different fields.
+ */
+ void multiplyThisBy(GFElement factor)
+ throws RuntimeException;
+
+ /**
+ * Compute the multiplicative inverse of this element.
+ *
+ * @return <tt>this<sup>-1</sup></tt> (newly created)
+ * @throws ArithmeticException if <tt>this</tt> is the zero element.
+ */
+ GFElement invert()
+ throws ArithmeticException;
+
+ // /////////////////////////////////////////////////////////////////////
+ // conversion
+ // /////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns this element as FlexiBigInt. The conversion is <a
+ * href="http://grouper.ieee.org/groups/1363/">P1363</a>-conform.
+ *
+ * @return this element as BigInt
+ */
+ BigInteger toFlexiBigInt();
+
+ /**
+ * Returns this element as byte array. The conversion is <a href =
+ * "http://grouper.ieee.org/groups/1363/">P1363</a>-conform.
+ *
+ * @return this element as byte array
+ */
+ byte[] toByteArray();
+
+ /**
+ * Return a String representation of this element.
+ *
+ * @return String representation of this element
+ */
+ String toString();
+
+ /**
+ * Return a String representation of this element. <tt>radix</tt>
+ * specifies the radix of the String representation.
+ *
+ * @param radix specifies the radix of the String representation
+ * @return String representation of this element with the specified radix
+ */
+ String toString(int radix);
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GoppaCode.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GoppaCode.java
new file mode 100644
index 00000000..2249d936
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/GoppaCode.java
@@ -0,0 +1,310 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes decoding operations of an irreducible binary Goppa code.
+ * A check matrix H of the Goppa code and an irreducible Goppa polynomial are
+ * used the operations are worked over a finite field GF(2^m)
+ *
+ * @see GF2mField
+ * @see PolynomialGF2mSmallM
+ */
+public final class GoppaCode
+{
+
+ /**
+ * Default constructor (private).
+ */
+ private GoppaCode()
+ {
+ // empty
+ }
+
+ /**
+ * This class is a container for two instances of {@link GF2Matrix} and one
+ * instance of {@link Permutation}. It is used to hold the systematic form
+ * <tt>S*H*P = (Id|M)</tt> of the check matrix <tt>H</tt> as returned by
+ * {@link GoppaCode#computeSystematicForm(GF2Matrix, SecureRandom)}.
+ *
+ * @see GF2Matrix
+ * @see Permutation
+ */
+ public static class MaMaPe
+ {
+
+ private GF2Matrix s, h;
+
+ private Permutation p;
+
+ /**
+ * Construct a new {@link MaMaPe} container with the given parameters.
+ *
+ * @param s the first matrix
+ * @param h the second matrix
+ * @param p the permutation
+ */
+ public MaMaPe(GF2Matrix s, GF2Matrix h, Permutation p)
+ {
+ this.s = s;
+ this.h = h;
+ this.p = p;
+ }
+
+ /**
+ * @return the first matrix
+ */
+ public GF2Matrix getFirstMatrix()
+ {
+ return s;
+ }
+
+ /**
+ * @return the second matrix
+ */
+ public GF2Matrix getSecondMatrix()
+ {
+ return h;
+ }
+
+ /**
+ * @return the permutation
+ */
+ public Permutation getPermutation()
+ {
+ return p;
+ }
+ }
+
+ /**
+ * This class is a container for an instance of {@link GF2Matrix} and one
+ * int[]. It is used to hold a generator matrix and the set of indices such
+ * that the submatrix of the generator matrix consisting of the specified
+ * columns is the identity.
+ *
+ * @see GF2Matrix
+ * @see Permutation
+ */
+ public static class MatrixSet
+ {
+
+ private GF2Matrix g;
+
+ private int[] setJ;
+
+ /**
+ * Construct a new {@link MatrixSet} container with the given
+ * parameters.
+ *
+ * @param g the generator matrix
+ * @param setJ the set of indices such that the submatrix of the
+ * generator matrix consisting of the specified columns
+ * is the identity
+ */
+ public MatrixSet(GF2Matrix g, int[] setJ)
+ {
+ this.g = g;
+ this.setJ = setJ;
+ }
+
+ /**
+ * @return the generator matrix
+ */
+ public GF2Matrix getG()
+ {
+ return g;
+ }
+
+ /**
+ * @return the set of indices such that the submatrix of the generator
+ * matrix consisting of the specified columns is the identity
+ */
+ public int[] getSetJ()
+ {
+ return setJ;
+ }
+ }
+
+ /**
+ * Construct the check matrix of a Goppa code in canonical form from the
+ * irreducible Goppa polynomial over the finite field
+ * <tt>GF(2<sup>m</sup>)</tt>.
+ *
+ * @param field the finite field
+ * @param gp the irreducible Goppa polynomial
+ */
+ public static GF2Matrix createCanonicalCheckMatrix(GF2mField field,
+ PolynomialGF2mSmallM gp)
+ {
+ int m = field.getDegree();
+ int n = 1 << m;
+ int t = gp.getDegree();
+
+ /* create matrix H over GF(2^m) */
+
+ int[][] hArray = new int[t][n];
+
+ // create matrix YZ
+ int[][] yz = new int[t][n];
+ for (int j = 0; j < n; j++)
+ {
+ // here j is used as index and as element of field GF(2^m)
+ yz[0][j] = field.inverse(gp.evaluateAt(j));
+ }
+
+ for (int i = 1; i < t; i++)
+ {
+ for (int j = 0; j < n; j++)
+ {
+ // here j is used as index and as element of field GF(2^m)
+ yz[i][j] = field.mult(yz[i - 1][j], j);
+ }
+ }
+
+ // create matrix H = XYZ
+ for (int i = 0; i < t; i++)
+ {
+ for (int j = 0; j < n; j++)
+ {
+ for (int k = 0; k <= i; k++)
+ {
+ hArray[i][j] = field.add(hArray[i][j], field.mult(yz[k][j],
+ gp.getCoefficient(t + k - i)));
+ }
+ }
+ }
+
+ /* convert to matrix over GF(2) */
+
+ int[][] result = new int[t * m][(n + 31) >>> 5];
+
+ for (int j = 0; j < n; j++)
+ {
+ int q = j >>> 5;
+ int r = 1 << (j & 0x1f);
+ for (int i = 0; i < t; i++)
+ {
+ int e = hArray[i][j];
+ for (int u = 0; u < m; u++)
+ {
+ int b = (e >>> u) & 1;
+ if (b != 0)
+ {
+ int ind = (i + 1) * m - u - 1;
+ result[ind][q] ^= r;
+ }
+ }
+ }
+ }
+
+ return new GF2Matrix(n, result);
+ }
+
+ /**
+ * Given a check matrix <tt>H</tt>, compute matrices <tt>S</tt>,
+ * <tt>M</tt>, and a random permutation <tt>P</tt> such that
+ * <tt>S*H*P = (Id|M)</tt>. Return <tt>S^-1</tt>, <tt>M</tt>, and
+ * <tt>P</tt> as {@link MaMaPe}. The matrix <tt>(Id | M)</tt> is called
+ * the systematic form of H.
+ *
+ * @param h the check matrix
+ * @param sr a source of randomness
+ * @return the tuple <tt>(S^-1, M, P)</tt>
+ */
+ public static MaMaPe computeSystematicForm(GF2Matrix h, SecureRandom sr)
+ {
+ int n = h.getNumColumns();
+ GF2Matrix hp, sInv;
+ GF2Matrix s = null;
+ Permutation p;
+ boolean found = false;
+
+ do
+ {
+ p = new Permutation(n, sr);
+ hp = (GF2Matrix)h.rightMultiply(p);
+ sInv = hp.getLeftSubMatrix();
+ try
+ {
+ found = true;
+ s = (GF2Matrix)sInv.computeInverse();
+ }
+ catch (ArithmeticException ae)
+ {
+ found = false;
+ }
+ }
+ while (!found);
+
+ GF2Matrix shp = (GF2Matrix)s.rightMultiply(hp);
+ GF2Matrix m = shp.getRightSubMatrix();
+
+ return new MaMaPe(sInv, m, p);
+ }
+
+ /**
+ * Find an error vector <tt>e</tt> over <tt>GF(2)</tt> from an input
+ * syndrome <tt>s</tt> over <tt>GF(2<sup>m</sup>)</tt>.
+ *
+ * @param syndVec the syndrome
+ * @param field the finite field
+ * @param gp the irreducible Goppa polynomial
+ * @param sqRootMatrix the matrix for computing square roots in
+ * <tt>(GF(2<sup>m</sup>))<sup>t</sup></tt>
+ * @return the error vector
+ */
+ public static GF2Vector syndromeDecode(GF2Vector syndVec, GF2mField field,
+ PolynomialGF2mSmallM gp, PolynomialGF2mSmallM[] sqRootMatrix)
+ {
+
+ int n = 1 << field.getDegree();
+
+ // the error vector
+ GF2Vector errors = new GF2Vector(n);
+
+ // if the syndrome vector is zero, the error vector is also zero
+ if (!syndVec.isZero())
+ {
+ // convert syndrome vector to polynomial over GF(2^m)
+ PolynomialGF2mSmallM syndrome = new PolynomialGF2mSmallM(syndVec
+ .toExtensionFieldVector(field));
+
+ // compute T = syndrome^-1 mod gp
+ PolynomialGF2mSmallM t = syndrome.modInverse(gp);
+
+ // compute tau = sqRoot(T + X) mod gp
+ PolynomialGF2mSmallM tau = t.addMonomial(1);
+ tau = tau.modSquareRootMatrix(sqRootMatrix);
+
+ // compute polynomials a and b satisfying a + b*tau = 0 mod gp
+ PolynomialGF2mSmallM[] ab = tau.modPolynomialToFracton(gp);
+
+ // compute the polynomial a^2 + X*b^2
+ PolynomialGF2mSmallM a2 = ab[0].multiply(ab[0]);
+ PolynomialGF2mSmallM b2 = ab[1].multiply(ab[1]);
+ PolynomialGF2mSmallM xb2 = b2.multWithMonomial(1);
+ PolynomialGF2mSmallM a2plusXb2 = a2.add(xb2);
+
+ // normalize a^2 + X*b^2 to obtain the error locator polynomial
+ int headCoeff = a2plusXb2.getHeadCoefficient();
+ int invHeadCoeff = field.inverse(headCoeff);
+ PolynomialGF2mSmallM elp = a2plusXb2.multWithElement(invHeadCoeff);
+
+ // for all elements i of GF(2^m)
+ for (int i = 0; i < n; i++)
+ {
+ // evaluate the error locator polynomial at i
+ int z = elp.evaluateAt(i);
+ // if polynomial evaluates to zero
+ if (z == 0)
+ {
+ // set the i-th coefficient of the error vector
+ errors.setBit(i);
+ }
+ }
+ }
+
+ return errors;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntUtils.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntUtils.java
new file mode 100644
index 00000000..e586666e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntUtils.java
@@ -0,0 +1,202 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+
+/**
+ *
+ *
+ *
+ */
+public final 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 boolean equals(int[] left, int[] right)
+ {
+ if (left.length != right.length)
+ {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--)
+ {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+ /**
+ * 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)
+ {
+ int[] result = new int[array.length];
+ System.arraycopy(array, 0, result, 0, array.length);
+ return result;
+ }
+
+ /**
+ * 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)
+ {
+ for (int i = array.length - 1; i >= 0; i--)
+ {
+ array[i] = 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;
+
+ for (int i = left; i < right; i++)
+ {
+ if (source[i] <= pivot)
+ {
+ int tmp = source[index];
+ source[index] = source[i];
+ source[i] = tmp;
+ index++;
+ }
+ }
+
+ int 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(final int[] input, final int start,
+ final int end)
+ {
+ int[] result = new int[end - start];
+ System.arraycopy(input, start, result, 0, end - start);
+ return result;
+ }
+
+ /**
+ * Convert an int array to a {@link FlexiBigInt} array.
+ *
+ * @param input the int array
+ * @return the {@link FlexiBigInt} array
+ */
+ public static BigInteger[] toFlexiBigIntArray(int[] input)
+ {
+ BigInteger[] result = new BigInteger[input.length];
+ for (int i = 0; i < input.length; i++)
+ {
+ result[i] = BigInteger.valueOf(input[i]);
+ }
+ 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;
+ }
+
+ /**
+ * @param input an int arary
+ * @return the int array as hex string
+ */
+ public static String toHexString(int[] input)
+ {
+ return ByteUtils.toHexString(BigEndianConversions.toByteArray(input));
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntegerFunctions.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntegerFunctions.java
new file mode 100644
index 00000000..a6d5a181
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/IntegerFunctions.java
@@ -0,0 +1,1423 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * Class of number-theory related functions for use with integers represented as
+ * <tt>int</tt>'s or <tt>BigInteger</tt> objects.
+ */
+public final class IntegerFunctions
+{
+
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ private static final BigInteger FOUR = BigInteger.valueOf(4);
+
+ private static final int[] SMALL_PRIMES = {3, 5, 7, 11, 13, 17, 19, 23,
+ 29, 31, 37, 41};
+
+ private static final long SMALL_PRIME_PRODUCT = 3L * 5 * 7 * 11 * 13 * 17
+ * 19 * 23 * 29 * 31 * 37 * 41;
+
+ private static SecureRandom sr = null;
+
+ // the jacobi function uses this lookup table
+ private static final int[] jacobiTable = {0, 1, 0, -1, 0, -1, 0, 1};
+
+ private IntegerFunctions()
+ {
+ // empty
+ }
+
+ /**
+ * Computes the value of the Jacobi symbol (A|B). The following properties
+ * hold for the Jacobi symbol which makes it a very efficient way to
+ * evaluate the Legendre symbol
+ * <p>
+ * (A|B) = 0 IF gcd(A,B) &gt; 1<br>
+ * (-1|B) = 1 IF n = 1 (mod 1)<br>
+ * (-1|B) = -1 IF n = 3 (mod 4)<br>
+ * (A|B) (C|B) = (AC|B)<br>
+ * (A|B) (A|C) = (A|CB)<br>
+ * (A|B) = (C|B) IF A = C (mod B)<br>
+ * (2|B) = 1 IF N = 1 OR 7 (mod 8)<br>
+ * (2|B) = 1 IF N = 3 OR 5 (mod 8)
+ *
+ * @param A integer value
+ * @param B integer value
+ * @return value of the jacobi symbol (A|B)
+ */
+ public static int jacobi(BigInteger A, BigInteger B)
+ {
+ BigInteger a, b, v;
+ long k = 1;
+
+ k = 1;
+
+ // test trivial cases
+ if (B.equals(ZERO))
+ {
+ a = A.abs();
+ return a.equals(ONE) ? 1 : 0;
+ }
+
+ if (!A.testBit(0) && !B.testBit(0))
+ {
+ return 0;
+ }
+
+ a = A;
+ b = B;
+
+ if (b.signum() == -1)
+ { // b < 0
+ b = b.negate(); // b = -b
+ if (a.signum() == -1)
+ {
+ k = -1;
+ }
+ }
+
+ v = ZERO;
+ while (!b.testBit(0))
+ {
+ v = v.add(ONE); // v = v + 1
+ b = b.divide(TWO); // b = b/2
+ }
+
+ if (v.testBit(0))
+ {
+ k = k * jacobiTable[a.intValue() & 7];
+ }
+
+ if (a.signum() < 0)
+ { // a < 0
+ if (b.testBit(1))
+ {
+ k = -k; // k = -k
+ }
+ a = a.negate(); // a = -a
+ }
+
+ // main loop
+ while (a.signum() != 0)
+ {
+ v = ZERO;
+ while (!a.testBit(0))
+ { // a is even
+ v = v.add(ONE);
+ a = a.divide(TWO);
+ }
+ if (v.testBit(0))
+ {
+ k = k * jacobiTable[b.intValue() & 7];
+ }
+
+ if (a.compareTo(b) < 0)
+ { // a < b
+ // swap and correct intermediate result
+ BigInteger x = a;
+ a = b;
+ b = x;
+ if (a.testBit(1) && b.testBit(1))
+ {
+ k = -k;
+ }
+ }
+ a = a.subtract(b);
+ }
+
+ return b.equals(ONE) ? (int)k : 0;
+ }
+
+ /**
+ * Computes the square root of a BigInteger modulo a prime employing the
+ * Shanks-Tonelli algorithm.
+ *
+ * @param a value out of which we extract the square root
+ * @param p prime modulus that determines the underlying field
+ * @return a number <tt>b</tt> such that b<sup>2</sup> = a (mod p) if
+ * <tt>a</tt> is a quadratic residue modulo <tt>p</tt>.
+ * @throws NoQuadraticResidueException if <tt>a</tt> is a quadratic non-residue modulo <tt>p</tt>
+ */
+ public static BigInteger ressol(BigInteger a, BigInteger p)
+ throws IllegalArgumentException
+ {
+
+ BigInteger v = null;
+
+ if (a.compareTo(ZERO) < 0)
+ {
+ a = a.add(p);
+ }
+
+ if (a.equals(ZERO))
+ {
+ return ZERO;
+ }
+
+ if (p.equals(TWO))
+ {
+ return a;
+ }
+
+ // p = 3 mod 4
+ if (p.testBit(0) && p.testBit(1))
+ {
+ if (jacobi(a, p) == 1)
+ { // a quadr. residue mod p
+ v = p.add(ONE); // v = p+1
+ v = v.shiftRight(2); // v = v/4
+ return a.modPow(v, p); // return a^v mod p
+ // return --> a^((p+1)/4) mod p
+ }
+ throw new IllegalArgumentException("No quadratic residue: " + a + ", " + p);
+ }
+
+ long t = 0;
+
+ // initialization
+ // compute k and s, where p = 2^s (2k+1) +1
+
+ BigInteger k = p.subtract(ONE); // k = p-1
+ long s = 0;
+ while (!k.testBit(0))
+ { // while k is even
+ s++; // s = s+1
+ k = k.shiftRight(1); // k = k/2
+ }
+
+ k = k.subtract(ONE); // k = k - 1
+ k = k.shiftRight(1); // k = k/2
+
+ // initial values
+ BigInteger r = a.modPow(k, p); // r = a^k mod p
+
+ BigInteger n = r.multiply(r).remainder(p); // n = r^2 % p
+ n = n.multiply(a).remainder(p); // n = n * a % p
+ r = r.multiply(a).remainder(p); // r = r * a %p
+
+ if (n.equals(ONE))
+ {
+ return r;
+ }
+
+ // non-quadratic residue
+ BigInteger z = TWO; // z = 2
+ while (jacobi(z, p) == 1)
+ {
+ // while z quadratic residue
+ z = z.add(ONE); // z = z + 1
+ }
+
+ v = k;
+ v = v.multiply(TWO); // v = 2k
+ v = v.add(ONE); // v = 2k + 1
+ BigInteger c = z.modPow(v, p); // c = z^v mod p
+
+ // iteration
+ while (n.compareTo(ONE) == 1)
+ { // n > 1
+ k = n; // k = n
+ t = s; // t = s
+ s = 0;
+
+ while (!k.equals(ONE))
+ { // k != 1
+ k = k.multiply(k).mod(p); // k = k^2 % p
+ s++; // s = s + 1
+ }
+
+ t -= s; // t = t - s
+ if (t == 0)
+ {
+ throw new IllegalArgumentException("No quadratic residue: " + a + ", " + p);
+ }
+
+ v = ONE;
+ for (long i = 0; i < t - 1; i++)
+ {
+ v = v.shiftLeft(1); // v = 1 * 2^(t - 1)
+ }
+ c = c.modPow(v, p); // c = c^v mod p
+ r = r.multiply(c).remainder(p); // r = r * c % p
+ c = c.multiply(c).remainder(p); // c = c^2 % p
+ n = n.multiply(c).mod(p); // n = n * c % p
+ }
+ return r;
+ }
+
+ /**
+ * Computes the greatest common divisor of the two specified integers
+ *
+ * @param u - first integer
+ * @param v - second integer
+ * @return gcd(a, b)
+ */
+ public static int gcd(int u, int v)
+ {
+ return BigInteger.valueOf(u).gcd(BigInteger.valueOf(v)).intValue();
+ }
+
+ /**
+ * Extended euclidian algorithm (computes gcd and representation).
+ *
+ * @param a the first integer
+ * @param b the second integer
+ * @return <tt>(g,u,v)</tt>, where <tt>g = gcd(abs(a),abs(b)) = ua + vb</tt>
+ */
+ public static int[] extGCD(int a, int b)
+ {
+ BigInteger ba = BigInteger.valueOf(a);
+ BigInteger bb = BigInteger.valueOf(b);
+ BigInteger[] bresult = extgcd(ba, bb);
+ int[] result = new int[3];
+ result[0] = bresult[0].intValue();
+ result[1] = bresult[1].intValue();
+ result[2] = bresult[2].intValue();
+ return result;
+ }
+
+ public static BigInteger divideAndRound(BigInteger a, BigInteger b)
+ {
+ if (a.signum() < 0)
+ {
+ return divideAndRound(a.negate(), b).negate();
+ }
+ if (b.signum() < 0)
+ {
+ return divideAndRound(a, b.negate()).negate();
+ }
+ return a.shiftLeft(1).add(b).divide(b.shiftLeft(1));
+ }
+
+ public static BigInteger[] divideAndRound(BigInteger[] a, BigInteger b)
+ {
+ BigInteger[] out = new BigInteger[a.length];
+ for (int i = 0; i < a.length; i++)
+ {
+ out[i] = divideAndRound(a[i], b);
+ }
+ return out;
+ }
+
+ /**
+ * Compute the smallest integer that is greater than or equal to the
+ * logarithm to the base 2 of the given BigInteger.
+ *
+ * @param a the integer
+ * @return ceil[log(a)]
+ */
+ public static int ceilLog(BigInteger a)
+ {
+ int result = 0;
+ BigInteger p = ONE;
+ while (p.compareTo(a) < 0)
+ {
+ result++;
+ p = p.shiftLeft(1);
+ }
+ return result;
+ }
+
+ /**
+ * Compute the smallest integer that is greater than or equal to the
+ * logarithm to the base 2 of the given integer.
+ *
+ * @param a the integer
+ * @return ceil[log(a)]
+ */
+ public static int ceilLog(int a)
+ {
+ int log = 0;
+ int i = 1;
+ while (i < a)
+ {
+ i <<= 1;
+ log++;
+ }
+ return log;
+ }
+
+ /**
+ * Compute <tt>ceil(log_256 n)</tt>, the number of bytes needed to encode
+ * the integer <tt>n</tt>.
+ *
+ * @param n the integer
+ * @return the number of bytes needed to encode <tt>n</tt>
+ */
+ public static int ceilLog256(int n)
+ {
+ if (n == 0)
+ {
+ return 1;
+ }
+ int m;
+ if (n < 0)
+ {
+ m = -n;
+ }
+ else
+ {
+ m = n;
+ }
+
+ int d = 0;
+ while (m > 0)
+ {
+ d++;
+ m >>>= 8;
+ }
+ return d;
+ }
+
+ /**
+ * Compute <tt>ceil(log_256 n)</tt>, the number of bytes needed to encode
+ * the long integer <tt>n</tt>.
+ *
+ * @param n the long integer
+ * @return the number of bytes needed to encode <tt>n</tt>
+ */
+ public static int ceilLog256(long n)
+ {
+ if (n == 0)
+ {
+ return 1;
+ }
+ long m;
+ if (n < 0)
+ {
+ m = -n;
+ }
+ else
+ {
+ m = n;
+ }
+
+ int d = 0;
+ while (m > 0)
+ {
+ d++;
+ m >>>= 8;
+ }
+ return d;
+ }
+
+ /**
+ * Compute the integer part of the logarithm to the base 2 of the given
+ * integer.
+ *
+ * @param a the integer
+ * @return floor[log(a)]
+ */
+ public static int floorLog(BigInteger a)
+ {
+ int result = -1;
+ BigInteger p = ONE;
+ while (p.compareTo(a) <= 0)
+ {
+ result++;
+ p = p.shiftLeft(1);
+ }
+ return result;
+ }
+
+ /**
+ * Compute the integer part of the logarithm to the base 2 of the given
+ * integer.
+ *
+ * @param a the integer
+ * @return floor[log(a)]
+ */
+ public static int floorLog(int a)
+ {
+ int h = 0;
+ if (a <= 0)
+ {
+ return -1;
+ }
+ int p = a >>> 1;
+ while (p > 0)
+ {
+ h++;
+ p >>>= 1;
+ }
+
+ return h;
+ }
+
+ /**
+ * Compute the largest <tt>h</tt> with <tt>2^h | a</tt> if <tt>a!=0</tt>.
+ *
+ * @param a an integer
+ * @return the largest <tt>h</tt> with <tt>2^h | a</tt> if <tt>a!=0</tt>,
+ * <tt>0</tt> otherwise
+ */
+ public static int maxPower(int a)
+ {
+ int h = 0;
+ if (a != 0)
+ {
+ int p = 1;
+ while ((a & p) == 0)
+ {
+ h++;
+ p <<= 1;
+ }
+ }
+
+ return h;
+ }
+
+ /**
+ * @param a an integer
+ * @return the number of ones in the binary representation of an integer
+ * <tt>a</tt>
+ */
+ public static int bitCount(int a)
+ {
+ int h = 0;
+ while (a != 0)
+ {
+ h += a & 1;
+ a >>>= 1;
+ }
+
+ return h;
+ }
+
+ /**
+ * determines the order of g modulo p, p prime and 1 &lt; g &lt; p. This algorithm
+ * is only efficient for small p (see X9.62-1998, p. 68).
+ *
+ * @param g an integer with 1 &lt; g &lt; p
+ * @param p a prime
+ * @return the order k of g (that is k is the smallest integer with
+ * g<sup>k</sup> = 1 mod p
+ */
+ public static int order(int g, int p)
+ {
+ int b, j;
+
+ b = g % p; // Reduce g mod p first.
+ j = 1;
+
+ // Check whether g == 0 mod p (avoiding endless loop).
+ if (b == 0)
+ {
+ throw new IllegalArgumentException(g + " is not an element of Z/("
+ + p + "Z)^*; it is not meaningful to compute its order.");
+ }
+
+ // Compute the order of g mod p:
+ while (b != 1)
+ {
+ b *= g;
+ b %= p;
+ if (b < 0)
+ {
+ b += p;
+ }
+ j++;
+ }
+
+ return j;
+ }
+
+ /**
+ * Reduces an integer into a given interval
+ *
+ * @param n - the integer
+ * @param begin - left bound of the interval
+ * @param end - right bound of the interval
+ * @return <tt>n</tt> reduced into <tt>[begin,end]</tt>
+ */
+ public static BigInteger reduceInto(BigInteger n, BigInteger begin,
+ BigInteger end)
+ {
+ return n.subtract(begin).mod(end.subtract(begin)).add(begin);
+ }
+
+ /**
+ * Compute <tt>a<sup>e</sup></tt>.
+ *
+ * @param a the base
+ * @param e the exponent
+ * @return <tt>a<sup>e</sup></tt>
+ */
+ public static int pow(int a, int e)
+ {
+ int result = 1;
+ while (e > 0)
+ {
+ if ((e & 1) == 1)
+ {
+ result *= a;
+ }
+ a *= a;
+ e >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Compute <tt>a<sup>e</sup></tt>.
+ *
+ * @param a the base
+ * @param e the exponent
+ * @return <tt>a<sup>e</sup></tt>
+ */
+ public static long pow(long a, int e)
+ {
+ long result = 1;
+ while (e > 0)
+ {
+ if ((e & 1) == 1)
+ {
+ result *= a;
+ }
+ a *= a;
+ e >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Compute <tt>a<sup>e</sup> mod n</tt>.
+ *
+ * @param a the base
+ * @param e the exponent
+ * @param n the modulus
+ * @return <tt>a<sup>e</sup> mod n</tt>
+ */
+ public static int modPow(int a, int e, int n)
+ {
+ if (n <= 0 || (n * n) > Integer.MAX_VALUE || e < 0)
+ {
+ return 0;
+ }
+ int result = 1;
+ a = (a % n + n) % n;
+ while (e > 0)
+ {
+ if ((e & 1) == 1)
+ {
+ result = (result * a) % n;
+ }
+ a = (a * a) % n;
+ e >>>= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Extended euclidian algorithm (computes gcd and representation).
+ *
+ * @param a - the first integer
+ * @param b - the second integer
+ * @return <tt>(d,u,v)</tt>, where <tt>d = gcd(a,b) = ua + vb</tt>
+ */
+ public static BigInteger[] extgcd(BigInteger a, BigInteger b)
+ {
+ BigInteger u = ONE;
+ BigInteger v = ZERO;
+ BigInteger d = a;
+ if (b.signum() != 0)
+ {
+ BigInteger v1 = ZERO;
+ BigInteger v3 = b;
+ while (v3.signum() != 0)
+ {
+ BigInteger[] tmp = d.divideAndRemainder(v3);
+ BigInteger q = tmp[0];
+ BigInteger t3 = tmp[1];
+ BigInteger t1 = u.subtract(q.multiply(v1));
+ u = v1;
+ d = v3;
+ v1 = t1;
+ v3 = t3;
+ }
+ v = d.subtract(a.multiply(u)).divide(b);
+ }
+ return new BigInteger[]{d, u, v};
+ }
+
+ /**
+ * Computation of the least common multiple of a set of BigIntegers.
+ *
+ * @param numbers - the set of numbers
+ * @return the lcm(numbers)
+ */
+ public static BigInteger leastCommonMultiple(BigInteger[] numbers)
+ {
+ int n = numbers.length;
+ BigInteger result = numbers[0];
+ for (int i = 1; i < n; i++)
+ {
+ BigInteger gcd = result.gcd(numbers[i]);
+ result = result.multiply(numbers[i]).divide(gcd);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a long integer whose value is <tt>(a mod m</tt>). This method
+ * differs from <tt>%</tt> in that it always returns a <i>non-negative</i>
+ * integer.
+ *
+ * @param a value on which the modulo operation has to be performed.
+ * @param m the modulus.
+ * @return <tt>a mod m</tt>
+ */
+ public static long mod(long a, long m)
+ {
+ long result = a % m;
+ if (result < 0)
+ {
+ result += m;
+ }
+ return result;
+ }
+
+ /**
+ * Computes the modular inverse of an integer a
+ *
+ * @param a - the integer to invert
+ * @param mod - the modulus
+ * @return <tt>a<sup>-1</sup> mod n</tt>
+ */
+ public static int modInverse(int a, int mod)
+ {
+ return BigInteger.valueOf(a).modInverse(BigInteger.valueOf(mod))
+ .intValue();
+ }
+
+ /**
+ * Computes the modular inverse of an integer a
+ *
+ * @param a - the integer to invert
+ * @param mod - the modulus
+ * @return <tt>a<sup>-1</sup> mod n</tt>
+ */
+ public static long modInverse(long a, long mod)
+ {
+ return BigInteger.valueOf(a).modInverse(BigInteger.valueOf(mod))
+ .longValue();
+ }
+
+ /**
+ * Tests whether an integer <tt>a</tt> is power of another integer
+ * <tt>p</tt>.
+ *
+ * @param a - the first integer
+ * @param p - the second integer
+ * @return n if a = p^n or -1 otherwise
+ */
+ public static int isPower(int a, int p)
+ {
+ if (a <= 0)
+ {
+ return -1;
+ }
+ int n = 0;
+ int d = a;
+ while (d > 1)
+ {
+ if (d % p != 0)
+ {
+ return -1;
+ }
+ d /= p;
+ n++;
+ }
+ return n;
+ }
+
+ /**
+ * Find and return the least non-trivial divisor of an integer <tt>a</tt>.
+ *
+ * @param a - the integer
+ * @return divisor p &gt;1 or 1 if a = -1,0,1
+ */
+ public static int leastDiv(int a)
+ {
+ if (a < 0)
+ {
+ a = -a;
+ }
+ if (a == 0)
+ {
+ return 1;
+ }
+ if ((a & 1) == 0)
+ {
+ return 2;
+ }
+ int p = 3;
+ while (p <= (a / p))
+ {
+ if ((a % p) == 0)
+ {
+ return p;
+ }
+ p += 2;
+ }
+
+ return a;
+ }
+
+ /**
+ * Miller-Rabin-Test, determines wether the given integer is probably prime
+ * or composite. This method returns <tt>true</tt> if the given integer is
+ * prime with probability <tt>1 - 2<sup>-20</sup></tt>.
+ *
+ * @param n the integer to test for primality
+ * @return <tt>true</tt> if the given integer is prime with probability
+ * 2<sup>-100</sup>, <tt>false</tt> otherwise
+ */
+ public static boolean isPrime(int n)
+ {
+ if (n < 2)
+ {
+ return false;
+ }
+ if (n == 2)
+ {
+ return true;
+ }
+ if ((n & 1) == 0)
+ {
+ return false;
+ }
+ if (n < 42)
+ {
+ for (int i = 0; i < SMALL_PRIMES.length; i++)
+ {
+ if (n == SMALL_PRIMES[i])
+ {
+ return true;
+ }
+ }
+ }
+
+ if ((n % 3 == 0) || (n % 5 == 0) || (n % 7 == 0) || (n % 11 == 0)
+ || (n % 13 == 0) || (n % 17 == 0) || (n % 19 == 0)
+ || (n % 23 == 0) || (n % 29 == 0) || (n % 31 == 0)
+ || (n % 37 == 0) || (n % 41 == 0))
+ {
+ return false;
+ }
+
+ return BigInteger.valueOf(n).isProbablePrime(20);
+ }
+
+ /**
+ * Short trial-division test to find out whether a number is not prime. This
+ * test is usually used before a Miller-Rabin primality test.
+ *
+ * @param candidate the number to test
+ * @return <tt>true</tt> if the number has no factor of the tested primes,
+ * <tt>false</tt> if the number is definitely composite
+ */
+ public static boolean passesSmallPrimeTest(BigInteger candidate)
+ {
+ final int[] smallPrime = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
+ 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103,
+ 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167,
+ 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
+ 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
+ 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
+ 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449,
+ 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523,
+ 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607,
+ 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677,
+ 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761,
+ 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853,
+ 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937,
+ 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019,
+ 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087,
+ 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153,
+ 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229,
+ 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297,
+ 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381,
+ 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453,
+ 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499};
+
+ for (int i = 0; i < smallPrime.length; i++)
+ {
+ if (candidate.mod(BigInteger.valueOf(smallPrime[i])).equals(
+ ZERO))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the largest prime smaller than the given integer
+ *
+ * @param n - upper bound
+ * @return the largest prime smaller than <tt>n</tt>, or <tt>1</tt> if
+ * <tt>n &lt;= 2</tt>
+ */
+ public static int nextSmallerPrime(int n)
+ {
+ if (n <= 2)
+ {
+ return 1;
+ }
+
+ if (n == 3)
+ {
+ return 2;
+ }
+
+ if ((n & 1) == 0)
+ {
+ n--;
+ }
+ else
+ {
+ n -= 2;
+ }
+
+ while (n > 3 & !isPrime(n))
+ {
+ n -= 2;
+ }
+ return n;
+ }
+
+ /**
+ * Compute the next probable prime greater than <tt>n</tt> with the
+ * specified certainty.
+ *
+ * @param n a integer number
+ * @param certainty the certainty that the generated number is prime
+ * @return the next prime greater than <tt>n</tt>
+ */
+ public static BigInteger nextProbablePrime(BigInteger n, int certainty)
+ {
+
+ if (n.signum() < 0 || n.signum() == 0 || n.equals(ONE))
+ {
+ return TWO;
+ }
+
+ BigInteger result = n.add(ONE);
+
+ // Ensure an odd number
+ if (!result.testBit(0))
+ {
+ result = result.add(ONE);
+ }
+
+ while (true)
+ {
+ // Do cheap "pre-test" if applicable
+ if (result.bitLength() > 6)
+ {
+ long r = result.remainder(
+ BigInteger.valueOf(SMALL_PRIME_PRODUCT)).longValue();
+ if ((r % 3 == 0) || (r % 5 == 0) || (r % 7 == 0)
+ || (r % 11 == 0) || (r % 13 == 0) || (r % 17 == 0)
+ || (r % 19 == 0) || (r % 23 == 0) || (r % 29 == 0)
+ || (r % 31 == 0) || (r % 37 == 0) || (r % 41 == 0))
+ {
+ result = result.add(TWO);
+ continue; // Candidate is composite; try another
+ }
+ }
+
+ // All candidates of bitLength 2 and 3 are prime by this point
+ if (result.bitLength() < 4)
+ {
+ return result;
+ }
+
+ // The expensive test
+ if (result.isProbablePrime(certainty))
+ {
+ return result;
+ }
+
+ result = result.add(TWO);
+ }
+ }
+
+ /**
+ * Compute the next probable prime greater than <tt>n</tt> with the default
+ * certainty (20).
+ *
+ * @param n a integer number
+ * @return the next prime greater than <tt>n</tt>
+ */
+ public static BigInteger nextProbablePrime(BigInteger n)
+ {
+ return nextProbablePrime(n, 20);
+ }
+
+ /**
+ * Computes the next prime greater than n.
+ *
+ * @param n a integer number
+ * @return the next prime greater than n
+ */
+ public static BigInteger nextPrime(long n)
+ {
+ long i;
+ boolean found = false;
+ long result = 0;
+
+ if (n <= 1)
+ {
+ return BigInteger.valueOf(2);
+ }
+ if (n == 2)
+ {
+ return BigInteger.valueOf(3);
+ }
+
+ for (i = n + 1 + (n & 1); (i <= n << 1) && !found; i += 2)
+ {
+ for (long j = 3; (j <= i >> 1) && !found; j += 2)
+ {
+ if (i % j == 0)
+ {
+ found = true;
+ }
+ }
+ if (found)
+ {
+ found = false;
+ }
+ else
+ {
+ result = i;
+ found = true;
+ }
+ }
+ return BigInteger.valueOf(result);
+ }
+
+ /**
+ * Computes the binomial coefficient (n|t) ("n over t"). Formula:
+ * <ul>
+ * <li>if n !=0 and t != 0 then (n|t) = Mult(i=1, t): (n-(i-1))/i</li>
+ * <li>if t = 0 then (n|t) = 1</li>
+ * <li>if n = 0 and t &gt; 0 then (n|t) = 0</li>
+ * </ul>
+ *
+ * @param n - the "upper" integer
+ * @param t - the "lower" integer
+ * @return the binomialcoefficient "n over t" as BigInteger
+ */
+ public static BigInteger binomial(int n, int t)
+ {
+
+ BigInteger result = ONE;
+
+ if (n == 0)
+ {
+ if (t == 0)
+ {
+ return result;
+ }
+ return ZERO;
+ }
+
+ // the property (n|t) = (n|n-t) be used to reduce numbers of operations
+ if (t > (n >>> 1))
+ {
+ t = n - t;
+ }
+
+ for (int i = 1; i <= t; i++)
+ {
+ result = (result.multiply(BigInteger.valueOf(n - (i - 1))))
+ .divide(BigInteger.valueOf(i));
+ }
+
+ return result;
+ }
+
+ public static BigInteger randomize(BigInteger upperBound)
+ {
+ if (sr == null)
+ {
+ sr = new SecureRandom();
+ }
+ return randomize(upperBound, sr);
+ }
+
+ public static BigInteger randomize(BigInteger upperBound,
+ SecureRandom prng)
+ {
+ int blen = upperBound.bitLength();
+ BigInteger randomNum = BigInteger.valueOf(0);
+
+ if (prng == null)
+ {
+ prng = sr != null ? sr : new SecureRandom();
+ }
+
+ for (int i = 0; i < 20; i++)
+ {
+ randomNum = new BigInteger(blen, prng);
+ if (randomNum.compareTo(upperBound) < 0)
+ {
+ return randomNum;
+ }
+ }
+ return randomNum.mod(upperBound);
+ }
+
+ /**
+ * Extract the truncated square root of a BigInteger.
+ *
+ * @param a - value out of which we extract the square root
+ * @return the truncated square root of <tt>a</tt>
+ */
+ public static BigInteger squareRoot(BigInteger a)
+ {
+ int bl;
+ BigInteger result, remainder, b;
+
+ if (a.compareTo(ZERO) < 0)
+ {
+ throw new ArithmeticException(
+ "cannot extract root of negative number" + a + ".");
+ }
+
+ bl = a.bitLength();
+ result = ZERO;
+ remainder = ZERO;
+
+ // if the bit length is odd then extra step
+ if ((bl & 1) != 0)
+ {
+ result = result.add(ONE);
+ bl--;
+ }
+
+ while (bl > 0)
+ {
+ remainder = remainder.multiply(FOUR);
+ remainder = remainder.add(BigInteger.valueOf((a.testBit(--bl) ? 2
+ : 0)
+ + (a.testBit(--bl) ? 1 : 0)));
+ b = result.multiply(FOUR).add(ONE);
+ result = result.multiply(TWO);
+ if (remainder.compareTo(b) != -1)
+ {
+ result = result.add(ONE);
+ remainder = remainder.subtract(b);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Takes an approximation of the root from an integer base, using newton's
+ * algorithm
+ *
+ * @param base the base to take the root from
+ * @param root the root, for example 2 for a square root
+ */
+ public static float intRoot(int base, int root)
+ {
+ float gNew = base / root;
+ float gOld = 0;
+ int counter = 0;
+ while (Math.abs(gOld - gNew) > 0.0001)
+ {
+ float gPow = floatPow(gNew, root);
+ while (Float.isInfinite(gPow))
+ {
+ gNew = (gNew + gOld) / 2;
+ gPow = floatPow(gNew, root);
+ }
+ counter += 1;
+ gOld = gNew;
+ gNew = gOld - (gPow - base) / (root * floatPow(gOld, root - 1));
+ }
+ return gNew;
+ }
+
+ /**
+ * Calculation of a logarithmus of a float param
+ *
+ * @param param
+ * @return
+ */
+ public static float floatLog(float param)
+ {
+ double arg = (param - 1) / (param + 1);
+ double arg2 = arg;
+ int counter = 1;
+ float result = (float)arg;
+
+ while (arg2 > 0.001)
+ {
+ counter += 2;
+ arg2 *= arg * arg;
+ result += (1. / counter) * arg2;
+ }
+ return 2 * result;
+ }
+
+ /**
+ * int power of a base float, only use for small ints
+ *
+ * @param f
+ * @param i
+ * @return
+ */
+ public static float floatPow(float f, int i)
+ {
+ float g = 1;
+ for (; i > 0; i--)
+ {
+ g *= f;
+ }
+ return g;
+ }
+
+ /**
+ * calculate the logarithm to the base 2.
+ *
+ * @param x any double value
+ * @return log_2(x)
+ * @deprecated use MathFunctions.log(double) instead
+ */
+ public static double log(double x)
+ {
+ if (x > 0 && x < 1)
+ {
+ double d = 1 / x;
+ double result = -log(d);
+ return result;
+ }
+
+ int tmp = 0;
+ double tmp2 = 1;
+ double d = x;
+
+ while (d > 2)
+ {
+ d = d / 2;
+ tmp += 1;
+ tmp2 *= 2;
+ }
+ double rem = x / tmp2;
+ rem = logBKM(rem);
+ return tmp + rem;
+ }
+
+ /**
+ * calculate the logarithm to the base 2.
+ *
+ * @param x any long value &gt;=1
+ * @return log_2(x)
+ * @deprecated use MathFunctions.log(long) instead
+ */
+ public static double log(long x)
+ {
+ int tmp = floorLog(BigInteger.valueOf(x));
+ long tmp2 = 1 << tmp;
+ double rem = (double)x / (double)tmp2;
+ rem = logBKM(rem);
+ return tmp + rem;
+ }
+
+ /**
+ * BKM Algorithm to calculate logarithms to the base 2.
+ *
+ * @param arg a double value with 1<= arg<= 4.768462058
+ * @return log_2(arg)
+ * @deprecated use MathFunctions.logBKM(double) instead
+ */
+ private static double logBKM(double arg)
+ {
+ double ae[] = // A_e[k] = log_2 (1 + 0.5^k)
+ {
+ 1.0000000000000000000000000000000000000000000000000000000000000000000000000000,
+ 0.5849625007211561814537389439478165087598144076924810604557526545410982276485,
+ 0.3219280948873623478703194294893901758648313930245806120547563958159347765589,
+ 0.1699250014423123629074778878956330175196288153849621209115053090821964552970,
+ 0.0874628412503394082540660108104043540112672823448206881266090643866965081686,
+ 0.0443941193584534376531019906736094674630459333742491317685543002674288465967,
+ 0.0223678130284545082671320837460849094932677948156179815932199216587899627785,
+ 0.0112272554232541203378805844158839407281095943600297940811823651462712311786,
+ 0.0056245491938781069198591026740666017211096815383520359072957784732489771013,
+ 0.0028150156070540381547362547502839489729507927389771959487826944878598909400,
+ 0.0014081943928083889066101665016890524233311715793462235597709051792834906001,
+ 0.0007042690112466432585379340422201964456668872087249334581924550139514213168,
+ 0.0003521774803010272377989609925281744988670304302127133979341729842842377649,
+ 0.0001760994864425060348637509459678580940163670081839283659942864068257522373,
+ 0.0000880524301221769086378699983597183301490534085738474534831071719854721939,
+ 0.0000440268868273167176441087067175806394819146645511899503059774914593663365,
+ 0.0000220136113603404964890728830697555571275493801909791504158295359319433723,
+ 0.0000110068476674814423006223021573490183469930819844945565597452748333526464,
+ 0.0000055034343306486037230640321058826431606183125807276574241540303833251704,
+ 0.0000027517197895612831123023958331509538486493412831626219340570294203116559,
+ 0.0000013758605508411382010566802834037147561973553922354232704569052932922954,
+ 0.0000006879304394358496786728937442939160483304056131990916985043387874690617,
+ 0.0000003439652607217645360118314743718005315334062644619363447395987584138324,
+ 0.0000001719826406118446361936972479533123619972434705828085978955697643547921,
+ 0.0000000859913228686632156462565208266682841603921494181830811515318381744650,
+ 0.0000000429956620750168703982940244684787907148132725669106053076409624949917,
+ 0.0000000214978311976797556164155504126645192380395989504741781512309853438587,
+ 0.0000000107489156388827085092095702361647949603617203979413516082280717515504,
+ 0.0000000053744578294520620044408178949217773318785601260677517784797554422804,
+ 0.0000000026872289172287079490026152352638891824761667284401180026908031182361,
+ 0.0000000013436144592400232123622589569799954658536700992739887706412976115422,
+ 0.0000000006718072297764289157920422846078078155859484240808550018085324187007,
+ 0.0000000003359036149273187853169587152657145221968468364663464125722491530858,
+ 0.0000000001679518074734354745159899223037458278711244127245990591908996412262,
+ 0.0000000000839759037391617577226571237484864917411614198675604731728132152582,
+ 0.0000000000419879518701918839775296677020135040214077417929807824842667285938,
+ 0.0000000000209939759352486932678195559552767641474249812845414125580747434389,
+ 0.0000000000104969879676625344536740142096218372850561859495065136990936290929,
+ 0.0000000000052484939838408141817781356260462777942148580518406975851213868092,
+ 0.0000000000026242469919227938296243586262369156865545638305682553644113887909,
+ 0.0000000000013121234959619935994960031017850191710121890821178731821983105443,
+ 0.0000000000006560617479811459709189576337295395590603644549624717910616347038,
+ 0.0000000000003280308739906102782522178545328259781415615142931952662153623493,
+ 0.0000000000001640154369953144623242936888032768768777422997704541618141646683,
+ 0.0000000000000820077184976595619616930350508356401599552034612281802599177300,
+ 0.0000000000000410038592488303636807330652208397742314215159774270270147020117,
+ 0.0000000000000205019296244153275153381695384157073687186580546938331088730952,
+ 0.0000000000000102509648122077001764119940017243502120046885379813510430378661,
+ 0.0000000000000051254824061038591928917243090559919209628584150482483994782302,
+ 0.0000000000000025627412030519318726172939815845367496027046030028595094737777,
+ 0.0000000000000012813706015259665053515049475574143952543145124550608158430592,
+ 0.0000000000000006406853007629833949364669629701200556369782295210193569318434,
+ 0.0000000000000003203426503814917330334121037829290364330169106716787999052925,
+ 0.0000000000000001601713251907458754080007074659337446341494733882570243497196,
+ 0.0000000000000000800856625953729399268240176265844257044861248416330071223615,
+ 0.0000000000000000400428312976864705191179247866966320469710511619971334577509,
+ 0.0000000000000000200214156488432353984854413866994246781519154793320684126179,
+ 0.0000000000000000100107078244216177339743404416874899847406043033792202127070,
+ 0.0000000000000000050053539122108088756700751579281894640362199287591340285355,
+ 0.0000000000000000025026769561054044400057638132352058574658089256646014899499,
+ 0.0000000000000000012513384780527022205455634651853807110362316427807660551208,
+ 0.0000000000000000006256692390263511104084521222346348012116229213309001913762,
+ 0.0000000000000000003128346195131755552381436585278035120438976487697544916191,
+ 0.0000000000000000001564173097565877776275512286165232838833090480508502328437,
+ 0.0000000000000000000782086548782938888158954641464170239072244145219054734086,
+ 0.0000000000000000000391043274391469444084776945327473574450334092075712154016,
+ 0.0000000000000000000195521637195734722043713378812583900953755962557525252782,
+ 0.0000000000000000000097760818597867361022187915943503728909029699365320287407,
+ 0.0000000000000000000048880409298933680511176764606054809062553340323879609794,
+ 0.0000000000000000000024440204649466840255609083961603140683286362962192177597,
+ 0.0000000000000000000012220102324733420127809717395445504379645613448652614939,
+ 0.0000000000000000000006110051162366710063906152551383735699323415812152114058,
+ 0.0000000000000000000003055025581183355031953399739107113727036860315024588989,
+ 0.0000000000000000000001527512790591677515976780735407368332862218276873443537,
+ 0.0000000000000000000000763756395295838757988410584167137033767056170417508383,
+ 0.0000000000000000000000381878197647919378994210346199431733717514843471513618,
+ 0.0000000000000000000000190939098823959689497106436628681671067254111334889005,
+ 0.0000000000000000000000095469549411979844748553534196582286585751228071408728,
+ 0.0000000000000000000000047734774705989922374276846068851506055906657137209047,
+ 0.0000000000000000000000023867387352994961187138442777065843718711089344045782,
+ 0.0000000000000000000000011933693676497480593569226324192944532044984865894525,
+ 0.0000000000000000000000005966846838248740296784614396011477934194852481410926,
+ 0.0000000000000000000000002983423419124370148392307506484490384140516252814304,
+ 0.0000000000000000000000001491711709562185074196153830361933046331030629430117,
+ 0.0000000000000000000000000745855854781092537098076934460888486730708440475045,
+ 0.0000000000000000000000000372927927390546268549038472050424734256652501673274,
+ 0.0000000000000000000000000186463963695273134274519237230207489851150821191330,
+ 0.0000000000000000000000000093231981847636567137259618916352525606281553180093,
+ 0.0000000000000000000000000046615990923818283568629809533488457973317312233323,
+ 0.0000000000000000000000000023307995461909141784314904785572277779202790023236,
+ 0.0000000000000000000000000011653997730954570892157452397493151087737428485431,
+ 0.0000000000000000000000000005826998865477285446078726199923328593402722606924,
+ 0.0000000000000000000000000002913499432738642723039363100255852559084863397344,
+ 0.0000000000000000000000000001456749716369321361519681550201473345138307215067,
+ 0.0000000000000000000000000000728374858184660680759840775119123438968122488047,
+ 0.0000000000000000000000000000364187429092330340379920387564158411083803465567,
+ 0.0000000000000000000000000000182093714546165170189960193783228378441837282509,
+ 0.0000000000000000000000000000091046857273082585094980096891901482445902524441,
+ 0.0000000000000000000000000000045523428636541292547490048446022564529197237262,
+ 0.0000000000000000000000000000022761714318270646273745024223029238091160103901};
+ int n = 53;
+ double x = 1;
+ double y = 0;
+ double z;
+ double s = 1;
+ int k;
+
+ for (k = 0; k < n; k++)
+ {
+ z = x + x * s;
+ if (z <= arg)
+ {
+ x = z;
+ y += ae[k];
+ }
+ s *= 0.5;
+ }
+ return y;
+ }
+
+ public static boolean isIncreasing(int[] a)
+ {
+ for (int i = 1; i < a.length; i++)
+ {
+ if (a[i - 1] >= a[i])
+ {
+ System.out.println("a[" + (i - 1) + "] = " + a[i - 1] + " >= "
+ + a[i] + " = a[" + i + "]");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static byte[] integerToOctets(BigInteger val)
+ {
+ byte[] valBytes = val.abs().toByteArray();
+
+ // check whether the array includes a sign bit
+ if ((val.bitLength() & 7) != 0)
+ {
+ return valBytes;
+ }
+ // get rid of the sign bit (first byte)
+ byte[] tmp = new byte[val.bitLength() >> 3];
+ System.arraycopy(valBytes, 1, tmp, 0, tmp.length);
+ return tmp;
+ }
+
+ public static BigInteger octetsToInteger(byte[] data, int offset,
+ int length)
+ {
+ byte[] val = new byte[length + 1];
+
+ val[0] = 0;
+ System.arraycopy(data, offset, val, 1, length);
+ return new BigInteger(val);
+ }
+
+ public static BigInteger octetsToInteger(byte[] data)
+ {
+ return octetsToInteger(data, 0, data.length);
+ }
+
+ public static void main(String[] args)
+ {
+ System.out.println("test");
+ // System.out.println(intRoot(37, 5));
+ // System.out.println(floatPow((float)2.5, 4));
+ System.out.println(floatLog(10));
+ System.out.println("test2");
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/LittleEndianConversions.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/LittleEndianConversions.java
new file mode 100644
index 00000000..11169521
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/LittleEndianConversions.java
@@ -0,0 +1,230 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This is a utility class containing data type conversions using little-endian
+ * byte order.
+ *
+ * @see BigEndianConversions
+ */
+public final 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 ((input[0] & 0xff)) | ((input[1] & 0xff) << 8)
+ | ((input[2] & 0xff) << 16) | ((input[3] & 0xff)) << 24;
+ }
+
+ /**
+ * 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)
+ {
+ int result = input[inOff++] & 0xff;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= (input[inOff] & 0xff) << 24;
+ return result;
+ }
+
+ /**
+ * 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)
+ {
+ long result = input[inOff++] & 0xff;
+ result |= (input[inOff++] & 0xff) << 8;
+ result |= (input[inOff++] & 0xff) << 16;
+ result |= ((long)input[inOff++] & 0xff) << 24;
+ result |= ((long)input[inOff++] & 0xff) << 32;
+ result |= ((long)input[inOff++] & 0xff) << 40;
+ result |= ((long)input[inOff++] & 0xff) << 48;
+ result |= ((long)input[inOff++] & 0xff) << 56;
+ return result;
+ }
+
+ /**
+ * 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)
+ {
+ byte[] result = new byte[4];
+ result[0] = (byte)x;
+ result[1] = (byte)(x >>> 8);
+ result[2] = (byte)(x >>> 16);
+ result[3] = (byte)(x >>> 24);
+ return result;
+ }
+
+ /**
+ * 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)
+ {
+ output[outOff++] = (byte)value;
+ output[outOff++] = (byte)(value >>> 8);
+ output[outOff++] = (byte)(value >>> 16);
+ output[outOff++] = (byte)(value >>> 24);
+ }
+
+ /**
+ * 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)
+ {
+ for (int i = outLen - 1; i >= 0; i--)
+ {
+ output[outOff + i] = (byte)(value >>> (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)
+ {
+ byte[] output = new byte[8];
+ output[0] = (byte)input;
+ output[1] = (byte)(input >>> 8);
+ output[2] = (byte)(input >>> 16);
+ output[3] = (byte)(input >>> 24);
+ output[4] = (byte)(input >>> 32);
+ output[5] = (byte)(input >>> 40);
+ output[6] = (byte)(input >>> 48);
+ output[7] = (byte)(input >>> 56);
+ return output;
+ }
+
+ /**
+ * 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)
+ {
+ output[outOff++] = (byte)input;
+ output[outOff++] = (byte)(input >>> 8);
+ output[outOff++] = (byte)(input >>> 16);
+ output[outOff++] = (byte)(input >>> 24);
+ output[outOff++] = (byte)(input >>> 32);
+ output[outOff++] = (byte)(input >>> 40);
+ output[outOff++] = (byte)(input >>> 48);
+ output[outOff] = (byte)(input >>> 56);
+ }
+
+ /**
+ * 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/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Matrix.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Matrix.java
new file mode 100644
index 00000000..170c5a21
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Matrix.java
@@ -0,0 +1,131 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This abstract class defines matrices. It holds the number of rows and the
+ * number of columns of the matrix and defines some basic methods.
+ */
+public abstract class Matrix
+{
+
+ /**
+ * number of rows
+ */
+ protected int numRows;
+
+ /**
+ * number of columns
+ */
+ protected int numColumns;
+
+ // ----------------------------------------------------
+ // some constants (matrix types)
+ // ----------------------------------------------------
+
+ /**
+ * zero matrix
+ */
+ public static final char MATRIX_TYPE_ZERO = 'Z';
+
+ /**
+ * unit matrix
+ */
+ public static final char MATRIX_TYPE_UNIT = 'I';
+
+ /**
+ * random lower triangular matrix
+ */
+ public static final char MATRIX_TYPE_RANDOM_LT = 'L';
+
+ /**
+ * random upper triangular matrix
+ */
+ public static final char MATRIX_TYPE_RANDOM_UT = 'U';
+
+ /**
+ * random regular matrix
+ */
+ public static final char MATRIX_TYPE_RANDOM_REGULAR = 'R';
+
+ // ----------------------------------------------------
+ // getters
+ // ----------------------------------------------------
+
+ /**
+ * @return the number of rows in the matrix
+ */
+ public int getNumRows()
+ {
+ return numRows;
+ }
+
+ /**
+ * @return the number of columns in the binary matrix
+ */
+ public int getNumColumns()
+ {
+ return numColumns;
+ }
+
+ /**
+ * @return the encoded matrix, i.e., this matrix in byte array form.
+ */
+ public abstract byte[] getEncoded();
+
+ // ----------------------------------------------------
+ // arithmetic
+ // ----------------------------------------------------
+
+ /**
+ * Compute the inverse of this matrix.
+ *
+ * @return the inverse of this matrix (newly created).
+ */
+ public abstract Matrix computeInverse();
+
+ /**
+ * Check if this is the zero matrix (i.e., all entries are zero).
+ *
+ * @return <tt>true</tt> if this is the zero matrix
+ */
+ public abstract boolean isZero();
+
+ /**
+ * Compute the product of this matrix and another matrix.
+ *
+ * @param a the other matrix
+ * @return <tt>this * a</tt> (newly created)
+ */
+ public abstract Matrix rightMultiply(Matrix a);
+
+ /**
+ * Compute the product of this matrix and a permutation.
+ *
+ * @param p the permutation
+ * @return <tt>this * p</tt> (newly created)
+ */
+ public abstract Matrix rightMultiply(Permutation p);
+
+ /**
+ * Compute the product of a vector and this matrix. If the length of the
+ * vector is greater than the number of rows of this matrix, the matrix is
+ * multiplied by each m-bit part of the vector.
+ *
+ * @param vector a vector
+ * @return <tt>vector * this</tt> (newly created)
+ */
+ public abstract Vector leftMultiply(Vector vector);
+
+ /**
+ * Compute the product of this matrix and a vector.
+ *
+ * @param vector a vector
+ * @return <tt>this * vector</tt> (newly created)
+ */
+ public abstract Vector rightMultiply(Vector vector);
+
+ /**
+ * @return a human readable form of the matrix.
+ */
+ public abstract String toString();
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Permutation.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Permutation.java
new file mode 100644
index 00000000..d156672f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Permutation.java
@@ -0,0 +1,247 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class implements permutations of the set {0,1,...,n-1} for some given n
+ * &gt; 0, i.e., ordered sequences containing each number <tt>m</tt> (<tt>0 &lt;=
+ * m &lt; 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 IllegalArgumentException("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 IllegalArgumentException(
+ "array is not a permutation vector");
+ }
+
+ this.perm = IntUtils.clone(perm);
+ }
+
+ /**
+ * Create a permutation from an encoded permutation.
+ *
+ * @param enc the encoded permutation
+ */
+ public Permutation(byte[] enc)
+ {
+ if (enc.length <= 4)
+ {
+ throw new IllegalArgumentException("invalid encoding");
+ }
+
+ int n = LittleEndianConversions.OS2IP(enc, 0);
+ int size = IntegerFunctions.ceilLog256(n - 1);
+
+ if (enc.length != 4 + n * size)
+ {
+ throw new IllegalArgumentException("invalid encoding");
+ }
+
+ perm = new int[n];
+ for (int i = 0; i < n; i++)
+ {
+ perm[i] = LittleEndianConversions.OS2IP(enc, 4 + i * size, size);
+ }
+
+ if (!isPermutation(perm))
+ {
+ throw new IllegalArgumentException("invalid encoding");
+ }
+
+ }
+
+ /**
+ * 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 IllegalArgumentException("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];
+ }
+ }
+
+ /**
+ * Encode this permutation as byte array.
+ *
+ * @return the encoded permutation
+ */
+ public byte[] getEncoded()
+ {
+ int n = perm.length;
+ int size = IntegerFunctions.ceilLog256(n - 1);
+ byte[] result = new byte[4 + n * size];
+ LittleEndianConversions.I2OSP(n, result, 0);
+ for (int i = 0; i < n; i++)
+ {
+ LittleEndianConversions.I2OSP(perm[i], result, 4 + i * size, size);
+ }
+ return result;
+ }
+
+ /**
+ * @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 IllegalArgumentException("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 boolean equals(Object other)
+ {
+
+ if (!(other instanceof 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;
+ }
+
+ /**
+ * @return the hash code of this permutation
+ */
+ public int hashCode()
+ {
+ return perm.hashCode();
+ }
+
+ /**
+ * 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 boolean isPermutation(int[] perm)
+ {
+ int n = perm.length;
+ boolean[] onlyOnce = new boolean[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/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java
new file mode 100644
index 00000000..6426a4ce
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java
@@ -0,0 +1,1124 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+/**
+ * This class describes operations with polynomials from the ring R =
+ * GF(2^m)[X], where 2 &lt;= m &lt;=31.
+ *
+ * @see GF2mField
+ * @see PolynomialRingGF2m
+ */
+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 static final 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 IllegalArgumentException(" 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 IllegalArgumentException(
+ " 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 IllegalArgumentException(
+ " 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 IllegalArgumentException(
+ " 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)
+ {
+ this(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)(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];
+ System.arraycopy(b, 0, result, 0, b.length);
+ addend = a;
+ }
+ else
+ {
+ result = new int[a.length];
+ System.arraycopy(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];
+ System.arraycopy(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) &lt;
+ * 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) &lt; 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;
+ System.arraycopy(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];
+ System.arraycopy(b, 0, a, 0, a.length);
+ b = new int[c.length];
+ System.arraycopy(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];
+ System.arraycopy(mult1, 0, res1, 0, res1.length);
+ System.arraycopy(mult1, d2, res2, 0, res2.length);
+ res1 = multiply(res1, mult2);
+ res2 = multiply(res2, mult2);
+ res2 = multWithMonomial(res2, d2);
+ result = add(res1, res2);
+ }
+ else
+ {
+ d2 = (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];
+ System
+ .arraycopy(mult1, 0, firstPartMult1, 0,
+ firstPartMult1.length);
+ System.arraycopy(mult1, d2, secondPartMult1, 0,
+ secondPartMult1.length);
+ System
+ .arraycopy(mult2, 0, firstPartMult2, 0,
+ firstPartMult2.length);
+ System.arraycopy(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 boolean isIrreducible(int[] a)
+ {
+ if (a[0] == 0)
+ {
+ return false;
+ }
+ int d = computeDegree(a) >> 1;
+ int[] u = {0, 1};
+ final 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);
+ System.arraycopy(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)&lt;=deg(g)/2.
+ *
+ * @param g the reduction polynomial
+ * @return PolynomialGF2mSmallM[] {a,b} with b*this = a mod g and deg(a)&lt;=
+ * 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 boolean equals(Object other)
+ {
+
+ if (other == null || !(other instanceof 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 boolean 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];
+ System.arraycopy(a, 0, result, 0, d + 1);
+ return result;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2.java
new file mode 100644
index 00000000..850b5dbf
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2.java
@@ -0,0 +1,278 @@
+package org.spongycastle.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 &lt;=32. For the polynomial representation the map f: R-&gt;Z,
+ * poly(X)-&gt;poly(2) is used, where integers have the binary representation. For
+ * example: X^7+X^3+X+1 -&gt; (00...0010001011)=139 Also for polynomials type
+ * Integer is used.
+ *
+ * @see GF2mField
+ */
+public final 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 >>>= 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 >>>= 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 >>>= 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 >>>= 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)
+ {
+ System.err.println("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)
+ {
+ System.err.println("Error: to be divided by 0");
+ return 0;
+ }
+ long q1 = q & 0x00000000ffffffffL;
+ while ((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 boolean isIrreducible(int p)
+ {
+ if (p == 0)
+ {
+ return false;
+ }
+ int d = degree(p) >>> 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)
+ {
+ System.err.println("The Degree is negative");
+ return 0;
+ }
+ if (deg > 31)
+ {
+ System.err.println("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/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java
new file mode 100644
index 00000000..7700fe52
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java
@@ -0,0 +1,175 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+/**
+ * This class represents polynomial rings <tt>GF(2^m)[X]/p(X)</tt> for
+ * <tt>m&lt;32</tt>. If <tt>p(X)</tt> is irreducible, the polynomial ring
+ * is in fact an extension field of <tt>GF(2^m)</tt>.
+ */
+public class PolynomialRingGF2m
+{
+
+ /**
+ * the finite field this polynomial ring is defined over
+ */
+ private GF2mField field;
+
+ /**
+ * the reduction polynomial
+ */
+ private PolynomialGF2mSmallM p;
+
+ /**
+ * the squaring matrix for this polynomial ring (given as the array of its
+ * row vectors)
+ */
+ protected PolynomialGF2mSmallM[] sqMatrix;
+
+ /**
+ * the matrix for computing square roots in this polynomial ring (given as
+ * the array of its row vectors). This matrix is computed as the inverse of
+ * the squaring matrix.
+ */
+ protected PolynomialGF2mSmallM[] sqRootMatrix;
+
+ /**
+ * Constructor.
+ *
+ * @param field the finite field
+ * @param p the reduction polynomial
+ */
+ public PolynomialRingGF2m(GF2mField field, PolynomialGF2mSmallM p)
+ {
+ this.field = field;
+ this.p = p;
+ computeSquaringMatrix();
+ computeSquareRootMatrix();
+ }
+
+ /**
+ * @return the squaring matrix for this polynomial ring
+ */
+ public PolynomialGF2mSmallM[] getSquaringMatrix()
+ {
+ return sqMatrix;
+ }
+
+ /**
+ * @return the matrix for computing square roots for this polynomial ring
+ */
+ public PolynomialGF2mSmallM[] getSquareRootMatrix()
+ {
+ return sqRootMatrix;
+ }
+
+ /**
+ * Compute the squaring matrix for this polynomial ring, using the base
+ * field and the reduction polynomial.
+ */
+ private void computeSquaringMatrix()
+ {
+ int numColumns = p.getDegree();
+ sqMatrix = new PolynomialGF2mSmallM[numColumns];
+ for (int i = 0; i < numColumns >> 1; i++)
+ {
+ int[] monomCoeffs = new int[(i << 1) + 1];
+ monomCoeffs[i << 1] = 1;
+ sqMatrix[i] = new PolynomialGF2mSmallM(field, monomCoeffs);
+ }
+ for (int i = numColumns >> 1; i < numColumns; i++)
+ {
+ int[] monomCoeffs = new int[(i << 1) + 1];
+ monomCoeffs[i << 1] = 1;
+ PolynomialGF2mSmallM monomial = new PolynomialGF2mSmallM(field,
+ monomCoeffs);
+ sqMatrix[i] = monomial.mod(p);
+ }
+ }
+
+ /**
+ * Compute the matrix for computing square roots in this polynomial ring by
+ * inverting the squaring matrix.
+ */
+ private void computeSquareRootMatrix()
+ {
+ int numColumns = p.getDegree();
+
+ // clone squaring matrix
+ PolynomialGF2mSmallM[] tmpMatrix = new PolynomialGF2mSmallM[numColumns];
+ for (int i = numColumns - 1; i >= 0; i--)
+ {
+ tmpMatrix[i] = new PolynomialGF2mSmallM(sqMatrix[i]);
+ }
+
+ // initialize square root matrix as unit matrix
+ sqRootMatrix = new PolynomialGF2mSmallM[numColumns];
+ for (int i = numColumns - 1; i >= 0; i--)
+ {
+ sqRootMatrix[i] = new PolynomialGF2mSmallM(field, i);
+ }
+
+ // simultaneously compute Gaussian reduction of squaring matrix and unit
+ // matrix
+ for (int i = 0; i < numColumns; i++)
+ {
+ // if diagonal element is zero
+ if (tmpMatrix[i].getCoefficient(i) == 0)
+ {
+ boolean foundNonZero = false;
+ // find a non-zero element in the same row
+ for (int j = i + 1; j < numColumns; j++)
+ {
+ if (tmpMatrix[j].getCoefficient(i) != 0)
+ {
+ // found it, swap columns ...
+ foundNonZero = true;
+ swapColumns(tmpMatrix, i, j);
+ swapColumns(sqRootMatrix, i, j);
+ // ... and quit searching
+ j = numColumns;
+ continue;
+ }
+ }
+ // if no non-zero element was found
+ if (!foundNonZero)
+ {
+ // the matrix is not invertible
+ throw new ArithmeticException(
+ "Squaring matrix is not invertible.");
+ }
+ }
+
+ // normalize i-th column
+ int coef = tmpMatrix[i].getCoefficient(i);
+ int invCoef = field.inverse(coef);
+ tmpMatrix[i].multThisWithElement(invCoef);
+ sqRootMatrix[i].multThisWithElement(invCoef);
+
+ // normalize all other columns
+ for (int j = 0; j < numColumns; j++)
+ {
+ if (j != i)
+ {
+ coef = tmpMatrix[j].getCoefficient(i);
+ if (coef != 0)
+ {
+ PolynomialGF2mSmallM tmpSqColumn = tmpMatrix[i]
+ .multWithElement(coef);
+ PolynomialGF2mSmallM tmpInvColumn = sqRootMatrix[i]
+ .multWithElement(coef);
+ tmpMatrix[j].addToThis(tmpSqColumn);
+ sqRootMatrix[j].addToThis(tmpInvColumn);
+ }
+ }
+ }
+ }
+ }
+
+ private static void swapColumns(PolynomialGF2mSmallM[] matrix, int first,
+ int second)
+ {
+ PolynomialGF2mSmallM tmp = matrix[first];
+ matrix[first] = matrix[second];
+ matrix[second] = tmp;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/RandUtils.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/RandUtils.java
new file mode 100644
index 00000000..34862d7c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/RandUtils.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.math.linearalgebra;
+
+import java.security.SecureRandom;
+
+public class RandUtils
+{
+ static int nextInt(SecureRandom rand, int n)
+ {
+
+ if ((n & -n) == n) // i.e., n is a power of 2
+ {
+ return (int)((n * (long)(rand.nextInt() >>> 1)) >> 31);
+ }
+
+ int bits, value;
+ do
+ {
+ bits = rand.nextInt() >>> 1;
+ value = bits % n;
+ }
+ while (bits - value + (n - 1) < 0);
+
+ return value;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Vector.java b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Vector.java
new file mode 100644
index 00000000..7f33d735
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/linearalgebra/Vector.java
@@ -0,0 +1,69 @@
+package org.spongycastle.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 final 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 boolean 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 boolean equals(Object other);
+
+ /**
+ * @return the hash code of this vector
+ */
+ public abstract int hashCode();
+
+ /**
+ * @return a human readable form of this vector
+ */
+ public abstract String toString();
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/BigIntEuclidean.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/BigIntEuclidean.java
new file mode 100644
index 00000000..e48d2a0c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/BigIntEuclidean.java
@@ -0,0 +1,54 @@
+package org.spongycastle.pqc.math.ntru.euclid;
+
+import java.math.BigInteger;
+
+/**
+ * Extended Euclidean Algorithm in <code>BigInteger</code>s
+ */
+public class BigIntEuclidean
+{
+ public BigInteger x, y, gcd;
+
+ private BigIntEuclidean()
+ {
+ }
+
+ /**
+ * Runs the EEA on two <code>BigInteger</code>s<br>
+ * Implemented from pseudocode on <a href="http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm">Wikipedia</a>.
+ *
+ * @param a
+ * @param b
+ * @return a <code>BigIntEuclidean</code> object that contains the result in the variables <code>x</code>, <code>y</code>, and <code>gcd</code>
+ */
+ public static BigIntEuclidean calculate(BigInteger a, BigInteger b)
+ {
+ BigInteger x = BigInteger.ZERO;
+ BigInteger lastx = BigInteger.ONE;
+ BigInteger y = BigInteger.ONE;
+ BigInteger lasty = BigInteger.ZERO;
+ while (!b.equals(BigInteger.ZERO))
+ {
+ BigInteger[] quotientAndRemainder = a.divideAndRemainder(b);
+ BigInteger quotient = quotientAndRemainder[0];
+
+ BigInteger temp = a;
+ a = b;
+ b = quotientAndRemainder[1];
+
+ temp = x;
+ x = lastx.subtract(quotient.multiply(x));
+ lastx = temp;
+
+ temp = y;
+ y = lasty.subtract(quotient.multiply(y));
+ lasty = temp;
+ }
+
+ BigIntEuclidean result = new BigIntEuclidean();
+ result.x = lastx;
+ result.y = lasty;
+ result.gcd = a;
+ return result;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/IntEuclidean.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/IntEuclidean.java
new file mode 100644
index 00000000..0791b01e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/euclid/IntEuclidean.java
@@ -0,0 +1,51 @@
+package org.spongycastle.pqc.math.ntru.euclid;
+
+/**
+ * Extended Euclidean Algorithm in <code>int</code>s
+ */
+public class IntEuclidean
+{
+ public int x, y, gcd;
+
+ private IntEuclidean()
+ {
+ }
+
+ /**
+ * Runs the EEA on two <code>int</code>s<br>
+ * Implemented from pseudocode on <a href="http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm">Wikipedia</a>.
+ *
+ * @param a
+ * @param b
+ * @return a <code>IntEuclidean</code> object that contains the result in the variables <code>x</code>, <code>y</code>, and <code>gcd</code>
+ */
+ public static IntEuclidean calculate(int a, int b)
+ {
+ int x = 0;
+ int lastx = 1;
+ int y = 1;
+ int lasty = 0;
+ while (b != 0)
+ {
+ int quotient = a / b;
+
+ int temp = a;
+ a = b;
+ b = temp % b;
+
+ temp = x;
+ x = lastx - quotient * x;
+ lastx = temp;
+
+ temp = y;
+ y = lasty - quotient * y;
+ lasty = temp;
+ }
+
+ IntEuclidean result = new IntEuclidean();
+ result.x = lastx;
+ result.y = lasty;
+ result.gcd = a;
+ return result;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigDecimalPolynomial.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigDecimalPolynomial.java
new file mode 100644
index 00000000..a496060c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigDecimalPolynomial.java
@@ -0,0 +1,258 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigDecimal;
+
+/**
+ * A polynomial with {@link BigDecimal} coefficients.
+ * Some methods (like <code>add</code>) change the polynomial, others (like <code>mult</code>) do
+ * not but return the result as a new polynomial.
+ */
+public class BigDecimalPolynomial
+{
+ private static final BigDecimal ZERO = new BigDecimal("0");
+ private static final BigDecimal ONE_HALF = new BigDecimal("0.5");
+
+ BigDecimal[] coeffs;
+
+ /**
+ * Constructs a new polynomial with <code>N</code> coefficients initialized to 0.
+ *
+ * @param N the number of coefficients
+ */
+ BigDecimalPolynomial(int N)
+ {
+ coeffs = new BigDecimal[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = ZERO;
+ }
+ }
+
+ /**
+ * Constructs a new polynomial with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ BigDecimalPolynomial(BigDecimal[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ /**
+ * Constructs a <code>BigDecimalPolynomial</code> from a <code>BigIntPolynomial</code>. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial
+ */
+ public BigDecimalPolynomial(BigIntPolynomial p)
+ {
+ int N = p.coeffs.length;
+ coeffs = new BigDecimal[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = new BigDecimal(p.coeffs[i]);
+ }
+ }
+
+ /**
+ * Divides all coefficients by 2.
+ */
+ public void halve()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].multiply(ONE_HALF);
+ }
+ }
+
+ /**
+ * Multiplies the polynomial by another. Does not change this polynomial
+ * but returns the result as a new polynomial.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ public BigDecimalPolynomial mult(BigIntPolynomial poly2)
+ {
+ return mult(new BigDecimalPolynomial(poly2));
+ }
+
+ /**
+ * Multiplies the polynomial by another, taking the indices mod N. Does not
+ * change this polynomial but returns the result as a new polynomial.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ public BigDecimalPolynomial mult(BigDecimalPolynomial poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ BigDecimalPolynomial c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = c.coeffs[k - N].add(c.coeffs[k]);
+ }
+ c.coeffs = copyOf(c.coeffs, N);
+ }
+ return c;
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private BigDecimalPolynomial multRecursive(BigDecimalPolynomial poly2)
+ {
+ BigDecimal[] a = coeffs;
+ BigDecimal[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 1)
+ {
+ BigDecimal[] c = coeffs.clone();
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ c[i] = c[i].multiply(poly2.coeffs[0]);
+ }
+ return new BigDecimalPolynomial(c);
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ BigDecimalPolynomial a1 = new BigDecimalPolynomial(copyOf(a, n1));
+ BigDecimalPolynomial a2 = new BigDecimalPolynomial(copyOfRange(a, n1, n));
+ BigDecimalPolynomial b1 = new BigDecimalPolynomial(copyOf(b, n1));
+ BigDecimalPolynomial b2 = new BigDecimalPolynomial(copyOfRange(b, n1, n));
+
+ BigDecimalPolynomial A = (BigDecimalPolynomial)a1.clone();
+ A.add(a2);
+ BigDecimalPolynomial B = (BigDecimalPolynomial)b1.clone();
+ B.add(b2);
+
+ BigDecimalPolynomial c1 = a1.multRecursive(b1);
+ BigDecimalPolynomial c2 = a2.multRecursive(b2);
+ BigDecimalPolynomial c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ BigDecimalPolynomial c = new BigDecimalPolynomial(2 * n - 1);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i];
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] = c.coeffs[n1 + i].add(c3.coeffs[i]);
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] = c.coeffs[2 * n1 + i].add(c2.coeffs[i]);
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void add(BigDecimalPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].add(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b
+ */
+ void sub(BigDecimalPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].subtract(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Rounds all coefficients to the nearest integer.
+ *
+ * @return a new polynomial with <code>BigInteger</code> coefficients
+ */
+ public BigIntPolynomial round()
+ {
+ int N = coeffs.length;
+ BigIntPolynomial p = new BigIntPolynomial(N);
+ for (int i = 0; i < N; i++)
+ {
+ p.coeffs[i] = coeffs[i].setScale(0, BigDecimal.ROUND_HALF_EVEN).toBigInteger();
+ }
+ return p;
+ }
+
+ /**
+ * Makes a copy of the polynomial that is independent of the original.
+ */
+ public Object clone()
+ {
+ return new BigDecimalPolynomial(coeffs.clone());
+ }
+
+ private BigDecimal[] copyOf(BigDecimal[] a, int length)
+ {
+ BigDecimal[] tmp = new BigDecimal[length];
+
+ System.arraycopy(a, 0, tmp, 0, a.length < length ? a.length : length);
+
+ return tmp;
+ }
+
+ private BigDecimal[] copyOfRange(BigDecimal[] a, int from, int to)
+ {
+ int newLength = to - from;
+ BigDecimal[] tmp = new BigDecimal[to - from];
+
+ System.arraycopy(a, from, tmp, 0, (a.length - from) < newLength ? (a.length - from) : newLength);
+
+ return tmp;
+ }
+
+ public BigDecimal[] getCoeffs()
+ {
+ BigDecimal[] tmp = new BigDecimal[coeffs.length];
+
+ System.arraycopy(coeffs, 0, tmp, 0, coeffs.length);
+
+ return tmp;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java
new file mode 100644
index 00000000..2869fec8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java
@@ -0,0 +1,394 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * A polynomial with {@link BigInteger} coefficients.<br>
+ * Some methods (like <code>add</code>) change the polynomial, others (like <code>mult</code>) do
+ * not but return the result as a new polynomial.
+ */
+public class BigIntPolynomial
+{
+ private final static double LOG_10_2 = Math.log10(2);
+
+ BigInteger[] coeffs;
+
+ /**
+ * Constructs a new polynomial with <code>N</code> coefficients initialized to 0.
+ *
+ * @param N the number of coefficients
+ */
+ BigIntPolynomial(int N)
+ {
+ coeffs = new BigInteger[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = Constants.BIGINT_ZERO;
+ }
+ }
+
+ /**
+ * Constructs a new polynomial with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ BigIntPolynomial(BigInteger[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ /**
+ * Constructs a <code>BigIntPolynomial</code> from a <code>IntegerPolynomial</code>. The two polynomials are
+ * independent of each other.
+ *
+ * @param p the original polynomial
+ */
+ public BigIntPolynomial(IntegerPolynomial p)
+ {
+ coeffs = new BigInteger[p.coeffs.length];
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = BigInteger.valueOf(p.coeffs[i]);
+ }
+ }
+
+ /**
+ * Generates a random polynomial with <code>numOnes</code> coefficients equal to 1,
+ * <code>numNegOnes</code> coefficients equal to -1, and the rest equal to 0.
+ *
+ * @param N number of coefficients
+ * @param numOnes number of 1's
+ * @param numNegOnes number of -1's
+ * @return
+ */
+ static BigIntPolynomial generateRandomSmall(int N, int numOnes, int numNegOnes)
+ {
+ List coeffs = new ArrayList();
+ for (int i = 0; i < numOnes; i++)
+ {
+ coeffs.add(Constants.BIGINT_ONE);
+ }
+ for (int i = 0; i < numNegOnes; i++)
+ {
+ coeffs.add(BigInteger.valueOf(-1));
+ }
+ while (coeffs.size() < N)
+ {
+ coeffs.add(Constants.BIGINT_ZERO);
+ }
+ Collections.shuffle(coeffs, new SecureRandom());
+
+ BigIntPolynomial poly = new BigIntPolynomial(N);
+ for (int i = 0; i < coeffs.size(); i++)
+ {
+ poly.coeffs[i] = (BigInteger)coeffs.get(i);
+ }
+ return poly;
+ }
+
+ /**
+ * Multiplies the polynomial by another, taking the indices mod N. Does not
+ * change this polynomial but returns the result as a new polynomial.<br>
+ * Both polynomials must have the same number of coefficients.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ public BigIntPolynomial mult(BigIntPolynomial poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ BigIntPolynomial c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = c.coeffs[k - N].add(c.coeffs[k]);
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ }
+ return c;
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private BigIntPolynomial multRecursive(BigIntPolynomial poly2)
+ {
+ BigInteger[] a = coeffs;
+ BigInteger[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 1)
+ {
+ BigInteger[] c = Arrays.clone(coeffs);
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ c[i] = c[i].multiply(poly2.coeffs[0]);
+ }
+ return new BigIntPolynomial(c);
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ BigIntPolynomial a1 = new BigIntPolynomial(Arrays.copyOf(a, n1));
+ BigIntPolynomial a2 = new BigIntPolynomial(Arrays.copyOfRange(a, n1, n));
+ BigIntPolynomial b1 = new BigIntPolynomial(Arrays.copyOf(b, n1));
+ BigIntPolynomial b2 = new BigIntPolynomial(Arrays.copyOfRange(b, n1, n));
+
+ BigIntPolynomial A = (BigIntPolynomial)a1.clone();
+ A.add(a2);
+ BigIntPolynomial B = (BigIntPolynomial)b1.clone();
+ B.add(b2);
+
+ BigIntPolynomial c1 = a1.multRecursive(b1);
+ BigIntPolynomial c2 = a2.multRecursive(b2);
+ BigIntPolynomial c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ BigIntPolynomial c = new BigIntPolynomial(2 * n - 1);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i];
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] = c.coeffs[n1 + i].add(c3.coeffs[i]);
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] = c.coeffs[2 * n1 + i].add(c2.coeffs[i]);
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients,
+ * and takes the coefficient values mod <code>modulus</code>.
+ *
+ * @param b another polynomial
+ */
+ void add(BigIntPolynomial b, BigInteger modulus)
+ {
+ add(b);
+ mod(modulus);
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void add(BigIntPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = Constants.BIGINT_ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].add(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void sub(BigIntPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ int N = coeffs.length;
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ for (int i = N; i < coeffs.length; i++)
+ {
+ coeffs[i] = Constants.BIGINT_ZERO;
+ }
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].subtract(b.coeffs[i]);
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a <code>BigInteger</code>. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param factor
+ */
+ public void mult(BigInteger factor)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].multiply(factor);
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a <code>int</code>. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param factor
+ */
+ void mult(int factor)
+ {
+ mult(BigInteger.valueOf(factor));
+ }
+
+ /**
+ * Divides each coefficient by a <code>BigInteger</code> and rounds the result to the nearest whole number.<br>
+ * Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param divisor the number to divide by
+ */
+ public void div(BigInteger divisor)
+ {
+ BigInteger d = divisor.add(Constants.BIGINT_ONE).divide(BigInteger.valueOf(2));
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].compareTo(Constants.BIGINT_ZERO) > 0 ? coeffs[i].add(d) : coeffs[i].add(d.negate());
+ coeffs[i] = coeffs[i].divide(divisor);
+ }
+ }
+
+ /**
+ * Divides each coefficient by a <code>BigDecimal</code> and rounds the result to <code>decimalPlaces</code> places.
+ *
+ * @param divisor the number to divide by
+ * @param decimalPlaces the number of fractional digits to round the result to
+ * @return a new <code>BigDecimalPolynomial</code>
+ */
+ public BigDecimalPolynomial div(BigDecimal divisor, int decimalPlaces)
+ {
+ BigInteger max = maxCoeffAbs();
+ int coeffLength = (int)(max.bitLength() * LOG_10_2) + 1;
+ // factor = 1/divisor
+ BigDecimal factor = Constants.BIGDEC_ONE.divide(divisor, coeffLength + decimalPlaces + 1, BigDecimal.ROUND_HALF_EVEN);
+
+ // multiply each coefficient by factor
+ BigDecimalPolynomial p = new BigDecimalPolynomial(coeffs.length);
+ for (int i = 0; i < coeffs.length; i++)
+ // multiply, then truncate after decimalPlaces so subsequent operations aren't slowed down
+ {
+ p.coeffs[i] = new BigDecimal(coeffs[i]).multiply(factor).setScale(decimalPlaces, BigDecimal.ROUND_HALF_EVEN);
+ }
+
+ return p;
+ }
+
+ /**
+ * Returns the base10 length of the largest coefficient.
+ *
+ * @return length of the longest coefficient
+ */
+ public int getMaxCoeffLength()
+ {
+ return (int)(maxCoeffAbs().bitLength() * LOG_10_2) + 1;
+ }
+
+ private BigInteger maxCoeffAbs()
+ {
+ BigInteger max = coeffs[0].abs();
+ for (int i = 1; i < coeffs.length; i++)
+ {
+ BigInteger coeff = coeffs[i].abs();
+ if (coeff.compareTo(max) > 0)
+ {
+ max = coeff;
+ }
+ }
+ return max;
+ }
+
+ /**
+ * Takes each coefficient modulo a number.
+ *
+ * @param modulus
+ */
+ public void mod(BigInteger modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = coeffs[i].mod(modulus);
+ }
+ }
+
+ /**
+ * Returns the sum of all coefficients, i.e. evaluates the polynomial at 0.
+ *
+ * @return the sum of all coefficients
+ */
+ BigInteger sumCoeffs()
+ {
+ BigInteger sum = Constants.BIGINT_ZERO;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ sum = sum.add(coeffs[i]);
+ }
+ return sum;
+ }
+
+ /**
+ * Makes a copy of the polynomial that is independent of the original.
+ */
+ public Object clone()
+ {
+ return new BigIntPolynomial(coeffs.clone());
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(coeffs);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ BigIntPolynomial other = (BigIntPolynomial)obj;
+ if (!Arrays.areEqual(coeffs, other.coeffs))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public BigInteger[] getCoeffs()
+ {
+ return Arrays.clone(coeffs);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Constants.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Constants.java
new file mode 100644
index 00000000..f29ce066
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Constants.java
@@ -0,0 +1,12 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public class Constants
+{
+ static final BigInteger BIGINT_ZERO = BigInteger.valueOf(0);
+ static final BigInteger BIGINT_ONE = BigInteger.valueOf(1);
+
+ static final BigDecimal BIGDEC_ONE = BigDecimal.valueOf(1);
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/DenseTernaryPolynomial.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/DenseTernaryPolynomial.java
new file mode 100644
index 00000000..007e7249
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/DenseTernaryPolynomial.java
@@ -0,0 +1,142 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.pqc.math.ntru.util.Util;
+import org.spongycastle.util.Arrays;
+
+/**
+ * A <code>TernaryPolynomial</code> with a "high" number of nonzero coefficients.
+ */
+public class DenseTernaryPolynomial
+ extends IntegerPolynomial
+ implements TernaryPolynomial
+{
+
+ /**
+ * Constructs a new <code>DenseTernaryPolynomial</code> with <code>N</code> coefficients.
+ *
+ * @param N the number of coefficients
+ */
+ DenseTernaryPolynomial(int N)
+ {
+ super(N);
+ checkTernarity();
+ }
+
+ /**
+ * Constructs a <code>DenseTernaryPolynomial</code> from a <code>IntegerPolynomial</code>. The two polynomials are
+ * independent of each other.
+ *
+ * @param intPoly the original polynomial
+ */
+ public DenseTernaryPolynomial(IntegerPolynomial intPoly)
+ {
+ this(intPoly.coeffs);
+ }
+
+ /**
+ * Constructs a new <code>DenseTernaryPolynomial</code> with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ public DenseTernaryPolynomial(int[] coeffs)
+ {
+ super(coeffs);
+ checkTernarity();
+ }
+
+ private void checkTernarity()
+ {
+ for (int i = 0; i != coeffs.length; i++)
+ {
+ int c = coeffs[i];
+ if (c < -1 || c > 1)
+ {
+ throw new IllegalStateException("Illegal value: " + c + ", must be one of {-1, 0, 1}");
+ }
+ }
+ }
+
+ /**
+ * Generates a random polynomial with <code>numOnes</code> coefficients equal to 1,
+ * <code>numNegOnes</code> coefficients equal to -1, and the rest equal to 0.
+ *
+ * @param N number of coefficients
+ * @param numOnes number of 1's
+ * @param numNegOnes number of -1's
+ */
+ public static DenseTernaryPolynomial generateRandom(int N, int numOnes, int numNegOnes, SecureRandom random)
+ {
+ int[] coeffs = Util.generateRandomTernary(N, numOnes, numNegOnes, random);
+ return new DenseTernaryPolynomial(coeffs);
+ }
+
+ /**
+ * Generates a polynomial with coefficients randomly selected from <code>{-1, 0, 1}</code>.
+ *
+ * @param N number of coefficients
+ */
+ public static DenseTernaryPolynomial generateRandom(int N, SecureRandom random)
+ {
+ DenseTernaryPolynomial poly = new DenseTernaryPolynomial(N);
+ for (int i = 0; i < N; i++)
+ {
+ poly.coeffs[i] = random.nextInt(3) - 1;
+ }
+ return poly;
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ // even on 32-bit systems, LongPolynomial5 multiplies faster than IntegerPolynomial
+ if (modulus == 2048)
+ {
+ IntegerPolynomial poly2Pos = (IntegerPolynomial)poly2.clone();
+ poly2Pos.modPositive(2048);
+ LongPolynomial5 poly5 = new LongPolynomial5(poly2Pos);
+ return poly5.mult(this).toIntegerPolynomial();
+ }
+ else
+ {
+ return super.mult(poly2, modulus);
+ }
+ }
+
+ public int[] getOnes()
+ {
+ int N = coeffs.length;
+ int[] ones = new int[N];
+ int onesIdx = 0;
+ for (int i = 0; i < N; i++)
+ {
+ int c = coeffs[i];
+ if (c == 1)
+ {
+ ones[onesIdx++] = i;
+ }
+ }
+ return Arrays.copyOf(ones, onesIdx);
+ }
+
+ public int[] getNegOnes()
+ {
+ int N = coeffs.length;
+ int[] negOnes = new int[N];
+ int negOnesIdx = 0;
+ for (int i = 0; i < N; i++)
+ {
+ int c = coeffs[i];
+ if (c == -1)
+ {
+ negOnes[negOnesIdx++] = i;
+ }
+ }
+ return Arrays.copyOf(negOnes, negOnesIdx);
+ }
+
+ public int size()
+ {
+ return coeffs.length;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/IntegerPolynomial.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/IntegerPolynomial.java
new file mode 100644
index 00000000..41e921bf
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/IntegerPolynomial.java
@@ -0,0 +1,1358 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.spongycastle.pqc.math.ntru.euclid.BigIntEuclidean;
+import org.spongycastle.pqc.math.ntru.util.ArrayEncoder;
+import org.spongycastle.pqc.math.ntru.util.Util;
+import org.spongycastle.util.Arrays;
+
+/**
+ * A polynomial with <code>int</code> coefficients.<br>
+ * Some methods (like <code>add</code>) change the polynomial, others (like <code>mult</code>) do
+ * not but return the result as a new polynomial.
+ */
+public class IntegerPolynomial
+ implements Polynomial
+{
+ private static final int NUM_EQUAL_RESULTANTS = 3;
+ /**
+ * Prime numbers &gt; 4500 for resultant computation. Starting them below ~4400 causes incorrect results occasionally.
+ * Fortunately, 4500 is about the optimum number for performance.<br/>
+ * This array contains enough prime numbers so primes never have to be computed on-line for any standard {@link org.spongycastle.pqc.crypto.ntru.NTRUSigningParameters}.
+ */
+ private static final int[] PRIMES = new int[]{
+ 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583,
+ 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
+ 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751,
+ 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831,
+ 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937,
+ 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003,
+ 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087,
+ 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179,
+ 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279,
+ 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387,
+ 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443,
+ 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521,
+ 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639,
+ 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693,
+ 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791,
+ 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857,
+ 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939,
+ 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053,
+ 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133,
+ 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221,
+ 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301,
+ 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367,
+ 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473,
+ 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571,
+ 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673,
+ 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761,
+ 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833,
+ 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917,
+ 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997,
+ 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103,
+ 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207,
+ 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297,
+ 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411,
+ 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499,
+ 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561,
+ 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643,
+ 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723,
+ 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829,
+ 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919,
+ 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017,
+ 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111,
+ 8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219,
+ 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291,
+ 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387,
+ 8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501,
+ 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597,
+ 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677,
+ 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741,
+ 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831,
+ 8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929,
+ 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011,
+ 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109,
+ 9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199,
+ 9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283,
+ 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377,
+ 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439,
+ 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533,
+ 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631,
+ 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733,
+ 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811,
+ 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887,
+ 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973};
+ private static final List BIGINT_PRIMES;
+
+ static
+ {
+ BIGINT_PRIMES = new ArrayList();
+ for (int i = 0; i != PRIMES.length; i++)
+ {
+ BIGINT_PRIMES.add(BigInteger.valueOf(PRIMES[i]));
+ }
+ }
+
+ public int[] coeffs;
+
+ /**
+ * Constructs a new polynomial with <code>N</code> coefficients initialized to 0.
+ *
+ * @param N the number of coefficients
+ */
+ public IntegerPolynomial(int N)
+ {
+ coeffs = new int[N];
+ }
+
+ /**
+ * Constructs a new polynomial with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ public IntegerPolynomial(int[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ /**
+ * Constructs a <code>IntegerPolynomial</code> from a <code>BigIntPolynomial</code>. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial
+ */
+ public IntegerPolynomial(BigIntPolynomial p)
+ {
+ coeffs = new int[p.coeffs.length];
+ for (int i = 0; i < p.coeffs.length; i++)
+ {
+ coeffs[i] = p.coeffs[i].intValue();
+ }
+ }
+
+ /**
+ * Decodes a byte array to a polynomial with <code>N</code> ternary coefficients<br>
+ * Ignores any excess bytes.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary3Sves(byte[] data, int N)
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeMod3Sves(data, N));
+ }
+
+ /**
+ * Converts a byte array produced by {@link #toBinary3Tight()} to a polynomial.
+ *
+ * @param b a byte array
+ * @param N number of coefficients
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary3Tight(byte[] b, int N)
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeMod3Tight(b, N));
+ }
+
+ /**
+ * Reads data produced by {@link #toBinary3Tight()} from an input stream and converts it to a polynomial.
+ *
+ * @param is an input stream
+ * @param N number of coefficients
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary3Tight(InputStream is, int N)
+ throws IOException
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeMod3Tight(is, N));
+ }
+
+ /**
+ * Returns a polynomial with N coefficients between <code>0</code> and <code>q-1</code>.<br>
+ * <code>q</code> must be a power of 2.<br>
+ * Ignores any excess bytes.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary(byte[] data, int N, int q)
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeModQ(data, N, q));
+ }
+
+ /**
+ * Returns a polynomial with N coefficients between <code>0</code> and <code>q-1</code>.<br>
+ * <code>q</code> must be a power of 2.<br>
+ * Ignores any excess bytes.
+ *
+ * @param is an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return the decoded polynomial
+ */
+ public static IntegerPolynomial fromBinary(InputStream is, int N, int q)
+ throws IOException
+ {
+ return new IntegerPolynomial(ArrayEncoder.decodeModQ(is, N, q));
+ }
+
+ /**
+ * Encodes a polynomial with ternary coefficients to binary.
+ * <code>coeffs[2*i]</code> and <code>coeffs[2*i+1]</code> must not both equal -1 for any integer <code>i</code>,
+ * so this method is only safe to use with polynomials produced by <code>fromBinary3Sves()</code>.
+ *
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary3Sves()
+ {
+ return ArrayEncoder.encodeMod3Sves(coeffs);
+ }
+
+ /**
+ * Converts a polynomial with ternary coefficients to binary.
+ *
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary3Tight()
+ {
+ BigInteger sum = Constants.BIGINT_ZERO;
+ for (int i = coeffs.length - 1; i >= 0; i--)
+ {
+ sum = sum.multiply(BigInteger.valueOf(3));
+ sum = sum.add(BigInteger.valueOf(coeffs[i] + 1));
+ }
+
+ int size = (BigInteger.valueOf(3).pow(coeffs.length).bitLength() + 7) / 8;
+ byte[] arr = sum.toByteArray();
+
+ if (arr.length < size)
+ {
+ // pad with leading zeros so arr.length==size
+ byte[] arr2 = new byte[size];
+ System.arraycopy(arr, 0, arr2, size - arr.length, arr.length);
+ return arr2;
+ }
+
+ if (arr.length > size)
+ // drop sign bit
+ {
+ arr = Arrays.copyOfRange(arr, 1, arr.length);
+ }
+ return arr;
+ }
+
+ /**
+ * Encodes a polynomial whose coefficients are between 0 and q, to binary. q must be a power of 2.
+ *
+ * @param q
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary(int q)
+ {
+ return ArrayEncoder.encodeModQ(coeffs, q);
+ }
+
+ /**
+ * Multiplies the polynomial with another, taking the values mod modulus and the indices mod N
+ */
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ IntegerPolynomial c = mult(poly2);
+ c.mod(modulus);
+ return c;
+ }
+
+ /**
+ * Multiplies the polynomial with another, taking the indices mod N
+ */
+ public IntegerPolynomial mult(IntegerPolynomial poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ IntegerPolynomial c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] += c.coeffs[k];
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ }
+ return c;
+ }
+
+ public BigIntPolynomial mult(BigIntPolynomial poly2)
+ {
+ return new BigIntPolynomial(this).mult(poly2);
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private IntegerPolynomial multRecursive(IntegerPolynomial poly2)
+ {
+ int[] a = coeffs;
+ int[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 32)
+ {
+ int cn = 2 * n - 1;
+ IntegerPolynomial c = new IntegerPolynomial(new int[cn]);
+ for (int k = 0; k < cn; k++)
+ {
+ for (int i = Math.max(0, k - n + 1); i <= Math.min(k, n - 1); i++)
+ {
+ c.coeffs[k] += b[i] * a[k - i];
+ }
+ }
+ return c;
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ IntegerPolynomial a1 = new IntegerPolynomial(Arrays.copyOf(a, n1));
+ IntegerPolynomial a2 = new IntegerPolynomial(Arrays.copyOfRange(a, n1, n));
+ IntegerPolynomial b1 = new IntegerPolynomial(Arrays.copyOf(b, n1));
+ IntegerPolynomial b2 = new IntegerPolynomial(Arrays.copyOfRange(b, n1, n));
+
+ IntegerPolynomial A = (IntegerPolynomial)a1.clone();
+ A.add(a2);
+ IntegerPolynomial B = (IntegerPolynomial)b1.clone();
+ B.add(b2);
+
+ IntegerPolynomial c1 = a1.multRecursive(b1);
+ IntegerPolynomial c2 = a2.multRecursive(b2);
+ IntegerPolynomial c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ IntegerPolynomial c = new IntegerPolynomial(2 * n - 1);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i];
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] += c3.coeffs[i];
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] += c2.coeffs[i];
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Computes the inverse mod <code>q; q</code> must be a power of 2.<br>
+ * Returns <code>null</code> if the polynomial is not invertible.
+ *
+ * @param q the modulus
+ * @return a new polynomial
+ */
+ public IntegerPolynomial invertFq(int q)
+ {
+ int N = coeffs.length;
+ int k = 0;
+ IntegerPolynomial b = new IntegerPolynomial(N + 1);
+ b.coeffs[0] = 1;
+ IntegerPolynomial c = new IntegerPolynomial(N + 1);
+ IntegerPolynomial f = new IntegerPolynomial(N + 1);
+ f.coeffs = Arrays.copyOf(coeffs, N + 1);
+ f.modPositive(2);
+ // set g(x) = x^N − 1
+ IntegerPolynomial g = new IntegerPolynomial(N + 1);
+ g.coeffs[0] = 1;
+ g.coeffs[N] = 1;
+ while (true)
+ {
+ while (f.coeffs[0] == 0)
+ {
+ for (int i = 1; i <= N; i++)
+ {
+ f.coeffs[i - 1] = f.coeffs[i]; // f(x) = f(x) / x
+ c.coeffs[N + 1 - i] = c.coeffs[N - i]; // c(x) = c(x) * x
+ }
+ f.coeffs[N] = 0;
+ c.coeffs[0] = 0;
+ k++;
+ if (f.equalsZero())
+ {
+ return null; // not invertible
+ }
+ }
+ if (f.equalsOne())
+ {
+ break;
+ }
+ if (f.degree() < g.degree())
+ {
+ // exchange f and g
+ IntegerPolynomial temp = f;
+ f = g;
+ g = temp;
+ // exchange b and c
+ temp = b;
+ b = c;
+ c = temp;
+ }
+ f.add(g, 2);
+ b.add(c, 2);
+ }
+
+ if (b.coeffs[N] != 0)
+ {
+ return null;
+ }
+ // Fq(x) = x^(N-k) * b(x)
+ IntegerPolynomial Fq = new IntegerPolynomial(N);
+ int j = 0;
+ k %= N;
+ for (int i = N - 1; i >= 0; i--)
+ {
+ j = i - k;
+ if (j < 0)
+ {
+ j += N;
+ }
+ Fq.coeffs[j] = b.coeffs[i];
+ }
+
+ return mod2ToModq(Fq, q);
+ }
+
+ /**
+ * Computes the inverse mod q from the inverse mod 2
+ *
+ * @param Fq
+ * @param q
+ * @return The inverse of this polynomial mod q
+ */
+ private IntegerPolynomial mod2ToModq(IntegerPolynomial Fq, int q)
+ {
+ if (Util.is64BitJVM() && q == 2048)
+ {
+ LongPolynomial2 thisLong = new LongPolynomial2(this);
+ LongPolynomial2 FqLong = new LongPolynomial2(Fq);
+ int v = 2;
+ while (v < q)
+ {
+ v *= 2;
+ LongPolynomial2 temp = (LongPolynomial2)FqLong.clone();
+ temp.mult2And(v - 1);
+ FqLong = thisLong.mult(FqLong).mult(FqLong);
+ temp.subAnd(FqLong, v - 1);
+ FqLong = temp;
+ }
+ return FqLong.toIntegerPolynomial();
+ }
+ else
+ {
+ int v = 2;
+ while (v < q)
+ {
+ v *= 2;
+ IntegerPolynomial temp = new IntegerPolynomial(Arrays.copyOf(Fq.coeffs, Fq.coeffs.length));
+ temp.mult2(v);
+ Fq = mult(Fq, v).mult(Fq, v);
+ temp.sub(Fq, v);
+ Fq = temp;
+ }
+ return Fq;
+ }
+ }
+
+ /**
+ * Computes the inverse mod 3.
+ * Returns <code>null</code> if the polynomial is not invertible.
+ *
+ * @return a new polynomial
+ */
+ public IntegerPolynomial invertF3()
+ {
+ int N = coeffs.length;
+ int k = 0;
+ IntegerPolynomial b = new IntegerPolynomial(N + 1);
+ b.coeffs[0] = 1;
+ IntegerPolynomial c = new IntegerPolynomial(N + 1);
+ IntegerPolynomial f = new IntegerPolynomial(N + 1);
+ f.coeffs = Arrays.copyOf(coeffs, N + 1);
+ f.modPositive(3);
+ // set g(x) = x^N − 1
+ IntegerPolynomial g = new IntegerPolynomial(N + 1);
+ g.coeffs[0] = -1;
+ g.coeffs[N] = 1;
+ while (true)
+ {
+ while (f.coeffs[0] == 0)
+ {
+ for (int i = 1; i <= N; i++)
+ {
+ f.coeffs[i - 1] = f.coeffs[i]; // f(x) = f(x) / x
+ c.coeffs[N + 1 - i] = c.coeffs[N - i]; // c(x) = c(x) * x
+ }
+ f.coeffs[N] = 0;
+ c.coeffs[0] = 0;
+ k++;
+ if (f.equalsZero())
+ {
+ return null; // not invertible
+ }
+ }
+ if (f.equalsAbsOne())
+ {
+ break;
+ }
+ if (f.degree() < g.degree())
+ {
+ // exchange f and g
+ IntegerPolynomial temp = f;
+ f = g;
+ g = temp;
+ // exchange b and c
+ temp = b;
+ b = c;
+ c = temp;
+ }
+ if (f.coeffs[0] == g.coeffs[0])
+ {
+ f.sub(g, 3);
+ b.sub(c, 3);
+ }
+ else
+ {
+ f.add(g, 3);
+ b.add(c, 3);
+ }
+ }
+
+ if (b.coeffs[N] != 0)
+ {
+ return null;
+ }
+ // Fp(x) = [+-] x^(N-k) * b(x)
+ IntegerPolynomial Fp = new IntegerPolynomial(N);
+ int j = 0;
+ k %= N;
+ for (int i = N - 1; i >= 0; i--)
+ {
+ j = i - k;
+ if (j < 0)
+ {
+ j += N;
+ }
+ Fp.coeffs[j] = f.coeffs[0] * b.coeffs[i];
+ }
+
+ Fp.ensurePositive(3);
+ return Fp;
+ }
+
+ /**
+ * Resultant of this polynomial with <code>x^n-1</code> using a probabilistic algorithm.
+ * <p>
+ * Unlike EESS, this implementation does not compute all resultants modulo primes
+ * such that their product exceeds the maximum possible resultant, but rather stops
+ * when <code>NUM_EQUAL_RESULTANTS</code> consecutive modular resultants are equal.<br>
+ * This means the return value may be incorrect. Experiments show this happens in
+ * about 1 out of 100 cases when <code>N=439</code> and <code>NUM_EQUAL_RESULTANTS=2</code>,
+ * so the likelyhood of leaving the loop too early is <code>(1/100)^(NUM_EQUAL_RESULTANTS-1)</code>.
+ * <p>
+ * Because of the above, callers must verify the output and try a different polynomial if necessary.
+ *
+ * @return <code>(rho, res)</code> satisfying <code>res = rho*this + t*(x^n-1)</code> for some integer <code>t</code>.
+ */
+ public Resultant resultant()
+ {
+ int N = coeffs.length;
+
+ // Compute resultants modulo prime numbers. Continue until NUM_EQUAL_RESULTANTS consecutive modular resultants are equal.
+ LinkedList<ModularResultant> modResultants = new LinkedList<ModularResultant>();
+ BigInteger prime = null;
+ BigInteger pProd = Constants.BIGINT_ONE;
+ BigInteger res = Constants.BIGINT_ONE;
+ int numEqual = 1; // number of consecutive modular resultants equal to each other
+ Iterator<BigInteger> primes = BIGINT_PRIMES.iterator();
+ while (true)
+ {
+ prime = primes.hasNext() ? primes.next() : prime.nextProbablePrime();
+ ModularResultant crr = resultant(prime.intValue());
+ modResultants.add(crr);
+
+ BigInteger temp = pProd.multiply(prime);
+ BigIntEuclidean er = BigIntEuclidean.calculate(prime, pProd);
+ BigInteger resPrev = res;
+ res = res.multiply(er.x.multiply(prime));
+ BigInteger res2 = crr.res.multiply(er.y.multiply(pProd));
+ res = res.add(res2).mod(temp);
+ pProd = temp;
+
+ BigInteger pProd2 = pProd.divide(BigInteger.valueOf(2));
+ BigInteger pProd2n = pProd2.negate();
+ if (res.compareTo(pProd2) > 0)
+ {
+ res = res.subtract(pProd);
+ }
+ else if (res.compareTo(pProd2n) < 0)
+ {
+ res = res.add(pProd);
+ }
+
+ if (res.equals(resPrev))
+ {
+ numEqual++;
+ if (numEqual >= NUM_EQUAL_RESULTANTS)
+ {
+ break;
+ }
+ }
+ else
+ {
+ numEqual = 1;
+ }
+ }
+
+ // Combine modular rho's to obtain the final rho.
+ // For efficiency, first combine all pairs of small resultants to bigger resultants,
+ // then combine pairs of those, etc. until only one is left.
+ while (modResultants.size() > 1)
+ {
+ ModularResultant modRes1 = modResultants.removeFirst();
+ ModularResultant modRes2 = modResultants.removeFirst();
+ ModularResultant modRes3 = ModularResultant.combineRho(modRes1, modRes2);
+ modResultants.addLast(modRes3);
+ }
+ BigIntPolynomial rhoP = modResultants.getFirst().rho;
+
+ BigInteger pProd2 = pProd.divide(BigInteger.valueOf(2));
+ BigInteger pProd2n = pProd2.negate();
+ if (res.compareTo(pProd2) > 0)
+ {
+ res = res.subtract(pProd);
+ }
+ if (res.compareTo(pProd2n) < 0)
+ {
+ res = res.add(pProd);
+ }
+
+ for (int i = 0; i < N; i++)
+ {
+ BigInteger c = rhoP.coeffs[i];
+ if (c.compareTo(pProd2) > 0)
+ {
+ rhoP.coeffs[i] = c.subtract(pProd);
+ }
+ if (c.compareTo(pProd2n) < 0)
+ {
+ rhoP.coeffs[i] = c.add(pProd);
+ }
+ }
+
+ return new Resultant(rhoP, res);
+ }
+
+ /**
+ * Multithreaded version of {@link #resultant()}.
+ *
+ * @return <code>(rho, res)</code> satisfying <code>res = rho*this + t*(x^n-1)</code> for some integer <code>t</code>.
+ */
+ public Resultant resultantMultiThread()
+ {
+ int N = coeffs.length;
+
+ // upper bound for resultant(f, g) = ||f, 2||^deg(g) * ||g, 2||^deg(f) = squaresum(f)^(N/2) * 2^(deg(f)/2) because g(x)=x^N-1
+ // see http://jondalon.mathematik.uni-osnabrueck.de/staff/phpages/brunsw/CompAlg.pdf chapter 3
+ BigInteger max = squareSum().pow((N + 1) / 2);
+ max = max.multiply(BigInteger.valueOf(2).pow((degree() + 1) / 2));
+ BigInteger max2 = max.multiply(BigInteger.valueOf(2));
+
+ // compute resultants modulo prime numbers
+ BigInteger prime = BigInteger.valueOf(10000);
+ BigInteger pProd = Constants.BIGINT_ONE;
+ LinkedBlockingQueue<Future<ModularResultant>> resultantTasks = new LinkedBlockingQueue<Future<ModularResultant>>();
+ Iterator<BigInteger> primes = BIGINT_PRIMES.iterator();
+ ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+ while (pProd.compareTo(max2) < 0)
+ {
+ if (primes.hasNext())
+ {
+ prime = primes.next();
+ }
+ else
+ {
+ prime = prime.nextProbablePrime();
+ }
+ Future<ModularResultant> task = executor.submit(new ModResultantTask(prime.intValue()));
+ resultantTasks.add(task);
+ pProd = pProd.multiply(prime);
+ }
+
+ // Combine modular resultants to obtain the resultant.
+ // For efficiency, first combine all pairs of small resultants to bigger resultants,
+ // then combine pairs of those, etc. until only one is left.
+ ModularResultant overallResultant = null;
+ while (!resultantTasks.isEmpty())
+ {
+ try
+ {
+ Future<ModularResultant> modRes1 = resultantTasks.take();
+ Future<ModularResultant> modRes2 = resultantTasks.poll();
+ if (modRes2 == null)
+ {
+ // modRes1 is the only one left
+ overallResultant = modRes1.get();
+ break;
+ }
+ Future<ModularResultant> newTask = executor.submit(new CombineTask(modRes1.get(), modRes2.get()));
+ resultantTasks.add(newTask);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException(e.toString());
+ }
+ }
+ executor.shutdown();
+ BigInteger res = overallResultant.res;
+ BigIntPolynomial rhoP = overallResultant.rho;
+
+ BigInteger pProd2 = pProd.divide(BigInteger.valueOf(2));
+ BigInteger pProd2n = pProd2.negate();
+
+ if (res.compareTo(pProd2) > 0)
+ {
+ res = res.subtract(pProd);
+ }
+ if (res.compareTo(pProd2n) < 0)
+ {
+ res = res.add(pProd);
+ }
+
+ for (int i = 0; i < N; i++)
+ {
+ BigInteger c = rhoP.coeffs[i];
+ if (c.compareTo(pProd2) > 0)
+ {
+ rhoP.coeffs[i] = c.subtract(pProd);
+ }
+ if (c.compareTo(pProd2n) < 0)
+ {
+ rhoP.coeffs[i] = c.add(pProd);
+ }
+ }
+
+ return new Resultant(rhoP, res);
+ }
+
+ /**
+ * Resultant of this polynomial with <code>x^n-1 mod p</code>.
+ *
+ * @return <code>(rho, res)</code> satisfying <code>res = rho*this + t*(x^n-1) mod p</code> for some integer <code>t</code>.
+ */
+ public ModularResultant resultant(int p)
+ {
+ // Add a coefficient as the following operations involve polynomials of degree deg(f)+1
+ int[] fcoeffs = Arrays.copyOf(coeffs, coeffs.length + 1);
+ IntegerPolynomial f = new IntegerPolynomial(fcoeffs);
+ int N = fcoeffs.length;
+
+ IntegerPolynomial a = new IntegerPolynomial(N);
+ a.coeffs[0] = -1;
+ a.coeffs[N - 1] = 1;
+ IntegerPolynomial b = new IntegerPolynomial(f.coeffs);
+ IntegerPolynomial v1 = new IntegerPolynomial(N);
+ IntegerPolynomial v2 = new IntegerPolynomial(N);
+ v2.coeffs[0] = 1;
+ int da = N - 1;
+ int db = b.degree();
+ int ta = da;
+ int c = 0;
+ int r = 1;
+ while (db > 0)
+ {
+ c = Util.invert(b.coeffs[db], p);
+ c = (c * a.coeffs[da]) % p;
+ a.multShiftSub(b, c, da - db, p);
+ v1.multShiftSub(v2, c, da - db, p);
+
+ da = a.degree();
+ if (da < db)
+ {
+ r *= Util.pow(b.coeffs[db], ta - da, p);
+ r %= p;
+ if (ta % 2 == 1 && db % 2 == 1)
+ {
+ r = (-r) % p;
+ }
+ IntegerPolynomial temp = a;
+ a = b;
+ b = temp;
+ int tempdeg = da;
+ da = db;
+ temp = v1;
+ v1 = v2;
+ v2 = temp;
+ ta = db;
+ db = tempdeg;
+ }
+ }
+ r *= Util.pow(b.coeffs[0], da, p);
+ r %= p;
+ c = Util.invert(b.coeffs[0], p);
+ v2.mult(c);
+ v2.mod(p);
+ v2.mult(r);
+ v2.mod(p);
+
+ // drop the highest coefficient so #coeffs matches the original input
+ v2.coeffs = Arrays.copyOf(v2.coeffs, v2.coeffs.length - 1);
+ return new ModularResultant(new BigIntPolynomial(v2), BigInteger.valueOf(r), BigInteger.valueOf(p));
+ }
+
+ /**
+ * Computes <code>this-b*c*(x^k) mod p</code> and stores the result in this polynomial.<br/>
+ * See steps 4a,4b in EESS algorithm 2.2.7.1.
+ *
+ * @param b
+ * @param c
+ * @param k
+ * @param p
+ */
+ private void multShiftSub(IntegerPolynomial b, int c, int k, int p)
+ {
+ int N = coeffs.length;
+ for (int i = k; i < N; i++)
+ {
+ coeffs[i] = (coeffs[i] - b.coeffs[i - k] * c) % p;
+ }
+ }
+
+ /**
+ * Adds the squares of all coefficients.
+ *
+ * @return the sum of squares
+ */
+ private BigInteger squareSum()
+ {
+ BigInteger sum = Constants.BIGINT_ZERO;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ sum = sum.add(BigInteger.valueOf(coeffs[i] * coeffs[i]));
+ }
+ return sum;
+ }
+
+ /**
+ * Returns the degree of the polynomial
+ *
+ * @return the degree
+ */
+ int degree()
+ {
+ int degree = coeffs.length - 1;
+ while (degree > 0 && coeffs[degree] == 0)
+ {
+ degree--;
+ }
+ return degree;
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients,
+ * and takes the coefficient values mod <code>modulus</code>.
+ *
+ * @param b another polynomial
+ */
+ public void add(IntegerPolynomial b, int modulus)
+ {
+ add(b);
+ mod(modulus);
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void add(IntegerPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] += b.coeffs[i];
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients,
+ * and takes the coefficient values mod <code>modulus</code>.
+ *
+ * @param b another polynomial
+ */
+ public void sub(IntegerPolynomial b, int modulus)
+ {
+ sub(b);
+ mod(modulus);
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ public void sub(IntegerPolynomial b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] -= b.coeffs[i];
+ }
+ }
+
+ /**
+ * Subtracts a <code>int</code> from each coefficient. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param b
+ */
+ void sub(int b)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] -= b;
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a <code>int</code>. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param factor
+ */
+ public void mult(int factor)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] *= factor;
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a 2 and applies a modulus. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param modulus a modulus
+ */
+ private void mult2(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] *= 2;
+ coeffs[i] %= modulus;
+ }
+ }
+
+ /**
+ * Multiplies each coefficient by a 2 and applies a modulus. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param modulus a modulus
+ */
+ public void mult3(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] *= 3;
+ coeffs[i] %= modulus;
+ }
+ }
+
+ /**
+ * Divides each coefficient by <code>k</code> and rounds to the nearest integer. Does not return a new polynomial but modifies this polynomial.
+ *
+ * @param k the divisor
+ */
+ public void div(int k)
+ {
+ int k2 = (k + 1) / 2;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] += coeffs[i] > 0 ? k2 : -k2;
+ coeffs[i] /= k;
+ }
+ }
+
+ /**
+ * Takes each coefficient modulo 3 such that all coefficients are ternary.
+ */
+ public void mod3()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] %= 3;
+ if (coeffs[i] > 1)
+ {
+ coeffs[i] -= 3;
+ }
+ if (coeffs[i] < -1)
+ {
+ coeffs[i] += 3;
+ }
+ }
+ }
+
+ /**
+ * Ensures all coefficients are between 0 and <code>modulus-1</code>
+ *
+ * @param modulus a modulus
+ */
+ public void modPositive(int modulus)
+ {
+ mod(modulus);
+ ensurePositive(modulus);
+ }
+
+ /**
+ * Reduces all coefficients to the interval [-modulus/2, modulus/2)
+ */
+ void modCenter(int modulus)
+ {
+ mod(modulus);
+ for (int j = 0; j < coeffs.length; j++)
+ {
+ while (coeffs[j] < modulus / 2)
+ {
+ coeffs[j] += modulus;
+ }
+ while (coeffs[j] >= modulus / 2)
+ {
+ coeffs[j] -= modulus;
+ }
+ }
+ }
+
+ /**
+ * Takes each coefficient modulo <code>modulus</code>.
+ */
+ public void mod(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] %= modulus;
+ }
+ }
+
+ /**
+ * Adds <code>modulus</code> until all coefficients are above 0.
+ *
+ * @param modulus a modulus
+ */
+ public void ensurePositive(int modulus)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ while (coeffs[i] < 0)
+ {
+ coeffs[i] += modulus;
+ }
+ }
+ }
+
+ /**
+ * Computes the centered euclidean norm of the polynomial.
+ *
+ * @param q a modulus
+ * @return the centered norm
+ */
+ public long centeredNormSq(int q)
+ {
+ int N = coeffs.length;
+ IntegerPolynomial p = (IntegerPolynomial)clone();
+ p.shiftGap(q);
+
+ long sum = 0;
+ long sqSum = 0;
+ for (int i = 0; i != p.coeffs.length; i++)
+ {
+ int c = p.coeffs[i];
+ sum += c;
+ sqSum += c * c;
+ }
+
+ long centeredNormSq = sqSum - sum * sum / N;
+ return centeredNormSq;
+ }
+
+ /**
+ * Shifts all coefficients so the largest gap is centered around <code>-q/2</code>.
+ *
+ * @param q a modulus
+ */
+ void shiftGap(int q)
+ {
+ modCenter(q);
+
+ int[] sorted = Arrays.clone(coeffs);
+
+ sort(sorted);
+
+ int maxrange = 0;
+ int maxrangeStart = 0;
+ for (int i = 0; i < sorted.length - 1; i++)
+ {
+ int range = sorted[i + 1] - sorted[i];
+ if (range > maxrange)
+ {
+ maxrange = range;
+ maxrangeStart = sorted[i];
+ }
+ }
+
+ int pmin = sorted[0];
+ int pmax = sorted[sorted.length - 1];
+
+ int j = q - pmax + pmin;
+ int shift;
+ if (j > maxrange)
+ {
+ shift = (pmax + pmin) / 2;
+ }
+ else
+ {
+ shift = maxrangeStart + maxrange / 2 + q / 2;
+ }
+
+ sub(shift);
+ }
+
+ private void sort(int[] ints)
+ {
+ boolean swap = true;
+
+ while (swap)
+ {
+ swap = false;
+ for (int i = 0; i != ints.length - 1; i++)
+ {
+ if (ints[i] > ints[i+1])
+ {
+ int tmp = ints[i];
+ ints[i] = ints[i+1];
+ ints[i+1] = tmp;
+ swap = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Shifts the values of all coefficients to the interval <code>[-q/2, q/2]</code>.
+ *
+ * @param q a modulus
+ */
+ public void center0(int q)
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ while (coeffs[i] < -q / 2)
+ {
+ coeffs[i] += q;
+ }
+ while (coeffs[i] > q / 2)
+ {
+ coeffs[i] -= q;
+ }
+ }
+ }
+
+ /**
+ * Returns the sum of all coefficients, i.e. evaluates the polynomial at 0.
+ *
+ * @return the sum of all coefficients
+ */
+ public int sumCoeffs()
+ {
+ int sum = 0;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ sum += coeffs[i];
+ }
+ return sum;
+ }
+
+ /**
+ * Tests if <code>p(x) = 0</code>.
+ *
+ * @return true iff all coefficients are zeros
+ */
+ private boolean equalsZero()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ if (coeffs[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tests if <code>p(x) = 1</code>.
+ *
+ * @return true iff all coefficients are equal to zero, except for the lowest coefficient which must equal 1
+ */
+ public boolean equalsOne()
+ {
+ for (int i = 1; i < coeffs.length; i++)
+ {
+ if (coeffs[i] != 0)
+ {
+ return false;
+ }
+ }
+ return coeffs[0] == 1;
+ }
+
+ /**
+ * Tests if <code>|p(x)| = 1</code>.
+ *
+ * @return true iff all coefficients are equal to zero, except for the lowest coefficient which must equal 1 or -1
+ */
+ private boolean equalsAbsOne()
+ {
+ for (int i = 1; i < coeffs.length; i++)
+ {
+ if (coeffs[i] != 0)
+ {
+ return false;
+ }
+ }
+ return Math.abs(coeffs[0]) == 1;
+ }
+
+ /**
+ * Counts the number of coefficients equal to an integer
+ *
+ * @param value an integer
+ * @return the number of coefficients equal to <code>value</code>
+ */
+ public int count(int value)
+ {
+ int count = 0;
+ for (int i = 0; i != coeffs.length; i++)
+ {
+ if (coeffs[i] == value)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Multiplication by <code>X</code> in <code>Z[X]/Z[X^n-1]</code>.
+ */
+ public void rotate1()
+ {
+ int clast = coeffs[coeffs.length - 1];
+ for (int i = coeffs.length - 1; i > 0; i--)
+ {
+ coeffs[i] = coeffs[i - 1];
+ }
+ coeffs[0] = clast;
+ }
+
+ public void clear()
+ {
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = 0;
+ }
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ return (IntegerPolynomial)clone();
+ }
+
+ public Object clone()
+ {
+ return new IntegerPolynomial(coeffs.clone());
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof IntegerPolynomial)
+ {
+ return Arrays.areEqual(coeffs, ((IntegerPolynomial)obj).coeffs);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Calls {@link IntegerPolynomial#resultant(int)
+ */
+ private class ModResultantTask
+ implements Callable<ModularResultant>
+ {
+ private int modulus;
+
+ private ModResultantTask(int modulus)
+ {
+ this.modulus = modulus;
+ }
+
+ public ModularResultant call()
+ {
+ return resultant(modulus);
+ }
+ }
+
+ /**
+ * Calls {@link ModularResultant#combineRho(ModularResultant, ModularResultant)
+ */
+ private class CombineTask
+ implements Callable<ModularResultant>
+ {
+ private ModularResultant modRes1;
+ private ModularResultant modRes2;
+
+ private CombineTask(ModularResultant modRes1, ModularResultant modRes2)
+ {
+ this.modRes1 = modRes1;
+ this.modRes2 = modRes2;
+ }
+
+ public ModularResultant call()
+ {
+ return ModularResultant.combineRho(modRes1, modRes2);
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/LongPolynomial2.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/LongPolynomial2.java
new file mode 100644
index 00000000..513cac50
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/LongPolynomial2.java
@@ -0,0 +1,255 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * A polynomial class that combines two coefficients into one <code>long</code> value for
+ * faster multiplication in 64 bit environments.<br>
+ * Coefficients can be between 0 and 2047 and are stored in pairs in the bits 0..10 and 24..34 of a <code>long</code> number.
+ */
+public class LongPolynomial2
+{
+ private long[] coeffs; // each representing two coefficients in the original IntegerPolynomial
+ private int numCoeffs;
+
+ /**
+ * Constructs a <code>LongPolynomial2</code> from a <code>IntegerPolynomial</code>. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial. Coefficients must be between 0 and 2047.
+ */
+ public LongPolynomial2(IntegerPolynomial p)
+ {
+ numCoeffs = p.coeffs.length;
+ coeffs = new long[(numCoeffs + 1) / 2];
+ int idx = 0;
+ for (int pIdx = 0; pIdx < numCoeffs; )
+ {
+ int c0 = p.coeffs[pIdx++];
+ while (c0 < 0)
+ {
+ c0 += 2048;
+ }
+ long c1 = pIdx < numCoeffs ? p.coeffs[pIdx++] : 0;
+ while (c1 < 0)
+ {
+ c1 += 2048;
+ }
+ coeffs[idx] = c0 + (c1 << 24);
+ idx++;
+ }
+ }
+
+ private LongPolynomial2(long[] coeffs)
+ {
+ this.coeffs = coeffs;
+ }
+
+ private LongPolynomial2(int N)
+ {
+ coeffs = new long[N];
+ }
+
+ /**
+ * Multiplies the polynomial with another, taking the indices mod N and the values mod 2048.
+ */
+ public LongPolynomial2 mult(LongPolynomial2 poly2)
+ {
+ int N = coeffs.length;
+ if (poly2.coeffs.length != N || numCoeffs != poly2.numCoeffs)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ LongPolynomial2 c = multRecursive(poly2);
+
+ if (c.coeffs.length > N)
+ {
+ if (numCoeffs % 2 == 0)
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = (c.coeffs[k - N] + c.coeffs[k]) & 0x7FF0007FFL;
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ }
+ else
+ {
+ for (int k = N; k < c.coeffs.length; k++)
+ {
+ c.coeffs[k - N] = c.coeffs[k - N] + (c.coeffs[k - 1] >> 24);
+ c.coeffs[k - N] = c.coeffs[k - N] + ((c.coeffs[k] & 2047) << 24);
+ c.coeffs[k - N] &= 0x7FF0007FFL;
+ }
+ c.coeffs = Arrays.copyOf(c.coeffs, N);
+ c.coeffs[c.coeffs.length - 1] &= 2047;
+ }
+ }
+
+ c = new LongPolynomial2(c.coeffs);
+ c.numCoeffs = numCoeffs;
+ return c;
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ int[] intCoeffs = new int[numCoeffs];
+ int uIdx = 0;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ intCoeffs[uIdx++] = (int)(coeffs[i] & 2047);
+ if (uIdx < numCoeffs)
+ {
+ intCoeffs[uIdx++] = (int)((coeffs[i] >> 24) & 2047);
+ }
+ }
+ return new IntegerPolynomial(intCoeffs);
+ }
+
+ /**
+ * Karazuba multiplication
+ */
+ private LongPolynomial2 multRecursive(LongPolynomial2 poly2)
+ {
+ long[] a = coeffs;
+ long[] b = poly2.coeffs;
+
+ int n = poly2.coeffs.length;
+ if (n <= 32)
+ {
+ int cn = 2 * n;
+ LongPolynomial2 c = new LongPolynomial2(new long[cn]);
+ for (int k = 0; k < cn; k++)
+ {
+ for (int i = Math.max(0, k - n + 1); i <= Math.min(k, n - 1); i++)
+ {
+ long c0 = a[k - i] * b[i];
+ long cu = c0 & 0x7FF000000L + (c0 & 2047);
+ long co = (c0 >>> 48) & 2047;
+
+ c.coeffs[k] = (c.coeffs[k] + cu) & 0x7FF0007FFL;
+ c.coeffs[k + 1] = (c.coeffs[k + 1] + co) & 0x7FF0007FFL;
+ }
+ }
+ return c;
+ }
+ else
+ {
+ int n1 = n / 2;
+
+ LongPolynomial2 a1 = new LongPolynomial2(Arrays.copyOf(a, n1));
+ LongPolynomial2 a2 = new LongPolynomial2(Arrays.copyOfRange(a, n1, n));
+ LongPolynomial2 b1 = new LongPolynomial2(Arrays.copyOf(b, n1));
+ LongPolynomial2 b2 = new LongPolynomial2(Arrays.copyOfRange(b, n1, n));
+
+ LongPolynomial2 A = (LongPolynomial2)a1.clone();
+ A.add(a2);
+ LongPolynomial2 B = (LongPolynomial2)b1.clone();
+ B.add(b2);
+
+ LongPolynomial2 c1 = a1.multRecursive(b1);
+ LongPolynomial2 c2 = a2.multRecursive(b2);
+ LongPolynomial2 c3 = A.multRecursive(B);
+ c3.sub(c1);
+ c3.sub(c2);
+
+ LongPolynomial2 c = new LongPolynomial2(2 * n);
+ for (int i = 0; i < c1.coeffs.length; i++)
+ {
+ c.coeffs[i] = c1.coeffs[i] & 0x7FF0007FFL;
+ }
+ for (int i = 0; i < c3.coeffs.length; i++)
+ {
+ c.coeffs[n1 + i] = (c.coeffs[n1 + i] + c3.coeffs[i]) & 0x7FF0007FFL;
+ }
+ for (int i = 0; i < c2.coeffs.length; i++)
+ {
+ c.coeffs[2 * n1 + i] = (c.coeffs[2 * n1 + i] + c2.coeffs[i]) & 0x7FF0007FFL;
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Adds another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ private void add(LongPolynomial2 b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = (coeffs[i] + b.coeffs[i]) & 0x7FF0007FFL;
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which can have a different number of coefficients.
+ *
+ * @param b another polynomial
+ */
+ private void sub(LongPolynomial2 b)
+ {
+ if (b.coeffs.length > coeffs.length)
+ {
+ coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
+ }
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = (0x0800000800000L + coeffs[i] - b.coeffs[i]) & 0x7FF0007FFL;
+ }
+ }
+
+ /**
+ * Subtracts another polynomial which must have the same number of coefficients,
+ * and applies an AND mask to the upper and lower halves of each coefficients.
+ *
+ * @param b another polynomial
+ * @param mask a bit mask less than 2048 to apply to each 11-bit coefficient
+ */
+ public void subAnd(LongPolynomial2 b, int mask)
+ {
+ long longMask = (((long)mask) << 24) + mask;
+ for (int i = 0; i < b.coeffs.length; i++)
+ {
+ coeffs[i] = (0x0800000800000L + coeffs[i] - b.coeffs[i]) & longMask;
+ }
+ }
+
+ /**
+ * Multiplies this polynomial by 2 and applies an AND mask to the upper and
+ * lower halves of each coefficients.
+ *
+ * @param mask a bit mask less than 2048 to apply to each 11-bit coefficient
+ */
+ public void mult2And(int mask)
+ {
+ long longMask = (((long)mask) << 24) + mask;
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ coeffs[i] = (coeffs[i] << 1) & longMask;
+ }
+ }
+
+ public Object clone()
+ {
+ LongPolynomial2 p = new LongPolynomial2(coeffs.clone());
+ p.numCoeffs = numCoeffs;
+ return p;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof LongPolynomial2)
+ {
+ return Arrays.areEqual(coeffs, ((LongPolynomial2)obj).coeffs);
+ }
+ else
+ {
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/LongPolynomial5.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/LongPolynomial5.java
new file mode 100644
index 00000000..f8764c68
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/LongPolynomial5.java
@@ -0,0 +1,149 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * A polynomial class that combines five coefficients into one <code>long</code> value for
+ * faster multiplication by a ternary polynomial.<br>
+ * Coefficients can be between 0 and 2047 and are stored in bits 0..11, 12..23, ..., 48..59 of a <code>long</code> number.
+ */
+public class LongPolynomial5
+{
+ private long[] coeffs; // groups of 5 coefficients
+ private int numCoeffs;
+
+ /**
+ * Constructs a <code>LongPolynomial5</code> from a <code>IntegerPolynomial</code>. The two polynomials are independent of each other.
+ *
+ * @param p the original polynomial. Coefficients must be between 0 and 2047.
+ */
+ public LongPolynomial5(IntegerPolynomial p)
+ {
+ numCoeffs = p.coeffs.length;
+
+ coeffs = new long[(numCoeffs + 4) / 5];
+ int cIdx = 0;
+ int shift = 0;
+ for (int i = 0; i < numCoeffs; i++)
+ {
+ coeffs[cIdx] |= ((long)p.coeffs[i]) << shift;
+ shift += 12;
+ if (shift >= 60)
+ {
+ shift = 0;
+ cIdx++;
+ }
+ }
+ }
+
+ private LongPolynomial5(long[] coeffs, int numCoeffs)
+ {
+ this.coeffs = coeffs;
+ this.numCoeffs = numCoeffs;
+ }
+
+ /**
+ * Multiplies the polynomial with a <code>TernaryPolynomial</code>, taking the indices mod N and the values mod 2048.
+ */
+ public LongPolynomial5 mult(TernaryPolynomial poly2)
+ {
+ long[][] prod = new long[5][coeffs.length + (poly2.size() + 4) / 5 - 1]; // intermediate results, the subarrays are shifted by 0,...,4 coefficients
+
+ // multiply ones
+ int[] ones = poly2.getOnes();
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int pIdx = ones[idx];
+ int cIdx = pIdx / 5;
+ int m = pIdx - cIdx * 5; // m = pIdx % 5
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ prod[m][cIdx] = (prod[m][cIdx] + coeffs[i]) & 0x7FF7FF7FF7FF7FFL;
+ cIdx++;
+ }
+ }
+
+ // multiply negative ones
+ int[] negOnes = poly2.getNegOnes();
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int pIdx = negOnes[idx];
+ int cIdx = pIdx / 5;
+ int m = pIdx - cIdx * 5; // m = pIdx % 5
+ for (int i = 0; i < coeffs.length; i++)
+ {
+ prod[m][cIdx] = (0x800800800800800L + prod[m][cIdx] - coeffs[i]) & 0x7FF7FF7FF7FF7FFL;
+ cIdx++;
+ }
+ }
+
+ // combine shifted coefficients (5 arrays) into a single array of length prod[*].length+1
+ long[] cCoeffs = Arrays.copyOf(prod[0], prod[0].length + 1);
+ for (int m = 1; m <= 4; m++)
+ {
+ int shift = m * 12;
+ int shift60 = 60 - shift;
+ long mask = (1L << shift60) - 1;
+ int pLen = prod[m].length;
+ for (int i = 0; i < pLen; i++)
+ {
+ long upper, lower;
+ upper = prod[m][i] >> shift60;
+ lower = prod[m][i] & mask;
+
+ cCoeffs[i] = (cCoeffs[i] + (lower << shift)) & 0x7FF7FF7FF7FF7FFL;
+ int nextIdx = i + 1;
+ cCoeffs[nextIdx] = (cCoeffs[nextIdx] + upper) & 0x7FF7FF7FF7FF7FFL;
+ }
+ }
+
+ // reduce indices of cCoeffs modulo numCoeffs
+ int shift = 12 * (numCoeffs % 5);
+ for (int cIdx = coeffs.length - 1; cIdx < cCoeffs.length; cIdx++)
+ {
+ long iCoeff; // coefficient to shift into the [0..numCoeffs-1] range
+ int newIdx;
+ if (cIdx == coeffs.length - 1)
+ {
+ iCoeff = numCoeffs == 5 ? 0 : cCoeffs[cIdx] >> shift;
+ newIdx = 0;
+ }
+ else
+ {
+ iCoeff = cCoeffs[cIdx];
+ newIdx = cIdx * 5 - numCoeffs;
+ }
+
+ int base = newIdx / 5;
+ int m = newIdx - base * 5; // m = newIdx % 5
+ long lower = iCoeff << (12 * m);
+ long upper = iCoeff >> (12 * (5 - m));
+ cCoeffs[base] = (cCoeffs[base] + lower) & 0x7FF7FF7FF7FF7FFL;
+ int base1 = base + 1;
+ if (base1 < coeffs.length)
+ {
+ cCoeffs[base1] = (cCoeffs[base1] + upper) & 0x7FF7FF7FF7FF7FFL;
+ }
+ }
+
+ return new LongPolynomial5(cCoeffs, numCoeffs);
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ int[] intCoeffs = new int[numCoeffs];
+ int cIdx = 0;
+ int shift = 0;
+ for (int i = 0; i < numCoeffs; i++)
+ {
+ intCoeffs[i] = (int)((coeffs[cIdx] >> shift) & 2047);
+ shift += 12;
+ if (shift >= 60)
+ {
+ shift = 0;
+ cIdx++;
+ }
+ }
+ return new IntegerPolynomial(intCoeffs);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ModularResultant.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ModularResultant.java
new file mode 100644
index 00000000..7e8031fe
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ModularResultant.java
@@ -0,0 +1,46 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigInteger;
+
+import org.spongycastle.pqc.math.ntru.euclid.BigIntEuclidean;
+
+/**
+ * A resultant modulo a <code>BigInteger</code>
+ */
+public class ModularResultant
+ extends Resultant
+{
+ BigInteger modulus;
+
+ ModularResultant(BigIntPolynomial rho, BigInteger res, BigInteger modulus)
+ {
+ super(rho, res);
+ this.modulus = modulus;
+ }
+
+ /**
+ * Calculates a <code>rho</code> modulo <code>m1*m2</code> from
+ * two resultants whose <code>rho</code>s are modulo <code>m1</code> and <code>m2</code>.<br/>
+ * </code>res</code> is set to <code>null</code>.
+ *
+ * @param modRes1
+ * @param modRes2
+ * @return <code>rho</code> modulo <code>modRes1.modulus * modRes2.modulus</code>, and <code>null</code> for </code>res</code>.
+ */
+ static ModularResultant combineRho(ModularResultant modRes1, ModularResultant modRes2)
+ {
+ BigInteger mod1 = modRes1.modulus;
+ BigInteger mod2 = modRes2.modulus;
+ BigInteger prod = mod1.multiply(mod2);
+ BigIntEuclidean er = BigIntEuclidean.calculate(mod2, mod1);
+
+ BigIntPolynomial rho1 = (BigIntPolynomial)modRes1.rho.clone();
+ rho1.mult(er.x.multiply(mod2));
+ BigIntPolynomial rho2 = (BigIntPolynomial)modRes2.rho.clone();
+ rho2.mult(er.y.multiply(mod1));
+ rho1.add(rho2);
+ rho1.mod(prod);
+
+ return new ModularResultant(rho1, null, prod);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Polynomial.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Polynomial.java
new file mode 100644
index 00000000..1d1f1dfb
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Polynomial.java
@@ -0,0 +1,42 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+public interface Polynomial
+{
+
+ /**
+ * Multiplies the polynomial by an <code>IntegerPolynomial</code>,
+ * taking the indices mod <code>N</code>.
+ *
+ * @param poly2 a polynomial
+ * @return the product of the two polynomials
+ */
+ IntegerPolynomial mult(IntegerPolynomial poly2);
+
+ /**
+ * Multiplies the polynomial by an <code>IntegerPolynomial</code>,
+ * taking the coefficient values mod <code>modulus</code> and the indices mod <code>N</code>.
+ *
+ * @param poly2 a polynomial
+ * @param modulus a modulus to apply
+ * @return the product of the two polynomials
+ */
+ IntegerPolynomial mult(IntegerPolynomial poly2, int modulus);
+
+ /**
+ * Returns a polynomial that is equal to this polynomial (in the sense that {@link #mult(IntegerPolynomial, int)}
+ * returns equal <code>IntegerPolynomial</code>s). The new polynomial is guaranteed to be independent of the original.
+ *
+ * @return a new <code>IntegerPolynomial</code>.
+ */
+ IntegerPolynomial toIntegerPolynomial();
+
+ /**
+ * Multiplies the polynomial by a <code>BigIntPolynomial</code>, taking the indices mod N. Does not
+ * change this polynomial but returns the result as a new polynomial.<br>
+ * Both polynomials must have the same number of coefficients.
+ *
+ * @param poly2 the polynomial to multiply by
+ * @return a new polynomial
+ */
+ BigIntPolynomial mult(BigIntPolynomial poly2);
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ProductFormPolynomial.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ProductFormPolynomial.java
new file mode 100644
index 00000000..e60ce8b8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/ProductFormPolynomial.java
@@ -0,0 +1,153 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * A polynomial of the form <code>f1*f2+f3</code>, where
+ * <code>f1,f2,f3</code> are very sparsely populated ternary polynomials.
+ */
+public class ProductFormPolynomial
+ implements Polynomial
+{
+ private SparseTernaryPolynomial f1, f2, f3;
+
+ public ProductFormPolynomial(SparseTernaryPolynomial f1, SparseTernaryPolynomial f2, SparseTernaryPolynomial f3)
+ {
+ this.f1 = f1;
+ this.f2 = f2;
+ this.f3 = f3;
+ }
+
+ public static ProductFormPolynomial generateRandom(int N, int df1, int df2, int df3Ones, int df3NegOnes, SecureRandom random)
+ {
+ SparseTernaryPolynomial f1 = SparseTernaryPolynomial.generateRandom(N, df1, df1, random);
+ SparseTernaryPolynomial f2 = SparseTernaryPolynomial.generateRandom(N, df2, df2, random);
+ SparseTernaryPolynomial f3 = SparseTernaryPolynomial.generateRandom(N, df3Ones, df3NegOnes, random);
+ return new ProductFormPolynomial(f1, f2, f3);
+ }
+
+ public static ProductFormPolynomial fromBinary(byte[] data, int N, int df1, int df2, int df3Ones, int df3NegOnes)
+ throws IOException
+ {
+ return fromBinary(new ByteArrayInputStream(data), N, df1, df2, df3Ones, df3NegOnes);
+ }
+
+ public static ProductFormPolynomial fromBinary(InputStream is, int N, int df1, int df2, int df3Ones, int df3NegOnes)
+ throws IOException
+ {
+ SparseTernaryPolynomial f1;
+
+ f1 = SparseTernaryPolynomial.fromBinary(is, N, df1, df1);
+ SparseTernaryPolynomial f2 = SparseTernaryPolynomial.fromBinary(is, N, df2, df2);
+ SparseTernaryPolynomial f3 = SparseTernaryPolynomial.fromBinary(is, N, df3Ones, df3NegOnes);
+ return new ProductFormPolynomial(f1, f2, f3);
+ }
+
+ public byte[] toBinary()
+ {
+ byte[] f1Bin = f1.toBinary();
+ byte[] f2Bin = f2.toBinary();
+ byte[] f3Bin = f3.toBinary();
+
+ byte[] all = Arrays.copyOf(f1Bin, f1Bin.length + f2Bin.length + f3Bin.length);
+ System.arraycopy(f2Bin, 0, all, f1Bin.length, f2Bin.length);
+ System.arraycopy(f3Bin, 0, all, f1Bin.length + f2Bin.length, f3Bin.length);
+ return all;
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial b)
+ {
+ IntegerPolynomial c = f1.mult(b);
+ c = f2.mult(c);
+ c.add(f3.mult(b));
+ return c;
+ }
+
+ public BigIntPolynomial mult(BigIntPolynomial b)
+ {
+ BigIntPolynomial c = f1.mult(b);
+ c = f2.mult(c);
+ c.add(f3.mult(b));
+ return c;
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ IntegerPolynomial i = f1.mult(f2.toIntegerPolynomial());
+ i.add(f3.toIntegerPolynomial());
+ return i;
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ IntegerPolynomial c = mult(poly2);
+ c.mod(modulus);
+ return c;
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((f1 == null) ? 0 : f1.hashCode());
+ result = prime * result + ((f2 == null) ? 0 : f2.hashCode());
+ result = prime * result + ((f3 == null) ? 0 : f3.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ ProductFormPolynomial other = (ProductFormPolynomial)obj;
+ if (f1 == null)
+ {
+ if (other.f1 != null)
+ {
+ return false;
+ }
+ }
+ else if (!f1.equals(other.f1))
+ {
+ return false;
+ }
+ if (f2 == null)
+ {
+ if (other.f2 != null)
+ {
+ return false;
+ }
+ }
+ else if (!f2.equals(other.f2))
+ {
+ return false;
+ }
+ if (f3 == null)
+ {
+ if (other.f3 != null)
+ {
+ return false;
+ }
+ }
+ else if (!f3.equals(other.f3))
+ {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Resultant.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Resultant.java
new file mode 100644
index 00000000..8fa13328
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/Resultant.java
@@ -0,0 +1,28 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.math.BigInteger;
+
+/**
+ * Contains a resultant and a polynomial <code>rho</code> such that
+ * <code>res = rho*this + t*(x^n-1) for some integer t</code>.
+ *
+ * @see IntegerPolynomial#resultant()
+ * @see IntegerPolynomial#resultant(int)
+ */
+public class Resultant
+{
+ /**
+ * A polynomial such that <code>res = rho*this + t*(x^n-1) for some integer t</code>
+ */
+ public BigIntPolynomial rho;
+ /**
+ * Resultant of a polynomial with <code>x^n-1</code>
+ */
+ public BigInteger res;
+
+ Resultant(BigIntPolynomial rho, BigInteger res)
+ {
+ this.rho = rho;
+ this.res = res;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/SparseTernaryPolynomial.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/SparseTernaryPolynomial.java
new file mode 100644
index 00000000..2fbd70a4
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/SparseTernaryPolynomial.java
@@ -0,0 +1,320 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.pqc.math.ntru.util.ArrayEncoder;
+import org.spongycastle.pqc.math.ntru.util.Util;
+import org.spongycastle.util.Arrays;
+
+/**
+ * A <code>TernaryPolynomial</code> with a "low" number of nonzero coefficients.
+ */
+public class SparseTernaryPolynomial
+ implements TernaryPolynomial
+{
+ /**
+ * Number of bits to use for each coefficient. Determines the upper bound for <code>N</code>.
+ */
+ private static final int BITS_PER_INDEX = 11;
+
+ private int N;
+ private int[] ones;
+ private int[] negOnes;
+
+ /**
+ * Constructs a new polynomial.
+ *
+ * @param N total number of coefficients including zeros
+ * @param ones indices of coefficients equal to 1
+ * @param negOnes indices of coefficients equal to -1
+ */
+ SparseTernaryPolynomial(int N, int[] ones, int[] negOnes)
+ {
+ this.N = N;
+ this.ones = ones;
+ this.negOnes = negOnes;
+ }
+
+ /**
+ * Constructs a <code>DenseTernaryPolynomial</code> from a <code>IntegerPolynomial</code>. The two polynomials are
+ * independent of each other.
+ *
+ * @param intPoly the original polynomial
+ */
+ public SparseTernaryPolynomial(IntegerPolynomial intPoly)
+ {
+ this(intPoly.coeffs);
+ }
+
+ /**
+ * Constructs a new <code>SparseTernaryPolynomial</code> with a given set of coefficients.
+ *
+ * @param coeffs the coefficients
+ */
+ public SparseTernaryPolynomial(int[] coeffs)
+ {
+ N = coeffs.length;
+ ones = new int[N];
+ negOnes = new int[N];
+ int onesIdx = 0;
+ int negOnesIdx = 0;
+ for (int i = 0; i < N; i++)
+ {
+ int c = coeffs[i];
+ switch (c)
+ {
+ case 1:
+ ones[onesIdx++] = i;
+ break;
+ case -1:
+ negOnes[negOnesIdx++] = i;
+ break;
+ case 0:
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal value: " + c + ", must be one of {-1, 0, 1}");
+ }
+ }
+ ones = Arrays.copyOf(ones, onesIdx);
+ negOnes = Arrays.copyOf(negOnes, negOnesIdx);
+ }
+
+ /**
+ * Decodes a byte array encoded with {@link #toBinary()} to a ploynomial.
+ *
+ * @param is an input stream containing an encoded polynomial
+ * @param N number of coefficients including zeros
+ * @param numOnes number of coefficients equal to 1
+ * @param numNegOnes number of coefficients equal to -1
+ * @return the decoded polynomial
+ * @throws IOException
+ */
+ public static SparseTernaryPolynomial fromBinary(InputStream is, int N, int numOnes, int numNegOnes)
+ throws IOException
+ {
+ int maxIndex = 1 << BITS_PER_INDEX;
+ int bitsPerIndex = 32 - Integer.numberOfLeadingZeros(maxIndex - 1);
+
+ int data1Len = (numOnes * bitsPerIndex + 7) / 8;
+ byte[] data1 = Util.readFullLength(is, data1Len);
+ int[] ones = ArrayEncoder.decodeModQ(data1, numOnes, maxIndex);
+
+ int data2Len = (numNegOnes * bitsPerIndex + 7) / 8;
+ byte[] data2 = Util.readFullLength(is, data2Len);
+ int[] negOnes = ArrayEncoder.decodeModQ(data2, numNegOnes, maxIndex);
+
+ return new SparseTernaryPolynomial(N, ones, negOnes);
+ }
+
+ /**
+ * Generates a random polynomial with <code>numOnes</code> coefficients equal to 1,
+ * <code>numNegOnes</code> coefficients equal to -1, and the rest equal to 0.
+ *
+ * @param N number of coefficients
+ * @param numOnes number of 1's
+ * @param numNegOnes number of -1's
+ */
+ public static SparseTernaryPolynomial generateRandom(int N, int numOnes, int numNegOnes, SecureRandom random)
+ {
+ int[] coeffs = Util.generateRandomTernary(N, numOnes, numNegOnes, random);
+ return new SparseTernaryPolynomial(coeffs);
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2)
+ {
+ int[] b = poly2.coeffs;
+ if (b.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ int[] c = new int[N];
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int i = ones[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] += b[j];
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int i = negOnes[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] -= b[j];
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ return new IntegerPolynomial(c);
+ }
+
+ public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
+ {
+ IntegerPolynomial c = mult(poly2);
+ c.mod(modulus);
+ return c;
+ }
+
+ public BigIntPolynomial mult(BigIntPolynomial poly2)
+ {
+ BigInteger[] b = poly2.coeffs;
+ if (b.length != N)
+ {
+ throw new IllegalArgumentException("Number of coefficients must be the same");
+ }
+
+ BigInteger[] c = new BigInteger[N];
+ for (int i = 0; i < N; i++)
+ {
+ c[i] = BigInteger.ZERO;
+ }
+
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int i = ones[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] = c[k].add(b[j]);
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int i = negOnes[idx];
+ int j = N - 1 - i;
+ for (int k = N - 1; k >= 0; k--)
+ {
+ c[k] = c[k].subtract(b[j]);
+ j--;
+ if (j < 0)
+ {
+ j = N - 1;
+ }
+ }
+ }
+
+ return new BigIntPolynomial(c);
+ }
+
+ public int[] getOnes()
+ {
+ return ones;
+ }
+
+ public int[] getNegOnes()
+ {
+ return negOnes;
+ }
+
+ /**
+ * Encodes the polynomial to a byte array writing <code>BITS_PER_INDEX</code> bits for each coefficient.
+ *
+ * @return the encoded polynomial
+ */
+ public byte[] toBinary()
+ {
+ int maxIndex = 1 << BITS_PER_INDEX;
+ byte[] bin1 = ArrayEncoder.encodeModQ(ones, maxIndex);
+ byte[] bin2 = ArrayEncoder.encodeModQ(negOnes, maxIndex);
+
+ byte[] bin = Arrays.copyOf(bin1, bin1.length + bin2.length);
+ System.arraycopy(bin2, 0, bin, bin1.length, bin2.length);
+ return bin;
+ }
+
+ public IntegerPolynomial toIntegerPolynomial()
+ {
+ int[] coeffs = new int[N];
+ for (int idx = 0; idx != ones.length; idx++)
+ {
+ int i = ones[idx];
+ coeffs[i] = 1;
+ }
+ for (int idx = 0; idx != negOnes.length; idx++)
+ {
+ int i = negOnes[idx];
+ coeffs[i] = -1;
+ }
+ return new IntegerPolynomial(coeffs);
+ }
+
+ public int size()
+ {
+ return N;
+ }
+
+ public void clear()
+ {
+ for (int i = 0; i < ones.length; i++)
+ {
+ ones[i] = 0;
+ }
+ for (int i = 0; i < negOnes.length; i++)
+ {
+ negOnes[i] = 0;
+ }
+ }
+
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + N;
+ result = prime * result + Arrays.hashCode(negOnes);
+ result = prime * result + Arrays.hashCode(ones);
+ return result;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ SparseTernaryPolynomial other = (SparseTernaryPolynomial)obj;
+ if (N != other.N)
+ {
+ return false;
+ }
+ if (!Arrays.areEqual(negOnes, other.negOnes))
+ {
+ return false;
+ }
+ if (!Arrays.areEqual(ones, other.ones))
+ {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/TernaryPolynomial.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/TernaryPolynomial.java
new file mode 100644
index 00000000..6b9530b3
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/polynomial/TernaryPolynomial.java
@@ -0,0 +1,25 @@
+package org.spongycastle.pqc.math.ntru.polynomial;
+
+/**
+ * A polynomial whose coefficients are all equal to -1, 0, or 1
+ */
+public interface TernaryPolynomial
+ extends Polynomial
+{
+
+ /**
+ * Multiplies the polynomial by an <code>IntegerPolynomial</code>, taking the indices mod N
+ */
+ IntegerPolynomial mult(IntegerPolynomial poly2);
+
+ int[] getOnes();
+
+ int[] getNegOnes();
+
+ /**
+ * Returns the maximum number of coefficients the polynomial can have
+ */
+ int size();
+
+ void clear();
+}
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/util/ArrayEncoder.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/util/ArrayEncoder.java
new file mode 100644
index 00000000..c4888cfc
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/util/ArrayEncoder.java
@@ -0,0 +1,292 @@
+package org.spongycastle.pqc.math.ntru.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+
+import org.spongycastle.util.Arrays;
+
+/**
+ * Converts a coefficient array to a compact byte array and vice versa.
+ */
+public class ArrayEncoder
+{
+ /**
+ * Bit string to coefficient conversion table from P1363.1. Also found at
+ * {@link http://stackoverflow.com/questions/1562548/how-to-make-a-message-into-a-polynomial}
+ * <p/>
+ * Convert each three-bit quantity to two ternary coefficients as follows, and concatenate the resulting
+ * ternary quantities to obtain [the output].
+ * <p/>
+ * <code>
+ * {0, 0, 0} -> {0, 0}<br/>
+ * {0, 0, 1} -> {0, 1}<br/>
+ * {0, 1, 0} -> {0, -1}<br/>
+ * {0, 1, 1} -> {1, 0}<br/>
+ * {1, 0, 0} -> {1, 1}<br/>
+ * {1, 0, 1} -> {1, -1}<br/>
+ * {1, 1, 0} -> {-1, 0}<br/>
+ * {1, 1, 1} -> {-1, 1}<br/>
+ * </code>
+ */
+ private static final int[] COEFF1_TABLE = {0, 0, 0, 1, 1, 1, -1, -1};
+ private static final int[] COEFF2_TABLE = {0, 1, -1, 0, 1, -1, 0, 1};
+ /**
+ * Coefficient to bit string conversion table from P1363.1. Also found at
+ * {@link http://stackoverflow.com/questions/1562548/how-to-make-a-message-into-a-polynomial}
+ * <p/>
+ * Convert each set of two ternary coefficients to three bits as follows, and concatenate the resulting bit
+ * quantities to obtain [the output]:
+ * <p/>
+ * <code>
+ * {-1, -1} -> set "fail" to 1 and set bit string to {1, 1, 1}
+ * {-1, 0} -> {1, 1, 0}<br/>
+ * {-1, 1} -> {1, 1, 1}<br/>
+ * {0, -1} -> {0, 1, 0}<br/>
+ * {0, 0} -> {0, 0, 0}<br/>
+ * {0, 1} -> {0, 0, 1}<br/>
+ * {1, -1} -> {1, 0, 1}<br/>
+ * {1, 0} -> {0, 1, 1}<br/>
+ * {1, 1} -> {1, 0, 0}<br/>
+ * </code>
+ */
+ private static final int[] BIT1_TABLE = {1, 1, 1, 0, 0, 0, 1, 0, 1};
+ private static final int[] BIT2_TABLE = {1, 1, 1, 1, 0, 0, 0, 1, 0};
+ private static final int[] BIT3_TABLE = {1, 0, 1, 0, 0, 1, 1, 1, 0};
+
+ /**
+ * Encodes an int array whose elements are between 0 and <code>q</code>,
+ * to a byte array leaving no gaps between bits.<br>
+ * <code>q</code> must be a power of 2.
+ *
+ * @param a the input array
+ * @param q the modulus
+ * @return the encoded array
+ */
+ public static byte[] encodeModQ(int[] a, int q)
+ {
+ int bitsPerCoeff = 31 - Integer.numberOfLeadingZeros(q);
+ int numBits = a.length * bitsPerCoeff;
+ int numBytes = (numBits + 7) / 8;
+ byte[] data = new byte[numBytes];
+ int bitIndex = 0;
+ int byteIndex = 0;
+ for (int i = 0; i < a.length; i++)
+ {
+ for (int j = 0; j < bitsPerCoeff; j++)
+ {
+ int currentBit = (a[i] >> j) & 1;
+ data[byteIndex] |= currentBit << bitIndex;
+ if (bitIndex == 7)
+ {
+ bitIndex = 0;
+ byteIndex++;
+ }
+ else
+ {
+ bitIndex++;
+ }
+ }
+ }
+ return data;
+ }
+
+ /**
+ * Decodes a <code>byte</code> array encoded with {@link #encodeModQ(int[], int)} back to an <code>int</code> array.<br>
+ * <code>N</code> is the number of coefficients. <code>q</code> must be a power of <code>2</code>.<br>
+ * Ignores any excess bytes.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return an array containing <code>N</code> coefficients between <code>0</code> and <code>q-1</code>
+ */
+ public static int[] decodeModQ(byte[] data, int N, int q)
+ {
+ int[] coeffs = new int[N];
+ int bitsPerCoeff = 31 - Integer.numberOfLeadingZeros(q);
+ int numBits = N * bitsPerCoeff;
+ int coeffIndex = 0;
+ for (int bitIndex = 0; bitIndex < numBits; bitIndex++)
+ {
+ if (bitIndex > 0 && bitIndex % bitsPerCoeff == 0)
+ {
+ coeffIndex++;
+ }
+ int bit = getBit(data, bitIndex);
+ coeffs[coeffIndex] += bit << (bitIndex % bitsPerCoeff);
+ }
+ return coeffs;
+ }
+
+ /**
+ * Decodes data encoded with {@link #encodeModQ(int[], int)} back to an <code>int</code> array.<br>
+ * <code>N</code> is the number of coefficients. <code>q</code> must be a power of <code>2</code>.<br>
+ * Ignores any excess bytes.
+ *
+ * @param is an encoded ternary polynomial
+ * @param N number of coefficients
+ * @param q
+ * @return the decoded polynomial
+ */
+ public static int[] decodeModQ(InputStream is, int N, int q)
+ throws IOException
+ {
+ int qBits = 31 - Integer.numberOfLeadingZeros(q);
+ int size = (N * qBits + 7) / 8;
+ byte[] arr = Util.readFullLength(is, size);
+ return decodeModQ(arr, N, q);
+ }
+
+ /**
+ * Decodes a <code>byte</code> array encoded with {@link #encodeMod3Sves(int[])} back to an <code>int</code> array
+ * with <code>N</code> coefficients between <code>-1</code> and <code>1</code>.<br>
+ * Ignores any excess bytes.<br>
+ * See P1363.1 section 9.2.2.
+ *
+ * @param data an encoded ternary polynomial
+ * @param N number of coefficients
+ * @return the decoded coefficients
+ */
+ public static int[] decodeMod3Sves(byte[] data, int N)
+ {
+ int[] coeffs = new int[N];
+ int coeffIndex = 0;
+ for (int bitIndex = 0; bitIndex < data.length * 8; )
+ {
+ int bit1 = getBit(data, bitIndex++);
+ int bit2 = getBit(data, bitIndex++);
+ int bit3 = getBit(data, bitIndex++);
+ int coeffTableIndex = bit1 * 4 + bit2 * 2 + bit3;
+ coeffs[coeffIndex++] = COEFF1_TABLE[coeffTableIndex];
+ coeffs[coeffIndex++] = COEFF2_TABLE[coeffTableIndex];
+ // ignore bytes that can't fit
+ if (coeffIndex > N - 2)
+ {
+ break;
+ }
+ }
+ return coeffs;
+ }
+
+ /**
+ * Encodes an <code>int</code> array whose elements are between <code>-1</code> and <code>1</code>, to a byte array.
+ * <code>coeffs[2*i]</code> and <code>coeffs[2*i+1]</code> must not both equal -1 for any integer <code>i</code>,
+ * so this method is only safe to use with arrays produced by {@link #decodeMod3Sves(byte[], int)}.<br>
+ * See P1363.1 section 9.2.3.
+ *
+ * @param arr
+ * @return the encoded array
+ */
+ public static byte[] encodeMod3Sves(int[] arr)
+ {
+ int numBits = (arr.length * 3 + 1) / 2;
+ int numBytes = (numBits + 7) / 8;
+ byte[] data = new byte[numBytes];
+ int bitIndex = 0;
+ int byteIndex = 0;
+ for (int i = 0; i < arr.length / 2 * 2; )
+ { // if length is an odd number, throw away the highest coeff
+ int coeff1 = arr[i++] + 1;
+ int coeff2 = arr[i++] + 1;
+ if (coeff1 == 0 && coeff2 == 0)
+ {
+ throw new IllegalStateException("Illegal encoding!");
+ }
+ int bitTableIndex = coeff1 * 3 + coeff2;
+ int[] bits = new int[]{BIT1_TABLE[bitTableIndex], BIT2_TABLE[bitTableIndex], BIT3_TABLE[bitTableIndex]};
+ for (int j = 0; j < 3; j++)
+ {
+ data[byteIndex] |= bits[j] << bitIndex;
+ if (bitIndex == 7)
+ {
+ bitIndex = 0;
+ byteIndex++;
+ }
+ else
+ {
+ bitIndex++;
+ }
+ }
+ }
+ return data;
+ }
+
+ /**
+ * Encodes an <code>int</code> array whose elements are between <code>-1</code> and <code>1</code>, to a byte array.
+ *
+ * @return the encoded array
+ */
+ public static byte[] encodeMod3Tight(int[] intArray)
+ {
+ BigInteger sum = BigInteger.ZERO;
+ for (int i = intArray.length - 1; i >= 0; i--)
+ {
+ sum = sum.multiply(BigInteger.valueOf(3));
+ sum = sum.add(BigInteger.valueOf(intArray[i] + 1));
+ }
+
+ int size = (BigInteger.valueOf(3).pow(intArray.length).bitLength() + 7) / 8;
+ byte[] arr = sum.toByteArray();
+
+ if (arr.length < size)
+ {
+ // pad with leading zeros so arr.length==size
+ byte[] arr2 = new byte[size];
+ System.arraycopy(arr, 0, arr2, size - arr.length, arr.length);
+ return arr2;
+ }
+
+ if (arr.length > size)
+ // drop sign bit
+ {
+ arr = Arrays.copyOfRange(arr, 1, arr.length);
+ }
+ return arr;
+ }
+
+ /**
+ * Converts a byte array produced by {@link #encodeMod3Tight(int[])} back to an <code>int</code> array.
+ *
+ * @param b a byte array
+ * @param N number of coefficients
+ * @return the decoded array
+ */
+ public static int[] decodeMod3Tight(byte[] b, int N)
+ {
+ BigInteger sum = new BigInteger(1, b);
+ int[] coeffs = new int[N];
+ for (int i = 0; i < N; i++)
+ {
+ coeffs[i] = sum.mod(BigInteger.valueOf(3)).intValue() - 1;
+ if (coeffs[i] > 1)
+ {
+ coeffs[i] -= 3;
+ }
+ sum = sum.divide(BigInteger.valueOf(3));
+ }
+ return coeffs;
+ }
+
+ /**
+ * Converts data produced by {@link #encodeMod3Tight(int[])} back to an <code>int</code> array.
+ *
+ * @param is an input stream containing the data to decode
+ * @param N number of coefficients
+ * @return the decoded array
+ */
+ public static int[] decodeMod3Tight(InputStream is, int N)
+ throws IOException
+ {
+ int size = (int)Math.ceil(N * Math.log(3) / Math.log(2) / 8);
+ byte[] arr = Util.readFullLength(is, size);
+ return decodeMod3Tight(arr, N);
+ }
+
+ private static int getBit(byte[] arr, int bitIndex)
+ {
+ int byteIndex = bitIndex / 8;
+ int arrElem = arr[byteIndex] & 0xFF;
+ return (arrElem >> (bitIndex % 8)) & 1;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/pqc/math/ntru/util/Util.java b/core/src/main/java/org/spongycastle/pqc/math/ntru/util/Util.java
new file mode 100644
index 00000000..b07e1cb8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/pqc/math/ntru/util/Util.java
@@ -0,0 +1,158 @@
+package org.spongycastle.pqc.math.ntru.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.spongycastle.pqc.math.ntru.euclid.IntEuclidean;
+import org.spongycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
+import org.spongycastle.pqc.math.ntru.polynomial.TernaryPolynomial;
+import org.spongycastle.util.Integers;
+
+public class Util
+{
+ private static volatile boolean IS_64_BITNESS_KNOWN;
+ private static volatile boolean IS_64_BIT_JVM;
+
+ /**
+ * Calculates the inverse of n mod modulus
+ */
+ public static int invert(int n, int modulus)
+ {
+ n %= modulus;
+ if (n < 0)
+ {
+ n += modulus;
+ }
+ return IntEuclidean.calculate(n, modulus).x;
+ }
+
+ /**
+ * Calculates a^b mod modulus
+ */
+ public static int pow(int a, int b, int modulus)
+ {
+ int p = 1;
+ for (int i = 0; i < b; i++)
+ {
+ p = (p * a) % modulus;
+ }
+ return p;
+ }
+
+ /**
+ * Calculates a^b mod modulus
+ */
+ public static long pow(long a, int b, long modulus)
+ {
+ long p = 1;
+ for (int i = 0; i < b; i++)
+ {
+ p = (p * a) % modulus;
+ }
+ return p;
+ }
+
+ /**
+ * Generates a "sparse" or "dense" polynomial containing numOnes ints equal to 1,
+ * numNegOnes int equal to -1, and the rest equal to 0.
+ *
+ * @param N
+ * @param numOnes
+ * @param numNegOnes
+ * @param sparse whether to create a {@link SparseTernaryPolynomial} or {@link DenseTernaryPolynomial}
+ * @return a ternary polynomial
+ */
+ public static TernaryPolynomial generateRandomTernary(int N, int numOnes, int numNegOnes, boolean sparse, SecureRandom random)
+ {
+ if (sparse)
+ {
+ return SparseTernaryPolynomial.generateRandom(N, numOnes, numNegOnes, random);
+ }
+ else
+ {
+ return DenseTernaryPolynomial.generateRandom(N, numOnes, numNegOnes, random);
+ }
+ }
+
+ /**
+ * Generates an array containing numOnes ints equal to 1,
+ * numNegOnes int equal to -1, and the rest equal to 0.
+ *
+ * @param N
+ * @param numOnes
+ * @param numNegOnes
+ * @return an array of integers
+ */
+ public static int[] generateRandomTernary(int N, int numOnes, int numNegOnes, SecureRandom random)
+ {
+ Integer one = Integers.valueOf(1);
+ Integer minusOne = Integers.valueOf(-1);
+ Integer zero = Integers.valueOf(0);
+
+ List list = new ArrayList();
+ for (int i = 0; i < numOnes; i++)
+ {
+ list.add(one);
+ }
+ for (int i = 0; i < numNegOnes; i++)
+ {
+ list.add(minusOne);
+ }
+ while (list.size() < N)
+ {
+ list.add(zero);
+ }
+
+ Collections.shuffle(list, random);
+
+ int[] arr = new int[N];
+ for (int i = 0; i < N; i++)
+ {
+ arr[i] = ((Integer)list.get(i)).intValue();
+ }
+ return arr;
+ }
+
+ /**
+ * Takes an educated guess as to whether 64 bits are supported by the JVM.
+ *
+ * @return <code>true</code> if 64-bit support detected, <code>false</code> otherwise
+ */
+ public static boolean is64BitJVM()
+ {
+ if (!IS_64_BITNESS_KNOWN)
+ {
+ String arch = System.getProperty("os.arch");
+ String sunModel = System.getProperty("sun.arch.data.model");
+ IS_64_BIT_JVM = "amd64".equals(arch) || "x86_64".equals(arch) || "ppc64".equals(arch) || "64".equals(sunModel);
+ IS_64_BITNESS_KNOWN = true;
+ }
+ return IS_64_BIT_JVM;
+ }
+
+ /**
+ * Reads a given number of bytes from an <code>InputStream</code>.
+ * If there are not enough bytes in the stream, an <code>IOException</code>
+ * is thrown.
+ *
+ * @param is
+ * @param length
+ * @return an array of length <code>length</code>
+ * @throws IOException
+ */
+ public static byte[] readFullLength(InputStream is, int length)
+ throws IOException
+ {
+ byte[] arr = new byte[length];
+ if (is.read(arr) != arr.length)
+ {
+ throw new IOException("Not enough bytes to read.");
+ }
+ return arr;
+ }
+} \ No newline at end of file