summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergio Giro <sgiro@google.com>2016-02-02 15:27:40 +0000
committerSergio Giro <sgiro@google.com>2016-02-02 15:30:05 +0000
commit11975162f2da08e65157d37cd272721485f2b34b (patch)
treee2d9c4866d82c0e93854d5e9a6200cfce47e6232
parentbdb7b3d37025690a0434040b4e0d0623d9fa74af (diff)
downloadbouncycastle-11975162f2da08e65157d37cd272721485f2b34b.tar.gz
bouncycastle: Android tree with upstream code for version 1.54.
Adding missing files Change-Id: Ife77e8b1df7ec05555b29fb48a984f4c0da2e562
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/path/test/AllTests.java67
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cms/PKCS7ProcessableObject.java65
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cms/PKCS7TypedStream.java62
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransAuthenticatedRecipient.java64
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransEnvelopedRecipient.java51
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java169
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipientInfoGenerator.java115
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cms/jcajce/KeyMaterialGenerator.java8
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cms/jcajce/RFC5753KeyMaterialGenerator.java26
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/mozilla/jcajce/JcaSignedPublicKeyAndChallenge.java81
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/openssl/CertificateTrustBlock.java132
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java57
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/openssl/bc/BcPEMDecryptorProvider.java34
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/openssl/bc/PEMUtilities.java297
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentSignerBuilder.java25
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentVerifierProviderBuilder.java40
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyUnwrapper.java82
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyWrapper.java96
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/tsp/test/GenTimeAccuracyUnitTest.java106
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/tsp/test/TimeStampTokenInfoUnitTest.java161
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java226
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java291
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/DERGraphicString.java124
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/DERVideotexString.java124
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java145
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSINamedCurves.java120
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSIObjectIdentifiers.java11
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAlgorithmProtection.java136
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/cms/GenericHybridParameters.java79
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/cms/RsaKemParameters.java87
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/ECCCMSSharedInfo.java117
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/iso/ISOIECObjectIdentifiers.java35
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/mozilla/SignedPublicKeyAndChallenge.java64
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidator.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidatorException.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java1920
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/x9/DomainParameters.java223
-rw-r--r--bcprov/src/main/java/org/bouncycastle/asn1/x9/ValidationParams.java99
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/CharToByteConverter.java22
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/Xof.java19
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java124
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java580
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java549
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java103
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/OldIESEngine.java61
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java267
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngineBase.java486
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/TnepresEngine.java303
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/EntropyUtil.java46
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java56
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java159
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java363
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RNGUtils.java11
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java305
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SM4Test.java81
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/TnepresTest.java144
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPListener.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPReader.java152
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFCounterTests.java119
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java107
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java88
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackCounterTests.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java89
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueInputStream.java63
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java32
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/ByteQueueInputStreamTest.java127
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestClientProtocol.java30
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestServerProtocol.java17
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolNonBlockingTest.java126
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientProtocol.java31
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerProtocol.java19
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/DERMacData.java114
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/DEROtherInfo.java109
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/DerUtil.java39
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1Key.java67
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1KeyWithParameters.java53
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2Key.java65
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2KeyWithParameters.java53
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/PBKDFKey.java11
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java167
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java45
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java314
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DESUtil.java53
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Blake2b.java114
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Keccak.java195
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java78
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/OpenSSLPBKDF.java86
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java162
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/spec/KTSParameterSpec.java157
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/spec/MQVParameterSpec.java70
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java21
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/util/AlgorithmParametersUtils.java68
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java55
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECIESVectorTest.java236
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/test/KeccakTest.java136
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM4Test.java153
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/test/nist/AllTests.java47
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/test/rsa3/AllTests.java46
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/Primes.java674
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Curve.java80
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Field.java220
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1FieldElement.java199
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Point.java308
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Curve.java77
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Point.java298
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Curve.java80
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Field.java187
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1FieldElement.java202
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Point.java308
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Curve.java80
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Field.java177
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2FieldElement.java217
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Point.java308
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113Field.java226
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113FieldElement.java221
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Curve.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Point.java308
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Curve.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Point.java308
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131Field.java332
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131FieldElement.java222
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Curve.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Point.java308
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Curve.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Point.java308
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163Field.java341
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163FieldElement.java221
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Curve.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Point.java314
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Curve.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Point.java308
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Curve.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Point.java309
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193Field.java306
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193FieldElement.java221
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Curve.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Point.java308
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Curve.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Point.java308
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233Field.java318
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233FieldElement.java221
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Curve.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Point.java323
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Curve.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Point.java309
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239Field.java329
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239FieldElement.java221
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Curve.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Point.java324
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283Field.java404
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283FieldElement.java222
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Curve.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Point.java324
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Curve.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Point.java309
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409Field.java333
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409FieldElement.java222
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Curve.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Point.java324
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Curve.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Point.java309
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571Field.java335
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571FieldElement.java221
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Curve.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Point.java324
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Curve.java105
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Point.java313
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/tools/F2mSqrtOptimizer.java72
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/ec/tools/TraceOptimizer.java138
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/raw/Interleave.java106
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/raw/Nat128.java866
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/raw/Nat160.java878
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/raw/Nat320.java98
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/raw/Nat448.java100
-rw-r--r--bcprov/src/main/java/org/bouncycastle/math/raw/Nat576.java102
-rw-r--r--bcprov/src/main/java/org/bouncycastle/util/Properties.java36
-rw-r--r--bcprov/src/main/java/org/bouncycastle/util/test/TestRandomEntropySourceProvider.java57
177 files changed, 31372 insertions, 0 deletions
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/test/AllTests.java
new file mode 100644
index 00000000..8644cb1d
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/test/AllTests.java
@@ -0,0 +1,67 @@
+package org.bouncycastle.cert.path.test;
+
+import java.security.Security;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.test.SimpleTestResult;
+
+public class AllTests
+ extends TestCase
+{
+ public void testSimpleTests()
+ {
+ org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] { new CertPathTest(), new CertPathValidationTest() };
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ SimpleTestResult result = (SimpleTestResult)tests[i].perform();
+
+ if (!result.isSuccessful())
+ {
+ if (result.getException() != null)
+ {
+ result.getException().printStackTrace();
+ }
+ fail(result.toString());
+ }
+ }
+ }
+
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("Cert Path Tests");
+
+ suite.addTestSuite(AllTests.class);
+
+ return new BCTestSetup(suite);
+ }
+
+ static class BCTestSetup
+ extends TestSetup
+ {
+ public BCTestSetup(Test test)
+ {
+ super(test);
+ }
+
+ protected void setUp()
+ {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ protected void tearDown()
+ {
+ Security.removeProvider("BC");
+ }
+ }
+
+} \ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7ProcessableObject.java b/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7ProcessableObject.java
new file mode 100644
index 00000000..077b2dc6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7ProcessableObject.java
@@ -0,0 +1,65 @@
+package org.bouncycastle.cms;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Iterator;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+
+public class PKCS7ProcessableObject
+ implements CMSTypedData
+{
+ private final ASN1ObjectIdentifier type;
+ private final ASN1Encodable structure;
+
+ public PKCS7ProcessableObject(
+ ASN1ObjectIdentifier type,
+ ASN1Encodable structure)
+ {
+ this.type = type;
+ this.structure = structure;
+ }
+
+ public ASN1ObjectIdentifier getContentType()
+ {
+ return type;
+ }
+
+ public void write(OutputStream cOut)
+ throws IOException, CMSException
+ {
+ if (structure instanceof ASN1Sequence)
+ {
+ ASN1Sequence s = ASN1Sequence.getInstance(structure);
+
+ for (Iterator it = s.iterator(); it.hasNext();)
+ {
+ ASN1Encodable enc = (ASN1Encodable)it.next();
+
+ cOut.write(enc.toASN1Primitive().getEncoded(ASN1Encoding.DER));
+ }
+ }
+ else
+ {
+ byte[] encoded = structure.toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ int index = 1;
+
+ while ((encoded[index] & 0xff) > 127)
+ {
+ index++;
+ }
+
+ index++;
+
+ cOut.write(encoded, index, encoded.length - index);
+ }
+ }
+
+ public Object getContent()
+ {
+ return structure;
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7TypedStream.java b/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7TypedStream.java
new file mode 100644
index 00000000..4128e542
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7TypedStream.java
@@ -0,0 +1,62 @@
+package org.bouncycastle.cms;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public class PKCS7TypedStream
+ extends CMSTypedStream
+{
+ private final ASN1Encodable content;
+
+ public PKCS7TypedStream(ASN1ObjectIdentifier oid, ASN1Encodable encodable)
+ throws IOException
+ {
+ super(oid);
+
+ content = encodable;
+ }
+
+ public ASN1Encodable getContent()
+ {
+ return content;
+ }
+
+ public InputStream getContentStream()
+ {
+ try
+ {
+ return getContentStream(content);
+ }
+ catch (IOException e)
+ {
+ throw new CMSRuntimeException("unable to convert content to stream: " + e.getMessage(), e);
+ }
+ }
+
+ public void drain()
+ throws IOException
+ {
+ getContentStream(content); // this will parse in the data
+ }
+
+ private InputStream getContentStream(ASN1Encodable encodable)
+ throws IOException
+ {
+ byte[] encoded = encodable.toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ int index = 1;
+
+ while ((encoded[index] & 0xff) > 127)
+ {
+ index++;
+ }
+
+ index++;
+
+ return new ByteArrayInputStream(encoded, index, encoded.length - index);
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransAuthenticatedRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransAuthenticatedRecipient.java
new file mode 100644
index 00000000..1d1d2a9a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransAuthenticatedRecipient.java
@@ -0,0 +1,64 @@
+package org.bouncycastle.cms.jcajce;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.PrivateKey;
+
+import javax.crypto.Mac;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.KeyTransRecipientId;
+import org.bouncycastle.cms.RecipientOperator;
+import org.bouncycastle.jcajce.io.MacOutputStream;
+import org.bouncycastle.operator.GenericKey;
+import org.bouncycastle.operator.MacCalculator;
+import org.bouncycastle.operator.jcajce.JceGenericKey;
+
+
+/**
+ * the KeyTransRecipient class for a recipient who has been sent secret
+ * key material encrypted using their public key that needs to be used to
+ * derive a key and authenticate a message.
+ */
+public class JceKTSKeyTransAuthenticatedRecipient
+ extends JceKTSKeyTransRecipient
+{
+ public JceKTSKeyTransAuthenticatedRecipient(PrivateKey recipientKey, KeyTransRecipientId recipientId)
+ throws IOException
+ {
+ super(recipientKey, getPartyVInfoFromRID(recipientId));
+ }
+
+ public RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncryptionAlgorithm, final AlgorithmIdentifier contentMacAlgorithm, byte[] encryptedContentEncryptionKey)
+ throws CMSException
+ {
+ final Key secretKey = extractSecretKey(keyEncryptionAlgorithm, contentMacAlgorithm, encryptedContentEncryptionKey);
+
+ final Mac dataMac = contentHelper.createContentMac(secretKey, contentMacAlgorithm);
+
+ return new RecipientOperator(new MacCalculator()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return contentMacAlgorithm;
+ }
+
+ public GenericKey getKey()
+ {
+ return new JceGenericKey(contentMacAlgorithm, secretKey);
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return new MacOutputStream(dataMac);
+ }
+
+ public byte[] getMac()
+ {
+ return dataMac.doFinal();
+ }
+ });
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransEnvelopedRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransEnvelopedRecipient.java
new file mode 100644
index 00000000..8568f820
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransEnvelopedRecipient.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.cms.jcajce;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Key;
+import java.security.PrivateKey;
+
+import javax.crypto.Cipher;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.KeyTransRecipientId;
+import org.bouncycastle.cms.RecipientOperator;
+import org.bouncycastle.jcajce.io.CipherInputStream;
+import org.bouncycastle.operator.InputDecryptor;
+
+/**
+ * the KeyTransRecipient class for a recipient who has been sent secret
+ * key material encrypted using their public key that needs to be used to
+ * derive a key and extract a message.
+ */
+public class JceKTSKeyTransEnvelopedRecipient
+ extends JceKTSKeyTransRecipient
+{
+ public JceKTSKeyTransEnvelopedRecipient(PrivateKey recipientKey, KeyTransRecipientId recipientId)
+ throws IOException
+ {
+ super(recipientKey, getPartyVInfoFromRID(recipientId));
+ }
+
+ public RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncryptionAlgorithm, final AlgorithmIdentifier contentEncryptionAlgorithm, byte[] encryptedContentEncryptionKey)
+ throws CMSException
+ {
+ Key secretKey = extractSecretKey(keyEncryptionAlgorithm, contentEncryptionAlgorithm, encryptedContentEncryptionKey);
+
+ final Cipher dataCipher = contentHelper.createContentCipher(secretKey, contentEncryptionAlgorithm);
+
+ return new RecipientOperator(new InputDecryptor()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return contentEncryptionAlgorithm;
+ }
+
+ public InputStream getInputStream(InputStream dataIn)
+ {
+ return new CipherInputStream(dataIn, dataCipher);
+ }
+ });
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java
new file mode 100644
index 00000000..01c57912
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java
@@ -0,0 +1,169 @@
+package org.bouncycastle.cms.jcajce;
+
+import java.io.IOException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.KeyTransRecipient;
+import org.bouncycastle.cms.KeyTransRecipientId;
+import org.bouncycastle.operator.OperatorException;
+import org.bouncycastle.operator.jcajce.JceKTSKeyUnwrapper;
+import org.bouncycastle.util.encoders.Hex;
+
+public abstract class JceKTSKeyTransRecipient
+ implements KeyTransRecipient
+{
+ private static final byte[] ANONYMOUS_SENDER = Hex.decode("0c14416e6f6e796d6f75732053656e64657220202020"); // "Anonymous Sender "
+ private final byte[] partyVInfo;
+
+ private PrivateKey recipientKey;
+
+ protected EnvelopedDataHelper helper = new EnvelopedDataHelper(new DefaultJcaJceExtHelper());
+ protected EnvelopedDataHelper contentHelper = helper;
+ protected Map extraMappings = new HashMap();
+ protected boolean validateKeySize = false;
+ protected boolean unwrappedKeyMustBeEncodable;
+
+ public JceKTSKeyTransRecipient(PrivateKey recipientKey, byte[] partyVInfo)
+ {
+ this.recipientKey = recipientKey;
+ this.partyVInfo = partyVInfo;
+ }
+
+ /**
+ * Set the provider to use for key recovery and content processing.
+ *
+ * @param provider provider to use.
+ * @return this recipient.
+ */
+ public JceKTSKeyTransRecipient setProvider(Provider provider)
+ {
+ this.helper = new EnvelopedDataHelper(new ProviderJcaJceExtHelper(provider));
+ this.contentHelper = helper;
+
+ return this;
+ }
+
+ /**
+ * Set the provider to use for key recovery and content processing.
+ *
+ * @param providerName the name of the provider to use.
+ * @return this recipient.
+ */
+ public JceKTSKeyTransRecipient setProvider(String providerName)
+ {
+ this.helper = new EnvelopedDataHelper(new NamedJcaJceExtHelper(providerName));
+ this.contentHelper = helper;
+
+ return this;
+ }
+
+ /**
+ * Internally algorithm ids are converted into cipher names using a lookup table. For some providers
+ * the standard lookup table won't work. Use this method to establish a specific mapping from an
+ * algorithm identifier to a specific algorithm.
+ * <p>
+ * For example:
+ * <pre>
+ * unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA");
+ * </pre>
+ * </p>
+ * @param algorithm OID of algorithm in recipient.
+ * @param algorithmName JCE algorithm name to use.
+ * @return the current Recipient.
+ */
+ public JceKTSKeyTransRecipient setAlgorithmMapping(ASN1ObjectIdentifier algorithm, String algorithmName)
+ {
+ extraMappings.put(algorithm, algorithmName);
+
+ return this;
+ }
+
+ /**
+ * Set the provider to use for content processing. If providerName is null a "no provider" search will be
+ * used to satisfy getInstance calls.
+ *
+ * @param provider the provider to use.
+ * @return this recipient.
+ */
+ public JceKTSKeyTransRecipient setContentProvider(Provider provider)
+ {
+ this.contentHelper = CMSUtils.createContentHelper(provider);
+
+ return this;
+ }
+
+ /**
+ * Set the provider to use for content processing. If providerName is null a "no provider" search will be
+ * used to satisfy getInstance calls.
+ *
+ * @param providerName the name of the provider to use.
+ * @return this recipient.
+ */
+ public JceKTSKeyTransRecipient setContentProvider(String providerName)
+ {
+ this.contentHelper = CMSUtils.createContentHelper(providerName);
+
+ return this;
+ }
+
+ /**
+ * Set validation of retrieved key sizes against the algorithm parameters for the encrypted key where possible - default is off.
+ * <p>
+ * This setting will not have any affect if the encryption algorithm in the recipient does not specify a particular key size, or
+ * if the unwrapper is a HSM and the byte encoding of the unwrapped secret key is not available.
+ * </p>
+ * @param doValidate true if unwrapped key's should be validated against the content encryption algorithm, false otherwise.
+ * @return this recipient.
+ */
+ public JceKTSKeyTransRecipient setKeySizeValidation(boolean doValidate)
+ {
+ this.validateKeySize = doValidate;
+
+ return this;
+ }
+
+ protected Key extractSecretKey(AlgorithmIdentifier keyEncryptionAlgorithm, AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedEncryptionKey)
+ throws CMSException
+ {
+ JceKTSKeyUnwrapper unwrapper = helper.createAsymmetricUnwrapper(keyEncryptionAlgorithm, recipientKey, ANONYMOUS_SENDER, partyVInfo);
+
+ try
+ {
+ Key key = helper.getJceKey(encryptedKeyAlgorithm.getAlgorithm(), unwrapper.generateUnwrappedKey(encryptedKeyAlgorithm, encryptedEncryptionKey));
+
+ if (validateKeySize)
+ {
+ helper.keySizeCheck(encryptedKeyAlgorithm, key);
+ }
+
+ return key;
+ }
+ catch (OperatorException e)
+ {
+ throw new CMSException("exception unwrapping key: " + e.getMessage(), e);
+ }
+ }
+
+ protected static byte[] getPartyVInfoFromRID(KeyTransRecipientId recipientId)
+ throws IOException
+ {
+ if (recipientId.getSerialNumber() != null)
+ {
+ return new IssuerAndSerialNumber(recipientId.getIssuer(), recipientId.getSerialNumber()).getEncoded(ASN1Encoding.DER);
+ }
+ else
+ {
+ return new DEROctetString(recipientId.getSubjectKeyIdentifier()).getEncoded();
+ }
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipientInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipientInfoGenerator.java
new file mode 100644
index 00000000..7a440b4f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipientInfoGenerator.java
@@ -0,0 +1,115 @@
+package org.bouncycastle.cms.jcajce;
+
+import java.io.IOException;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.cms.KeyTransRecipientInfoGenerator;
+import org.bouncycastle.operator.jcajce.JceAsymmetricKeyWrapper;
+import org.bouncycastle.operator.jcajce.JceKTSKeyWrapper;
+import org.bouncycastle.util.encoders.Hex;
+
+public class JceKTSKeyTransRecipientInfoGenerator
+ extends KeyTransRecipientInfoGenerator
+{
+ private static final byte[] ANONYMOUS_SENDER = Hex.decode("0c14416e6f6e796d6f75732053656e64657220202020"); // "Anonymous Sender "
+
+ private JceKTSKeyTransRecipientInfoGenerator(X509Certificate recipientCert, IssuerAndSerialNumber recipientID, String symmetricWrappingAlg, int keySizeInBits)
+ throws CertificateEncodingException
+ {
+ super(recipientID, new JceKTSKeyWrapper(recipientCert, symmetricWrappingAlg, keySizeInBits, ANONYMOUS_SENDER, getEncodedRecipID(recipientID)));
+ }
+
+ public JceKTSKeyTransRecipientInfoGenerator(X509Certificate recipientCert, String symmetricWrappingAlg, int keySizeInBits)
+ throws CertificateEncodingException
+ {
+ this(recipientCert, new IssuerAndSerialNumber(new JcaX509CertificateHolder(recipientCert).toASN1Structure()), symmetricWrappingAlg, keySizeInBits);
+ }
+
+ public JceKTSKeyTransRecipientInfoGenerator(byte[] subjectKeyIdentifier, PublicKey publicKey, String symmetricWrappingAlg, int keySizeInBits)
+ {
+ super(subjectKeyIdentifier, new JceKTSKeyWrapper(publicKey, symmetricWrappingAlg, keySizeInBits, ANONYMOUS_SENDER, getEncodedSubKeyId(subjectKeyIdentifier)));
+ }
+
+ private static byte[] getEncodedRecipID(IssuerAndSerialNumber recipientID)
+ throws CertificateEncodingException
+ {
+ try
+ {
+ return recipientID.getEncoded(ASN1Encoding.DER);
+ }
+ catch (final IOException e)
+ {
+ throw new CertificateEncodingException("Cannot process extracted IssuerAndSerialNumber: " + e.getMessage())
+ {
+ public Throwable getCause()
+ {
+ return e;
+ }
+ };
+ }
+ }
+
+ private static byte[] getEncodedSubKeyId(byte[] subjectKeyIdentifier)
+ {
+ try
+ {
+ return new DEROctetString(subjectKeyIdentifier).getEncoded();
+ }
+ catch (final IOException e)
+ {
+ throw new IllegalArgumentException("Cannot process subject key identifier: " + e.getMessage())
+ {
+ public Throwable getCause()
+ {
+ return e;
+ }
+ };
+ }
+ }
+
+ /**
+ * Create a generator overriding the algorithm type implied by the public key in the certificate passed in.
+ *
+ * @param recipientCert certificate carrying the public key.
+ * @param algorithmIdentifier the identifier and parameters for the encryption algorithm to be used.
+ */
+ public JceKTSKeyTransRecipientInfoGenerator(X509Certificate recipientCert, AlgorithmIdentifier algorithmIdentifier)
+ throws CertificateEncodingException
+ {
+ super(new IssuerAndSerialNumber(new JcaX509CertificateHolder(recipientCert).toASN1Structure()), new JceAsymmetricKeyWrapper(algorithmIdentifier, recipientCert.getPublicKey()));
+ }
+
+ /**
+ * Create a generator overriding the algorithm type implied by the public key passed in.
+ *
+ * @param subjectKeyIdentifier the subject key identifier value to associate with the public key.
+ * @param algorithmIdentifier the identifier and parameters for the encryption algorithm to be used.
+ * @param publicKey the public key to use.
+ */
+ public JceKTSKeyTransRecipientInfoGenerator(byte[] subjectKeyIdentifier, AlgorithmIdentifier algorithmIdentifier, PublicKey publicKey)
+ {
+ super(subjectKeyIdentifier, new JceAsymmetricKeyWrapper(algorithmIdentifier, publicKey));
+ }
+
+ public JceKTSKeyTransRecipientInfoGenerator setProvider(String providerName)
+ {
+ ((JceKTSKeyWrapper)this.wrapper).setProvider(providerName);
+
+ return this;
+ }
+
+ public JceKTSKeyTransRecipientInfoGenerator setProvider(Provider provider)
+ {
+ ((JceKTSKeyWrapper)this.wrapper).setProvider(provider);
+
+ return this;
+ }
+} \ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/KeyMaterialGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/KeyMaterialGenerator.java
new file mode 100644
index 00000000..b5170f2f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/KeyMaterialGenerator.java
@@ -0,0 +1,8 @@
+package org.bouncycastle.cms.jcajce;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+interface KeyMaterialGenerator
+{
+ byte[] generateKDFMaterial(AlgorithmIdentifier keyAlgorithm, int keySize, byte[] userKeyMaterialParameters);
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/RFC5753KeyMaterialGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/RFC5753KeyMaterialGenerator.java
new file mode 100644
index 00000000..a07eedb1
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/RFC5753KeyMaterialGenerator.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.cms.jcajce;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.cms.ecc.ECCCMSSharedInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.util.Pack;
+
+class RFC5753KeyMaterialGenerator
+ implements KeyMaterialGenerator
+{
+ public byte[] generateKDFMaterial(AlgorithmIdentifier keyAlgorithm, int keySize, byte[] userKeyMaterialParameters)
+ {
+ ECCCMSSharedInfo eccInfo = new ECCCMSSharedInfo(keyAlgorithm, userKeyMaterialParameters, Pack.intToBigEndian(keySize));
+
+ try
+ {
+ return eccInfo.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Unable to create KDF material: " + e);
+ }
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mozilla/jcajce/JcaSignedPublicKeyAndChallenge.java b/bcpkix/src/main/java/org/bouncycastle/mozilla/jcajce/JcaSignedPublicKeyAndChallenge.java
new file mode 100644
index 00000000..83440fff
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mozilla/jcajce/JcaSignedPublicKeyAndChallenge.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.mozilla.jcajce;
+
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
+import org.bouncycastle.jcajce.util.JcaJceHelper;
+import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
+import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
+import org.bouncycastle.mozilla.SignedPublicKeyAndChallenge;
+
+/**
+ * This is designed to parse the SignedPublicKeyAndChallenge created by the
+ * KEYGEN tag included by Mozilla based browsers.
+ * <pre>
+ * PublicKeyAndChallenge ::= SEQUENCE {
+ * spki SubjectPublicKeyInfo,
+ * challenge IA5STRING
+ * }
+ *
+ * SignedPublicKeyAndChallenge ::= SEQUENCE {
+ * publicKeyAndChallenge PublicKeyAndChallenge,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING
+ * }
+ * </pre>
+ */
+public class JcaSignedPublicKeyAndChallenge
+ extends SignedPublicKeyAndChallenge
+{
+ JcaJceHelper helper = new DefaultJcaJceHelper();
+
+ private JcaSignedPublicKeyAndChallenge(org.bouncycastle.asn1.mozilla.SignedPublicKeyAndChallenge struct, JcaJceHelper helper)
+ {
+ super(struct);
+ this.helper = helper;
+ }
+
+ public JcaSignedPublicKeyAndChallenge(byte[] bytes)
+ {
+ super(bytes);
+ }
+
+ public JcaSignedPublicKeyAndChallenge setProvider(String providerName)
+ {
+ return new JcaSignedPublicKeyAndChallenge(this.spkacSeq, new NamedJcaJceHelper(providerName));
+ }
+
+ public JcaSignedPublicKeyAndChallenge setProvider(Provider provider)
+ {
+ return new JcaSignedPublicKeyAndChallenge(this.spkacSeq, new ProviderJcaJceHelper(provider));
+ }
+
+ public PublicKey getPublicKey()
+ throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
+ {
+ try
+ {
+ SubjectPublicKeyInfo subjectPublicKeyInfo = spkacSeq.getPublicKeyAndChallenge().getSubjectPublicKeyInfo();
+ X509EncodedKeySpec xspec = new X509EncodedKeySpec(subjectPublicKeyInfo.getEncoded());
+
+
+ AlgorithmIdentifier keyAlg = subjectPublicKeyInfo.getAlgorithm();
+
+ KeyFactory factory = helper.createKeyFactory(keyAlg.getAlgorithm().getId());
+
+ return factory.generatePublic(xspec);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidKeyException("error encoding public key");
+ }
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/CertificateTrustBlock.java b/bcpkix/src/main/java/org/bouncycastle/openssl/CertificateTrustBlock.java
new file mode 100644
index 00000000..9a76084b
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/CertificateTrustBlock.java
@@ -0,0 +1,132 @@
+package org.bouncycastle.openssl;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.DERUTF8String;
+
+public class CertificateTrustBlock
+{
+ private ASN1Sequence uses;
+ private ASN1Sequence prohibitions;
+ private String alias;
+
+ public CertificateTrustBlock(Set<ASN1ObjectIdentifier> uses)
+ {
+ this(null, uses, null);
+ }
+
+ public CertificateTrustBlock(String alias, Set<ASN1ObjectIdentifier> uses)
+ {
+ this(alias, uses, null);
+ }
+
+ public CertificateTrustBlock(String alias, Set<ASN1ObjectIdentifier> uses, Set<ASN1ObjectIdentifier> prohibitions)
+ {
+ this.alias = alias;
+ this.uses = toSequence(uses);
+ this.prohibitions = toSequence(prohibitions);
+ }
+
+ CertificateTrustBlock(byte[] encoded)
+ {
+ ASN1Sequence seq = ASN1Sequence.getInstance(encoded);
+
+ for (Enumeration en = seq.getObjects(); en.hasMoreElements();)
+ {
+ ASN1Encodable obj = (ASN1Encodable)en.nextElement();
+
+ if (obj instanceof ASN1Sequence)
+ {
+ this.uses = ASN1Sequence.getInstance(obj);
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ this.prohibitions = ASN1Sequence.getInstance((ASN1TaggedObject)obj, false);
+ }
+ else if (obj instanceof DERUTF8String)
+ {
+ this.alias = DERUTF8String.getInstance(obj).getString();
+ }
+ }
+ }
+
+ public String getAlias()
+ {
+ return alias;
+ }
+
+ public Set<ASN1ObjectIdentifier> getUses()
+ {
+ return toSet(uses);
+ }
+
+ public Set<ASN1ObjectIdentifier> getProhibitions()
+ {
+ return toSet(prohibitions);
+ }
+
+ private Set<ASN1ObjectIdentifier> toSet(ASN1Sequence seq)
+ {
+ if (seq != null)
+ {
+ Set<ASN1ObjectIdentifier> oids = new HashSet<ASN1ObjectIdentifier>(seq.size());
+
+ for (Enumeration en = seq.getObjects(); en.hasMoreElements(); )
+ {
+ oids.add(ASN1ObjectIdentifier.getInstance(en.nextElement()));
+ }
+
+ return oids;
+ }
+
+ return Collections.EMPTY_SET;
+ }
+
+ private ASN1Sequence toSequence(Set<ASN1ObjectIdentifier> oids)
+ {
+ if (oids == null || oids.isEmpty())
+ {
+ return null;
+ }
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (Iterator it = oids.iterator(); it.hasNext();)
+ {
+ v.add((ASN1Encodable)it.next());
+ }
+
+ return new DERSequence(v);
+ }
+
+ ASN1Sequence toASN1Sequence()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (uses != null)
+ {
+ v.add(uses);
+ }
+ if (prohibitions != null)
+ {
+ v.add(new DERTaggedObject(false, 0, prohibitions));
+ }
+ if (alias != null)
+ {
+ v.add(new DERUTF8String(alias));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java b/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java
new file mode 100644
index 00000000..709af31a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.openssl;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Holder for an OpenSSL trusted certificate block.
+ */
+public class X509TrustedCertificateBlock
+{
+ private final X509CertificateHolder certificateHolder;
+ private final CertificateTrustBlock trustBlock;
+
+ public X509TrustedCertificateBlock(X509CertificateHolder certificateHolder, CertificateTrustBlock trustBlock)
+ {
+ this.certificateHolder = certificateHolder;
+ this.trustBlock = trustBlock;
+ }
+
+ public X509TrustedCertificateBlock(byte[] encoding)
+ throws IOException
+ {
+ ASN1InputStream aIn = new ASN1InputStream(encoding);
+
+ this.certificateHolder = new X509CertificateHolder(aIn.readObject().getEncoded());
+ this.trustBlock = new CertificateTrustBlock(aIn.readObject().getEncoded());
+ }
+
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return Arrays.concatenate(certificateHolder.getEncoded(), trustBlock.toASN1Sequence().getEncoded());
+ }
+
+ /**
+ * Return the certificate associated with this Trusted Certificate
+ *
+ * @return the certificate holder.
+ */
+ public X509CertificateHolder getCertificateHolder()
+ {
+ return certificateHolder;
+ }
+
+ /**
+ * Return the trust block associated with this Trusted Certificate
+ *
+ * @return the trust block.
+ */
+ public CertificateTrustBlock getTrustBlock()
+ {
+ return trustBlock;
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/bc/BcPEMDecryptorProvider.java b/bcpkix/src/main/java/org/bouncycastle/openssl/bc/BcPEMDecryptorProvider.java
new file mode 100644
index 00000000..d387da8f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/bc/BcPEMDecryptorProvider.java
@@ -0,0 +1,34 @@
+package org.bouncycastle.openssl.bc;
+
+import org.bouncycastle.openssl.PEMDecryptor;
+import org.bouncycastle.openssl.PEMDecryptorProvider;
+import org.bouncycastle.openssl.PEMException;
+import org.bouncycastle.openssl.PasswordException;
+
+public class BcPEMDecryptorProvider
+ implements PEMDecryptorProvider
+{
+ private final char[] password;
+
+ public BcPEMDecryptorProvider(char[] password)
+ {
+ this.password = password;
+ }
+
+ public PEMDecryptor get(final String dekAlgName)
+ {
+ return new PEMDecryptor()
+ {
+ public byte[] decrypt(byte[] keyBytes, byte[] iv)
+ throws PEMException
+ {
+ if (password == null)
+ {
+ throw new PasswordException("Password is null, but a password is required");
+ }
+
+ return PEMUtilities.crypt(false, keyBytes, password, dekAlgName, iv);
+ }
+ };
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/bc/PEMUtilities.java b/bcpkix/src/main/java/org/bouncycastle/openssl/bc/PEMUtilities.java
new file mode 100644
index 00000000..e5829011
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/bc/PEMUtilities.java
@@ -0,0 +1,297 @@
+package org.bouncycastle.openssl.bc;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.PBEParametersGenerator;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.BlowfishEngine;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.engines.RC2Engine;
+import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
+import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+import org.bouncycastle.crypto.paddings.BlockCipherPadding;
+import org.bouncycastle.crypto.paddings.PKCS7Padding;
+import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.RC2Parameters;
+import org.bouncycastle.openssl.EncryptionException;
+import org.bouncycastle.openssl.PEMException;
+import org.bouncycastle.util.Integers;
+
+class PEMUtilities
+{
+ private static final Map KEYSIZES = new HashMap();
+ private static final Set PKCS5_SCHEME_1 = new HashSet();
+ private static final Set PKCS5_SCHEME_2 = new HashSet();
+
+ static
+ {
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC);
+
+ PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.id_PBES2);
+ PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.des_EDE3_CBC);
+ PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes128_CBC);
+ PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes192_CBC);
+ PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes256_CBC);
+
+ KEYSIZES.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192));
+ KEYSIZES.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), Integers.valueOf(128));
+ KEYSIZES.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), Integers.valueOf(192));
+ KEYSIZES.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), Integers.valueOf(256));
+ KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4.getId(), Integers.valueOf(128));
+ KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC4, Integers.valueOf(40));
+ KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC, Integers.valueOf(128));
+ KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, Integers.valueOf(192));
+ KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, Integers.valueOf(128));
+ KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, Integers.valueOf(40));
+ }
+
+ static int getKeySize(String algorithm)
+ {
+ if (!KEYSIZES.containsKey(algorithm))
+ {
+ throw new IllegalStateException("no key size for algorithm: " + algorithm);
+ }
+
+ return ((Integer)KEYSIZES.get(algorithm)).intValue();
+ }
+
+ static boolean isPKCS5Scheme1(ASN1ObjectIdentifier algOid)
+ {
+ return PKCS5_SCHEME_1.contains(algOid);
+ }
+
+ static boolean isPKCS5Scheme2(ASN1ObjectIdentifier algOid)
+ {
+ return PKCS5_SCHEME_2.contains(algOid);
+ }
+
+ public static boolean isPKCS12(ASN1ObjectIdentifier algOid)
+ {
+ return algOid.getId().startsWith(PKCSObjectIdentifiers.pkcs_12PbeIds.getId());
+ }
+
+ public static KeyParameter generateSecretKeyForPKCS5Scheme2(String algorithm, char[] password, byte[] salt, int iterationCount)
+ {
+ PBEParametersGenerator paramsGen = new PKCS5S2ParametersGenerator(new SHA1Digest());
+
+ paramsGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt, iterationCount);
+
+ return (KeyParameter)paramsGen.generateDerivedParameters(PEMUtilities.getKeySize(algorithm));
+ }
+
+ static byte[] crypt(
+ boolean encrypt,
+ byte[] bytes,
+ char[] password,
+ String dekAlgName,
+ byte[] iv)
+ throws PEMException
+ {
+ byte[] ivValue = iv;
+ String blockMode = "CBC";
+ BlockCipher engine;
+ BlockCipherPadding padding = new PKCS7Padding();
+ KeyParameter sKey;
+
+ // Figure out block mode and padding.
+ if (dekAlgName.endsWith("-CFB"))
+ {
+ blockMode = "CFB";
+ padding = null;
+ }
+ if (dekAlgName.endsWith("-ECB") ||
+ "DES-EDE".equals(dekAlgName) ||
+ "DES-EDE3".equals(dekAlgName))
+ {
+ // ECB is actually the default (though seldom used) when OpenSSL
+ // uses DES-EDE (des2) or DES-EDE3 (des3).
+ blockMode = "ECB";
+ ivValue = null;
+ }
+ if (dekAlgName.endsWith("-OFB"))
+ {
+ blockMode = "OFB";
+ padding = null;
+ }
+
+ // Figure out algorithm and key size.
+ if (dekAlgName.startsWith("DES-EDE"))
+ {
+ // "DES-EDE" is actually des2 in OpenSSL-speak!
+ // "DES-EDE3" is des3.
+ boolean des2 = !dekAlgName.startsWith("DES-EDE3");
+ sKey = getKey(password, 24, iv, des2);
+ engine = new DESedeEngine();
+ }
+ else if (dekAlgName.startsWith("DES-"))
+ {
+ sKey = getKey(password, 8, iv);
+ engine = new DESEngine();
+ }
+ else if (dekAlgName.startsWith("BF-"))
+ {
+ sKey = getKey(password, 16, iv);
+ engine = new BlowfishEngine();
+ }
+ else if (dekAlgName.startsWith("RC2-"))
+ {
+ int keyBits = 128;
+ if (dekAlgName.startsWith("RC2-40-"))
+ {
+ keyBits = 40;
+ }
+ else if (dekAlgName.startsWith("RC2-64-"))
+ {
+ keyBits = 64;
+ }
+ sKey = new RC2Parameters(getKey(password, keyBits / 8, iv).getKey(), keyBits);;
+ engine = new RC2Engine();
+ }
+ else if (dekAlgName.startsWith("AES-"))
+ {
+ byte[] salt = iv;
+ if (salt.length > 8)
+ {
+ salt = new byte[8];
+ System.arraycopy(iv, 0, salt, 0, 8);
+ }
+
+ int keyBits;
+ if (dekAlgName.startsWith("AES-128-"))
+ {
+ keyBits = 128;
+ }
+ else if (dekAlgName.startsWith("AES-192-"))
+ {
+ keyBits = 192;
+ }
+ else if (dekAlgName.startsWith("AES-256-"))
+ {
+ keyBits = 256;
+ }
+ else
+ {
+ throw new EncryptionException("unknown AES encryption with private key: " + dekAlgName);
+ }
+ sKey = getKey(password, keyBits / 8, salt);
+ engine = new AESFastEngine();
+ }
+ else
+ {
+ throw new EncryptionException("unknown encryption with private key: " + dekAlgName);
+ }
+
+ if (blockMode.equals("CBC"))
+ {
+ engine = new CBCBlockCipher(engine);
+ }
+ else if (blockMode.equals("CFB"))
+ {
+ engine = new CFBBlockCipher(engine, engine.getBlockSize() * 8);
+ }
+ else if (blockMode.equals("OFB"))
+ {
+ engine = new OFBBlockCipher(engine, engine.getBlockSize() * 8);
+ }
+
+ try
+ {
+ BufferedBlockCipher c;
+ if (padding == null)
+ {
+ c = new BufferedBlockCipher(engine);
+ }
+ else
+ {
+ c = new PaddedBufferedBlockCipher(engine, padding);
+ }
+
+ if (ivValue == null) // ECB block mode
+ {
+ c.init(encrypt, sKey);
+ }
+ else
+ {
+ c.init(encrypt, new ParametersWithIV(sKey, ivValue));
+ }
+
+ byte[] out = new byte[c.getOutputSize(bytes.length)];
+
+ int procLen = c.processBytes(bytes, 0, bytes.length, out, 0);
+
+ procLen += c.doFinal(out, procLen);
+
+ if (procLen == out.length)
+ {
+ return out;
+ }
+ else
+ {
+ byte[] rv = new byte[procLen];
+
+ System.arraycopy(out, 0, rv, 0, procLen);
+
+ return rv;
+ }
+ }
+ catch (Exception e)
+ {
+ throw new EncryptionException("exception using cipher - please check password and data.", e);
+ }
+ }
+
+ private static KeyParameter getKey(
+ char[] password,
+ int keyLength,
+ byte[] salt)
+ throws PEMException
+ {
+ return getKey(password, keyLength, salt, false);
+ }
+
+ private static KeyParameter getKey(
+ char[] password,
+ int keyLength,
+ byte[] salt,
+ boolean des2)
+ throws PEMException
+ {
+ PBEParametersGenerator paramsGen = new OpenSSLPBEParametersGenerator();
+
+ paramsGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt, 1);
+
+ KeyParameter kp = (KeyParameter)paramsGen.generateDerivedParameters(keyLength * 8);
+
+ if (des2 && kp.getKey().length == 24)
+ {
+ // For DES2, we must copy first 8 bytes into the last 8 bytes.
+ byte[] key = kp.getKey();
+
+ System.arraycopy(key, 0, key, 16, 8);
+
+ return new KeyParameter(key);
+ }
+
+ return kp;
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentSignerBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentSignerBuilder.java
new file mode 100644
index 00000000..baf866b5
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentSignerBuilder.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.operator.bc;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.signers.DSADigestSigner;
+import org.bouncycastle.crypto.signers.ECDSASigner;
+import org.bouncycastle.operator.OperatorCreationException;
+
+public class BcECContentSignerBuilder
+ extends BcContentSignerBuilder
+{
+ public BcECContentSignerBuilder(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId)
+ {
+ super(sigAlgId, digAlgId);
+ }
+
+ protected Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId)
+ throws OperatorCreationException
+ {
+ Digest dig = digestProvider.get(digAlgId);
+
+ return new DSADigestSigner(new ECDSASigner(), dig);
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentVerifierProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentVerifierProviderBuilder.java
new file mode 100644
index 00000000..0d0d5625
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentVerifierProviderBuilder.java
@@ -0,0 +1,40 @@
+package org.bouncycastle.operator.bc;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.signers.DSADigestSigner;
+import org.bouncycastle.crypto.signers.ECDSASigner;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
+import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.OperatorCreationException;
+
+public class BcECContentVerifierProviderBuilder
+ extends BcContentVerifierProviderBuilder
+{
+ private DigestAlgorithmIdentifierFinder digestAlgorithmFinder;
+
+ public BcECContentVerifierProviderBuilder(DigestAlgorithmIdentifierFinder digestAlgorithmFinder)
+ {
+ this.digestAlgorithmFinder = digestAlgorithmFinder;
+ }
+
+ protected Signer createSigner(AlgorithmIdentifier sigAlgId)
+ throws OperatorCreationException
+ {
+ AlgorithmIdentifier digAlg = digestAlgorithmFinder.find(sigAlgId);
+ Digest dig = digestProvider.get(digAlg);
+
+ return new DSADigestSigner(new ECDSASigner(), dig);
+ }
+
+ protected AsymmetricKeyParameter extractKeyParameters(SubjectPublicKeyInfo publicKeyInfo)
+ throws IOException
+ {
+ return PublicKeyFactory.createKey(publicKeyInfo);
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyUnwrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyUnwrapper.java
new file mode 100644
index 00000000..3e190839
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyUnwrapper.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.operator.jcajce;
+
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.Cipher;
+
+import org.bouncycastle.asn1.cms.GenericHybridParameters;
+import org.bouncycastle.asn1.cms.RsaKemParameters;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.util.DEROtherInfo;
+import org.bouncycastle.jcajce.spec.KTSParameterSpec;
+import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
+import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
+import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
+import org.bouncycastle.operator.AsymmetricKeyUnwrapper;
+import org.bouncycastle.operator.GenericKey;
+import org.bouncycastle.operator.OperatorException;
+import org.bouncycastle.util.Arrays;
+
+public class JceKTSKeyUnwrapper
+ extends AsymmetricKeyUnwrapper
+{
+ private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
+ private Map extraMappings = new HashMap();
+ private PrivateKey privKey;
+ private byte[] partyUInfo;
+ private byte[] partyVInfo;
+
+ public JceKTSKeyUnwrapper(AlgorithmIdentifier algorithmIdentifier, PrivateKey privKey, byte[] partyUInfo, byte[] partyVInfo)
+ {
+ super(algorithmIdentifier);
+
+ this.privKey = privKey;
+ this.partyUInfo = Arrays.clone(partyUInfo);
+ this.partyVInfo = Arrays.clone(partyVInfo);
+ }
+
+ public JceKTSKeyUnwrapper setProvider(Provider provider)
+ {
+ this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
+
+ return this;
+ }
+
+ public JceKTSKeyUnwrapper setProvider(String providerName)
+ {
+ this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+
+ return this;
+ }
+
+ public GenericKey generateUnwrappedKey(AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedKey)
+ throws OperatorException
+ {
+ GenericHybridParameters params = GenericHybridParameters.getInstance(this.getAlgorithmIdentifier().getParameters());
+ Cipher keyCipher = helper.createAsymmetricWrapper(this.getAlgorithmIdentifier().getAlgorithm(), extraMappings);
+ String symmetricWrappingAlg = helper.getWrappingAlgorithmName(params.getDem().getAlgorithm());
+ RsaKemParameters kemParameters = RsaKemParameters.getInstance(params.getKem().getParameters());
+ int keySizeInBits = kemParameters.getKeyLength().intValue() * 8;
+ Key sKey;
+
+ try
+ {
+ DEROtherInfo otherInfo = new DEROtherInfo.Builder(params.getDem(), partyUInfo, partyVInfo).build();
+ KTSParameterSpec ktsSpec = new KTSParameterSpec.Builder(symmetricWrappingAlg, keySizeInBits, otherInfo.getEncoded()).withKdfAlgorithm(kemParameters.getKeyDerivationFunction()).build();
+
+ keyCipher.init(Cipher.UNWRAP_MODE, privKey, ktsSpec);
+
+ sKey = keyCipher.unwrap(encryptedKey, helper.getKeyAlgorithmName(encryptedKeyAlgorithm.getAlgorithm()), Cipher.SECRET_KEY);
+ }
+ catch (Exception e)
+ {
+ throw new OperatorException("Unable to unwrap contents key: " + e.getMessage(), e);
+ }
+
+ return new JceGenericKey(encryptedKeyAlgorithm, sKey);
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyWrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyWrapper.java
new file mode 100644
index 00000000..63194404
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyWrapper.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.operator.jcajce;
+
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+
+import javax.crypto.Cipher;
+
+import org.bouncycastle.asn1.cms.GenericHybridParameters;
+import org.bouncycastle.asn1.cms.RsaKemParameters;
+import org.bouncycastle.asn1.iso.ISOIECObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.util.DEROtherInfo;
+import org.bouncycastle.jcajce.spec.KTSParameterSpec;
+import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
+import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
+import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
+import org.bouncycastle.operator.AsymmetricKeyWrapper;
+import org.bouncycastle.operator.GenericKey;
+import org.bouncycastle.operator.OperatorException;
+import org.bouncycastle.util.Arrays;
+
+public class JceKTSKeyWrapper
+ extends AsymmetricKeyWrapper
+{
+ private final String symmetricWrappingAlg;
+ private final int keySizeInBits;
+ private final byte[] partyUInfo;
+ private final byte[] partyVInfo;
+
+ private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
+ private PublicKey publicKey;
+ private SecureRandom random;
+
+ public JceKTSKeyWrapper(PublicKey publicKey, String symmetricWrappingAlg, int keySizeInBits, byte[] partyUInfo, byte[] partyVInfo)
+ {
+ super(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_rsa_KEM, new GenericHybridParameters(new AlgorithmIdentifier(ISOIECObjectIdentifiers.id_kem_rsa, new RsaKemParameters(new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf3, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256)), (keySizeInBits + 7) / 8)), JceSymmetricKeyWrapper.determineKeyEncAlg(symmetricWrappingAlg, keySizeInBits))));
+
+ this.publicKey = publicKey;
+ this.symmetricWrappingAlg = symmetricWrappingAlg;
+ this.keySizeInBits = keySizeInBits;
+ this.partyUInfo = Arrays.clone(partyUInfo);
+ this.partyVInfo = Arrays.clone(partyVInfo);
+ }
+
+ public JceKTSKeyWrapper(X509Certificate certificate, String symmetricWrappingAlg, int keySizeInBits, byte[] partyUInfo, byte[] partyVInfo)
+ {
+ this(certificate.getPublicKey(), symmetricWrappingAlg, keySizeInBits, partyUInfo, partyVInfo);
+ }
+
+ public JceKTSKeyWrapper setProvider(Provider provider)
+ {
+ this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
+
+ return this;
+ }
+
+ public JceKTSKeyWrapper setProvider(String providerName)
+ {
+ this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+
+ return this;
+ }
+
+ public JceKTSKeyWrapper setSecureRandom(SecureRandom random)
+ {
+ this.random = random;
+
+ return this;
+ }
+
+ public byte[] generateWrappedKey(GenericKey encryptionKey)
+ throws OperatorException
+ {
+ Cipher keyEncryptionCipher = helper.createAsymmetricWrapper(getAlgorithmIdentifier().getAlgorithm(), new HashMap());
+
+ try
+ {
+ DEROtherInfo otherInfo = new DEROtherInfo.Builder(JceSymmetricKeyWrapper.determineKeyEncAlg(symmetricWrappingAlg, keySizeInBits), partyUInfo, partyVInfo).build();
+ KTSParameterSpec ktsSpec = new KTSParameterSpec.Builder(symmetricWrappingAlg, keySizeInBits, otherInfo.getEncoded()).build();
+
+ keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, ktsSpec, random);
+
+ return keyEncryptionCipher.wrap(OperatorUtils.getJceKey(encryptionKey));
+ }
+ catch (Exception e)
+ {
+ throw new OperatorException("Unable to wrap contents key: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/test/GenTimeAccuracyUnitTest.java b/bcpkix/src/main/java/org/bouncycastle/tsp/test/GenTimeAccuracyUnitTest.java
new file mode 100644
index 00000000..a809f071
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/test/GenTimeAccuracyUnitTest.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.tsp.test;
+
+import junit.framework.TestCase;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.tsp.Accuracy;
+import org.bouncycastle.tsp.GenTimeAccuracy;
+
+public class GenTimeAccuracyUnitTest
+ extends TestCase
+{
+ private static final ASN1Integer ZERO_VALUE = new ASN1Integer(0);
+ private static final ASN1Integer ONE_VALUE = new ASN1Integer(1);
+ private static final ASN1Integer TWO_VALUE = new ASN1Integer(2);
+ private static final ASN1Integer THREE_VALUE = new ASN1Integer(3);
+
+ public void testOneTwoThree()
+ {
+ GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(ONE_VALUE, TWO_VALUE, THREE_VALUE));
+
+ checkValues(accuracy, ONE_VALUE, TWO_VALUE, THREE_VALUE);
+
+ checkToString(accuracy, "1.002003");
+ }
+
+ public void testThreeTwoOne()
+ {
+ GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(THREE_VALUE, TWO_VALUE, ONE_VALUE));
+
+ checkValues(accuracy, THREE_VALUE, TWO_VALUE, ONE_VALUE);
+
+ checkToString(accuracy, "3.002001");
+ }
+
+ public void testTwoThreeTwo()
+ {
+ GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(TWO_VALUE, THREE_VALUE, TWO_VALUE));
+
+ checkValues(accuracy, TWO_VALUE, THREE_VALUE, TWO_VALUE);
+
+ checkToString(accuracy, "2.003002");
+ }
+
+
+ public void testZeroTwoThree()
+ {
+ GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(ZERO_VALUE, TWO_VALUE, THREE_VALUE));
+
+ checkValues(accuracy, ZERO_VALUE, TWO_VALUE, THREE_VALUE);
+
+ checkToString(accuracy, "0.002003");
+ }
+
+ public void testThreeTwoNull()
+ {
+ GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(THREE_VALUE, TWO_VALUE, null));
+
+ checkValues(accuracy, THREE_VALUE, TWO_VALUE, ZERO_VALUE);
+
+ checkToString(accuracy, "3.002000");
+ }
+
+ public void testOneNullOne()
+ {
+ GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(ONE_VALUE, null, ONE_VALUE));
+
+ checkValues(accuracy, ONE_VALUE, ZERO_VALUE, ONE_VALUE);
+
+ checkToString(accuracy, "1.000001");
+ }
+
+ public void testZeroNullNull()
+ {
+ GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(ZERO_VALUE, null, null));
+
+ checkValues(accuracy, ZERO_VALUE, ZERO_VALUE, ZERO_VALUE);
+
+ checkToString(accuracy, "0.000000");
+ }
+
+ public void testNullNullNull()
+ {
+ GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(null, null, null));
+
+ checkValues(accuracy, ZERO_VALUE, ZERO_VALUE, ZERO_VALUE);
+
+ checkToString(accuracy, "0.000000");
+ }
+
+ private void checkValues(
+ GenTimeAccuracy accuracy,
+ ASN1Integer secs,
+ ASN1Integer millis,
+ ASN1Integer micros)
+ {
+ assertEquals(secs.getValue().intValue(), accuracy.getSeconds());
+ assertEquals(millis.getValue().intValue(), accuracy.getMillis());
+ assertEquals(micros.getValue().intValue(), accuracy.getMicros());
+ }
+
+ private void checkToString(
+ GenTimeAccuracy accuracy,
+ String expected)
+ {
+ assertEquals(expected, accuracy.toString());
+ }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/test/TimeStampTokenInfoUnitTest.java b/bcpkix/src/main/java/org/bouncycastle/tsp/test/TimeStampTokenInfoUnitTest.java
new file mode 100644
index 00000000..61102830
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/test/TimeStampTokenInfoUnitTest.java
@@ -0,0 +1,161 @@
+package org.bouncycastle.tsp.test;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigInteger;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.tsp.TSTInfo;
+import org.bouncycastle.tsp.GenTimeAccuracy;
+import org.bouncycastle.tsp.TSPAlgorithms;
+import org.bouncycastle.tsp.TSPException;
+import org.bouncycastle.tsp.TimeStampTokenInfo;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+public class TimeStampTokenInfoUnitTest
+ extends TestCase
+{
+ private static final byte[] tstInfo1 = Hex.decode(
+ "303e02010106022a033021300906052b0e03021a050004140000000000000000000000000000000000000000"
+ + "020118180f32303035313130313038313732315a");
+
+ private static final byte[] tstInfo2 = Hex.decode(
+ "304c02010106022a033021300906052b0e03021a05000414ffffffffffffffffffffffffffffffffffffffff"
+ + "020117180f32303035313130313038323934355a3009020103800101810102020164");
+
+ private static final byte[] tstInfo3 = Hex.decode(
+ "304f02010106022a033021300906052b0e03021a050004140000000000000000000000000000000000000000"
+ + "020117180f32303035313130313038343733355a30090201038001018101020101ff020164");
+
+ private static final byte[] tstInfoDudDate = Hex.decode(
+ "303e02010106022a033021300906052b0e03021a050004140000000000000000000000000000000000000000"
+ + "020118180f32303056313130313038313732315a");
+
+ public void testTstInfo1()
+ throws Exception
+ {
+ TimeStampTokenInfo tstInfo = getTimeStampTokenInfo(tstInfo1);
+
+ //
+ // verify
+ //
+ GenTimeAccuracy accuracy = tstInfo.getGenTimeAccuracy();
+
+ assertNull(accuracy);
+
+ assertEquals(new BigInteger("24"), tstInfo.getSerialNumber());
+
+ assertEquals(1130833041000L, tstInfo.getGenTime().getTime());
+
+ assertEquals("1.2.3", tstInfo.getPolicy().getId());
+
+ assertEquals(false, tstInfo.isOrdered());
+
+ assertNull(tstInfo.getNonce());
+
+ Assert.assertEquals(TSPAlgorithms.SHA1, tstInfo.getMessageImprintAlgOID());
+
+ assertTrue(Arrays.areEqual(new byte[20], tstInfo.getMessageImprintDigest()));
+
+ assertTrue(Arrays.areEqual(tstInfo1, tstInfo.getEncoded()));
+ }
+
+ public void testTstInfo2()
+ throws Exception
+ {
+ TimeStampTokenInfo tstInfo = getTimeStampTokenInfo(tstInfo2);
+
+ //
+ // verify
+ //
+ GenTimeAccuracy accuracy = tstInfo.getGenTimeAccuracy();
+
+ assertEquals(3, accuracy.getSeconds());
+ assertEquals(1, accuracy.getMillis());
+ assertEquals(2, accuracy.getMicros());
+
+ assertEquals(new BigInteger("23"), tstInfo.getSerialNumber());
+
+ assertEquals(1130833785000L, tstInfo.getGenTime().getTime());
+
+ assertEquals("1.2.3", tstInfo.getPolicy().getId());
+
+ assertEquals(false, tstInfo.isOrdered());
+
+ assertEquals(tstInfo.getNonce(), BigInteger.valueOf(100));
+
+ assertTrue(Arrays.areEqual(Hex.decode("ffffffffffffffffffffffffffffffffffffffff"), tstInfo.getMessageImprintDigest()));
+
+ assertTrue(Arrays.areEqual(tstInfo2, tstInfo.getEncoded()));
+ }
+
+ public void testTstInfo3()
+ throws Exception
+ {
+ TimeStampTokenInfo tstInfo = getTimeStampTokenInfo(tstInfo3);
+
+ //
+ // verify
+ //
+ GenTimeAccuracy accuracy = tstInfo.getGenTimeAccuracy();
+
+ assertEquals(3, accuracy.getSeconds());
+ assertEquals(1, accuracy.getMillis());
+ assertEquals(2, accuracy.getMicros());
+
+ assertEquals(new BigInteger("23"), tstInfo.getSerialNumber());
+
+ assertEquals(1130834855000L, tstInfo.getGenTime().getTime());
+
+ assertEquals("1.2.3", tstInfo.getPolicy().getId());
+
+ assertEquals(true, tstInfo.isOrdered());
+
+ assertEquals(tstInfo.getNonce(), BigInteger.valueOf(100));
+
+ assertEquals(TSPAlgorithms.SHA1, tstInfo.getMessageImprintAlgOID());
+
+ assertTrue(Arrays.areEqual(new byte[20], tstInfo.getMessageImprintDigest()));
+
+ assertTrue(Arrays.areEqual(tstInfo3, tstInfo.getEncoded()));
+ }
+
+ public void testTstInfoDudDate()
+ throws Exception
+ {
+ try
+ {
+ getTimeStampTokenInfo(tstInfoDudDate);
+
+ fail("dud date not detected.");
+ }
+ catch (TSPException e)
+ {
+ // expected
+ }
+ }
+
+ private TimeStampTokenInfo getTimeStampTokenInfo(
+ byte[] tstInfo)
+ throws Exception
+ {
+ ASN1InputStream aIn = new ASN1InputStream(tstInfo);
+ TSTInfo info = TSTInfo.getInstance(aIn.readObject());
+
+ final Constructor constructor = TimeStampTokenInfo.class.getDeclaredConstructor(TSTInfo.class);
+
+ constructor.setAccessible(true);
+
+ try
+ {
+ return (TimeStampTokenInfo)constructor.newInstance(new Object[]{info});
+ }
+ catch (InvocationTargetException e)
+ {
+ throw (Exception)e.getTargetException();
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java
new file mode 100644
index 00000000..fca4e01a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java
@@ -0,0 +1,226 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Base class for an application specific object
+ */
+public abstract class ASN1ApplicationSpecific
+ extends ASN1Primitive
+{
+ protected final boolean isConstructed;
+ protected final int tag;
+ protected final byte[] octets;
+
+ ASN1ApplicationSpecific(
+ boolean isConstructed,
+ int tag,
+ byte[] octets)
+ {
+ this.isConstructed = isConstructed;
+ this.tag = tag;
+ this.octets = octets;
+ }
+
+ /**
+ * Return an ASN1ApplicationSpecific from the passed in object, which may be a byte array, or null.
+ *
+ * @param obj the object to be converted.
+ * @return obj's representation as an ASN1ApplicationSpecific object.
+ */
+ public static ASN1ApplicationSpecific getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof ASN1ApplicationSpecific)
+ {
+ return (ASN1ApplicationSpecific)obj;
+ }
+ else if (obj instanceof byte[])
+ {
+ try
+ {
+ return ASN1ApplicationSpecific.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("Failed to construct object from byte[]: " + e.getMessage());
+ }
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
+ }
+
+ protected static int getLengthOfHeader(byte[] data)
+ {
+ int length = data[1] & 0xff; // TODO: assumes 1 byte tag
+
+ if (length == 0x80)
+ {
+ return 2; // indefinite-length encoding
+ }
+
+ if (length > 127)
+ {
+ int size = length & 0x7f;
+
+ // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
+ if (size > 4)
+ {
+ throw new IllegalStateException("DER length more than 4 bytes: " + size);
+ }
+
+ return size + 2;
+ }
+
+ return 2;
+ }
+
+ /**
+ * Return true if the object is marked as constructed, false otherwise.
+ *
+ * @return true if constructed, otherwise false.
+ */
+ public boolean isConstructed()
+ {
+ return isConstructed;
+ }
+
+ /**
+ * Return the contents of this object as a byte[]
+ *
+ * @return the encoded contents of the object.
+ */
+ public byte[] getContents()
+ {
+ return octets;
+ }
+
+ /**
+ * Return the tag number associated with this object,
+ *
+ * @return the application tag number.
+ */
+ public int getApplicationTag()
+ {
+ return tag;
+ }
+
+ /**
+ * Return the enclosed object assuming explicit tagging.
+ *
+ * @return the resulting object
+ * @throws IOException if reconstruction fails.
+ */
+ public ASN1Primitive getObject()
+ throws IOException
+ {
+ return new ASN1InputStream(getContents()).readObject();
+ }
+
+ /**
+ * Return the enclosed object assuming implicit tagging.
+ *
+ * @param derTagNo the type tag that should be applied to the object's contents.
+ * @return the resulting object
+ * @throws IOException if reconstruction fails.
+ */
+ public ASN1Primitive getObject(int derTagNo)
+ throws IOException
+ {
+ if (derTagNo >= 0x1f)
+ {
+ throw new IOException("unsupported tag number");
+ }
+
+ byte[] orig = this.getEncoded();
+ byte[] tmp = replaceTagNumber(derTagNo, orig);
+
+ if ((orig[0] & BERTags.CONSTRUCTED) != 0)
+ {
+ tmp[0] |= BERTags.CONSTRUCTED;
+ }
+
+ return new ASN1InputStream(tmp).readObject();
+ }
+
+ int encodedLength()
+ throws IOException
+ {
+ return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length;
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
+ */
+ void encode(ASN1OutputStream out) throws IOException
+ {
+ int classBits = BERTags.APPLICATION;
+ if (isConstructed)
+ {
+ classBits |= BERTags.CONSTRUCTED;
+ }
+
+ out.writeEncoded(classBits, tag, octets);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof ASN1ApplicationSpecific))
+ {
+ return false;
+ }
+
+ ASN1ApplicationSpecific other = (ASN1ApplicationSpecific)o;
+
+ return isConstructed == other.isConstructed
+ && tag == other.tag
+ && Arrays.areEqual(octets, other.octets);
+ }
+
+ public int hashCode()
+ {
+ return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets);
+ }
+
+ private byte[] replaceTagNumber(int newTag, byte[] input)
+ throws IOException
+ {
+ int tagNo = input[0] & 0x1f;
+ int index = 1;
+ //
+ // with tagged object tag number is bottom 5 bits, or stored at the start of the content
+ //
+ if (tagNo == 0x1f)
+ {
+ tagNo = 0;
+
+ int b = input[index++] & 0xff;
+
+ // X.690-0207 8.1.2.4.2
+ // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
+ if ((b & 0x7f) == 0) // Note: -1 will pass
+ {
+ throw new ASN1ParsingException("corrupted stream - invalid high tag number found");
+ }
+
+ while ((b >= 0) && ((b & 0x80) != 0))
+ {
+ tagNo |= (b & 0x7f);
+ tagNo <<= 7;
+ b = input[index++] & 0xff;
+ }
+
+// tagNo |= (b & 0x7f);
+ }
+
+ byte[] tmp = new byte[input.length - index + 1];
+
+ System.arraycopy(input, index, tmp, 1, tmp.length - 1);
+
+ tmp[0] = (byte)newTag;
+
+ return tmp;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java
new file mode 100644
index 00000000..513d4e5b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java
@@ -0,0 +1,291 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+/**
+ * Base class for BIT STRING objects
+ */
+public abstract class ASN1BitString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ protected final byte[] data;
+ protected final int padBits;
+
+ /**
+ * @param bitString an int containing the BIT STRING
+ * @return the correct number of pad bits for a bit string defined in
+ * a 32 bit constant
+ */
+ static protected int getPadBits(
+ int bitString)
+ {
+ int val = 0;
+ for (int i = 3; i >= 0; i--)
+ {
+ //
+ // this may look a little odd, but if it isn't done like this pre jdk1.2
+ // JVM's break!
+ //
+ if (i != 0)
+ {
+ if ((bitString >> (i * 8)) != 0)
+ {
+ val = (bitString >> (i * 8)) & 0xFF;
+ break;
+ }
+ }
+ else
+ {
+ if (bitString != 0)
+ {
+ val = bitString & 0xFF;
+ break;
+ }
+ }
+ }
+
+ if (val == 0)
+ {
+ return 0;
+ }
+
+ int bits = 1;
+
+ while (((val <<= 1) & 0xFF) != 0)
+ {
+ bits++;
+ }
+
+ return 8 - bits;
+ }
+
+ /**
+ * @param bitString an int containing the BIT STRING
+ * @return the correct number of bytes for a bit string defined in
+ * a 32 bit constant
+ */
+ static protected byte[] getBytes(int bitString)
+ {
+ if (bitString == 0)
+ {
+ return new byte[0];
+ }
+
+ int bytes = 4;
+ for (int i = 3; i >= 1; i--)
+ {
+ if ((bitString & (0xFF << (i * 8))) != 0)
+ {
+ break;
+ }
+ bytes--;
+ }
+
+ byte[] result = new byte[bytes];
+ for (int i = 0; i < bytes; i++)
+ {
+ result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
+ }
+
+ return result;
+ }
+
+ /**
+ * Base constructor.
+ *
+ * @param data the octets making up the bit string.
+ * @param padBits the number of extra bits at the end of the string.
+ */
+ public ASN1BitString(
+ byte[] data,
+ int padBits)
+ {
+ if (data == null)
+ {
+ throw new NullPointerException("data cannot be null");
+ }
+ if (data.length == 0 && padBits != 0)
+ {
+ throw new IllegalArgumentException("zero length data with non-zero pad bits");
+ }
+ if (padBits > 7 || padBits < 0)
+ {
+ throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
+ }
+
+ this.data = Arrays.clone(data);
+ this.padBits = padBits;
+ }
+
+ /**
+ * Return a String representation of this BIT STRING
+ *
+ * @return a String representation.
+ */
+ public String getString()
+ {
+ StringBuffer buf = new StringBuffer("#");
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ try
+ {
+ aOut.writeObject(this);
+ }
+ catch (IOException e)
+ {
+ throw new ASN1ParsingException("Internal error encoding BitString: " + e.getMessage(), e);
+ }
+
+ byte[] string = bOut.toByteArray();
+
+ for (int i = 0; i != string.length; i++)
+ {
+ buf.append(table[(string[i] >>> 4) & 0xf]);
+ buf.append(table[string[i] & 0xf]);
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * @return the value of the bit string as an int (truncating if necessary)
+ */
+ public int intValue()
+ {
+ int value = 0;
+ byte[] string = data;
+
+ if (padBits > 0 && data.length <= 4)
+ {
+ string = derForm(data, padBits);
+ }
+
+ for (int i = 0; i != string.length && i != 4; i++)
+ {
+ value |= (string[i] & 0xff) << (8 * i);
+ }
+
+ return value;
+ }
+
+ /**
+ * Return the octets contained in this BIT STRING, checking that this BIT STRING really
+ * does represent an octet aligned string. Only use this method when the standard you are
+ * following dictates that the BIT STRING will be octet aligned.
+ *
+ * @return a copy of the octet aligned data.
+ */
+ public byte[] getOctets()
+ {
+ if (padBits != 0)
+ {
+ throw new IllegalStateException("attempt to get non-octet aligned data from BIT STRING");
+ }
+
+ return Arrays.clone(data);
+ }
+
+ public byte[] getBytes()
+ {
+ return derForm(data, padBits);
+ }
+
+ public int getPadBits()
+ {
+ return padBits;
+ }
+
+ public String toString()
+ {
+ return getString();
+ }
+
+ public int hashCode()
+ {
+ return padBits ^ Arrays.hashCode(this.getBytes());
+ }
+
+ protected boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof ASN1BitString))
+ {
+ return false;
+ }
+
+ ASN1BitString other = (ASN1BitString)o;
+
+ return this.padBits == other.padBits
+ && Arrays.areEqual(this.getBytes(), other.getBytes());
+ }
+
+ protected static byte[] derForm(byte[] data, int padBits)
+ {
+ byte[] rv = Arrays.clone(data);
+ // DER requires pad bits be zero
+ if (padBits > 0)
+ {
+ rv[data.length - 1] &= 0xff << padBits;
+ }
+
+ return rv;
+ }
+
+ static ASN1BitString fromInputStream(int length, InputStream stream)
+ throws IOException
+ {
+ if (length < 1)
+ {
+ throw new IllegalArgumentException("truncated BIT STRING detected");
+ }
+
+ int padBits = stream.read();
+ byte[] data = new byte[length - 1];
+
+ if (data.length != 0)
+ {
+ if (Streams.readFully(stream, data) != data.length)
+ {
+ throw new EOFException("EOF encountered in middle of BIT STRING");
+ }
+
+ if (padBits > 0 && padBits < 8)
+ {
+ if (data[data.length - 1] != (byte)(data[data.length - 1] & (0xff << padBits)))
+ {
+ return new DLBitString(data, padBits);
+ }
+ }
+ }
+
+ return new DERBitString(data, padBits);
+ }
+
+ public ASN1Primitive getLoadedObject()
+ {
+ return this.toASN1Primitive();
+ }
+
+ ASN1Primitive toDERObject()
+ {
+ return new DERBitString(data, padBits);
+ }
+
+ ASN1Primitive toDLObject()
+ {
+ return new DLBitString(data, padBits);
+ }
+
+ abstract void encode(ASN1OutputStream out)
+ throws IOException;
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERGraphicString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERGraphicString.java
new file mode 100644
index 00000000..01baf0f9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERGraphicString.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+public class DERGraphicString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private final byte[] string;
+
+ /**
+ * return a Graphic String from the passed in object
+ *
+ * @param obj a DERGraphicString or an object that can be converted into one.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ * @return a DERGraphicString instance, or null.
+ */
+ public static DERGraphicString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERGraphicString)
+ {
+ return (DERGraphicString)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERGraphicString)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Graphic String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ * @return a DERGraphicString instance, or null.
+ */
+ public static DERGraphicString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERGraphicString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERGraphicString(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - with bytes.
+ * @param string the byte encoding of the characters making up the string.
+ */
+ public DERGraphicString(
+ byte[] string)
+ {
+ this.string = Arrays.clone(string);
+ }
+
+ public byte[] getOctets()
+ {
+ return Arrays.clone(string);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.GRAPHIC_STRING, string);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERGraphicString))
+ {
+ return false;
+ }
+
+ DERGraphicString s = (DERGraphicString)o;
+
+ return Arrays.areEqual(string, s.string);
+ }
+
+ public String getString()
+ {
+ return Strings.fromByteArray(string);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERVideotexString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERVideotexString.java
new file mode 100644
index 00000000..da231e15
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERVideotexString.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+public class DERVideotexString
+ extends ASN1Primitive
+ implements ASN1String
+{
+ private final byte[] string;
+
+ /**
+ * return a Videotex String from the passed in object
+ *
+ * @param obj a DERVideotexString or an object that can be converted into one.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ * @return a DERVideotexString instance, or null.
+ */
+ public static DERVideotexString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERVideotexString)
+ {
+ return (DERVideotexString)obj;
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return (DERVideotexString)fromByteArray((byte[])obj);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+ }
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Videotex String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ * @return a DERVideotexString instance, or null.
+ */
+ public static DERVideotexString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DERVideotexString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return new DERVideotexString(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ /**
+ * basic constructor - with bytes.
+ * @param string the byte encoding of the characters making up the string.
+ */
+ public DERVideotexString(
+ byte[] string)
+ {
+ this.string = Arrays.clone(string);
+ }
+
+ public byte[] getOctets()
+ {
+ return Arrays.clone(string);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(BERTags.VIDEOTEX_STRING, string);
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(string);
+ }
+
+ boolean asn1Equals(
+ ASN1Primitive o)
+ {
+ if (!(o instanceof DERVideotexString))
+ {
+ return false;
+ }
+
+ DERVideotexString s = (DERVideotexString)o;
+
+ return Arrays.areEqual(string, s.string);
+ }
+
+ public String getString()
+ {
+ return Strings.fromByteArray(string);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java
new file mode 100644
index 00000000..c81f0ab9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java
@@ -0,0 +1,145 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * A Definite length BIT STRING
+ */
+public class DLBitString
+ extends ASN1BitString
+{
+ /**
+ * return a Bit String that can be definite-length encoded from the passed in object.
+ *
+ * @param obj a DL or DER BitString or an object that can be converted into one.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ * @return an ASN1BitString instance, or null.
+ */
+ public static ASN1BitString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DLBitString)
+ {
+ return (DLBitString)obj;
+ }
+ if (obj instanceof DERBitString)
+ {
+ return (DERBitString)obj;
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Bit String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ * @return an ASN1BitString instance, or null.
+ */
+ public static ASN1BitString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ ASN1Primitive o = obj.getObject();
+
+ if (explicit || o instanceof DLBitString)
+ {
+ return getInstance(o);
+ }
+ else
+ {
+ return fromOctetString(((ASN1OctetString)o).getOctets());
+ }
+ }
+
+ protected DLBitString(
+ byte data,
+ int padBits)
+ {
+ this(toByteArray(data), padBits);
+ }
+
+ private static byte[] toByteArray(byte data)
+ {
+ byte[] rv = new byte[1];
+
+ rv[0] = data;
+
+ return rv;
+ }
+
+ /**
+ * @param data the octets making up the bit string.
+ * @param padBits the number of extra bits at the end of the string.
+ */
+ public DLBitString(
+ byte[] data,
+ int padBits)
+ {
+ super(data, padBits);
+ }
+
+ public DLBitString(
+ byte[] data)
+ {
+ this(data, 0);
+ }
+
+ public DLBitString(
+ int value)
+ {
+ super(getBytes(value), getPadBits(value));
+ }
+
+ public DLBitString(
+ ASN1Encodable obj)
+ throws IOException
+ {
+ super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0);
+ }
+
+ boolean isConstructed()
+ {
+ return false;
+ }
+
+ int encodedLength()
+ {
+ return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
+ }
+
+ void encode(
+ ASN1OutputStream out)
+ throws IOException
+ {
+ byte[] string = data;
+ byte[] bytes = new byte[string.length + 1];
+
+ bytes[0] = (byte)getPadBits();
+ System.arraycopy(string, 0, bytes, 1, bytes.length - 1);
+
+ out.writeEncoded(BERTags.BIT_STRING, bytes);
+ }
+
+ static DLBitString fromOctetString(byte[] bytes)
+ {
+ if (bytes.length < 1)
+ {
+ throw new IllegalArgumentException("truncated BIT STRING detected");
+ }
+
+ int padBits = bytes[0];
+ byte[] data = new byte[bytes.length - 1];
+
+ if (data.length != 0)
+ {
+ System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
+ }
+
+ return new DLBitString(data, padBits);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSINamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSINamedCurves.java
new file mode 100644
index 00000000..87f0a250
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSINamedCurves.java
@@ -0,0 +1,120 @@
+package org.bouncycastle.asn1.anssi;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ECParametersHolder;
+import org.bouncycastle.asn1.x9.X9ECPoint;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * ANSSI Elliptic curve table.
+ */
+public class ANSSINamedCurves
+{
+ private static ECCurve configureCurve(ECCurve curve)
+ {
+ return curve;
+ }
+
+ private static BigInteger fromHex(
+ String hex)
+ {
+ return new BigInteger(1, Hex.decode(hex));
+ }
+
+ /*
+ * FRP256v1
+ */
+ static X9ECParametersHolder FRP256v1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ BigInteger p = fromHex("F1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C03");
+ BigInteger a = fromHex("F1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C00");
+ BigInteger b = fromHex("EE353FCA5428A9300D4ABA754A44C00FDFEC0C9AE4B1A1803075ED967B7BB73F");
+ byte[] S = null;
+ BigInteger n = fromHex("F1FD178C0B3AD58F10126DE8CE42435B53DC67E140D2BF941FFDD459C6D655E1");
+ BigInteger h = BigInteger.valueOf(1);
+
+ ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+ X9ECPoint G = new X9ECPoint(curve, Hex.decode("04"
+ + "B6B3D4C356C139EB31183D4749D423958C27D2DCAF98B70164C97A2DD98F5CFF"
+ + "6142E0F7C8B204911F9271F0F3ECEF8C2701C307E8E4C9E183115A1554062CFB"));
+
+ return new X9ECParameters(curve, G, n, h, S);
+ }
+ };
+
+
+ static final Hashtable objIds = new Hashtable();
+ static final Hashtable curves = new Hashtable();
+ static final Hashtable names = new Hashtable();
+
+ static void defineCurve(String name, ASN1ObjectIdentifier oid, X9ECParametersHolder holder)
+ {
+ objIds.put(name.toLowerCase(), oid);
+ names.put(oid, name);
+ curves.put(oid, holder);
+ }
+
+ static
+ {
+ defineCurve("FRP256v1", ANSSIObjectIdentifiers.FRP256v1, FRP256v1);
+ }
+
+ public static X9ECParameters getByName(
+ String name)
+ {
+ ASN1ObjectIdentifier oid = getOID(name);
+ return oid == null ? null : getByOID(oid);
+ }
+
+ /**
+ * return the X9ECParameters object for the named curve represented by
+ * the passed in object identifier. Null if the curve isn't present.
+ *
+ * @param oid an object identifier representing a named curve, if present.
+ */
+ public static X9ECParameters getByOID(
+ ASN1ObjectIdentifier oid)
+ {
+ X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid);
+ return holder == null ? null : holder.getParameters();
+ }
+
+ /**
+ * return the object identifier signified by the passed in name. Null
+ * if there is no object identifier associated with name.
+ *
+ * @return the object identifier associated with name, if present.
+ */
+ public static ASN1ObjectIdentifier getOID(
+ String name)
+ {
+ return (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name));
+ }
+
+ /**
+ * return the named curve name represented by the given object identifier.
+ */
+ public static String getName(
+ ASN1ObjectIdentifier oid)
+ {
+ return (String)names.get(oid);
+ }
+
+ /**
+ * returns an enumeration containing the name strings for curves
+ * contained in this structure.
+ */
+ public static Enumeration getNames()
+ {
+ return names.elements();
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSIObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSIObjectIdentifiers.java
new file mode 100644
index 00000000..1cc8d674
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSIObjectIdentifiers.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.asn1.anssi;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * Object Identifiers belong to the French Agency, ANSSI.
+ */
+public interface ANSSIObjectIdentifiers
+{
+ static final ASN1ObjectIdentifier FRP256v1 = new ASN1ObjectIdentifier("1.2.250.1.223.101.256.1");
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAlgorithmProtection.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAlgorithmProtection.java
new file mode 100644
index 00000000..d18fe4bc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAlgorithmProtection.java
@@ -0,0 +1,136 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * From RFC 6211
+ * <pre>
+ * CMSAlgorithmProtection ::= SEQUENCE {
+ * digestAlgorithm DigestAlgorithmIdentifier,
+ * signatureAlgorithm [1] SignatureAlgorithmIdentifier OPTIONAL,
+ * macAlgorithm [2] MessageAuthenticationCodeAlgorithm
+ * OPTIONAL
+ * }
+ * (WITH COMPONENTS { signatureAlgorithm PRESENT,
+ * macAlgorithm ABSENT } |
+ * WITH COMPONENTS { signatureAlgorithm ABSENT,
+ * macAlgorithm PRESENT })
+ * </pre>
+ */
+public class CMSAlgorithmProtection
+ extends ASN1Object
+{
+ public static final int SIGNATURE = 1;
+ public static final int MAC = 2;
+
+ private final AlgorithmIdentifier digestAlgorithm;
+ private final AlgorithmIdentifier signatureAlgorithm;
+ private final AlgorithmIdentifier macAlgorithm;
+
+ public CMSAlgorithmProtection(AlgorithmIdentifier digestAlgorithm, int type, AlgorithmIdentifier algorithmIdentifier)
+ {
+ if (digestAlgorithm == null || algorithmIdentifier == null)
+ {
+ throw new NullPointerException("AlgorithmIdentifiers cannot be null");
+ }
+
+ this.digestAlgorithm = digestAlgorithm;
+
+ if (type == 1)
+ {
+ this.signatureAlgorithm = algorithmIdentifier;
+ this.macAlgorithm = null;
+ }
+ else if (type == 2)
+ {
+ this.signatureAlgorithm = null;
+ this.macAlgorithm = algorithmIdentifier;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ }
+
+ private CMSAlgorithmProtection(ASN1Sequence sequence)
+ {
+ if (sequence.size() != 2)
+ {
+ throw new IllegalArgumentException("Sequence wrong size: One of signatureAlgorithm or macAlgorithm must be present");
+ }
+
+ this.digestAlgorithm = AlgorithmIdentifier.getInstance(sequence.getObjectAt(0));
+
+ ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(sequence.getObjectAt(1));
+ if (tagged.getTagNo() == 1)
+ {
+ this.signatureAlgorithm = AlgorithmIdentifier.getInstance(tagged, false);
+ this.macAlgorithm = null;
+ }
+ else if (tagged.getTagNo() == 2)
+ {
+ this.signatureAlgorithm = null;
+
+ this.macAlgorithm = AlgorithmIdentifier.getInstance(tagged, false);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unknown tag found: " + tagged.getTagNo());
+ }
+ }
+
+ public static CMSAlgorithmProtection getInstance(
+ Object obj)
+ {
+ if (obj instanceof CMSAlgorithmProtection)
+ {
+ return (CMSAlgorithmProtection)obj;
+ }
+ else if (obj != null)
+ {
+ return new CMSAlgorithmProtection(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+
+ public AlgorithmIdentifier getDigestAlgorithm()
+ {
+ return digestAlgorithm;
+ }
+
+ public AlgorithmIdentifier getMacAlgorithm()
+ {
+ return macAlgorithm;
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return signatureAlgorithm;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(digestAlgorithm);
+ if (signatureAlgorithm != null)
+ {
+ v.add(new DERTaggedObject(false, 1, signatureAlgorithm));
+ }
+ if (macAlgorithm != null)
+ {
+ v.add(new DERTaggedObject(false, 2, macAlgorithm));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/GenericHybridParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/GenericHybridParameters.java
new file mode 100644
index 00000000..e0157b45
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/GenericHybridParameters.java
@@ -0,0 +1,79 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * RFC 5990 GenericHybridParameters class.
+ * <pre>
+ * GenericHybridParameters ::= SEQUENCE {
+ * kem KeyEncapsulationMechanism,
+ * dem DataEncapsulationMechanism
+ * }
+ *
+ * KeyEncapsulationMechanism ::= AlgorithmIdentifier {{KEMAlgorithms}}
+ * DataEncapsulationMechanism ::= AlgorithmIdentifier {{DEMAlgorithms}}
+ * </pre>
+ */
+public class GenericHybridParameters
+ extends ASN1Object
+{
+ private final AlgorithmIdentifier kem;
+ private final AlgorithmIdentifier dem;
+
+ private GenericHybridParameters(ASN1Sequence sequence)
+ {
+ if (sequence.size() != 2)
+ {
+ throw new IllegalArgumentException("ASN.1 SEQUENCE should be of length 2");
+ }
+
+ this.kem = AlgorithmIdentifier.getInstance(sequence.getObjectAt(0));
+ this.dem = AlgorithmIdentifier.getInstance(sequence.getObjectAt(1));
+ }
+
+ public static GenericHybridParameters getInstance(
+ Object o)
+ {
+ if (o instanceof GenericHybridParameters)
+ {
+ return (GenericHybridParameters)o;
+ }
+ else if (o != null)
+ {
+ return new GenericHybridParameters(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public GenericHybridParameters(AlgorithmIdentifier kem, AlgorithmIdentifier dem)
+ {
+ this.kem = kem;
+ this.dem = dem;
+ }
+
+ public AlgorithmIdentifier getDem()
+ {
+ return dem;
+ }
+
+ public AlgorithmIdentifier getKem()
+ {
+ return kem;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(kem);
+ v.add(dem);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RsaKemParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RsaKemParameters.java
new file mode 100644
index 00000000..07315a61
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RsaKemParameters.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.asn1.cms;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * RFC 5990 RSA KEM parameters class.
+ * <pre>
+ * RsaKemParameters ::= SEQUENCE {
+ * keyDerivationFunction KeyDerivationFunction,
+ * keyLength KeyLength
+ * }
+ *
+ * KeyDerivationFunction ::= AlgorithmIdentifier
+ * KeyLength ::= INTEGER (1..MAX)
+ * </pre>
+ */
+public class RsaKemParameters
+ extends ASN1Object
+{
+ private final AlgorithmIdentifier keyDerivationFunction;
+ private final BigInteger keyLength;
+
+ private RsaKemParameters(ASN1Sequence sequence)
+ {
+ if (sequence.size() != 2)
+ {
+ throw new IllegalArgumentException("ASN.1 SEQUENCE should be of length 2");
+ }
+ this.keyDerivationFunction = AlgorithmIdentifier.getInstance(sequence.getObjectAt(0));
+ this.keyLength = ASN1Integer.getInstance(sequence.getObjectAt(1)).getValue();
+ }
+
+ public static RsaKemParameters getInstance(
+ Object o)
+ {
+ if (o instanceof RsaKemParameters)
+ {
+ return (RsaKemParameters)o;
+ }
+ else if (o != null)
+ {
+ return new RsaKemParameters(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ /**
+ * Base constructor.
+ *
+ * @param keyDerivationFunction algorithm ID describing the key derivation function.
+ * @param keyLength length of key to be derived (in bytes).
+ */
+ public RsaKemParameters(AlgorithmIdentifier keyDerivationFunction, int keyLength)
+ {
+ this.keyDerivationFunction = keyDerivationFunction;
+ this.keyLength = BigInteger.valueOf(keyLength);
+ }
+
+ public AlgorithmIdentifier getKeyDerivationFunction()
+ {
+ return keyDerivationFunction;
+ }
+
+ public BigInteger getKeyLength()
+ {
+ return keyLength;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(keyDerivationFunction);
+ v.add(new ASN1Integer(keyLength));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/ECCCMSSharedInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/ECCCMSSharedInfo.java
new file mode 100644
index 00000000..5bfdc869
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/ECCCMSSharedInfo.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.asn1.cms.ecc;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * <pre>
+ * ECC-CMS-SharedInfo ::= SEQUENCE {
+ * keyInfo AlgorithmIdentifier,
+ * entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL,
+ * suppPubInfo [2] EXPLICIT OCTET STRING }
+ * </pre>
+ */
+public class ECCCMSSharedInfo
+ extends ASN1Object
+{
+
+ private final AlgorithmIdentifier keyInfo;
+ private final byte[] entityUInfo;
+ private final byte[] suppPubInfo;
+
+ public ECCCMSSharedInfo(
+ AlgorithmIdentifier keyInfo,
+ byte[] entityUInfo,
+ byte[] suppPubInfo)
+ {
+ this.keyInfo = keyInfo;
+ this.entityUInfo = Arrays.clone(entityUInfo);
+ this.suppPubInfo = Arrays.clone(suppPubInfo);
+ }
+
+ public ECCCMSSharedInfo(
+ AlgorithmIdentifier keyInfo,
+ byte[] suppPubInfo)
+ {
+ this.keyInfo = keyInfo;
+ this.entityUInfo = null;
+ this.suppPubInfo = Arrays.clone(suppPubInfo);
+ }
+
+ private ECCCMSSharedInfo(
+ ASN1Sequence seq)
+ {
+ this.keyInfo = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() == 2)
+ {
+ this.entityUInfo = null;
+ this.suppPubInfo = ASN1OctetString.getInstance((ASN1TaggedObject)seq.getObjectAt(1), true).getOctets();
+ }
+ else
+ {
+ this.entityUInfo = ASN1OctetString.getInstance((ASN1TaggedObject)seq.getObjectAt(1), true).getOctets();
+ this.suppPubInfo = ASN1OctetString.getInstance((ASN1TaggedObject)seq.getObjectAt(2), true).getOctets();
+ }
+ }
+
+ /**
+ * Return an ECC-CMS-SharedInfo object from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want.
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @throws IllegalArgumentException if the object held by the
+ * tagged object cannot be converted.
+ */
+ public static ECCCMSSharedInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static ECCCMSSharedInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof ECCCMSSharedInfo)
+ {
+ return (ECCCMSSharedInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new ECCCMSSharedInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(keyInfo);
+
+ if (entityUInfo != null)
+ {
+ v.add(new DERTaggedObject(true, 0, new DEROctetString(entityUInfo)));
+ }
+
+ v.add(new DERTaggedObject(true, 2, new DEROctetString(suppPubInfo)));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/iso/ISOIECObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/iso/ISOIECObjectIdentifiers.java
new file mode 100644
index 00000000..a9816b10
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/iso/ISOIECObjectIdentifiers.java
@@ -0,0 +1,35 @@
+package org.bouncycastle.asn1.iso;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * OIDS from ISO/IEC 10118-3:2004
+ */
+public interface ISOIECObjectIdentifiers
+{
+ ASN1ObjectIdentifier iso_encryption_algorithms = new ASN1ObjectIdentifier("1.0.10118");
+
+ ASN1ObjectIdentifier hash_algorithms = iso_encryption_algorithms.branch("3.0");
+
+ ASN1ObjectIdentifier ripemd160 = hash_algorithms.branch("49");
+ ASN1ObjectIdentifier ripemd128 = hash_algorithms.branch("50");
+ ASN1ObjectIdentifier whirlpool = hash_algorithms.branch("55");
+
+
+
+ /**
+ * -- ISO/IEC 18033-2 arc
+
+ is18033-2 OID ::= { iso(1) standard(0) is18033(18033) part2(2) }
+ */
+ ASN1ObjectIdentifier is18033_2 = new ASN1ObjectIdentifier("1.0.18033.2");
+
+ ASN1ObjectIdentifier id_ac_generic_hybrid = is18033_2.branch("1.2");
+
+ /**
+ id-kem-rsa OID ::= {
+ is18033-2 key-encapsulation-mechanism(2) rsa(4)
+ }
+ */
+ ASN1ObjectIdentifier id_kem_rsa = is18033_2.branch("2.4");
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/SignedPublicKeyAndChallenge.java b/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/SignedPublicKeyAndChallenge.java
new file mode 100644
index 00000000..14d6534d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/SignedPublicKeyAndChallenge.java
@@ -0,0 +1,64 @@
+package org.bouncycastle.asn1.mozilla;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * <pre>
+ * SignedPublicKeyAndChallenge ::= SEQUENCE {
+ * publicKeyAndChallenge PublicKeyAndChallenge,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING
+ * }
+ *
+ * </pre>
+ */
+public class SignedPublicKeyAndChallenge
+ extends ASN1Object
+{
+ private final PublicKeyAndChallenge pubKeyAndChal;
+ private final ASN1Sequence pkacSeq;
+
+ public static SignedPublicKeyAndChallenge getInstance(Object obj)
+ {
+ if (obj instanceof SignedPublicKeyAndChallenge)
+ {
+ return (SignedPublicKeyAndChallenge)obj;
+ }
+ else if (obj != null)
+ {
+ return new SignedPublicKeyAndChallenge(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private SignedPublicKeyAndChallenge(ASN1Sequence seq)
+ {
+ pkacSeq = seq;
+ pubKeyAndChal = PublicKeyAndChallenge.getInstance(seq.getObjectAt(0));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return pkacSeq;
+ }
+
+ public PublicKeyAndChallenge getPublicKeyAndChallenge()
+ {
+ return pubKeyAndChal;
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return AlgorithmIdentifier.getInstance(pkacSeq.getObjectAt(1));
+ }
+
+ public DERBitString getSignature()
+ {
+ return DERBitString.getInstance(pkacSeq.getObjectAt(2));
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidator.java
new file mode 100644
index 00000000..4596fe0c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidator.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.asn1.x509;
+
+public interface NameConstraintValidator
+{
+ void checkPermitted(GeneralName name)
+ throws NameConstraintValidatorException;
+
+ void checkExcluded(GeneralName name)
+ throws NameConstraintValidatorException;
+
+ void intersectPermittedSubtree(GeneralSubtree permitted);
+
+ void intersectPermittedSubtree(GeneralSubtree[] permitted);
+
+ void intersectEmptyPermittedSubtree(int nameType);
+
+ void addExcludedSubtree(GeneralSubtree subtree);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidatorException.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidatorException.java
new file mode 100644
index 00000000..517fddb0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidatorException.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.asn1.x509;
+
+public class NameConstraintValidatorException
+ extends Exception
+{
+ public NameConstraintValidatorException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
new file mode 100644
index 00000000..0f15dae1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
@@ -0,0 +1,1920 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.Strings;
+
+public class PKIXNameConstraintValidator
+ implements NameConstraintValidator
+{
+ private Set excludedSubtreesDN = new HashSet();
+
+ private Set excludedSubtreesDNS = new HashSet();
+
+ private Set excludedSubtreesEmail = new HashSet();
+
+ private Set excludedSubtreesURI = new HashSet();
+
+ private Set excludedSubtreesIP = new HashSet();
+
+ private Set permittedSubtreesDN;
+
+ private Set permittedSubtreesDNS;
+
+ private Set permittedSubtreesEmail;
+
+ private Set permittedSubtreesURI;
+
+ private Set permittedSubtreesIP;
+
+ public PKIXNameConstraintValidator()
+ {
+ }
+
+ /**
+ * Checks if the given GeneralName is in the permitted set.
+ *
+ * @param name The GeneralName
+ * @throws NameConstraintValidatorException If the <code>name</code>
+ */
+ public void checkPermitted(GeneralName name)
+ throws NameConstraintValidatorException
+ {
+ switch (name.getTagNo())
+ {
+ case GeneralName.rfc822Name:
+ checkPermittedEmail(permittedSubtreesEmail,
+ extractNameAsString(name));
+ break;
+ case GeneralName.dNSName:
+ checkPermittedDNS(permittedSubtreesDNS, DERIA5String.getInstance(
+ name.getName()).getString());
+ break;
+ case GeneralName.directoryName:
+ checkPermittedDN(X500Name.getInstance(name.getName()));
+ break;
+ case GeneralName.uniformResourceIdentifier:
+ checkPermittedURI(permittedSubtreesURI, DERIA5String.getInstance(
+ name.getName()).getString());
+ break;
+ case GeneralName.iPAddress:
+ byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets();
+
+ checkPermittedIP(permittedSubtreesIP, ip);
+ }
+ }
+
+ /**
+ * Check if the given GeneralName is contained in the excluded set.
+ *
+ * @param name The GeneralName.
+ * @throws NameConstraintValidatorException If the <code>name</code> is
+ * excluded.
+ */
+ public void checkExcluded(GeneralName name)
+ throws NameConstraintValidatorException
+ {
+ switch (name.getTagNo())
+ {
+ case GeneralName.rfc822Name:
+ checkExcludedEmail(excludedSubtreesEmail, extractNameAsString(name));
+ break;
+ case GeneralName.dNSName:
+ checkExcludedDNS(excludedSubtreesDNS, DERIA5String.getInstance(
+ name.getName()).getString());
+ break;
+ case GeneralName.directoryName:
+ checkExcludedDN(X500Name.getInstance(name.getName()));
+ break;
+ case GeneralName.uniformResourceIdentifier:
+ checkExcludedURI(excludedSubtreesURI, DERIA5String.getInstance(
+ name.getName()).getString());
+ break;
+ case GeneralName.iPAddress:
+ byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets();
+
+ checkExcludedIP(excludedSubtreesIP, ip);
+ }
+ }
+
+ public void intersectPermittedSubtree(GeneralSubtree permitted)
+ {
+ intersectPermittedSubtree(new GeneralSubtree[]{permitted});
+ }
+
+ /**
+ * Updates the permitted set of these name constraints with the intersection
+ * with the given subtree.
+ *
+ * @param permitted The permitted subtrees
+ */
+ public void intersectPermittedSubtree(GeneralSubtree[] permitted)
+ {
+ Map subtreesMap = new HashMap();
+
+ // group in sets in a map ordered by tag no.
+ for (int i = 0; i != permitted.length; i++)
+ {
+ GeneralSubtree subtree = permitted[i];
+ Integer tagNo = Integers.valueOf(subtree.getBase().getTagNo());
+ if (subtreesMap.get(tagNo) == null)
+ {
+ subtreesMap.put(tagNo, new HashSet());
+ }
+ ((Set)subtreesMap.get(tagNo)).add(subtree);
+ }
+
+ for (Iterator it = subtreesMap.entrySet().iterator(); it.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry)it.next();
+
+ // go through all subtree groups
+ switch (((Integer)entry.getKey()).intValue())
+ {
+ case GeneralName.rfc822Name:
+ permittedSubtreesEmail = intersectEmail(permittedSubtreesEmail,
+ (Set)entry.getValue());
+ break;
+ case GeneralName.dNSName:
+ permittedSubtreesDNS = intersectDNS(permittedSubtreesDNS,
+ (Set)entry.getValue());
+ break;
+ case GeneralName.directoryName:
+ permittedSubtreesDN = intersectDN(permittedSubtreesDN,
+ (Set)entry.getValue());
+ break;
+ case GeneralName.uniformResourceIdentifier:
+ permittedSubtreesURI = intersectURI(permittedSubtreesURI,
+ (Set)entry.getValue());
+ break;
+ case GeneralName.iPAddress:
+ permittedSubtreesIP = intersectIP(permittedSubtreesIP,
+ (Set)entry.getValue());
+ }
+ }
+ }
+
+ public void intersectEmptyPermittedSubtree(int nameType)
+ {
+ switch (nameType)
+ {
+ case GeneralName.rfc822Name:
+ permittedSubtreesEmail = new HashSet();
+ break;
+ case GeneralName.dNSName:
+ permittedSubtreesDNS = new HashSet();
+ break;
+ case GeneralName.directoryName:
+ permittedSubtreesDN = new HashSet();
+ break;
+ case GeneralName.uniformResourceIdentifier:
+ permittedSubtreesURI = new HashSet();
+ break;
+ case GeneralName.iPAddress:
+ permittedSubtreesIP = new HashSet();
+ }
+ }
+
+ /**
+ * Adds a subtree to the excluded set of these name constraints.
+ *
+ * @param subtree A subtree with an excluded GeneralName.
+ */
+ public void addExcludedSubtree(GeneralSubtree subtree)
+ {
+ GeneralName base = subtree.getBase();
+
+ switch (base.getTagNo())
+ {
+ case GeneralName.rfc822Name:
+ excludedSubtreesEmail = unionEmail(excludedSubtreesEmail,
+ extractNameAsString(base));
+ break;
+ case GeneralName.dNSName:
+ excludedSubtreesDNS = unionDNS(excludedSubtreesDNS,
+ extractNameAsString(base));
+ break;
+ case GeneralName.directoryName:
+ excludedSubtreesDN = unionDN(excludedSubtreesDN,
+ (ASN1Sequence)base.getName().toASN1Primitive());
+ break;
+ case GeneralName.uniformResourceIdentifier:
+ excludedSubtreesURI = unionURI(excludedSubtreesURI,
+ extractNameAsString(base));
+ break;
+ case GeneralName.iPAddress:
+ excludedSubtreesIP = unionIP(excludedSubtreesIP, ASN1OctetString
+ .getInstance(base.getName()).getOctets());
+ break;
+ }
+ }
+
+ public int hashCode()
+ {
+ return hashCollection(excludedSubtreesDN)
+ + hashCollection(excludedSubtreesDNS)
+ + hashCollection(excludedSubtreesEmail)
+ + hashCollection(excludedSubtreesIP)
+ + hashCollection(excludedSubtreesURI)
+ + hashCollection(permittedSubtreesDN)
+ + hashCollection(permittedSubtreesDNS)
+ + hashCollection(permittedSubtreesEmail)
+ + hashCollection(permittedSubtreesIP)
+ + hashCollection(permittedSubtreesURI);
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof PKIXNameConstraintValidator))
+ {
+ return false;
+ }
+ PKIXNameConstraintValidator constraintValidator = (PKIXNameConstraintValidator)o;
+ return collectionsAreEqual(constraintValidator.excludedSubtreesDN, excludedSubtreesDN)
+ && collectionsAreEqual(constraintValidator.excludedSubtreesDNS, excludedSubtreesDNS)
+ && collectionsAreEqual(constraintValidator.excludedSubtreesEmail, excludedSubtreesEmail)
+ && collectionsAreEqual(constraintValidator.excludedSubtreesIP, excludedSubtreesIP)
+ && collectionsAreEqual(constraintValidator.excludedSubtreesURI, excludedSubtreesURI)
+ && collectionsAreEqual(constraintValidator.permittedSubtreesDN, permittedSubtreesDN)
+ && collectionsAreEqual(constraintValidator.permittedSubtreesDNS, permittedSubtreesDNS)
+ && collectionsAreEqual(constraintValidator.permittedSubtreesEmail, permittedSubtreesEmail)
+ && collectionsAreEqual(constraintValidator.permittedSubtreesIP, permittedSubtreesIP)
+ && collectionsAreEqual(constraintValidator.permittedSubtreesURI, permittedSubtreesURI);
+ }
+
+ public String toString()
+ {
+ String temp = "";
+ temp += "permitted:\n";
+ if (permittedSubtreesDN != null)
+ {
+ temp += "DN:\n";
+ temp += permittedSubtreesDN.toString() + "\n";
+ }
+ if (permittedSubtreesDNS != null)
+ {
+ temp += "DNS:\n";
+ temp += permittedSubtreesDNS.toString() + "\n";
+ }
+ if (permittedSubtreesEmail != null)
+ {
+ temp += "Email:\n";
+ temp += permittedSubtreesEmail.toString() + "\n";
+ }
+ if (permittedSubtreesURI != null)
+ {
+ temp += "URI:\n";
+ temp += permittedSubtreesURI.toString() + "\n";
+ }
+ if (permittedSubtreesIP != null)
+ {
+ temp += "IP:\n";
+ temp += stringifyIPCollection(permittedSubtreesIP) + "\n";
+ }
+ temp += "excluded:\n";
+ if (!excludedSubtreesDN.isEmpty())
+ {
+ temp += "DN:\n";
+ temp += excludedSubtreesDN.toString() + "\n";
+ }
+ if (!excludedSubtreesDNS.isEmpty())
+ {
+ temp += "DNS:\n";
+ temp += excludedSubtreesDNS.toString() + "\n";
+ }
+ if (!excludedSubtreesEmail.isEmpty())
+ {
+ temp += "Email:\n";
+ temp += excludedSubtreesEmail.toString() + "\n";
+ }
+ if (!excludedSubtreesURI.isEmpty())
+ {
+ temp += "URI:\n";
+ temp += excludedSubtreesURI.toString() + "\n";
+ }
+ if (!excludedSubtreesIP.isEmpty())
+ {
+ temp += "IP:\n";
+ temp += stringifyIPCollection(excludedSubtreesIP) + "\n";
+ }
+ return temp;
+ }
+
+ private void checkPermittedDN(X500Name dns)
+ throws NameConstraintValidatorException
+ {
+ checkPermittedDN(permittedSubtreesDN, ASN1Sequence.getInstance(dns.toASN1Primitive()));
+ }
+
+ private void checkExcludedDN(X500Name dns)
+ throws NameConstraintValidatorException
+ {
+ checkExcludedDN(excludedSubtreesDN, ASN1Sequence.getInstance(dns));
+ }
+
+ private static boolean withinDNSubtree(
+ ASN1Sequence dns,
+ ASN1Sequence subtree)
+ {
+ if (subtree.size() < 1)
+ {
+ return false;
+ }
+
+ if (subtree.size() > dns.size())
+ {
+ return false;
+ }
+
+ for (int j = subtree.size() - 1; j >= 0; j--)
+ {
+ if (!subtree.getObjectAt(j).equals(dns.getObjectAt(j)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void checkPermittedDN(Set permitted, ASN1Sequence dns)
+ throws NameConstraintValidatorException
+ {
+ if (permitted == null)
+ {
+ return;
+ }
+
+ if (permitted.isEmpty() && dns.size() == 0)
+ {
+ return;
+ }
+ Iterator it = permitted.iterator();
+
+ while (it.hasNext())
+ {
+ ASN1Sequence subtree = (ASN1Sequence)it.next();
+
+ if (withinDNSubtree(dns, subtree))
+ {
+ return;
+ }
+ }
+
+ throw new NameConstraintValidatorException(
+ "Subject distinguished name is not from a permitted subtree");
+ }
+
+ private void checkExcludedDN(Set excluded, ASN1Sequence dns)
+ throws NameConstraintValidatorException
+ {
+ if (excluded.isEmpty())
+ {
+ return;
+ }
+
+ Iterator it = excluded.iterator();
+
+ while (it.hasNext())
+ {
+ ASN1Sequence subtree = (ASN1Sequence)it.next();
+
+ if (withinDNSubtree(dns, subtree))
+ {
+ throw new NameConstraintValidatorException(
+ "Subject distinguished name is from an excluded subtree");
+ }
+ }
+ }
+
+ private Set intersectDN(Set permitted, Set dns)
+ {
+ Set intersect = new HashSet();
+ for (Iterator it = dns.iterator(); it.hasNext(); )
+ {
+ ASN1Sequence dn = ASN1Sequence.getInstance(((GeneralSubtree)it
+ .next()).getBase().getName().toASN1Primitive());
+ if (permitted == null)
+ {
+ if (dn != null)
+ {
+ intersect.add(dn);
+ }
+ }
+ else
+ {
+ Iterator _iter = permitted.iterator();
+ while (_iter.hasNext())
+ {
+ ASN1Sequence subtree = (ASN1Sequence)_iter.next();
+
+ if (withinDNSubtree(dn, subtree))
+ {
+ intersect.add(dn);
+ }
+ else if (withinDNSubtree(subtree, dn))
+ {
+ intersect.add(subtree);
+ }
+ }
+ }
+ }
+ return intersect;
+ }
+
+ private Set unionDN(Set excluded, ASN1Sequence dn)
+ {
+ if (excluded.isEmpty())
+ {
+ if (dn == null)
+ {
+ return excluded;
+ }
+ excluded.add(dn);
+
+ return excluded;
+ }
+ else
+ {
+ Set intersect = new HashSet();
+
+ Iterator it = excluded.iterator();
+ while (it.hasNext())
+ {
+ ASN1Sequence subtree = (ASN1Sequence)it.next();
+
+ if (withinDNSubtree(dn, subtree))
+ {
+ intersect.add(subtree);
+ }
+ else if (withinDNSubtree(subtree, dn))
+ {
+ intersect.add(dn);
+ }
+ else
+ {
+ intersect.add(subtree);
+ intersect.add(dn);
+ }
+ }
+
+ return intersect;
+ }
+ }
+
+ private Set intersectEmail(Set permitted, Set emails)
+ {
+ Set intersect = new HashSet();
+ for (Iterator it = emails.iterator(); it.hasNext(); )
+ {
+ String email = extractNameAsString(((GeneralSubtree)it.next())
+ .getBase());
+
+ if (permitted == null)
+ {
+ if (email != null)
+ {
+ intersect.add(email);
+ }
+ }
+ else
+ {
+ Iterator it2 = permitted.iterator();
+ while (it2.hasNext())
+ {
+ String _permitted = (String)it2.next();
+
+ intersectEmail(email, _permitted, intersect);
+ }
+ }
+ }
+ return intersect;
+ }
+
+ private Set unionEmail(Set excluded, String email)
+ {
+ if (excluded.isEmpty())
+ {
+ if (email == null)
+ {
+ return excluded;
+ }
+ excluded.add(email);
+ return excluded;
+ }
+ else
+ {
+ Set union = new HashSet();
+
+ Iterator it = excluded.iterator();
+ while (it.hasNext())
+ {
+ String _excluded = (String)it.next();
+
+ unionEmail(_excluded, email, union);
+ }
+
+ return union;
+ }
+ }
+
+ /**
+ * Returns the intersection of the permitted IP ranges in
+ * <code>permitted</code> with <code>ip</code>.
+ *
+ * @param permitted A <code>Set</code> of permitted IP addresses with
+ * their subnet mask as byte arrays.
+ * @param ips The IP address with its subnet mask.
+ * @return The <code>Set</code> of permitted IP ranges intersected with
+ * <code>ip</code>.
+ */
+ private Set intersectIP(Set permitted, Set ips)
+ {
+ Set intersect = new HashSet();
+ for (Iterator it = ips.iterator(); it.hasNext(); )
+ {
+ byte[] ip = ASN1OctetString.getInstance(
+ ((GeneralSubtree)it.next()).getBase().getName()).getOctets();
+ if (permitted == null)
+ {
+ if (ip != null)
+ {
+ intersect.add(ip);
+ }
+ }
+ else
+ {
+ Iterator it2 = permitted.iterator();
+ while (it2.hasNext())
+ {
+ byte[] _permitted = (byte[])it2.next();
+ intersect.addAll(intersectIPRange(_permitted, ip));
+ }
+ }
+ }
+ return intersect;
+ }
+
+ /**
+ * Returns the union of the excluded IP ranges in <code>excluded</code>
+ * with <code>ip</code>.
+ *
+ * @param excluded A <code>Set</code> of excluded IP addresses with their
+ * subnet mask as byte arrays.
+ * @param ip The IP address with its subnet mask.
+ * @return The <code>Set</code> of excluded IP ranges unified with
+ * <code>ip</code> as byte arrays.
+ */
+ private Set unionIP(Set excluded, byte[] ip)
+ {
+ if (excluded.isEmpty())
+ {
+ if (ip == null)
+ {
+ return excluded;
+ }
+ excluded.add(ip);
+
+ return excluded;
+ }
+ else
+ {
+ Set union = new HashSet();
+
+ Iterator it = excluded.iterator();
+ while (it.hasNext())
+ {
+ byte[] _excluded = (byte[])it.next();
+ union.addAll(unionIPRange(_excluded, ip));
+ }
+
+ return union;
+ }
+ }
+
+ /**
+ * Calculates the union if two IP ranges.
+ *
+ * @param ipWithSubmask1 The first IP address with its subnet mask.
+ * @param ipWithSubmask2 The second IP address with its subnet mask.
+ * @return A <code>Set</code> with the union of both addresses.
+ */
+ private Set unionIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
+ {
+ Set set = new HashSet();
+
+ // difficult, adding always all IPs is not wrong
+ if (Arrays.areEqual(ipWithSubmask1, ipWithSubmask2))
+ {
+ set.add(ipWithSubmask1);
+ }
+ else
+ {
+ set.add(ipWithSubmask1);
+ set.add(ipWithSubmask2);
+ }
+ return set;
+ }
+
+ /**
+ * Calculates the interesction if two IP ranges.
+ *
+ * @param ipWithSubmask1 The first IP address with its subnet mask.
+ * @param ipWithSubmask2 The second IP address with its subnet mask.
+ * @return A <code>Set</code> with the single IP address with its subnet
+ * mask as a byte array or an empty <code>Set</code>.
+ */
+ private Set intersectIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
+ {
+ if (ipWithSubmask1.length != ipWithSubmask2.length)
+ {
+ return Collections.EMPTY_SET;
+ }
+ byte[][] temp = extractIPsAndSubnetMasks(ipWithSubmask1, ipWithSubmask2);
+ byte ip1[] = temp[0];
+ byte subnetmask1[] = temp[1];
+ byte ip2[] = temp[2];
+ byte subnetmask2[] = temp[3];
+
+ byte minMax[][] = minMaxIPs(ip1, subnetmask1, ip2, subnetmask2);
+ byte[] min;
+ byte[] max;
+ max = min(minMax[1], minMax[3]);
+ min = max(minMax[0], minMax[2]);
+
+ // minimum IP address must be bigger than max
+ if (compareTo(min, max) == 1)
+ {
+ return Collections.EMPTY_SET;
+ }
+ // OR keeps all significant bits
+ byte[] ip = or(minMax[0], minMax[2]);
+ byte[] subnetmask = or(subnetmask1, subnetmask2);
+ return Collections.singleton(ipWithSubnetMask(ip, subnetmask));
+ }
+
+ /**
+ * Concatenates the IP address with its subnet mask.
+ *
+ * @param ip The IP address.
+ * @param subnetMask Its subnet mask.
+ * @return The concatenated IP address with its subnet mask.
+ */
+ private byte[] ipWithSubnetMask(byte[] ip, byte[] subnetMask)
+ {
+ int ipLength = ip.length;
+ byte[] temp = new byte[ipLength * 2];
+ System.arraycopy(ip, 0, temp, 0, ipLength);
+ System.arraycopy(subnetMask, 0, temp, ipLength, ipLength);
+ return temp;
+ }
+
+ /**
+ * Splits the IP addresses and their subnet mask.
+ *
+ * @param ipWithSubmask1 The first IP address with the subnet mask.
+ * @param ipWithSubmask2 The second IP address with the subnet mask.
+ * @return An array with two elements. Each element contains the IP address
+ * and the subnet mask in this order.
+ */
+ private byte[][] extractIPsAndSubnetMasks(
+ byte[] ipWithSubmask1,
+ byte[] ipWithSubmask2)
+ {
+ int ipLength = ipWithSubmask1.length / 2;
+ byte ip1[] = new byte[ipLength];
+ byte subnetmask1[] = new byte[ipLength];
+ System.arraycopy(ipWithSubmask1, 0, ip1, 0, ipLength);
+ System.arraycopy(ipWithSubmask1, ipLength, subnetmask1, 0, ipLength);
+
+ byte ip2[] = new byte[ipLength];
+ byte subnetmask2[] = new byte[ipLength];
+ System.arraycopy(ipWithSubmask2, 0, ip2, 0, ipLength);
+ System.arraycopy(ipWithSubmask2, ipLength, subnetmask2, 0, ipLength);
+ return new byte[][]
+ {ip1, subnetmask1, ip2, subnetmask2};
+ }
+
+ /**
+ * Based on the two IP addresses and their subnet masks the IP range is
+ * computed for each IP address - subnet mask pair and returned as the
+ * minimum IP address and the maximum address of the range.
+ *
+ * @param ip1 The first IP address.
+ * @param subnetmask1 The subnet mask of the first IP address.
+ * @param ip2 The second IP address.
+ * @param subnetmask2 The subnet mask of the second IP address.
+ * @return A array with two elements. The first/second element contains the
+ * min and max IP address of the first/second IP address and its
+ * subnet mask.
+ */
+ private byte[][] minMaxIPs(
+ byte[] ip1,
+ byte[] subnetmask1,
+ byte[] ip2,
+ byte[] subnetmask2)
+ {
+ int ipLength = ip1.length;
+ byte[] min1 = new byte[ipLength];
+ byte[] max1 = new byte[ipLength];
+
+ byte[] min2 = new byte[ipLength];
+ byte[] max2 = new byte[ipLength];
+
+ for (int i = 0; i < ipLength; i++)
+ {
+ min1[i] = (byte)(ip1[i] & subnetmask1[i]);
+ max1[i] = (byte)(ip1[i] & subnetmask1[i] | ~subnetmask1[i]);
+
+ min2[i] = (byte)(ip2[i] & subnetmask2[i]);
+ max2[i] = (byte)(ip2[i] & subnetmask2[i] | ~subnetmask2[i]);
+ }
+
+ return new byte[][]{min1, max1, min2, max2};
+ }
+
+ private void checkPermittedEmail(Set permitted, String email)
+ throws NameConstraintValidatorException
+ {
+ if (permitted == null)
+ {
+ return;
+ }
+
+ Iterator it = permitted.iterator();
+
+ while (it.hasNext())
+ {
+ String str = ((String)it.next());
+
+ if (emailIsConstrained(email, str))
+ {
+ return;
+ }
+ }
+
+ if (email.length() == 0 && permitted.size() == 0)
+ {
+ return;
+ }
+
+ throw new NameConstraintValidatorException(
+ "Subject email address is not from a permitted subtree.");
+ }
+
+ private void checkExcludedEmail(Set excluded, String email)
+ throws NameConstraintValidatorException
+ {
+ if (excluded.isEmpty())
+ {
+ return;
+ }
+
+ Iterator it = excluded.iterator();
+
+ while (it.hasNext())
+ {
+ String str = (String)it.next();
+
+ if (emailIsConstrained(email, str))
+ {
+ throw new NameConstraintValidatorException(
+ "Email address is from an excluded subtree.");
+ }
+ }
+ }
+
+ /**
+ * Checks if the IP <code>ip</code> is included in the permitted set
+ * <code>permitted</code>.
+ *
+ * @param permitted A <code>Set</code> of permitted IP addresses with
+ * their subnet mask as byte arrays.
+ * @param ip The IP address.
+ * @throws NameConstraintValidatorException if the IP is not permitted.
+ */
+ private void checkPermittedIP(Set permitted, byte[] ip)
+ throws NameConstraintValidatorException
+ {
+ if (permitted == null)
+ {
+ return;
+ }
+
+ Iterator it = permitted.iterator();
+
+ while (it.hasNext())
+ {
+ byte[] ipWithSubnet = (byte[])it.next();
+
+ if (isIPConstrained(ip, ipWithSubnet))
+ {
+ return;
+ }
+ }
+ if (ip.length == 0 && permitted.size() == 0)
+ {
+ return;
+ }
+ throw new NameConstraintValidatorException(
+ "IP is not from a permitted subtree.");
+ }
+
+ /**
+ * Checks if the IP <code>ip</code> is included in the excluded set
+ * <code>excluded</code>.
+ *
+ * @param excluded A <code>Set</code> of excluded IP addresses with their
+ * subnet mask as byte arrays.
+ * @param ip The IP address.
+ * @throws NameConstraintValidatorException if the IP is excluded.
+ */
+ private void checkExcludedIP(Set excluded, byte[] ip)
+ throws NameConstraintValidatorException
+ {
+ if (excluded.isEmpty())
+ {
+ return;
+ }
+
+ Iterator it = excluded.iterator();
+
+ while (it.hasNext())
+ {
+ byte[] ipWithSubnet = (byte[])it.next();
+
+ if (isIPConstrained(ip, ipWithSubnet))
+ {
+ throw new NameConstraintValidatorException(
+ "IP is from an excluded subtree.");
+ }
+ }
+ }
+
+ /**
+ * Checks if the IP address <code>ip</code> is constrained by
+ * <code>constraint</code>.
+ *
+ * @param ip The IP address.
+ * @param constraint The constraint. This is an IP address concatenated with
+ * its subnetmask.
+ * @return <code>true</code> if constrained, <code>false</code>
+ * otherwise.
+ */
+ private boolean isIPConstrained(byte ip[], byte[] constraint)
+ {
+ int ipLength = ip.length;
+
+ if (ipLength != (constraint.length / 2))
+ {
+ return false;
+ }
+
+ byte[] subnetMask = new byte[ipLength];
+ System.arraycopy(constraint, ipLength, subnetMask, 0, ipLength);
+
+ byte[] permittedSubnetAddress = new byte[ipLength];
+
+ byte[] ipSubnetAddress = new byte[ipLength];
+
+ // the resulting IP address by applying the subnet mask
+ for (int i = 0; i < ipLength; i++)
+ {
+ permittedSubnetAddress[i] = (byte)(constraint[i] & subnetMask[i]);
+ ipSubnetAddress[i] = (byte)(ip[i] & subnetMask[i]);
+ }
+
+ return Arrays.areEqual(permittedSubnetAddress, ipSubnetAddress);
+ }
+
+ private boolean emailIsConstrained(String email, String constraint)
+ {
+ String sub = email.substring(email.indexOf('@') + 1);
+ // a particular mailbox
+ if (constraint.indexOf('@') != -1)
+ {
+ if (email.equalsIgnoreCase(constraint))
+ {
+ return true;
+ }
+ }
+ // on particular host
+ else if (!(constraint.charAt(0) == '.'))
+ {
+ if (sub.equalsIgnoreCase(constraint))
+ {
+ return true;
+ }
+ }
+ // address in sub domain
+ else if (withinDomain(sub, constraint))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean withinDomain(String testDomain, String domain)
+ {
+ String tempDomain = domain;
+ if (tempDomain.startsWith("."))
+ {
+ tempDomain = tempDomain.substring(1);
+ }
+ String[] domainParts = Strings.split(tempDomain, '.');
+ String[] testDomainParts = Strings.split(testDomain, '.');
+ // must have at least one subdomain
+ if (testDomainParts.length <= domainParts.length)
+ {
+ return false;
+ }
+ int d = testDomainParts.length - domainParts.length;
+ for (int i = -1; i < domainParts.length; i++)
+ {
+ if (i == -1)
+ {
+ if (testDomainParts[i + d].equals(""))
+ {
+ return false;
+ }
+ }
+ else if (!domainParts[i].equalsIgnoreCase(testDomainParts[i + d]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void checkPermittedDNS(Set permitted, String dns)
+ throws NameConstraintValidatorException
+ {
+ if (permitted == null)
+ {
+ return;
+ }
+
+ Iterator it = permitted.iterator();
+
+ while (it.hasNext())
+ {
+ String str = ((String)it.next());
+
+ // is sub domain
+ if (withinDomain(dns, str) || dns.equalsIgnoreCase(str))
+ {
+ return;
+ }
+ }
+ if (dns.length() == 0 && permitted.size() == 0)
+ {
+ return;
+ }
+ throw new NameConstraintValidatorException(
+ "DNS is not from a permitted subtree.");
+ }
+
+ private void checkExcludedDNS(Set excluded, String dns)
+ throws NameConstraintValidatorException
+ {
+ if (excluded.isEmpty())
+ {
+ return;
+ }
+
+ Iterator it = excluded.iterator();
+
+ while (it.hasNext())
+ {
+ String str = ((String)it.next());
+
+ // is sub domain or the same
+ if (withinDomain(dns, str) || dns.equalsIgnoreCase(str))
+ {
+ throw new NameConstraintValidatorException(
+ "DNS is from an excluded subtree.");
+ }
+ }
+ }
+
+ /**
+ * The common part of <code>email1</code> and <code>email2</code> is
+ * added to the union <code>union</code>. If <code>email1</code> and
+ * <code>email2</code> have nothing in common they are added both.
+ *
+ * @param email1 Email address constraint 1.
+ * @param email2 Email address constraint 2.
+ * @param union The union.
+ */
+ private void unionEmail(String email1, String email2, Set union)
+ {
+ // email1 is a particular address
+ if (email1.indexOf('@') != -1)
+ {
+ String _sub = email1.substring(email1.indexOf('@') + 1);
+ // both are a particular mailbox
+ if (email2.indexOf('@') != -1)
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(_sub, email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (_sub.equalsIgnoreCase(email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ // email1 specifies a domain
+ else if (email1.startsWith("."))
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (withinDomain(_sub, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2)
+ || email1.equalsIgnoreCase(email2))
+ {
+ union.add(email2);
+ }
+ else if (withinDomain(email2, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ else
+ {
+ if (withinDomain(email2, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ // email specifies a host
+ else
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (_sub.equalsIgnoreCase(email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ }
+
+ private void unionURI(String email1, String email2, Set union)
+ {
+ // email1 is a particular address
+ if (email1.indexOf('@') != -1)
+ {
+ String _sub = email1.substring(email1.indexOf('@') + 1);
+ // both are a particular mailbox
+ if (email2.indexOf('@') != -1)
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(_sub, email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (_sub.equalsIgnoreCase(email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ // email1 specifies a domain
+ else if (email1.startsWith("."))
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (withinDomain(_sub, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2)
+ || email1.equalsIgnoreCase(email2))
+ {
+ union.add(email2);
+ }
+ else if (withinDomain(email2, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ else
+ {
+ if (withinDomain(email2, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ // email specifies a host
+ else
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (_sub.equalsIgnoreCase(email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ }
+
+ private Set intersectDNS(Set permitted, Set dnss)
+ {
+ Set intersect = new HashSet();
+ for (Iterator it = dnss.iterator(); it.hasNext(); )
+ {
+ String dns = extractNameAsString(((GeneralSubtree)it.next())
+ .getBase());
+ if (permitted == null)
+ {
+ if (dns != null)
+ {
+ intersect.add(dns);
+ }
+ }
+ else
+ {
+ Iterator _iter = permitted.iterator();
+ while (_iter.hasNext())
+ {
+ String _permitted = (String)_iter.next();
+
+ if (withinDomain(_permitted, dns))
+ {
+ intersect.add(_permitted);
+ }
+ else if (withinDomain(dns, _permitted))
+ {
+ intersect.add(dns);
+ }
+ }
+ }
+ }
+
+ return intersect;
+ }
+
+ private Set unionDNS(Set excluded, String dns)
+ {
+ if (excluded.isEmpty())
+ {
+ if (dns == null)
+ {
+ return excluded;
+ }
+ excluded.add(dns);
+
+ return excluded;
+ }
+ else
+ {
+ Set union = new HashSet();
+
+ Iterator _iter = excluded.iterator();
+ while (_iter.hasNext())
+ {
+ String _permitted = (String)_iter.next();
+
+ if (withinDomain(_permitted, dns))
+ {
+ union.add(dns);
+ }
+ else if (withinDomain(dns, _permitted))
+ {
+ union.add(_permitted);
+ }
+ else
+ {
+ union.add(_permitted);
+ union.add(dns);
+ }
+ }
+
+ return union;
+ }
+ }
+
+ /**
+ * The most restricting part from <code>email1</code> and
+ * <code>email2</code> is added to the intersection <code>intersect</code>.
+ *
+ * @param email1 Email address constraint 1.
+ * @param email2 Email address constraint 2.
+ * @param intersect The intersection.
+ */
+ private void intersectEmail(String email1, String email2, Set intersect)
+ {
+ // email1 is a particular address
+ if (email1.indexOf('@') != -1)
+ {
+ String _sub = email1.substring(email1.indexOf('@') + 1);
+ // both are a particular mailbox
+ if (email2.indexOf('@') != -1)
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(_sub, email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (_sub.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ }
+ // email specifies a domain
+ else if (email1.startsWith("."))
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (withinDomain(_sub, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2)
+ || email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ else if (withinDomain(email2, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ else
+ {
+ if (withinDomain(email2, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ }
+ // email1 specifies a host
+ else
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email2.indexOf('@') + 1);
+ if (_sub.equalsIgnoreCase(email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ }
+ }
+
+ private void checkExcludedURI(Set excluded, String uri)
+ throws NameConstraintValidatorException
+ {
+ if (excluded.isEmpty())
+ {
+ return;
+ }
+
+ Iterator it = excluded.iterator();
+
+ while (it.hasNext())
+ {
+ String str = ((String)it.next());
+
+ if (isUriConstrained(uri, str))
+ {
+ throw new NameConstraintValidatorException(
+ "URI is from an excluded subtree.");
+ }
+ }
+ }
+
+ private Set intersectURI(Set permitted, Set uris)
+ {
+ Set intersect = new HashSet();
+ for (Iterator it = uris.iterator(); it.hasNext(); )
+ {
+ String uri = extractNameAsString(((GeneralSubtree)it.next())
+ .getBase());
+ if (permitted == null)
+ {
+ if (uri != null)
+ {
+ intersect.add(uri);
+ }
+ }
+ else
+ {
+ Iterator _iter = permitted.iterator();
+ while (_iter.hasNext())
+ {
+ String _permitted = (String)_iter.next();
+ intersectURI(_permitted, uri, intersect);
+ }
+ }
+ }
+ return intersect;
+ }
+
+ private Set unionURI(Set excluded, String uri)
+ {
+ if (excluded.isEmpty())
+ {
+ if (uri == null)
+ {
+ return excluded;
+ }
+ excluded.add(uri);
+
+ return excluded;
+ }
+ else
+ {
+ Set union = new HashSet();
+
+ Iterator _iter = excluded.iterator();
+ while (_iter.hasNext())
+ {
+ String _excluded = (String)_iter.next();
+
+ unionURI(_excluded, uri, union);
+ }
+
+ return union;
+ }
+ }
+
+ private void intersectURI(String email1, String email2, Set intersect)
+ {
+ // email1 is a particular address
+ if (email1.indexOf('@') != -1)
+ {
+ String _sub = email1.substring(email1.indexOf('@') + 1);
+ // both are a particular mailbox
+ if (email2.indexOf('@') != -1)
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(_sub, email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (_sub.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ }
+ // email specifies a domain
+ else if (email1.startsWith("."))
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (withinDomain(_sub, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2)
+ || email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ else if (withinDomain(email2, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ else
+ {
+ if (withinDomain(email2, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ }
+ // email1 specifies a host
+ else
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email2.indexOf('@') + 1);
+ if (_sub.equalsIgnoreCase(email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ }
+ }
+
+ private void checkPermittedURI(Set permitted, String uri)
+ throws NameConstraintValidatorException
+ {
+ if (permitted == null)
+ {
+ return;
+ }
+
+ Iterator it = permitted.iterator();
+
+ while (it.hasNext())
+ {
+ String str = ((String)it.next());
+
+ if (isUriConstrained(uri, str))
+ {
+ return;
+ }
+ }
+ if (uri.length() == 0 && permitted.size() == 0)
+ {
+ return;
+ }
+ throw new NameConstraintValidatorException(
+ "URI is not from a permitted subtree.");
+ }
+
+ private boolean isUriConstrained(String uri, String constraint)
+ {
+ String host = extractHostFromURL(uri);
+ // a host
+ if (!constraint.startsWith("."))
+ {
+ if (host.equalsIgnoreCase(constraint))
+ {
+ return true;
+ }
+ }
+
+ // in sub domain or domain
+ else if (withinDomain(host, constraint))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static String extractHostFromURL(String url)
+ {
+ // see RFC 1738
+ // remove ':' after protocol, e.g. http:
+ String sub = url.substring(url.indexOf(':') + 1);
+ // extract host from Common Internet Scheme Syntax, e.g. http://
+ if (sub.indexOf("//") != -1)
+ {
+ sub = sub.substring(sub.indexOf("//") + 2);
+ }
+ // first remove port, e.g. http://test.com:21
+ if (sub.lastIndexOf(':') != -1)
+ {
+ sub = sub.substring(0, sub.lastIndexOf(':'));
+ }
+ // remove user and password, e.g. http://john:password@test.com
+ sub = sub.substring(sub.indexOf(':') + 1);
+ sub = sub.substring(sub.indexOf('@') + 1);
+ // remove local parts, e.g. http://test.com/bla
+ if (sub.indexOf('/') != -1)
+ {
+ sub = sub.substring(0, sub.indexOf('/'));
+ }
+ return sub;
+ }
+
+ private String extractNameAsString(GeneralName name)
+ {
+ return DERIA5String.getInstance(name.getName()).getString();
+ }
+
+ /**
+ * Returns the maximum IP address.
+ *
+ * @param ip1 The first IP address.
+ * @param ip2 The second IP address.
+ * @return The maximum IP address.
+ */
+ private static byte[] max(byte[] ip1, byte[] ip2)
+ {
+ for (int i = 0; i < ip1.length; i++)
+ {
+ if ((ip1[i] & 0xFFFF) > (ip2[i] & 0xFFFF))
+ {
+ return ip1;
+ }
+ }
+ return ip2;
+ }
+
+ /**
+ * Returns the minimum IP address.
+ *
+ * @param ip1 The first IP address.
+ * @param ip2 The second IP address.
+ * @return The minimum IP address.
+ */
+ private static byte[] min(byte[] ip1, byte[] ip2)
+ {
+ for (int i = 0; i < ip1.length; i++)
+ {
+ if ((ip1[i] & 0xFFFF) < (ip2[i] & 0xFFFF))
+ {
+ return ip1;
+ }
+ }
+ return ip2;
+ }
+
+ /**
+ * Compares IP address <code>ip1</code> with <code>ip2</code>. If ip1
+ * is equal to ip2 0 is returned. If ip1 is bigger 1 is returned, -1
+ * otherwise.
+ *
+ * @param ip1 The first IP address.
+ * @param ip2 The second IP address.
+ * @return 0 if ip1 is equal to ip2, 1 if ip1 is bigger, -1 otherwise.
+ */
+ private static int compareTo(byte[] ip1, byte[] ip2)
+ {
+ if (Arrays.areEqual(ip1, ip2))
+ {
+ return 0;
+ }
+ if (Arrays.areEqual(max(ip1, ip2), ip1))
+ {
+ return 1;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the logical OR of the IP addresses <code>ip1</code> and
+ * <code>ip2</code>.
+ *
+ * @param ip1 The first IP address.
+ * @param ip2 The second IP address.
+ * @return The OR of <code>ip1</code> and <code>ip2</code>.
+ */
+ private static byte[] or(byte[] ip1, byte[] ip2)
+ {
+ byte[] temp = new byte[ip1.length];
+ for (int i = 0; i < ip1.length; i++)
+ {
+ temp[i] = (byte)(ip1[i] | ip2[i]);
+ }
+ return temp;
+ }
+
+ private int hashCollection(Collection coll)
+ {
+ if (coll == null)
+ {
+ return 0;
+ }
+ int hash = 0;
+ Iterator it1 = coll.iterator();
+ while (it1.hasNext())
+ {
+ Object o = it1.next();
+ if (o instanceof byte[])
+ {
+ hash += Arrays.hashCode((byte[])o);
+ }
+ else
+ {
+ hash += o.hashCode();
+ }
+ }
+ return hash;
+ }
+
+ private boolean collectionsAreEqual(Collection coll1, Collection coll2)
+ {
+ if (coll1 == coll2)
+ {
+ return true;
+ }
+ if (coll1 == null || coll2 == null)
+ {
+ return false;
+ }
+ if (coll1.size() != coll2.size())
+ {
+ return false;
+ }
+ Iterator it1 = coll1.iterator();
+
+ while (it1.hasNext())
+ {
+ Object a = it1.next();
+ Iterator it2 = coll2.iterator();
+ boolean found = false;
+ while (it2.hasNext())
+ {
+ Object b = it2.next();
+ if (equals(a, b))
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean equals(Object o1, Object o2)
+ {
+ if (o1 == o2)
+ {
+ return true;
+ }
+ if (o1 == null || o2 == null)
+ {
+ return false;
+ }
+ if (o1 instanceof byte[] && o2 instanceof byte[])
+ {
+ return Arrays.areEqual((byte[])o1, (byte[])o2);
+ }
+ else
+ {
+ return o1.equals(o2);
+ }
+ }
+
+ /**
+ * Stringifies an IPv4 or v6 address with subnet mask.
+ *
+ * @param ip The IP with subnet mask.
+ * @return The stringified IP address.
+ */
+ private String stringifyIP(byte[] ip)
+ {
+ String temp = "";
+ for (int i = 0; i < ip.length / 2; i++)
+ {
+ temp += Integer.toString(ip[i] & 0x00FF) + ".";
+ }
+ temp = temp.substring(0, temp.length() - 1);
+ temp += "/";
+ for (int i = ip.length / 2; i < ip.length; i++)
+ {
+ temp += Integer.toString(ip[i] & 0x00FF) + ".";
+ }
+ temp = temp.substring(0, temp.length() - 1);
+ return temp;
+ }
+
+ private String stringifyIPCollection(Set ips)
+ {
+ String temp = "";
+ temp += "[";
+ for (Iterator it = ips.iterator(); it.hasNext(); )
+ {
+ temp += stringifyIP((byte[])it.next()) + ",";
+ }
+ if (temp.length() > 1)
+ {
+ temp = temp.substring(0, temp.length() - 1);
+ }
+ temp += "]";
+ return temp;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/DomainParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/DomainParameters.java
new file mode 100644
index 00000000..e23f1b84
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/DomainParameters.java
@@ -0,0 +1,223 @@
+package org.bouncycastle.asn1.x9;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * X9.44 Diffie-Hellman domain parameters.
+ * <pre>
+ * DomainParameters ::= SEQUENCE {
+ * p INTEGER, -- odd prime, p=jq +1
+ * g INTEGER, -- generator, g
+ * q INTEGER, -- factor of p-1
+ * j INTEGER OPTIONAL, -- subgroup factor, j>= 2
+ * validationParams ValidationParams OPTIONAL
+ * }
+ * </pre>
+ */
+public class DomainParameters
+ extends ASN1Object
+{
+ private final ASN1Integer p, g, q, j;
+ private final ValidationParams validationParams;
+
+ /**
+ * Return a DomainParameters object from the passed in tagged object.
+ *
+ * @param obj a tagged object.
+ * @param explicit true if the contents of the object is explictly tagged, false otherwise.
+ * @return a DomainParameters
+ */
+ public static DomainParameters getInstance(ASN1TaggedObject obj, boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * Return a DomainParameters object from the passed in object.
+ *
+ * @param obj an object for conversion or a byte[].
+ * @return a DomainParameters
+ */
+ public static DomainParameters getInstance(Object obj)
+ {
+ if (obj instanceof DomainParameters)
+ {
+ return (DomainParameters)obj;
+ }
+ else if (obj != null)
+ {
+ return new DomainParameters(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Base constructor - the full domain parameter set.
+ *
+ * @param p the prime p defining the Galois field.
+ * @param g the generator of the multiplicative subgroup of order g.
+ * @param q specifies the prime factor of p - 1
+ * @param j optionally specifies the value that satisfies the equation p = jq+1
+ * @param validationParams parameters for validating these domain parameters.
+ */
+ public DomainParameters(BigInteger p, BigInteger g, BigInteger q, BigInteger j,
+ ValidationParams validationParams)
+ {
+ if (p == null)
+ {
+ throw new IllegalArgumentException("'p' cannot be null");
+ }
+ if (g == null)
+ {
+ throw new IllegalArgumentException("'g' cannot be null");
+ }
+ if (q == null)
+ {
+ throw new IllegalArgumentException("'q' cannot be null");
+ }
+
+ this.p = new ASN1Integer(p);
+ this.g = new ASN1Integer(g);
+ this.q = new ASN1Integer(q);
+
+ if (j != null)
+ {
+ this.j = new ASN1Integer(j);
+ }
+ else
+ {
+ this.j = null;
+ }
+ this.validationParams = validationParams;
+ }
+
+ private DomainParameters(ASN1Sequence seq)
+ {
+ if (seq.size() < 3 || seq.size() > 5)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+ this.p = ASN1Integer.getInstance(e.nextElement());
+ this.g = ASN1Integer.getInstance(e.nextElement());
+ this.q = ASN1Integer.getInstance(e.nextElement());
+
+ ASN1Encodable next = getNext(e);
+
+ if (next != null && next instanceof ASN1Integer)
+ {
+ this.j = ASN1Integer.getInstance(next);
+ next = getNext(e);
+ }
+ else
+ {
+ this.j = null;
+ }
+
+ if (next != null)
+ {
+ this.validationParams = ValidationParams.getInstance(next.toASN1Primitive());
+ }
+ else
+ {
+ this.validationParams = null;
+ }
+ }
+
+ private static ASN1Encodable getNext(Enumeration e)
+ {
+ return e.hasMoreElements() ? (ASN1Encodable)e.nextElement() : null;
+ }
+
+ /**
+ * Return the prime p defining the Galois field.
+ *
+ * @return the prime p.
+ */
+ public BigInteger getP()
+ {
+ return this.p.getPositiveValue();
+ }
+
+ /**
+ * Return the generator of the multiplicative subgroup of order g.
+ *
+ * @return the generator g.
+ */
+ public BigInteger getG()
+ {
+ return this.g.getPositiveValue();
+ }
+
+ /**
+ * Return q, the prime factor of p - 1
+ *
+ * @return q value
+ */
+ public BigInteger getQ()
+ {
+ return this.q.getPositiveValue();
+ }
+
+ /**
+ * Return the value that satisfies the equation p = jq+1 (if present).
+ *
+ * @return j value or null.
+ */
+ public BigInteger getJ()
+ {
+ if (this.j == null)
+ {
+ return null;
+ }
+
+ return this.j.getPositiveValue();
+ }
+
+ /**
+ * Return the validation parameters for this set (if present).
+ *
+ * @return validation parameters, or null if absent.
+ */
+ public ValidationParams getValidationParams()
+ {
+ return this.validationParams;
+ }
+
+ /**
+ * Return an ASN.1 primitive representation of this object.
+ *
+ * @return a DERSequence containing the parameter values.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(this.p);
+ v.add(this.g);
+ v.add(this.q);
+
+ if (this.j != null)
+ {
+ v.add(this.j);
+ }
+
+ if (this.validationParams != null)
+ {
+ v.add(this.validationParams);
+ }
+
+ return new DERSequence(v);
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ValidationParams.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ValidationParams.java
new file mode 100644
index 00000000..34ad746a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ValidationParams.java
@@ -0,0 +1,99 @@
+package org.bouncycastle.asn1.x9;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * ValidationParams ::= SEQUENCE {
+ * seed BIT STRING,
+ * pgenCounter INTEGER
+ * }
+ */
+public class ValidationParams
+ extends ASN1Object
+{
+ private DERBitString seed;
+ private ASN1Integer pgenCounter;
+
+ public static ValidationParams getInstance(ASN1TaggedObject obj, boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static ValidationParams getInstance(Object obj)
+ {
+ if (obj instanceof ValidationParams)
+ {
+ return (ValidationParams)obj;
+ }
+ else if (obj != null)
+ {
+ return new ValidationParams(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ValidationParams(byte[] seed, int pgenCounter)
+ {
+ if (seed == null)
+ {
+ throw new IllegalArgumentException("'seed' cannot be null");
+ }
+
+ this.seed = new DERBitString(seed);
+ this.pgenCounter = new ASN1Integer(pgenCounter);
+ }
+
+ public ValidationParams(DERBitString seed, ASN1Integer pgenCounter)
+ {
+ if (seed == null)
+ {
+ throw new IllegalArgumentException("'seed' cannot be null");
+ }
+ if (pgenCounter == null)
+ {
+ throw new IllegalArgumentException("'pgenCounter' cannot be null");
+ }
+
+ this.seed = seed;
+ this.pgenCounter = pgenCounter;
+ }
+
+ private ValidationParams(ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ this.seed = DERBitString.getInstance(seq.getObjectAt(0));
+ this.pgenCounter = ASN1Integer.getInstance(seq.getObjectAt(1));
+ }
+
+ public byte[] getSeed()
+ {
+ return this.seed.getBytes();
+ }
+
+ public BigInteger getPgenCounter()
+ {
+ return this.pgenCounter.getPositiveValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(this.seed);
+ v.add(this.pgenCounter);
+ return new DERSequence(v);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CharToByteConverter.java b/bcprov/src/main/java/org/bouncycastle/crypto/CharToByteConverter.java
new file mode 100644
index 00000000..8b640993
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/CharToByteConverter.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.crypto;
+
+/**
+ * Interface for a converter that produces a byte encoding for a char array.
+ */
+public interface CharToByteConverter
+{
+ /**
+ * Return the type of the conversion.
+ *
+ * @return a type name for the conversion.
+ */
+ String getType();
+
+ /**
+ * Return a byte encoded representation of the passed in password.
+ *
+ * @param password the characters to encode.
+ * @return a byte encoding of password.
+ */
+ byte[] convert(char[] password);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/Xof.java b/bcprov/src/main/java/org/bouncycastle/crypto/Xof.java
new file mode 100644
index 00000000..9183a7a2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/Xof.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.crypto;
+
+/**
+ * With FIPS PUB 202 a new kind of message digest was announced which supported extendable output, or variable digest sizes.
+ * This interface provides the extra method required to support variable output on an extended digest implementation.
+ */
+public interface Xof
+ extends ExtendedDigest
+{
+ /**
+ * Output the results of the final calculation for this digest to outLen number of bytes.
+ *
+ * @param out output array to write the output bytes to.
+ * @param outOff offset to start writing the bytes at.
+ * @param outLen the number of output bytes requested.
+ * @return the number of bytes written
+ */
+ int doFinal(byte[] out, int outOff, int outLen);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java
new file mode 100644
index 00000000..c3353f9d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.crypto.agreement.kdf;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.KDFParameters;
+
+/**
+ * Generator for Concatenation Key Derivation Function defined in NIST SP 800-56A, Sect 5.8.1
+ */
+public class ConcatenationKDFGenerator
+ implements DerivationFunction
+{
+ private Digest digest;
+ private byte[] shared;
+ private byte[] otherInfo;
+ private int hLen;
+
+ /**
+ * @param digest the digest to be used as the source of generated bytes
+ */
+ public ConcatenationKDFGenerator(
+ Digest digest)
+ {
+ this.digest = digest;
+ this.hLen = digest.getDigestSize();
+ }
+
+ public void init(
+ DerivationParameters param)
+ {
+ if (param instanceof KDFParameters)
+ {
+ KDFParameters p = (KDFParameters)param;
+
+ shared = p.getSharedSecret();
+ otherInfo = p.getIV();
+ }
+ else
+ {
+ throw new IllegalArgumentException("KDF parameters required for KDF2Generator");
+ }
+ }
+
+ /**
+ * return the underlying digest.
+ */
+ public Digest getDigest()
+ {
+ return digest;
+ }
+
+ /**
+ * int to octet string.
+ */
+ private void ItoOSP(
+ int i,
+ byte[] sp)
+ {
+ sp[0] = (byte)(i >>> 24);
+ sp[1] = (byte)(i >>> 16);
+ sp[2] = (byte)(i >>> 8);
+ sp[3] = (byte)(i >>> 0);
+ }
+
+ /**
+ * fill len bytes of the output buffer with bytes generated from
+ * the derivation function.
+ *
+ * @throws DataLengthException if the out buffer is too small.
+ */
+ public int generateBytes(
+ byte[] out,
+ int outOff,
+ int len)
+ throws DataLengthException, IllegalArgumentException
+ {
+ if ((out.length - len) < outOff)
+ {
+ throw new DataLengthException("output buffer too small");
+ }
+
+ byte[] hashBuf = new byte[hLen];
+ byte[] C = new byte[4];
+ int counter = 1;
+ int outputLen = 0;
+
+ digest.reset();
+
+ if (len > hLen)
+ {
+ do
+ {
+ ItoOSP(counter, C);
+
+ digest.update(C, 0, C.length);
+ digest.update(shared, 0, shared.length);
+ digest.update(otherInfo, 0, otherInfo.length);
+
+ digest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, out, outOff + outputLen, hLen);
+ outputLen += hLen;
+ }
+ while ((counter++) < (len / hLen));
+ }
+
+ if (outputLen < len)
+ {
+ ItoOSP(counter, C);
+
+ digest.update(C, 0, C.length);
+ digest.update(shared, 0, shared.length);
+ digest.update(otherInfo, 0, otherInfo.length);
+
+ digest.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, out, outOff + outputLen, len - outputLen);
+ }
+
+ return len;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java
new file mode 100644
index 00000000..0fd3ebb9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java
@@ -0,0 +1,580 @@
+package org.bouncycastle.crypto.digests;
+
+
+/* The BLAKE2 cryptographic hash function was designed by Jean-
+ Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian
+ Winnerlein.
+
+ Reference Implementation and Description can be found at: https://blake2.net/
+ Internet Draft: https://tools.ietf.org/html/draft-saarinen-blake2-02
+
+ This implementation does not support the Tree Hashing Mode.
+
+ For unkeyed hashing, developers adapting BLAKE2 to ASN.1 - based
+ message formats SHOULD use the OID tree at x = 1.3.6.1.4.1.1722.12.2.
+
+ Algorithm | Target | Collision | Hash | Hash ASN.1 |
+ Identifier | Arch | Security | nn | OID Suffix |
+ ---------------+--------+-----------+------+------------+
+ id-blake2b160 | 64-bit | 2**80 | 20 | x.1.20 |
+ id-blake2b256 | 64-bit | 2**128 | 32 | x.1.32 |
+ id-blake2b384 | 64-bit | 2**192 | 48 | x.1.48 |
+ id-blake2b512 | 64-bit | 2**256 | 64 | x.1.64 |
+ ---------------+--------+-----------+------+------------+
+ */
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Arrays;
+
+
+/**
+ * Implementation of the cryptographic hash function Blakbe2b.
+ *
+ * Blake2b offers a built-in keying mechanism to be used directly
+ * for authentication ("Prefix-MAC") rather than a HMAC construction.
+ *
+ * Blake2b offers a built-in support for a salt for randomized hashing
+ * and a personal string for defining a unique hash function for each application.
+ *
+ * BLAKE2b is optimized for 64-bit platforms and produces digests of any size
+ * between 1 and 64 bytes.
+ */
+public class Blake2bDigest
+ implements ExtendedDigest
+{
+ // Blake2b Initialization Vector:
+ private final static long blake2b_IV[] =
+ // Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19.
+ // The same as SHA-512 IV.
+ {
+ 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL,
+ 0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL,
+ 0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L
+ };
+
+ // Message word permutations:
+ private final static byte[][] blake2b_sigma =
+ {
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
+ };
+
+ private static int rOUNDS = 12; // to use for Catenas H'
+ private final static int BLOCK_LENGTH_BYTES = 128;// bytes
+
+ // General parameters:
+ private int digestLength = 64; // 1- 64 bytes
+ private int keyLength = 0; // 0 - 64 bytes for keyed hashing for MAC
+ private byte[] salt = null;// new byte[16];
+ private byte[] personalization = null;// new byte[16];
+
+ // the key
+ private byte[] key = null;
+
+ // Tree hashing parameters:
+ // Because this class does not implement the Tree Hashing Mode,
+ // these parameters can be treated as constants (see init() function)
+ /*
+ * private int fanout = 1; // 0-255 private int depth = 1; // 1 - 255
+ * private int leafLength= 0; private long nodeOffset = 0L; private int
+ * nodeDepth = 0; private int innerHashLength = 0;
+ */
+
+ // whenever this buffer overflows, it will be processed
+ // in the compress() function.
+ // For performance issues, long messages will not use this buffer.
+ private byte[] buffer = null;// new byte[BLOCK_LENGTH_BYTES];
+ // Position of last inserted byte:
+ private int bufferPos = 0;// a value from 0 up to 128
+
+ private long[] internalState = new long[16]; // In the Blake2b paper it is
+ // called: v
+ private long[] chainValue = null; // state vector, in the Blake2b paper it
+ // is called: h
+
+ private long t0 = 0L; // holds last significant bits, counter (counts bytes)
+ private long t1 = 0L; // counter: Length up to 2^128 are supported
+ private long f0 = 0L; // finalization flag, for last block: ~0L
+
+ // For Tree Hashing Mode, not used here:
+ // private long f1 = 0L; // finalization flag, for last node: ~0L
+
+ public Blake2bDigest()
+ {
+ this(512);
+ }
+
+ public Blake2bDigest(Blake2bDigest digest)
+ {
+ this.bufferPos = digest.bufferPos;
+ this.buffer = Arrays.clone(digest.buffer);
+ this.keyLength = digest.keyLength;
+ this.key = Arrays.clone(digest.key);
+ this.digestLength = digest.digestLength;
+ this.chainValue = Arrays.clone(digest.chainValue);
+ this.personalization = Arrays.clone(personalization);
+ }
+
+ public Blake2bDigest(int digestLength)
+ {
+ if (digestLength != 160 && digestLength != 256 && digestLength != 384 && digestLength != 512)
+ {
+ throw new IllegalArgumentException("Blake2b digest restricted to one of [160, 256, 384, 512]");
+ }
+
+ buffer = new byte[BLOCK_LENGTH_BYTES];
+ keyLength = 0;
+ this.digestLength = digestLength / 8;
+ init();
+ }
+
+ /**
+ * Blake2b for authentication ("Prefix-MAC mode").
+ * After calling the doFinal() method, the key will
+ * remain to be used for further computations of
+ * this instance.
+ * The key can be overwritten using the clearKey() method.
+ *
+ * @param key A key up to 64 bytes or null
+ */
+ public Blake2bDigest(byte[] key)
+ {
+ buffer = new byte[BLOCK_LENGTH_BYTES];
+ if (key != null)
+ {
+ this.key = new byte[key.length];
+ System.arraycopy(key, 0, this.key, 0, key.length);
+
+ if (key.length > 64)
+ {
+ throw new IllegalArgumentException(
+ "Keys > 64 are not supported");
+ }
+ keyLength = key.length;
+ System.arraycopy(key, 0, buffer, 0, key.length);
+ bufferPos = BLOCK_LENGTH_BYTES; // zero padding
+ }
+ digestLength = 64;
+ init();
+ }
+
+ /**
+ * Blake2b with key, required digest length, salt and personalization.
+ * After calling the doFinal() method, the key, the salt and the personal string
+ * will remain and might be used for further computations with this instance.
+ * The key can be overwritten using the clearKey() method, the salt (pepper)
+ * can be overwritten using the clearSalt() method.
+ *
+ * @param key A key up to 64 bytes or null
+ * @param digestLength from 1 up to 64 bytes
+ * @param salt up to 16 bytes or null
+ * @param personalization up to 16 bytes or null
+ */
+ public Blake2bDigest(byte[] key, int digestLength, byte[] salt,
+ byte[] personalization)
+ {
+
+ buffer = new byte[BLOCK_LENGTH_BYTES];
+ if (digestLength < 1 || digestLength > 64)
+ {
+ throw new IllegalArgumentException(
+ "Invalid digest length (required: 1 - 64)");
+ }
+ this.digestLength = digestLength;
+ if (salt != null)
+ {
+ if (salt.length != 16)
+ {
+ throw new IllegalArgumentException(
+ "salt length must be exactly 16 bytes");
+ }
+ this.salt = new byte[16];
+ System.arraycopy(salt, 0, this.salt, 0, salt.length);
+ }
+ if (personalization != null)
+ {
+ if (personalization.length != 16)
+ {
+ throw new IllegalArgumentException(
+ "personalization length must be exactly 16 bytes");
+ }
+ this.personalization = new byte[16];
+ System.arraycopy(personalization, 0, this.personalization, 0,
+ personalization.length);
+ }
+ if (key != null)
+ {
+ this.key = new byte[key.length];
+ System.arraycopy(key, 0, this.key, 0, key.length);
+
+ if (key.length > 64)
+ {
+ throw new IllegalArgumentException(
+ "Keys > 64 are not supported");
+ }
+ keyLength = key.length;
+ System.arraycopy(key, 0, buffer, 0, key.length);
+ bufferPos = BLOCK_LENGTH_BYTES; // zero padding
+ }
+ init();
+ }
+
+ // initialize chainValue
+ private void init()
+ {
+
+ if (chainValue == null)
+ {
+ chainValue = new long[8];
+
+ chainValue[0] = blake2b_IV[0]
+ ^ (digestLength | (keyLength << 8) | 0x1010000);
+ // 0x1010000 = ((fanout << 16) | (depth << 24) | (leafLength <<
+ // 32));
+ // with fanout = 1; depth = 0; leafLength = 0;
+ chainValue[1] = blake2b_IV[1];// ^ nodeOffset; with nodeOffset = 0;
+ chainValue[2] = blake2b_IV[2];// ^ ( nodeDepth | (innerHashLength <<
+ // 8) );
+ // with nodeDepth = 0; innerHashLength = 0;
+
+ chainValue[3] = blake2b_IV[3];
+
+ chainValue[4] = blake2b_IV[4];
+ chainValue[5] = blake2b_IV[5];
+ if (salt != null)
+ {
+ chainValue[4] ^= (bytes2long(salt, 0));
+ chainValue[5] ^= (bytes2long(salt, 8));
+ }
+
+ chainValue[6] = blake2b_IV[6];
+ chainValue[7] = blake2b_IV[7];
+ if (personalization != null)
+ {
+ chainValue[6] ^= (bytes2long(personalization, 0));
+ chainValue[7] ^= (bytes2long(personalization, 8));
+ }
+ }
+ }
+
+ private void initializeInternalState()
+ {
+ // initialize v:
+ System.arraycopy(chainValue, 0, internalState, 0, chainValue.length);
+ System.arraycopy(blake2b_IV, 0, internalState, chainValue.length, 4);
+ internalState[12] = t0 ^ blake2b_IV[4];
+ internalState[13] = t1 ^ blake2b_IV[5];
+ internalState[14] = f0 ^ blake2b_IV[6];
+ internalState[15] = blake2b_IV[7];// ^ f1 with f1 = 0
+ }
+
+ /**
+ * update the message digest with a single byte.
+ *
+ * @param b the input byte to be entered.
+ */
+ public void update(byte b)
+ {
+ int remainingLength = 0; // left bytes of buffer
+
+ // process the buffer if full else add to buffer:
+ remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ if (remainingLength == 0)
+ { // full buffer
+ t0 += BLOCK_LENGTH_BYTES;
+ if (t0 == 0)
+ { // if message > 2^64
+ t1++;
+ }
+ compress(buffer, 0);
+ Arrays.fill(buffer, (byte)0);// clear buffer
+ buffer[0] = b;
+ bufferPos = 1;
+ } else
+ {
+ buffer[bufferPos] = b;
+ bufferPos++;
+ return;
+ }
+ }
+
+ /**
+ * update the message digest with a block of bytes.
+ *
+ * @param message the byte array containing the data.
+ * @param offset the offset into the byte array where the data starts.
+ * @param len the length of the data.
+ */
+ public void update(byte[] message, int offset, int len)
+ {
+
+ if (message == null || len == 0)
+ return;
+
+ int remainingLength = 0; // left bytes of buffer
+
+ if (bufferPos != 0)
+ { // commenced, incomplete buffer
+
+ // complete the buffer:
+ remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ if (remainingLength < len)
+ { // full buffer + at least 1 byte
+ System.arraycopy(message, offset, buffer, bufferPos,
+ remainingLength);
+ t0 += BLOCK_LENGTH_BYTES;
+ if (t0 == 0)
+ { // if message > 2^64
+ t1++;
+ }
+ compress(buffer, 0);
+ bufferPos = 0;
+ Arrays.fill(buffer, (byte) 0);// clear buffer
+ } else
+ {
+ System.arraycopy(message, offset, buffer, bufferPos, len);
+ bufferPos += len;
+ return;
+ }
+ }
+
+ // process blocks except last block (also if last block is full)
+ int messagePos;
+ int blockWiseLastPos = offset + len - BLOCK_LENGTH_BYTES;
+ for (messagePos = offset + remainingLength; messagePos < blockWiseLastPos; messagePos += BLOCK_LENGTH_BYTES)
+ { // block wise 128 bytes
+ // without buffer:
+ t0 += BLOCK_LENGTH_BYTES;
+ if (t0 == 0)
+ {
+ t1++;
+ }
+ compress(message, messagePos);
+ }
+
+ // fill the buffer with left bytes, this might be a full block
+ System.arraycopy(message, messagePos, buffer, 0, offset + len
+ - messagePos);
+ bufferPos += offset + len - messagePos;
+ }
+
+ /**
+ * close the digest, producing the final digest value. The doFinal
+ * call leaves the digest reset.
+ * Key, salt and personal string remain.
+ *
+ * @param out the array the digest is to be copied into.
+ * @param outOffset the offset into the out array the digest is to start at.
+ */
+ public int doFinal(byte[] out, int outOffset)
+ {
+
+ f0 = 0xFFFFFFFFFFFFFFFFL;
+ t0 += bufferPos;
+ // bufferPos may be < 128, so (t0 == 0) does not work
+ // for 2^64 < message length > 2^64 - 127
+ if ((t0 < 0) && (bufferPos > -t0))
+ {
+ t1++;
+ }
+ compress(buffer, 0);
+ Arrays.fill(buffer, (byte) 0);// Holds eventually the key if input is null
+ Arrays.fill(internalState, 0L);
+
+ for (int i = 0; i < chainValue.length && (i * 8 < digestLength); i++)
+ {
+ byte[] bytes = long2bytes(chainValue[i]);
+
+ if (i * 8 < digestLength - 8)
+ {
+ System.arraycopy(bytes, 0, out, outOffset + i * 8, 8);
+ }
+ else
+ {
+ System.arraycopy(bytes, 0, out, outOffset + i * 8, digestLength - (i * 8));
+ }
+ }
+
+ Arrays.fill(chainValue, 0L);
+
+ reset();
+
+ return digestLength;
+ }
+
+ /**
+ * Reset the digest back to it's initial state.
+ * The key, the salt and the personal string will
+ * remain for further computations.
+ */
+ public void reset()
+ {
+ bufferPos = 0;
+ f0 = 0L;
+ t0 = 0L;
+ t1 = 0L;
+ chainValue = null;
+ if (key != null)
+ {
+ System.arraycopy(key, 0, buffer, 0, key.length);
+ bufferPos = BLOCK_LENGTH_BYTES; // zero padding
+ }
+ init();
+ }
+
+ private void compress(byte[] message, int messagePos)
+ {
+
+ initializeInternalState();
+
+ long[] m = new long[16];
+ for (int j = 0; j < 16; j++)
+ {
+ m[j] = bytes2long(message, messagePos + j * 8);
+ }
+
+ for (int round = 0; round < rOUNDS; round++)
+ {
+
+ // G apply to columns of internalState:m[blake2b_sigma[round][2 *
+ // blockPos]] /+1
+ G(m[blake2b_sigma[round][0]], m[blake2b_sigma[round][1]], 0, 4, 8,
+ 12);
+ G(m[blake2b_sigma[round][2]], m[blake2b_sigma[round][3]], 1, 5, 9,
+ 13);
+ G(m[blake2b_sigma[round][4]], m[blake2b_sigma[round][5]], 2, 6, 10,
+ 14);
+ G(m[blake2b_sigma[round][6]], m[blake2b_sigma[round][7]], 3, 7, 11,
+ 15);
+ // G apply to diagonals of internalState:
+ G(m[blake2b_sigma[round][8]], m[blake2b_sigma[round][9]], 0, 5, 10,
+ 15);
+ G(m[blake2b_sigma[round][10]], m[blake2b_sigma[round][11]], 1, 6,
+ 11, 12);
+ G(m[blake2b_sigma[round][12]], m[blake2b_sigma[round][13]], 2, 7,
+ 8, 13);
+ G(m[blake2b_sigma[round][14]], m[blake2b_sigma[round][15]], 3, 4,
+ 9, 14);
+ }
+
+ // update chain values:
+ for (int offset = 0; offset < chainValue.length; offset++)
+ {
+ chainValue[offset] = chainValue[offset] ^ internalState[offset]
+ ^ internalState[offset + 8];
+ }
+ }
+
+ private void G(long m1, long m2, int posA, int posB, int posC, int posD)
+ {
+
+ internalState[posA] = internalState[posA] + internalState[posB] + m1;
+ internalState[posD] = rotr64(internalState[posD] ^ internalState[posA],
+ 32);
+ internalState[posC] = internalState[posC] + internalState[posD];
+ internalState[posB] = rotr64(internalState[posB] ^ internalState[posC],
+ 24); // replaces 25 of BLAKE
+ internalState[posA] = internalState[posA] + internalState[posB] + m2;
+ internalState[posD] = rotr64(internalState[posD] ^ internalState[posA],
+ 16);
+ internalState[posC] = internalState[posC] + internalState[posD];
+ internalState[posB] = rotr64(internalState[posB] ^ internalState[posC],
+ 63); // replaces 11 of BLAKE
+ }
+
+ private long rotr64(long x, int rot)
+ {
+ return x >>> rot | (x << (64 - rot));
+ }
+
+ // convert one long value in byte array
+ // little-endian byte order!
+ private final byte[] long2bytes(long longValue)
+ {
+ return new byte[]
+ { (byte) longValue, (byte) (longValue >> 8),
+ (byte) (longValue >> 16), (byte) (longValue >> 24),
+ (byte) (longValue >> 32), (byte) (longValue >> 40),
+ (byte) (longValue >> 48), (byte) (longValue >> 56)
+ };
+ }
+
+ // little-endian byte order!
+ private final long bytes2long(byte[] byteArray, int offset)
+ {
+ return (((long) byteArray[offset] & 0xFF)
+ | (((long) byteArray[offset + 1] & 0xFF) << 8)
+ | (((long) byteArray[offset + 2] & 0xFF) << 16)
+ | (((long) byteArray[offset + 3] & 0xFF) << 24)
+ | (((long) byteArray[offset + 4] & 0xFF) << 32)
+ | (((long) byteArray[offset + 5] & 0xFF) << 40)
+ | (((long) byteArray[offset + 6] & 0xFF) << 48)
+ | (((long) byteArray[offset + 7] & 0xFF) << 56));
+ }
+
+ /**
+ * return the algorithm name
+ *
+ * @return the algorithm name
+ */
+ public String getAlgorithmName()
+ {
+ return "Blake2b";
+ }
+
+ /**
+ * return the size, in bytes, of the digest produced by this message digest.
+ *
+ * @return the size, in bytes, of the digest produced by this message digest.
+ */
+ public int getDigestSize()
+ {
+ return digestLength;
+ }
+
+ /**
+ * Return the size in bytes of the internal buffer the digest applies it's compression
+ * function to.
+ *
+ * @return byte length of the digests internal buffer.
+ */
+ public int getByteLength()
+ {
+ return BLOCK_LENGTH_BYTES;
+ }
+
+ /**
+ * Overwrite the key
+ * if it is no longer used (zeroization)
+ */
+ public void clearKey()
+ {
+ if (key != null)
+ {
+ Arrays.fill(key, (byte) 0);
+ Arrays.fill(buffer, (byte) 0);
+ }
+ }
+
+ /**
+ * Overwrite the salt (pepper) if it
+ * is secret and no longer used (zeroization)
+ */
+ public void clearSalt()
+ {
+ if (salt != null)
+ {
+ Arrays.fill(salt, (byte) 0);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java
new file mode 100644
index 00000000..40194461
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java
@@ -0,0 +1,549 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
+ * <p>
+ * Following the naming conventions used in the C source code to enable easy review of the implementation.
+ */
+public class KeccakDigest
+ implements ExtendedDigest
+{
+ private static long[] KeccakRoundConstants = keccakInitializeRoundConstants();
+
+ private static int[] KeccakRhoOffsets = keccakInitializeRhoOffsets();
+
+ private static long[] keccakInitializeRoundConstants()
+ {
+ long[] keccakRoundConstants = new long[24];
+ byte[] LFSRstate = new byte[1];
+
+ LFSRstate[0] = 0x01;
+ int i, j, bitPosition;
+
+ for (i = 0; i < 24; i++)
+ {
+ keccakRoundConstants[i] = 0;
+ for (j = 0; j < 7; j++)
+ {
+ bitPosition = (1 << j) - 1;
+ if (LFSR86540(LFSRstate))
+ {
+ keccakRoundConstants[i] ^= 1L << bitPosition;
+ }
+ }
+ }
+
+ return keccakRoundConstants;
+ }
+
+ private static boolean LFSR86540(byte[] LFSR)
+ {
+ boolean result = (((LFSR[0]) & 0x01) != 0);
+ if (((LFSR[0]) & 0x80) != 0)
+ {
+ LFSR[0] = (byte)(((LFSR[0]) << 1) ^ 0x71);
+ }
+ else
+ {
+ LFSR[0] <<= 1;
+ }
+
+ return result;
+ }
+
+ private static int[] keccakInitializeRhoOffsets()
+ {
+ int[] keccakRhoOffsets = new int[25];
+ int x, y, t, newX, newY;
+
+ keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = 0;
+ x = 1;
+ y = 0;
+ for (t = 0; t < 24; t++)
+ {
+ keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = ((t + 1) * (t + 2) / 2) % 64;
+ newX = (0 * x + 1 * y) % 5;
+ newY = (2 * x + 3 * y) % 5;
+ x = newX;
+ y = newY;
+ }
+
+ return keccakRhoOffsets;
+ }
+
+ protected byte[] state = new byte[(1600 / 8)];
+ protected byte[] dataQueue = new byte[(1536 / 8)];
+ protected int rate;
+ protected int bitsInQueue;
+ protected int fixedOutputLength;
+ protected boolean squeezing;
+ protected int bitsAvailableForSqueezing;
+ protected byte[] chunk;
+ protected byte[] oneByte;
+
+ private void clearDataQueueSection(int off, int len)
+ {
+ for (int i = off; i != off + len; i++)
+ {
+ dataQueue[i] = 0;
+ }
+ }
+
+ public KeccakDigest()
+ {
+ this(288);
+ }
+
+ public KeccakDigest(int bitLength)
+ {
+ init(bitLength);
+ }
+
+ public KeccakDigest(KeccakDigest source) {
+ System.arraycopy(source.state, 0, this.state, 0, source.state.length);
+ System.arraycopy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.length);
+ this.rate = source.rate;
+ this.bitsInQueue = source.bitsInQueue;
+ this.fixedOutputLength = source.fixedOutputLength;
+ this.squeezing = source.squeezing;
+ this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing;
+ this.chunk = Arrays.clone(source.chunk);
+ this.oneByte = Arrays.clone(source.oneByte);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Keccak-" + fixedOutputLength;
+ }
+
+ public int getDigestSize()
+ {
+ return fixedOutputLength / 8;
+ }
+
+ public void update(byte in)
+ {
+ oneByte[0] = in;
+
+ absorb(oneByte, 0, 8L);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ absorb(in, inOff, len * 8L);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ squeeze(out, outOff, fixedOutputLength);
+
+ reset();
+
+ return getDigestSize();
+ }
+
+ /*
+ * TODO Possible API change to support partial-byte suffixes.
+ */
+ protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits)
+ {
+ if (partialBits > 0)
+ {
+ oneByte[0] = partialByte;
+ absorb(oneByte, 0, partialBits);
+ }
+
+ squeeze(out, outOff, fixedOutputLength);
+
+ reset();
+
+ return getDigestSize();
+ }
+
+ public void reset()
+ {
+ init(fixedOutputLength);
+ }
+
+ /**
+ * Return the size of block that the compression function is applied to in bytes.
+ *
+ * @return internal byte length of a block.
+ */
+ public int getByteLength()
+ {
+ return rate / 8;
+ }
+
+ private void init(int bitLength)
+ {
+ switch (bitLength)
+ {
+ case 288:
+ initSponge(1024, 576);
+ break;
+ case 128:
+ initSponge(1344, 256);
+ break;
+ case 224:
+ initSponge(1152, 448);
+ break;
+ case 256:
+ initSponge(1088, 512);
+ break;
+ case 384:
+ initSponge(832, 768);
+ break;
+ case 512:
+ initSponge(576, 1024);
+ break;
+ default:
+ throw new IllegalArgumentException("bitLength must be one of 128, 224, 256, 288, 384, or 512.");
+ }
+ }
+
+ private void initSponge(int rate, int capacity)
+ {
+ if (rate + capacity != 1600)
+ {
+ throw new IllegalStateException("rate + capacity != 1600");
+ }
+ if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0))
+ {
+ throw new IllegalStateException("invalid rate value");
+ }
+
+ this.rate = rate;
+ // this is never read, need to check to see why we want to save it
+ // this.capacity = capacity;
+ Arrays.fill(this.state, (byte)0);
+ Arrays.fill(this.dataQueue, (byte)0);
+ this.bitsInQueue = 0;
+ this.squeezing = false;
+ this.bitsAvailableForSqueezing = 0;
+ this.fixedOutputLength = capacity / 2;
+ this.chunk = new byte[rate / 8];
+ this.oneByte = new byte[1];
+ }
+
+ private void absorbQueue()
+ {
+ KeccakAbsorb(state, dataQueue, rate / 8);
+
+ bitsInQueue = 0;
+ }
+
+ protected void absorb(byte[] data, int off, long databitlen)
+ {
+ long i, j, wholeBlocks;
+
+ if ((bitsInQueue % 8) != 0)
+ {
+ throw new IllegalStateException("attempt to absorb with odd length queue.");
+ }
+ if (squeezing)
+ {
+ throw new IllegalStateException("attempt to absorb while squeezing.");
+ }
+
+ i = 0;
+ while (i < databitlen)
+ {
+ if ((bitsInQueue == 0) && (databitlen >= rate) && (i <= (databitlen - rate)))
+ {
+ wholeBlocks = (databitlen - i) / rate;
+
+ for (j = 0; j < wholeBlocks; j++)
+ {
+ System.arraycopy(data, (int)(off + (i / 8) + (j * chunk.length)), chunk, 0, chunk.length);
+
+// displayIntermediateValues.displayBytes(1, "Block to be absorbed", curData, rate / 8);
+
+ KeccakAbsorb(state, chunk, chunk.length);
+ }
+
+ i += wholeBlocks * rate;
+ }
+ else
+ {
+ int partialBlock = (int)(databitlen - i);
+ if (partialBlock + bitsInQueue > rate)
+ {
+ partialBlock = rate - bitsInQueue;
+ }
+ int partialByte = partialBlock % 8;
+ partialBlock -= partialByte;
+ System.arraycopy(data, off + (int)(i / 8), dataQueue, bitsInQueue / 8, partialBlock / 8);
+
+ bitsInQueue += partialBlock;
+ i += partialBlock;
+ if (bitsInQueue == rate)
+ {
+ absorbQueue();
+ }
+ if (partialByte > 0)
+ {
+ int mask = (1 << partialByte) - 1;
+ dataQueue[bitsInQueue / 8] = (byte)(data[off + ((int)(i / 8))] & mask);
+ bitsInQueue += partialByte;
+ i += partialByte;
+ }
+ }
+ }
+ }
+
+ private void padAndSwitchToSqueezingPhase()
+ {
+ if (bitsInQueue + 1 == rate)
+ {
+ dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8);
+ absorbQueue();
+ clearDataQueueSection(0, rate / 8);
+ }
+ else
+ {
+ clearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8);
+ dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8);
+ }
+ dataQueue[(rate - 1) / 8] |= 1 << ((rate - 1) % 8);
+ absorbQueue();
+
+
+// displayIntermediateValues.displayText(1, "--- Switching to squeezing phase ---");
+
+
+ if (rate == 1024)
+ {
+ KeccakExtract1024bits(state, dataQueue);
+ bitsAvailableForSqueezing = 1024;
+ }
+ else
+
+ {
+ KeccakExtract(state, dataQueue, rate / 64);
+ bitsAvailableForSqueezing = rate;
+ }
+
+// displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8);
+
+ squeezing = true;
+ }
+
+ protected void squeeze(byte[] output, int offset, long outputLength)
+ {
+ long i;
+ int partialBlock;
+
+ if (!squeezing)
+ {
+ padAndSwitchToSqueezingPhase();
+ }
+ if ((outputLength % 8) != 0)
+ {
+ throw new IllegalStateException("outputLength not a multiple of 8");
+ }
+
+ i = 0;
+ while (i < outputLength)
+ {
+ if (bitsAvailableForSqueezing == 0)
+ {
+ keccakPermutation(state);
+
+ if (rate == 1024)
+ {
+ KeccakExtract1024bits(state, dataQueue);
+ bitsAvailableForSqueezing = 1024;
+ }
+ else
+
+ {
+ KeccakExtract(state, dataQueue, rate / 64);
+ bitsAvailableForSqueezing = rate;
+ }
+
+// displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8);
+
+ }
+ partialBlock = bitsAvailableForSqueezing;
+ if ((long)partialBlock > outputLength - i)
+ {
+ partialBlock = (int)(outputLength - i);
+ }
+
+ System.arraycopy(dataQueue, (rate - bitsAvailableForSqueezing) / 8, output, offset + (int)(i / 8), partialBlock / 8);
+ bitsAvailableForSqueezing -= partialBlock;
+ i += partialBlock;
+ }
+ }
+
+ private void fromBytesToWords(long[] stateAsWords, byte[] state)
+ {
+ for (int i = 0; i < (1600 / 64); i++)
+ {
+ stateAsWords[i] = 0;
+ int index = i * (64 / 8);
+ for (int j = 0; j < (64 / 8); j++)
+ {
+ stateAsWords[i] |= ((long)state[index + j] & 0xff) << ((8 * j));
+ }
+ }
+ }
+
+ private void fromWordsToBytes(byte[] state, long[] stateAsWords)
+ {
+ for (int i = 0; i < (1600 / 64); i++)
+ {
+ int index = i * (64 / 8);
+ for (int j = 0; j < (64 / 8); j++)
+ {
+ state[index + j] = (byte)((stateAsWords[i] >>> ((8 * j))) & 0xFF);
+ }
+ }
+ }
+
+ private void keccakPermutation(byte[] state)
+ {
+ long[] longState = new long[state.length / 8];
+
+ fromBytesToWords(longState, state);
+
+// displayIntermediateValues.displayStateAsBytes(1, "Input of permutation", longState);
+
+ keccakPermutationOnWords(longState);
+
+// displayIntermediateValues.displayStateAsBytes(1, "State after permutation", longState);
+
+ fromWordsToBytes(state, longState);
+ }
+
+ private void keccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes)
+ {
+ int i;
+
+ for (i = 0; i < dataLengthInBytes; i++)
+ {
+ state[i] ^= data[i];
+ }
+
+ keccakPermutation(state);
+ }
+
+ private void keccakPermutationOnWords(long[] state)
+ {
+ int i;
+
+// displayIntermediateValues.displayStateAs64bitWords(3, "Same, with lanes as 64-bit words", state);
+
+ for (i = 0; i < 24; i++)
+ {
+// displayIntermediateValues.displayRoundNumber(3, i);
+
+ theta(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After theta", state);
+
+ rho(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After rho", state);
+
+ pi(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After pi", state);
+
+ chi(state);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After chi", state);
+
+ iota(state, i);
+// displayIntermediateValues.displayStateAs64bitWords(3, "After iota", state);
+ }
+ }
+
+ long[] C = new long[5];
+
+ private void theta(long[] A)
+ {
+ for (int x = 0; x < 5; x++)
+ {
+ C[x] = 0;
+ for (int y = 0; y < 5; y++)
+ {
+ C[x] ^= A[x + 5 * y];
+ }
+ }
+ for (int x = 0; x < 5; x++)
+ {
+ long dX = ((((C[(x + 1) % 5]) << 1) ^ ((C[(x + 1) % 5]) >>> (64 - 1)))) ^ C[(x + 4) % 5];
+ for (int y = 0; y < 5; y++)
+ {
+ A[x + 5 * y] ^= dX;
+ }
+ }
+ }
+
+ private void rho(long[] A)
+ {
+ for (int x = 0; x < 5; x++)
+ {
+ for (int y = 0; y < 5; y++)
+ {
+ int index = x + 5 * y;
+ A[index] = ((KeccakRhoOffsets[index] != 0) ? (((A[index]) << KeccakRhoOffsets[index]) ^ ((A[index]) >>> (64 - KeccakRhoOffsets[index]))) : A[index]);
+ }
+ }
+ }
+
+ long[] tempA = new long[25];
+
+ private void pi(long[] A)
+ {
+ System.arraycopy(A, 0, tempA, 0, tempA.length);
+
+ for (int x = 0; x < 5; x++)
+ {
+ for (int y = 0; y < 5; y++)
+ {
+ A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y];
+ }
+ }
+ }
+
+ long[] chiC = new long[5];
+
+ private void chi(long[] A)
+ {
+ for (int y = 0; y < 5; y++)
+ {
+ for (int x = 0; x < 5; x++)
+ {
+ chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]);
+ }
+ for (int x = 0; x < 5; x++)
+ {
+ A[x + 5 * y] = chiC[x];
+ }
+ }
+ }
+
+ private void iota(long[] A, int indexRound)
+ {
+ A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound];
+ }
+
+ private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes)
+ {
+ keccakPermutationAfterXor(byteState, data, dataInBytes);
+ }
+
+
+ private void KeccakExtract1024bits(byte[] byteState, byte[] data)
+ {
+ System.arraycopy(byteState, 0, data, 0, 128);
+ }
+
+
+ private void KeccakExtract(byte[] byteState, byte[] data, int laneCount)
+ {
+ System.arraycopy(byteState, 0, data, 0, laneCount * 8);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java
new file mode 100644
index 00000000..20dc641c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java
@@ -0,0 +1,103 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.Xof;
+
+
+/**
+ * implementation of SHAKE based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
+ * <p>
+ * Following the naming conventions used in the C source code to enable easy review of the implementation.
+ */
+public class SHAKEDigest
+ extends KeccakDigest
+ implements Xof
+{
+ private static int checkBitLength(int bitLength)
+ {
+ switch (bitLength)
+ {
+ case 128:
+ case 256:
+ return bitLength;
+ default:
+ throw new IllegalArgumentException("'bitLength' " + bitLength + " not supported for SHAKE");
+ }
+ }
+
+ public SHAKEDigest()
+ {
+ this(128);
+ }
+
+ public SHAKEDigest(int bitLength)
+ {
+ super(checkBitLength(bitLength));
+ }
+
+ public SHAKEDigest(SHAKEDigest source) {
+ super(source);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SHAKE" + fixedOutputLength;
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ return doFinal(out, outOff, getDigestSize());
+ }
+
+ public int doFinal(byte[] out, int outOff, int outLen)
+ {
+ absorb(new byte[]{ 0x0F }, 0, 4);
+
+ squeeze(out, outOff, ((long)outLen) * 8);
+
+ reset();
+
+ return outLen;
+ }
+
+ /*
+ * TODO Possible API change to support partial-byte suffixes.
+ */
+ protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits)
+ {
+ return doFinal(out, outOff, getDigestSize(), partialByte, partialBits);
+ }
+
+ /*
+ * TODO Possible API change to support partial-byte suffixes.
+ */
+ protected int doFinal(byte[] out, int outOff, int outLen, byte partialByte, int partialBits)
+ {
+ if (partialBits < 0 || partialBits > 7)
+ {
+ throw new IllegalArgumentException("'partialBits' must be in the range [0,7]");
+ }
+
+ int finalInput = (partialByte & ((1 << partialBits) - 1)) | (0x0F << partialBits);
+ int finalBits = partialBits + 4;
+
+ if (finalBits >= 8)
+ {
+ oneByte[0] = (byte)finalInput;
+ absorb(oneByte, 0, 8);
+ finalBits -= 8;
+ finalInput >>>= 8;
+ }
+
+ if (finalBits > 0)
+ {
+ oneByte[0] = (byte)finalInput;
+ absorb(oneByte, 0, finalBits);
+ }
+
+ squeeze(out, outOff, ((long)outLen) * 8);
+
+ reset();
+
+ return outLen;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/OldIESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/OldIESEngine.java
new file mode 100644
index 00000000..48ecbe3c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/OldIESEngine.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BasicAgreement;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.util.Pack;
+
+/**
+ * Support class for constructing integrated encryption ciphers
+ * for doing basic message exchanges on top of key agreement ciphers.
+ * Follows the description given in IEEE Std 1363a.
+ */
+public class OldIESEngine
+ extends IESEngine
+{
+ /**
+ * set up for use with stream mode, where the key derivation function
+ * is used to provide a stream of bytes to xor with the message.
+ *
+ * @param agree the key agreement used as the basis for the encryption
+ * @param kdf the key derivation function used for byte generation
+ * @param mac the message authentication code generator for the message
+ */
+ public OldIESEngine(
+ BasicAgreement agree,
+ DerivationFunction kdf,
+ Mac mac)
+ {
+ super(agree, kdf, mac);
+ }
+
+
+ /**
+ * set up for use in conjunction with a block cipher to handle the
+ * message.
+ *
+ * @param agree the key agreement used as the basis for the encryption
+ * @param kdf the key derivation function used for byte generation
+ * @param mac the message authentication code generator for the message
+ * @param cipher the cipher to used for encrypting the message
+ */
+ public OldIESEngine(
+ BasicAgreement agree,
+ DerivationFunction kdf,
+ Mac mac,
+ BufferedBlockCipher cipher)
+ {
+ super(agree, kdf, mac, cipher);
+ }
+
+ protected byte[] getLengthTag(byte[] p2)
+ {
+ byte[] L2 = new byte[4];
+ if (p2 != null)
+ {
+ Pack.intToBigEndian(p2.length * 8, L2, 0);
+ }
+ return L2;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java
new file mode 100644
index 00000000..bf70eadb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java
@@ -0,0 +1,267 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Pack;
+
+/**
+ * SM4 Block Cipher - SM4 is a 128 bit block cipher with a 128 bit key.
+ * <p>
+ * The implementation here is based on the document <a href="http://eprint.iacr.org/2008/329.pdf">http://eprint.iacr.org/2008/329.pdf</a>
+ * by Whitfield Diffie and George Ledin, which is a translation of Prof. LU Shu-wang's original standard.
+ * </p>
+ */
+public class SM4Engine
+ implements BlockCipher
+{
+ private static final int BLOCK_SIZE = 16;
+
+ private final static byte[] Sbox =
+ {
+ (byte)0xd6, (byte)0x90, (byte)0xe9, (byte)0xfe, (byte)0xcc, (byte)0xe1, (byte)0x3d, (byte)0xb7, (byte)0x16, (byte)0xb6, (byte)0x14, (byte)0xc2, (byte)0x28, (byte)0xfb, (byte)0x2c, (byte)0x05,
+ (byte)0x2b, (byte)0x67, (byte)0x9a, (byte)0x76, (byte)0x2a, (byte)0xbe, (byte)0x04, (byte)0xc3, (byte)0xaa, (byte)0x44, (byte)0x13, (byte)0x26, (byte)0x49, (byte)0x86, (byte)0x06, (byte)0x99,
+ (byte)0x9c, (byte)0x42, (byte)0x50, (byte)0xf4, (byte)0x91, (byte)0xef, (byte)0x98, (byte)0x7a, (byte)0x33, (byte)0x54, (byte)0x0b, (byte)0x43, (byte)0xed, (byte)0xcf, (byte)0xac, (byte)0x62,
+ (byte)0xe4, (byte)0xb3, (byte)0x1c, (byte)0xa9, (byte)0xc9, (byte)0x08, (byte)0xe8, (byte)0x95, (byte)0x80, (byte)0xdf, (byte)0x94, (byte)0xfa, (byte)0x75, (byte)0x8f, (byte)0x3f, (byte)0xa6,
+ (byte)0x47, (byte)0x07, (byte)0xa7, (byte)0xfc, (byte)0xf3, (byte)0x73, (byte)0x17, (byte)0xba, (byte)0x83, (byte)0x59, (byte)0x3c, (byte)0x19, (byte)0xe6, (byte)0x85, (byte)0x4f, (byte)0xa8,
+ (byte)0x68, (byte)0x6b, (byte)0x81, (byte)0xb2, (byte)0x71, (byte)0x64, (byte)0xda, (byte)0x8b, (byte)0xf8, (byte)0xeb, (byte)0x0f, (byte)0x4b, (byte)0x70, (byte)0x56, (byte)0x9d, (byte)0x35,
+ (byte)0x1e, (byte)0x24, (byte)0x0e, (byte)0x5e, (byte)0x63, (byte)0x58, (byte)0xd1, (byte)0xa2, (byte)0x25, (byte)0x22, (byte)0x7c, (byte)0x3b, (byte)0x01, (byte)0x21, (byte)0x78, (byte)0x87,
+ (byte)0xd4, (byte)0x00, (byte)0x46, (byte)0x57, (byte)0x9f, (byte)0xd3, (byte)0x27, (byte)0x52, (byte)0x4c, (byte)0x36, (byte)0x02, (byte)0xe7, (byte)0xa0, (byte)0xc4, (byte)0xc8, (byte)0x9e,
+ (byte)0xea, (byte)0xbf, (byte)0x8a, (byte)0xd2, (byte)0x40, (byte)0xc7, (byte)0x38, (byte)0xb5, (byte)0xa3, (byte)0xf7, (byte)0xf2, (byte)0xce, (byte)0xf9, (byte)0x61, (byte)0x15, (byte)0xa1,
+ (byte)0xe0, (byte)0xae, (byte)0x5d, (byte)0xa4, (byte)0x9b, (byte)0x34, (byte)0x1a, (byte)0x55, (byte)0xad, (byte)0x93, (byte)0x32, (byte)0x30, (byte)0xf5, (byte)0x8c, (byte)0xb1, (byte)0xe3,
+ (byte)0x1d, (byte)0xf6, (byte)0xe2, (byte)0x2e, (byte)0x82, (byte)0x66, (byte)0xca, (byte)0x60, (byte)0xc0, (byte)0x29, (byte)0x23, (byte)0xab, (byte)0x0d, (byte)0x53, (byte)0x4e, (byte)0x6f,
+ (byte)0xd5, (byte)0xdb, (byte)0x37, (byte)0x45, (byte)0xde, (byte)0xfd, (byte)0x8e, (byte)0x2f, (byte)0x03, (byte)0xff, (byte)0x6a, (byte)0x72, (byte)0x6d, (byte)0x6c, (byte)0x5b, (byte)0x51,
+ (byte)0x8d, (byte)0x1b, (byte)0xaf, (byte)0x92, (byte)0xbb, (byte)0xdd, (byte)0xbc, (byte)0x7f, (byte)0x11, (byte)0xd9, (byte)0x5c, (byte)0x41, (byte)0x1f, (byte)0x10, (byte)0x5a, (byte)0xd8,
+ (byte)0x0a, (byte)0xc1, (byte)0x31, (byte)0x88, (byte)0xa5, (byte)0xcd, (byte)0x7b, (byte)0xbd, (byte)0x2d, (byte)0x74, (byte)0xd0, (byte)0x12, (byte)0xb8, (byte)0xe5, (byte)0xb4, (byte)0xb0,
+ (byte)0x89, (byte)0x69, (byte)0x97, (byte)0x4a, (byte)0x0c, (byte)0x96, (byte)0x77, (byte)0x7e, (byte)0x65, (byte)0xb9, (byte)0xf1, (byte)0x09, (byte)0xc5, (byte)0x6e, (byte)0xc6, (byte)0x84,
+ (byte)0x18, (byte)0xf0, (byte)0x7d, (byte)0xec, (byte)0x3a, (byte)0xdc, (byte)0x4d, (byte)0x20, (byte)0x79, (byte)0xee, (byte)0x5f, (byte)0x3e, (byte)0xd7, (byte)0xcb, (byte)0x39, (byte)0x48
+ };
+
+ private final static int[] CK =
+ {
+ 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
+ 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
+ 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
+ 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
+ 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
+ 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
+ 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
+ 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
+ };
+
+ private final static int[] FK =
+ {
+ 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc
+ };
+
+ private final int[] X = new int[4];
+
+ private int[] rk;
+
+ private int rotateLeft(
+ int x,
+ int bits)
+ {
+ return (x << bits) | (x >>> -bits);
+ }
+
+ // non-linear substitution tau.
+ private int tau(
+ int A)
+ {
+ int b0 = Sbox[(A >> 24) & 0xff] & 0xff;
+ int b1 = Sbox[(A >> 16) & 0xff] & 0xff;
+ int b2 = Sbox[(A >> 8) & 0xff] & 0xff;
+ int b3 = Sbox[A & 0xff] & 0xff;
+
+ return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
+ }
+
+ private int L_ap(
+ int B)
+ {
+ return (B ^ (rotateLeft(B, 13)) ^ (rotateLeft(B, 23)));
+ }
+
+ private int T_ap(
+ int Z)
+ {
+ return L_ap(tau(Z));
+ }
+
+ // Key expansion
+ private int[] expandKey(boolean forEncryption, byte[] key)
+ {
+ int[] rk = new int[32];
+ int[] MK = new int[4];
+
+ MK[0] = Pack.bigEndianToInt(key, 0);
+ MK[1] = Pack.bigEndianToInt(key, 4);
+ MK[2] = Pack.bigEndianToInt(key, 8);
+ MK[3] = Pack.bigEndianToInt(key, 12);
+
+ int i;
+ int[] K = new int[4];
+ K[0] = MK[0] ^ FK[0];
+ K[1] = MK[1] ^ FK[1];
+ K[2] = MK[2] ^ FK[2];
+ K[3] = MK[3] ^ FK[3];
+
+ if (forEncryption)
+ {
+ rk[0] = K[0] ^ T_ap(K[1] ^ K[2] ^ K[3] ^ CK[0]);
+ rk[1] = K[1] ^ T_ap(K[2] ^ K[3] ^ rk[0] ^ CK[1]);
+ rk[2] = K[2] ^ T_ap(K[3] ^ rk[0] ^ rk[1] ^ CK[2]);
+ rk[3] = K[3] ^ T_ap(rk[0] ^ rk[1] ^ rk[2] ^ CK[3]);
+ for (i = 4; i < 32; i++)
+ {
+ rk[i] = rk[i - 4] ^ T_ap(rk[i - 3] ^ rk[i - 2] ^ rk[i - 1] ^ CK[i]);
+ }
+ }
+ else
+ {
+ rk[31] = K[0] ^ T_ap(K[1] ^ K[2] ^ K[3] ^ CK[0]);
+ rk[30] = K[1] ^ T_ap(K[2] ^ K[3] ^ rk[31] ^ CK[1]);
+ rk[29] = K[2] ^ T_ap(K[3] ^ rk[31] ^ rk[30] ^ CK[2]);
+ rk[28] = K[3] ^ T_ap(rk[31] ^ rk[30] ^ rk[29] ^ CK[3]);
+ for (i = 27; i >= 0; i--)
+ {
+ rk[i] = rk[i + 4] ^ T_ap(rk[i + 3] ^ rk[i + 2] ^ rk[i + 1] ^ CK[31 - i]);
+ }
+ }
+
+ return rk;
+ }
+
+
+ // Linear substitution L
+ private int L(int B)
+ {
+ int C;
+ C = (B ^ (rotateLeft(B, 2)) ^ (rotateLeft(B, 10)) ^ (rotateLeft(B,
+ 18)) ^ (rotateLeft(B, 24)));
+ return C;
+ }
+
+ // Mixer-substitution T
+ private int T(int Z)
+ {
+ return L(tau(Z));
+ }
+
+ // reverse substitution
+ private void R(int[] A, int off)
+ {
+ int off0 = off;
+ int off1 = off + 1;
+ int off2 = off + 2;
+ int off3 = off + 3;
+
+ A[off0] = A[off0] ^ A[off3];
+ A[off3] = A[off0] ^ A[off3];
+ A[off0] = A[off0] ^ A[off3];
+ A[off1] = A[off1] ^ A[off2];
+ A[off2] = A[off1] ^ A[off2];
+ A[off1] = A[off1] ^ A[off2];
+ }
+
+ // The round functions
+ private int F0(int[] X, int rk)
+ {
+ return (X[0] ^ T(X[1] ^ X[2] ^ X[3] ^ rk));
+ }
+
+ private int F1(int[] X, int rk)
+ {
+ return (X[1] ^ T(X[2] ^ X[3] ^ X[0] ^ rk));
+ }
+
+ private int F2(int[] X, int rk)
+ {
+ return (X[2] ^ T(X[3] ^ X[0] ^ X[1] ^ rk));
+ }
+
+ private int F3(int[] X, int rk)
+ {
+ return (X[3] ^ T(X[0] ^ X[1] ^ X[2] ^ rk));
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (params instanceof KeyParameter)
+ {
+ byte[] key = ((KeyParameter)params).getKey();
+
+ if (key.length != 16)
+ {
+ throw new IllegalArgumentException("SM4 requires a 128 bit key");
+ }
+
+ rk = expandKey(forEncryption, key);
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameter passed to SM4 init - " + params.getClass().getName());
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SM4";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (rk == null)
+ {
+ throw new IllegalStateException("SM4 not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ X[0] = Pack.bigEndianToInt(in, inOff);
+ X[1] = Pack.bigEndianToInt(in, inOff + 4);
+ X[2] = Pack.bigEndianToInt(in, inOff + 8);
+ X[3] = Pack.bigEndianToInt(in, inOff + 12);
+
+ int i;
+
+ for (i = 0; i < 32; i += 4)
+ {
+ X[0] = F0(X, rk[i]);
+ X[1] = F1(X, rk[i + 1]);
+ X[2] = F2(X, rk[i + 2]);
+ X[3] = F3(X, rk[i + 3]);
+ }
+ R(X, 0);
+
+ Pack.intToBigEndian(X[0], out, outOff);
+ Pack.intToBigEndian(X[1], out, outOff + 4);
+ Pack.intToBigEndian(X[2], out, outOff + 8);
+ Pack.intToBigEndian(X[3], out, outOff + 12);
+
+ return 16;
+ }
+
+ public void reset()
+ {
+
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngineBase.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngineBase.java
new file mode 100644
index 00000000..c47b2c26
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngineBase.java
@@ -0,0 +1,486 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+public abstract class SerpentEngineBase
+ implements BlockCipher
+{
+ protected static final int BLOCK_SIZE = 16;
+
+ static final int ROUNDS = 32;
+ static final int PHI = 0x9E3779B9; // (sqrt(5) - 1) * 2**31
+
+ protected boolean encrypting;
+ protected int[] wKey;
+
+ protected int X0, X1, X2, X3; // registers
+
+ SerpentEngineBase()
+ {
+
+ }
+
+ /**
+ * initialise a Serpent cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @throws IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ this.encrypting = encrypting;
+ this.wKey = makeWorkingKey(((KeyParameter)params).getKey());
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to " + getAlgorithmName() + " init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Serpent";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @return the number of bytes processed and produced.
+ * @throws DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @throws IllegalStateException if the cipher isn't initialised.
+ */
+ public final int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (wKey == null)
+ {
+ throw new IllegalStateException(getAlgorithmName() + " not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ protected static int rotateLeft(
+ int x,
+ int bits)
+ {
+ return (x << bits) | (x >>> -bits);
+ }
+
+ protected static int rotateRight(
+ int x,
+ int bits)
+ {
+ return (x >>> bits) | (x << -bits);
+ }
+
+ /**
+ * The sboxes below are based on the work of Brian Gladman and
+ * Sam Simpson, whose original notice appears below.
+ * <p>
+ * For further details see:
+ * http://fp.gladman.plus.com/cryptography_technology/serpent/
+ */
+
+ /* Partially optimised Serpent S Box boolean functions derived */
+ /* using a recursive descent analyser but without a full search */
+ /* of all subtrees. This set of S boxes is the result of work */
+ /* by Sam Simpson and Brian Gladman using the spare time on a */
+ /* cluster of high capacity servers to search for S boxes with */
+ /* this customised search engine. There are now an average of */
+ /* 15.375 terms per S box. */
+ /* */
+ /* Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) */
+ /* and Sam Simpson (s.simpson@mia.co.uk) */
+ /* 17th December 1998 */
+ /* */
+ /* We hereby give permission for information in this file to be */
+ /* used freely subject only to acknowledgement of its origin. */
+
+ /**
+ * S0 - { 3, 8,15, 1,10, 6, 5,11,14,13, 4, 2, 7, 0, 9,12 } - 15 terms.
+ */
+ protected final void sb0(int a, int b, int c, int d)
+ {
+ int t1 = a ^ d;
+ int t3 = c ^ t1;
+ int t4 = b ^ t3;
+ X3 = (a & d) ^ t4;
+ int t7 = a ^ (b & t1);
+ X2 = t4 ^ (c | t7);
+ int t12 = X3 & (t3 ^ t7);
+ X1 = (~t3) ^ t12;
+ X0 = t12 ^ (~t7);
+ }
+
+ /**
+ * InvSO - {13, 3,11, 0,10, 6, 5,12, 1,14, 4, 7,15, 9, 8, 2 } - 15 terms.
+ */
+ protected final void ib0(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t4 = d ^ (t1 | t2);
+ int t5 = c ^ t4;
+ X2 = t2 ^ t5;
+ int t8 = t1 ^ (d & t2);
+ X1 = t4 ^ (X2 & t8);
+ X3 = (a & t4) ^ (t5 | X1);
+ X0 = X3 ^ (t5 ^ t8);
+ }
+
+ /**
+ * S1 - {15,12, 2, 7, 9, 0, 5,10, 1,11,14, 8, 6,13, 3, 4 } - 14 terms.
+ */
+ protected final void sb1(int a, int b, int c, int d)
+ {
+ int t2 = b ^ (~a);
+ int t5 = c ^ (a | t2);
+ X2 = d ^ t5;
+ int t7 = b ^ (d | t2);
+ int t8 = t2 ^ X2;
+ X3 = t8 ^ (t5 & t7);
+ int t11 = t5 ^ t7;
+ X1 = X3 ^ t11;
+ X0 = t5 ^ (t8 & t11);
+ }
+
+ /**
+ * InvS1 - { 5, 8, 2,14,15, 6,12, 3,11, 4, 7, 9, 1,13,10, 0 } - 14 steps.
+ */
+ protected final void ib1(int a, int b, int c, int d)
+ {
+ int t1 = b ^ d;
+ int t3 = a ^ (b & t1);
+ int t4 = t1 ^ t3;
+ X3 = c ^ t4;
+ int t7 = b ^ (t1 & t3);
+ int t8 = X3 | t7;
+ X1 = t3 ^ t8;
+ int t10 = ~X1;
+ int t11 = X3 ^ t7;
+ X0 = t10 ^ t11;
+ X2 = t4 ^ (t10 | t11);
+ }
+
+ /**
+ * S2 - { 8, 6, 7, 9, 3,12,10,15,13, 1,14, 4, 0,11, 5, 2 } - 16 terms.
+ */
+ protected final void sb2(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = b ^ d;
+ int t3 = c & t1;
+ X0 = t2 ^ t3;
+ int t5 = c ^ t1;
+ int t6 = c ^ X0;
+ int t7 = b & t6;
+ X3 = t5 ^ t7;
+ X2 = a ^ ((d | t7) & (X0 | t5));
+ X1 = (t2 ^ X3) ^ (X2 ^ (d | t1));
+ }
+
+ /**
+ * InvS2 - {12, 9,15, 4,11,14, 1, 2, 0, 3, 6,13, 5, 8,10, 7 } - 16 steps.
+ */
+ protected final void ib2(int a, int b, int c, int d)
+ {
+ int t1 = b ^ d;
+ int t2 = ~t1;
+ int t3 = a ^ c;
+ int t4 = c ^ t1;
+ int t5 = b & t4;
+ X0 = t3 ^ t5;
+ int t7 = a | t2;
+ int t8 = d ^ t7;
+ int t9 = t3 | t8;
+ X3 = t1 ^ t9;
+ int t11 = ~t4;
+ int t12 = X0 | X3;
+ X1 = t11 ^ t12;
+ X2 = (d & t11) ^ (t3 ^ t12);
+ }
+
+ /**
+ * S3 - { 0,15,11, 8,12, 9, 6, 3,13, 1, 2, 4,10, 7, 5,14 } - 16 terms.
+ */
+ protected final void sb3(int a, int b, int c, int d)
+ {
+ int t1 = a ^ b;
+ int t2 = a & c;
+ int t3 = a | d;
+ int t4 = c ^ d;
+ int t5 = t1 & t3;
+ int t6 = t2 | t5;
+ X2 = t4 ^ t6;
+ int t8 = b ^ t3;
+ int t9 = t6 ^ t8;
+ int t10 = t4 & t9;
+ X0 = t1 ^ t10;
+ int t12 = X2 & X0;
+ X1 = t9 ^ t12;
+ X3 = (b | d) ^ (t4 ^ t12);
+ }
+
+ /**
+ * InvS3 - { 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1 } - 15 terms
+ */
+ protected final void ib3(int a, int b, int c, int d)
+ {
+ int t1 = a | b;
+ int t2 = b ^ c;
+ int t3 = b & t2;
+ int t4 = a ^ t3;
+ int t5 = c ^ t4;
+ int t6 = d | t4;
+ X0 = t2 ^ t6;
+ int t8 = t2 | t6;
+ int t9 = d ^ t8;
+ X2 = t5 ^ t9;
+ int t11 = t1 ^ t9;
+ int t12 = X0 & t11;
+ X3 = t4 ^ t12;
+ X1 = X3 ^ (X0 ^ t11);
+ }
+
+ /**
+ * S4 - { 1,15, 8, 3,12, 0,11, 6, 2, 5, 4,10, 9,14, 7,13 } - 15 terms.
+ */
+ protected final void sb4(int a, int b, int c, int d)
+ {
+ int t1 = a ^ d;
+ int t2 = d & t1;
+ int t3 = c ^ t2;
+ int t4 = b | t3;
+ X3 = t1 ^ t4;
+ int t6 = ~b;
+ int t7 = t1 | t6;
+ X0 = t3 ^ t7;
+ int t9 = a & X0;
+ int t10 = t1 ^ t6;
+ int t11 = t4 & t10;
+ X2 = t9 ^ t11;
+ X1 = (a ^ t3) ^ (t10 & X2);
+ }
+
+ /**
+ * InvS4 - { 5, 0, 8, 3,10, 9, 7,14, 2,12,11, 6, 4,15,13, 1 } - 15 terms.
+ */
+ protected final void ib4(int a, int b, int c, int d)
+ {
+ int t1 = c | d;
+ int t2 = a & t1;
+ int t3 = b ^ t2;
+ int t4 = a & t3;
+ int t5 = c ^ t4;
+ X1 = d ^ t5;
+ int t7 = ~a;
+ int t8 = t5 & X1;
+ X3 = t3 ^ t8;
+ int t10 = X1 | t7;
+ int t11 = d ^ t10;
+ X0 = X3 ^ t11;
+ X2 = (t3 & t11) ^ (X1 ^ t7);
+ }
+
+ /**
+ * S5 - {15, 5, 2,11, 4,10, 9,12, 0, 3,14, 8,13, 6, 7, 1 } - 16 terms.
+ */
+ protected final void sb5(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t3 = a ^ d;
+ int t4 = c ^ t1;
+ int t5 = t2 | t3;
+ X0 = t4 ^ t5;
+ int t7 = d & X0;
+ int t8 = t2 ^ X0;
+ X1 = t7 ^ t8;
+ int t10 = t1 | X0;
+ int t11 = t2 | t7;
+ int t12 = t3 ^ t10;
+ X2 = t11 ^ t12;
+ X3 = (b ^ t7) ^ (X1 & t12);
+ }
+
+ /**
+ * InvS5 - { 8,15, 2, 9, 4, 1,13,14,11, 6, 5, 3, 7,12,10, 0 } - 16 terms.
+ */
+ protected final void ib5(int a, int b, int c, int d)
+ {
+ int t1 = ~c;
+ int t2 = b & t1;
+ int t3 = d ^ t2;
+ int t4 = a & t3;
+ int t5 = b ^ t1;
+ X3 = t4 ^ t5;
+ int t7 = b | X3;
+ int t8 = a & t7;
+ X1 = t3 ^ t8;
+ int t10 = a | d;
+ int t11 = t1 ^ t7;
+ X0 = t10 ^ t11;
+ X2 = (b & t10) ^ (t4 | (a ^ c));
+ }
+
+ /**
+ * S6 - { 7, 2,12, 5, 8, 4, 6,11,14, 9, 1,15,13, 3,10, 0 } - 15 terms.
+ */
+ protected final void sb6(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ d;
+ int t3 = b ^ t2;
+ int t4 = t1 | t2;
+ int t5 = c ^ t4;
+ X1 = b ^ t5;
+ int t7 = t2 | X1;
+ int t8 = d ^ t7;
+ int t9 = t5 & t8;
+ X2 = t3 ^ t9;
+ int t11 = t5 ^ t8;
+ X0 = X2 ^ t11;
+ X3 = (~t5) ^ (t3 & t11);
+ }
+
+ /**
+ * InvS6 - {15,10, 1,13, 5, 3, 6, 0, 4, 9,14, 7, 2,12, 8,11 } - 15 terms.
+ */
+ protected final void ib6(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t3 = c ^ t2;
+ int t4 = c | t1;
+ int t5 = d ^ t4;
+ X1 = t3 ^ t5;
+ int t7 = t3 & t5;
+ int t8 = t2 ^ t7;
+ int t9 = b | t8;
+ X3 = t5 ^ t9;
+ int t11 = b | X3;
+ X0 = t8 ^ t11;
+ X2 = (d & t1) ^ (t3 ^ t11);
+ }
+
+ /**
+ * S7 - { 1,13,15, 0,14, 8, 2,11, 7, 4,12,10, 9, 3, 5, 6 } - 16 terms.
+ */
+ protected final void sb7(int a, int b, int c, int d)
+ {
+ int t1 = b ^ c;
+ int t2 = c & t1;
+ int t3 = d ^ t2;
+ int t4 = a ^ t3;
+ int t5 = d | t1;
+ int t6 = t4 & t5;
+ X1 = b ^ t6;
+ int t8 = t3 | X1;
+ int t9 = a & t4;
+ X3 = t1 ^ t9;
+ int t11 = t4 ^ t8;
+ int t12 = X3 & t11;
+ X2 = t3 ^ t12;
+ X0 = (~t11) ^ (X3 & X2);
+ }
+
+ /**
+ * InvS7 - { 3, 0, 6,13, 9,14,15, 8, 5,12,11, 7,10, 1, 4, 2 } - 17 terms.
+ */
+ protected final void ib7(int a, int b, int c, int d)
+ {
+ int t3 = c | (a & b);
+ int t4 = d & (a | b);
+ X3 = t3 ^ t4;
+ int t6 = ~d;
+ int t7 = b ^ t4;
+ int t9 = t7 | (X3 ^ t6);
+ X1 = a ^ t9;
+ X0 = (c ^ t7) ^ (d | X1);
+ X2 = (t3 ^ X1) ^ (X0 ^ (a & X3));
+ }
+
+ /**
+ * Apply the linear transformation to the register set.
+ */
+ protected final void LT()
+ {
+ int x0 = rotateLeft(X0, 13);
+ int x2 = rotateLeft(X2, 3);
+ int x1 = X1 ^ x0 ^ x2 ;
+ int x3 = X3 ^ x2 ^ x0 << 3;
+
+ X1 = rotateLeft(x1, 1);
+ X3 = rotateLeft(x3, 7);
+ X0 = rotateLeft(x0 ^ X1 ^ X3, 5);
+ X2 = rotateLeft(x2 ^ X3 ^ (X1 << 7), 22);
+ }
+
+ /**
+ * Apply the inverse of the linear transformation to the register set.
+ */
+ protected final void inverseLT()
+ {
+ int x2 = rotateRight(X2, 22) ^ X3 ^ (X1 << 7);
+ int x0 = rotateRight(X0, 5) ^ X1 ^ X3;
+ int x3 = rotateRight(X3, 7);
+ int x1 = rotateRight(X1, 1);
+ X3 = x3 ^ x2 ^ x0 << 3;
+ X1 = x1 ^ x0 ^ x2;
+ X2 = rotateRight(x2, 3);
+ X0 = rotateRight(x0, 13);
+ }
+
+ protected abstract int[] makeWorkingKey(byte[] key);
+
+ protected abstract void encryptBlock(byte[] input, int inOff, byte[] output, int outOff);
+
+ protected abstract void decryptBlock(byte[] input, int inOff, byte[] output, int outOff);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/TnepresEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TnepresEngine.java
new file mode 100644
index 00000000..a34e0162
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TnepresEngine.java
@@ -0,0 +1,303 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.util.Pack;
+
+/**
+ * Tnepres is a 128-bit 32-round block cipher with variable key lengths,
+ * including 128, 192 and 256 bit keys conjectured to be at least as
+ * secure as three-key triple-DES.
+ * <p>
+ * Tnepres is based on Serpent which was designed by Ross Anderson, Eli Biham and Lars Knudsen as a
+ * candidate algorithm for the NIST AES Quest. Unfortunately there was an endianness issue
+ * with test vectors in the AES submission and the resulting confusion lead to the Tnepres cipher
+ * as well, which is a byte swapped version of Serpent.
+ * <p>
+ * For full details see <a href="http://www.cl.cam.ac.uk/~rja14/serpent.html">The Serpent home page</a>
+ */
+public final class TnepresEngine
+ extends SerpentEngineBase
+{
+ public String getAlgorithmName()
+ {
+ return "Tnepres";
+ }
+
+ /**
+ * Expand a user-supplied key material into a session key.
+ *
+ * @param key The user-key bytes (multiples of 4) to use.
+ * @exception IllegalArgumentException
+ */
+ protected int[] makeWorkingKey(
+ byte[] key)
+ throws IllegalArgumentException
+ {
+ //
+ // pad key to 256 bits
+ //
+ int[] kPad = new int[16];
+ int off = 0;
+ int length = 0;
+
+ for (off = key.length - 4; off > 0; off -= 4)
+ {
+ kPad[length++] = Pack.bigEndianToInt(key, off);
+ }
+
+ if (off == 0)
+ {
+ kPad[length++] = Pack.bigEndianToInt(key, 0);
+ if (length < 8)
+ {
+ kPad[length] = 1;
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("key must be a multiple of 4 bytes");
+ }
+
+ //
+ // expand the padded key up to 33 x 128 bits of key material
+ //
+ int amount = (ROUNDS + 1) * 4;
+ int[] w = new int[amount];
+
+ //
+ // compute w0 to w7 from w-8 to w-1
+ //
+ for (int i = 8; i < 16; i++)
+ {
+ kPad[i] = rotateLeft(kPad[i - 8] ^ kPad[i - 5] ^ kPad[i - 3] ^ kPad[i - 1] ^ PHI ^ (i - 8), 11);
+ }
+
+ System.arraycopy(kPad, 8, w, 0, 8);
+
+ //
+ // compute w8 to w136
+ //
+ for (int i = 8; i < amount; i++)
+ {
+ w[i] = rotateLeft(w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11);
+ }
+
+ //
+ // create the working keys by processing w with the Sbox and IP
+ //
+ sb3(w[0], w[1], w[2], w[3]);
+ w[0] = X0; w[1] = X1; w[2] = X2; w[3] = X3;
+ sb2(w[4], w[5], w[6], w[7]);
+ w[4] = X0; w[5] = X1; w[6] = X2; w[7] = X3;
+ sb1(w[8], w[9], w[10], w[11]);
+ w[8] = X0; w[9] = X1; w[10] = X2; w[11] = X3;
+ sb0(w[12], w[13], w[14], w[15]);
+ w[12] = X0; w[13] = X1; w[14] = X2; w[15] = X3;
+ sb7(w[16], w[17], w[18], w[19]);
+ w[16] = X0; w[17] = X1; w[18] = X2; w[19] = X3;
+ sb6(w[20], w[21], w[22], w[23]);
+ w[20] = X0; w[21] = X1; w[22] = X2; w[23] = X3;
+ sb5(w[24], w[25], w[26], w[27]);
+ w[24] = X0; w[25] = X1; w[26] = X2; w[27] = X3;
+ sb4(w[28], w[29], w[30], w[31]);
+ w[28] = X0; w[29] = X1; w[30] = X2; w[31] = X3;
+ sb3(w[32], w[33], w[34], w[35]);
+ w[32] = X0; w[33] = X1; w[34] = X2; w[35] = X3;
+ sb2(w[36], w[37], w[38], w[39]);
+ w[36] = X0; w[37] = X1; w[38] = X2; w[39] = X3;
+ sb1(w[40], w[41], w[42], w[43]);
+ w[40] = X0; w[41] = X1; w[42] = X2; w[43] = X3;
+ sb0(w[44], w[45], w[46], w[47]);
+ w[44] = X0; w[45] = X1; w[46] = X2; w[47] = X3;
+ sb7(w[48], w[49], w[50], w[51]);
+ w[48] = X0; w[49] = X1; w[50] = X2; w[51] = X3;
+ sb6(w[52], w[53], w[54], w[55]);
+ w[52] = X0; w[53] = X1; w[54] = X2; w[55] = X3;
+ sb5(w[56], w[57], w[58], w[59]);
+ w[56] = X0; w[57] = X1; w[58] = X2; w[59] = X3;
+ sb4(w[60], w[61], w[62], w[63]);
+ w[60] = X0; w[61] = X1; w[62] = X2; w[63] = X3;
+ sb3(w[64], w[65], w[66], w[67]);
+ w[64] = X0; w[65] = X1; w[66] = X2; w[67] = X3;
+ sb2(w[68], w[69], w[70], w[71]);
+ w[68] = X0; w[69] = X1; w[70] = X2; w[71] = X3;
+ sb1(w[72], w[73], w[74], w[75]);
+ w[72] = X0; w[73] = X1; w[74] = X2; w[75] = X3;
+ sb0(w[76], w[77], w[78], w[79]);
+ w[76] = X0; w[77] = X1; w[78] = X2; w[79] = X3;
+ sb7(w[80], w[81], w[82], w[83]);
+ w[80] = X0; w[81] = X1; w[82] = X2; w[83] = X3;
+ sb6(w[84], w[85], w[86], w[87]);
+ w[84] = X0; w[85] = X1; w[86] = X2; w[87] = X3;
+ sb5(w[88], w[89], w[90], w[91]);
+ w[88] = X0; w[89] = X1; w[90] = X2; w[91] = X3;
+ sb4(w[92], w[93], w[94], w[95]);
+ w[92] = X0; w[93] = X1; w[94] = X2; w[95] = X3;
+ sb3(w[96], w[97], w[98], w[99]);
+ w[96] = X0; w[97] = X1; w[98] = X2; w[99] = X3;
+ sb2(w[100], w[101], w[102], w[103]);
+ w[100] = X0; w[101] = X1; w[102] = X2; w[103] = X3;
+ sb1(w[104], w[105], w[106], w[107]);
+ w[104] = X0; w[105] = X1; w[106] = X2; w[107] = X3;
+ sb0(w[108], w[109], w[110], w[111]);
+ w[108] = X0; w[109] = X1; w[110] = X2; w[111] = X3;
+ sb7(w[112], w[113], w[114], w[115]);
+ w[112] = X0; w[113] = X1; w[114] = X2; w[115] = X3;
+ sb6(w[116], w[117], w[118], w[119]);
+ w[116] = X0; w[117] = X1; w[118] = X2; w[119] = X3;
+ sb5(w[120], w[121], w[122], w[123]);
+ w[120] = X0; w[121] = X1; w[122] = X2; w[123] = X3;
+ sb4(w[124], w[125], w[126], w[127]);
+ w[124] = X0; w[125] = X1; w[126] = X2; w[127] = X3;
+ sb3(w[128], w[129], w[130], w[131]);
+ w[128] = X0; w[129] = X1; w[130] = X2; w[131] = X3;
+
+ return w;
+ }
+
+ /**
+ * Encrypt one block of plaintext.
+ *
+ * @param input the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param output the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ */
+ protected void encryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ X3 = Pack.bigEndianToInt(input, inOff);
+ X2 = Pack.bigEndianToInt(input, inOff + 4);
+ X1 = Pack.bigEndianToInt(input, inOff + 8);
+ X0 = Pack.bigEndianToInt(input, inOff + 12);
+
+ sb0(wKey[0] ^ X0, wKey[1] ^ X1, wKey[2] ^ X2, wKey[3] ^ X3); LT();
+ sb1(wKey[4] ^ X0, wKey[5] ^ X1, wKey[6] ^ X2, wKey[7] ^ X3); LT();
+ sb2(wKey[8] ^ X0, wKey[9] ^ X1, wKey[10] ^ X2, wKey[11] ^ X3); LT();
+ sb3(wKey[12] ^ X0, wKey[13] ^ X1, wKey[14] ^ X2, wKey[15] ^ X3); LT();
+ sb4(wKey[16] ^ X0, wKey[17] ^ X1, wKey[18] ^ X2, wKey[19] ^ X3); LT();
+ sb5(wKey[20] ^ X0, wKey[21] ^ X1, wKey[22] ^ X2, wKey[23] ^ X3); LT();
+ sb6(wKey[24] ^ X0, wKey[25] ^ X1, wKey[26] ^ X2, wKey[27] ^ X3); LT();
+ sb7(wKey[28] ^ X0, wKey[29] ^ X1, wKey[30] ^ X2, wKey[31] ^ X3); LT();
+ sb0(wKey[32] ^ X0, wKey[33] ^ X1, wKey[34] ^ X2, wKey[35] ^ X3); LT();
+ sb1(wKey[36] ^ X0, wKey[37] ^ X1, wKey[38] ^ X2, wKey[39] ^ X3); LT();
+ sb2(wKey[40] ^ X0, wKey[41] ^ X1, wKey[42] ^ X2, wKey[43] ^ X3); LT();
+ sb3(wKey[44] ^ X0, wKey[45] ^ X1, wKey[46] ^ X2, wKey[47] ^ X3); LT();
+ sb4(wKey[48] ^ X0, wKey[49] ^ X1, wKey[50] ^ X2, wKey[51] ^ X3); LT();
+ sb5(wKey[52] ^ X0, wKey[53] ^ X1, wKey[54] ^ X2, wKey[55] ^ X3); LT();
+ sb6(wKey[56] ^ X0, wKey[57] ^ X1, wKey[58] ^ X2, wKey[59] ^ X3); LT();
+ sb7(wKey[60] ^ X0, wKey[61] ^ X1, wKey[62] ^ X2, wKey[63] ^ X3); LT();
+ sb0(wKey[64] ^ X0, wKey[65] ^ X1, wKey[66] ^ X2, wKey[67] ^ X3); LT();
+ sb1(wKey[68] ^ X0, wKey[69] ^ X1, wKey[70] ^ X2, wKey[71] ^ X3); LT();
+ sb2(wKey[72] ^ X0, wKey[73] ^ X1, wKey[74] ^ X2, wKey[75] ^ X3); LT();
+ sb3(wKey[76] ^ X0, wKey[77] ^ X1, wKey[78] ^ X2, wKey[79] ^ X3); LT();
+ sb4(wKey[80] ^ X0, wKey[81] ^ X1, wKey[82] ^ X2, wKey[83] ^ X3); LT();
+ sb5(wKey[84] ^ X0, wKey[85] ^ X1, wKey[86] ^ X2, wKey[87] ^ X3); LT();
+ sb6(wKey[88] ^ X0, wKey[89] ^ X1, wKey[90] ^ X2, wKey[91] ^ X3); LT();
+ sb7(wKey[92] ^ X0, wKey[93] ^ X1, wKey[94] ^ X2, wKey[95] ^ X3); LT();
+ sb0(wKey[96] ^ X0, wKey[97] ^ X1, wKey[98] ^ X2, wKey[99] ^ X3); LT();
+ sb1(wKey[100] ^ X0, wKey[101] ^ X1, wKey[102] ^ X2, wKey[103] ^ X3); LT();
+ sb2(wKey[104] ^ X0, wKey[105] ^ X1, wKey[106] ^ X2, wKey[107] ^ X3); LT();
+ sb3(wKey[108] ^ X0, wKey[109] ^ X1, wKey[110] ^ X2, wKey[111] ^ X3); LT();
+ sb4(wKey[112] ^ X0, wKey[113] ^ X1, wKey[114] ^ X2, wKey[115] ^ X3); LT();
+ sb5(wKey[116] ^ X0, wKey[117] ^ X1, wKey[118] ^ X2, wKey[119] ^ X3); LT();
+ sb6(wKey[120] ^ X0, wKey[121] ^ X1, wKey[122] ^ X2, wKey[123] ^ X3); LT();
+ sb7(wKey[124] ^ X0, wKey[125] ^ X1, wKey[126] ^ X2, wKey[127] ^ X3);
+
+ Pack.intToBigEndian(wKey[131] ^ X3, output, outOff);
+ Pack.intToBigEndian(wKey[130] ^ X2, output, outOff + 4);
+ Pack.intToBigEndian(wKey[129] ^ X1, output, outOff + 8);
+ Pack.intToBigEndian(wKey[128] ^ X0, output, outOff + 12);
+ }
+
+ /**
+ * Decrypt one block of ciphertext.
+ *
+ * @param input the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param output the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ */
+ protected void decryptBlock(
+ byte[] input,
+ int inOff,
+ byte[] output,
+ int outOff)
+ {
+ X3 = wKey[131] ^ Pack.bigEndianToInt(input, inOff);
+ X2 = wKey[130] ^ Pack.bigEndianToInt(input, inOff + 4);
+ X1 = wKey[129] ^ Pack.bigEndianToInt(input, inOff + 8);
+ X0 = wKey[128] ^ Pack.bigEndianToInt(input, inOff + 12);
+
+ ib7(X0, X1, X2, X3);
+ X0 ^= wKey[124]; X1 ^= wKey[125]; X2 ^= wKey[126]; X3 ^= wKey[127];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[120]; X1 ^= wKey[121]; X2 ^= wKey[122]; X3 ^= wKey[123];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[116]; X1 ^= wKey[117]; X2 ^= wKey[118]; X3 ^= wKey[119];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[112]; X1 ^= wKey[113]; X2 ^= wKey[114]; X3 ^= wKey[115];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[108]; X1 ^= wKey[109]; X2 ^= wKey[110]; X3 ^= wKey[111];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[104]; X1 ^= wKey[105]; X2 ^= wKey[106]; X3 ^= wKey[107];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[100]; X1 ^= wKey[101]; X2 ^= wKey[102]; X3 ^= wKey[103];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[96]; X1 ^= wKey[97]; X2 ^= wKey[98]; X3 ^= wKey[99];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[92]; X1 ^= wKey[93]; X2 ^= wKey[94]; X3 ^= wKey[95];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[88]; X1 ^= wKey[89]; X2 ^= wKey[90]; X3 ^= wKey[91];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[84]; X1 ^= wKey[85]; X2 ^= wKey[86]; X3 ^= wKey[87];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[80]; X1 ^= wKey[81]; X2 ^= wKey[82]; X3 ^= wKey[83];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[76]; X1 ^= wKey[77]; X2 ^= wKey[78]; X3 ^= wKey[79];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[72]; X1 ^= wKey[73]; X2 ^= wKey[74]; X3 ^= wKey[75];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[68]; X1 ^= wKey[69]; X2 ^= wKey[70]; X3 ^= wKey[71];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[64]; X1 ^= wKey[65]; X2 ^= wKey[66]; X3 ^= wKey[67];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[60]; X1 ^= wKey[61]; X2 ^= wKey[62]; X3 ^= wKey[63];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[56]; X1 ^= wKey[57]; X2 ^= wKey[58]; X3 ^= wKey[59];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[52]; X1 ^= wKey[53]; X2 ^= wKey[54]; X3 ^= wKey[55];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[48]; X1 ^= wKey[49]; X2 ^= wKey[50]; X3 ^= wKey[51];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[44]; X1 ^= wKey[45]; X2 ^= wKey[46]; X3 ^= wKey[47];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[40]; X1 ^= wKey[41]; X2 ^= wKey[42]; X3 ^= wKey[43];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[36]; X1 ^= wKey[37]; X2 ^= wKey[38]; X3 ^= wKey[39];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[32]; X1 ^= wKey[33]; X2 ^= wKey[34]; X3 ^= wKey[35];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[28]; X1 ^= wKey[29]; X2 ^= wKey[30]; X3 ^= wKey[31];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[24]; X1 ^= wKey[25]; X2 ^= wKey[26]; X3 ^= wKey[27];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[20]; X1 ^= wKey[21]; X2 ^= wKey[22]; X3 ^= wKey[23];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[16]; X1 ^= wKey[17]; X2 ^= wKey[18]; X3 ^= wKey[19];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[12]; X1 ^= wKey[13]; X2 ^= wKey[14]; X3 ^= wKey[15];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[8]; X1 ^= wKey[9]; X2 ^= wKey[10]; X3 ^= wKey[11];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[4]; X1 ^= wKey[5]; X2 ^= wKey[6]; X3 ^= wKey[7];
+ inverseLT(); ib0(X0, X1, X2, X3);
+
+ Pack.intToBigEndian(X3 ^ wKey[3], output, outOff);
+ Pack.intToBigEndian(X2 ^ wKey[2], output, outOff + 4);
+ Pack.intToBigEndian(X1 ^ wKey[1], output, outOff + 8);
+ Pack.intToBigEndian(X0 ^ wKey[0], output, outOff + 12);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/EntropyUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/EntropyUtil.java
new file mode 100644
index 00000000..b34e9938
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/EntropyUtil.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.crypto.prng;
+
+/**
+ * Utility methods for making use of EntropySources.
+ */
+public class EntropyUtil
+{
+ /**
+ * Generate numBytes worth of entropy from the passed in entropy source.
+ *
+ * @param entropySource the entropy source to request the data from.
+ * @param numBytes the number of bytes of entropy requested.
+ * @return a byte array populated with the random data.
+ */
+ public static byte[] generateSeed(EntropySource entropySource, int numBytes)
+ {
+ byte[] bytes = new byte[numBytes];
+
+ if (numBytes * 8 <= entropySource.entropySize())
+ {
+ byte[] ent = entropySource.getEntropy();
+
+ System.arraycopy(ent, 0, bytes, 0, bytes.length);
+ }
+ else
+ {
+ int entSize = entropySource.entropySize() / 8;
+
+ for (int i = 0; i < bytes.length; i += entSize)
+ {
+ byte[] ent = entropySource.getEntropy();
+
+ if (ent.length <= bytes.length - i)
+ {
+ System.arraycopy(ent, 0, bytes, i, ent.length);
+ }
+ else
+ {
+ System.arraycopy(ent, 0, bytes, i, bytes.length - i);
+ }
+ }
+ }
+
+ return bytes;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java
new file mode 100644
index 00000000..2793d60a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.crypto.signers;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Integers;
+
+public class ISOTrailers
+{
+ private static final Map<String, Integer> trailerMap;
+
+ static final public int TRAILER_IMPLICIT = 0xBC;
+
+ static final public int TRAILER_RIPEMD160 = 0x31CC;
+ static final public int TRAILER_RIPEMD128 = 0x32CC;
+ static final public int TRAILER_SHA1 = 0x33CC;
+ static final public int TRAILER_SHA256 = 0x34CC;
+ static final public int TRAILER_SHA512 = 0x35CC;
+ static final public int TRAILER_SHA384 = 0x36CC;
+ static final public int TRAILER_WHIRLPOOL = 0x37CC;
+ static final public int TRAILER_SHA224 = 0x38CC;
+ static final public int TRAILER_SHA512_224 = 0x39CC;
+ static final public int TRAILER_SHA512_256 = 0x40CC;
+
+ static
+ {
+ Map<String, Integer> trailers = new HashMap<String, Integer>();
+
+ trailers.put("RIPEMD128", Integers.valueOf(TRAILER_RIPEMD128));
+ trailers.put("RIPEMD160", Integers.valueOf(TRAILER_RIPEMD160));
+
+ trailers.put("SHA-1", Integers.valueOf(TRAILER_SHA1));
+ trailers.put("SHA-224", Integers.valueOf(TRAILER_SHA224));
+ trailers.put("SHA-256", Integers.valueOf(TRAILER_SHA256));
+ trailers.put("SHA-384", Integers.valueOf(TRAILER_SHA384));
+ trailers.put("SHA-512", Integers.valueOf(TRAILER_SHA512));
+ trailers.put("SHA-512/224", Integers.valueOf(TRAILER_SHA512_224));
+ trailers.put("SHA-512/256", Integers.valueOf(TRAILER_SHA512_256));
+
+ trailers.put("Whirlpool", Integers.valueOf(TRAILER_WHIRLPOOL));
+
+ trailerMap = Collections.unmodifiableMap(trailers);
+ }
+
+ public static Integer getTrailer(Digest digest)
+ {
+ return (Integer)trailerMap.get(digest.getAlgorithmName()); // JDK 1.4 compatibility
+ }
+
+ public static boolean noTrailerAvailable(Digest digest)
+ {
+ return !trailerMap.containsKey(digest.getAlgorithmName());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java
new file mode 100644
index 00000000..e962af94
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java
@@ -0,0 +1,159 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.UnsupportedEncodingException;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.Blake2bDigest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class Blake2bDigestTest
+ extends SimpleTest
+{
+
+ private static final String[][] keyedTestVectors =
+ { // input/message, key, hash
+
+ // Vectors from BLAKE2 web site: https://blake2.net/blake2b-test.txt
+ {
+ "",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568" },
+
+ {
+ "00",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd" },
+
+ {
+ "0001",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965" },
+
+ {
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd" },
+
+ {
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f" },
+
+ {
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+ "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461" } };
+
+ private final static String[][] unkeyedTestVectors =
+ { // from: http://fossies.org/linux/john/src/rawBLAKE2_512_fmt_plug.c
+ // hash, input/message
+ // digests without leading $BLAKE2$
+ {
+ "4245af08b46fbb290222ab8a68613621d92ce78577152d712467742417ebc1153668f1c9e1ec1e152a32a9c242dc686d175e087906377f0c483c5be2cb68953e",
+ "blake2" },
+ {
+ "021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0",
+ "hello world" },
+ {
+ "1f7d9b7c9a90f7bfc66e52b69f3b6c3befbd6aee11aac860e99347a495526f30c9e51f6b0db01c24825092a09dd1a15740f0ade8def87e60c15da487571bcef7",
+ "verystrongandlongpassword" },
+ {
+ "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918",
+ "The quick brown fox jumps over the lazy dog" },
+ {
+ "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce",
+ "" },
+ {
+ "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
+ "abc" },
+ };
+
+ public String getName()
+ {
+ return "Blake2b";
+ }
+
+ private void offsetTest(
+ Digest digest,
+ byte[] input,
+ byte[] expected)
+ {
+ byte[] resBuf = new byte[expected.length + 11];
+
+ digest.update(input, 0, input.length);
+
+ digest.doFinal(resBuf, 11);
+
+ if (!areEqual(Arrays.copyOfRange(resBuf, 11, resBuf.length), expected))
+ {
+ fail("Offset failed got " + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ public void performTest() throws Exception
+ {
+ // test keyed test vectors:
+
+ Blake2bDigest blake2bkeyed = new Blake2bDigest(Hex.decode(keyedTestVectors[0][1]));
+ for (int tv = 0; tv < keyedTestVectors.length; tv++)
+ {
+
+ byte[] input = Hex.decode(keyedTestVectors[tv][0]);
+ blake2bkeyed.reset();
+
+ blake2bkeyed.update(input, 0, input.length);
+ byte[] keyedHash = new byte[64];
+ blake2bkeyed.doFinal(keyedHash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(keyedTestVectors[tv][2]), keyedHash))
+ {
+ fail("Blake2b mismatch on test vector ",
+ keyedTestVectors[tv][2],
+ new String(Hex.encode(keyedHash)));
+ }
+
+ offsetTest(blake2bkeyed, input, keyedHash);
+ }
+
+ Blake2bDigest blake2bunkeyed = new Blake2bDigest();
+ // test unkeyed test vectors:
+ for (int i = 0; i < unkeyedTestVectors.length; i++)
+ {
+
+ try
+ {
+ // blake2bunkeyed.update(
+ // unkeyedTestVectors[i][1].getBytes("UTF-8"));
+ // test update(byte b)
+ byte[] unkeyedInput = unkeyedTestVectors[i][1]
+ .getBytes("UTF-8");
+ for (int j = 0; j < unkeyedInput.length; j++)
+ {
+ blake2bunkeyed.update(unkeyedInput[j]);
+ }
+ } catch (UnsupportedEncodingException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ byte[] unkeyedHash = new byte[64];
+ blake2bunkeyed.doFinal(unkeyedHash, 0);
+ blake2bunkeyed.reset();
+
+ if (!Arrays.areEqual(Hex.decode(unkeyedTestVectors[i][0]),
+ unkeyedHash))
+ {
+ fail("Blake2b mismatch on test vector ",
+ unkeyedTestVectors[i][0],
+ new String(Hex.encode(unkeyedHash)));
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception
+ {
+ runTest(new Blake2bDigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java
new file mode 100644
index 00000000..7d9bdf12
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java
@@ -0,0 +1,363 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.digests.KeccakDigest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Keccak Digest Test
+ */
+public class KeccakDigestTest
+ extends SimpleTest
+{
+ final static String[] messages = {
+ "",
+ "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67",
+ "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e"
+ };
+
+ final static String[] digests288 = { // the default settings
+ "6753e3380c09e385d0339eb6b050a68f66cfd60a73476e6fd6adeb72f5edd7c6f04a5d01", // message[0]
+ "0bbe6afae0d7e89054085c1cc47b1689772c89a41796891e197d1ca1b76f288154933ded", // message[1]
+ "82558a209b960ddeb531e6dcb281885b2400ca160472462486e79f071e88a3330a8a303d", // message[2]
+ "94049e1ad7ef5d5b0df2b880489e7ab09ec937c3bfc1b04470e503e1ac7b1133c18f86da", // 64k a-test
+ "a9cb5a75b5b81b7528301e72553ed6770214fa963956e790528afe420de33c074e6f4220", // random alphabet test
+ "eadaf5ba2ad6a2f6f338fce0e1efdad2a61bb38f6be6068b01093977acf99e97a5d5827c" // extremely long data test
+ };
+
+ final static String[] digests224 = {
+ "f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd",
+ "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe",
+ "c59d4eaeac728671c635ff645014e2afa935bebffdb5fbd207ffdeab",
+ "f621e11c142fbf35fa8c22841c3a812ba1e0151be4f38d80b9f1ff53",
+ "68b5fc8c87193155bba68a2485377e809ee4f81a85ef023b9e64add0",
+ "c42e4aee858e1a8ad2976896b9d23dd187f64436ee15969afdbc68c5"
+ };
+
+ final static String[] digests256 = {
+ "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15",
+ "578951e24efd62a3d63a86f7cd19aaa53c898fe287d2552133220370240b572d",
+ "0047a916daa1f92130d870b542e22d3108444f5a7e4429f05762fb647e6ed9ed",
+ "db368762253ede6d4f1db87e0b799b96e554eae005747a2ea687456ca8bcbd03",
+ "5f313c39963dcf792b5470d4ade9f3a356a3e4021748690a958372e2b06f82a4"
+ };
+
+ final static String[] digests384 = {
+ "2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff",
+ "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3",
+ "9ad8e17325408eddb6edee6147f13856ad819bb7532668b605a24a2d958f88bd5c169e56dc4b2f89ffd325f6006d820b",
+ "c704cfe7a1a53208ca9526cd24251e0acdc252ecd978eee05acd16425cfb404ea81f5a9e2e5e97784d63ee6a0618a398",
+ "d4fe8586fd8f858dd2e4dee0bafc19b4c12b4e2a856054abc4b14927354931675cdcaf942267f204ea706c19f7beefc4",
+ "9b7168b4494a80a86408e6b9dc4e5a1837c85dd8ff452ed410f2832959c08c8c0d040a892eb9a755776372d4a8732315"
+ };
+
+ final static String[] digests512 = {
+ "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e",
+ "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609",
+ "ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded6387889064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760",
+ "34341ead153aa1d1fdcf6cf624c2b4f6894b6fd16dc38bd4ec971ac0385ad54fafcb2e0ed86a1e509456f4246fdcb02c3172824cd649d9ad54c51f7fb49ea67c",
+ "dc44d4f4d36b07ab5fc04016cbe53548e5a7778671c58a43cb379fd00c06719b8073141fc22191ffc3db5f8b8983ae8341fa37f18c1c969664393aa5ceade64e",
+ "3e122edaf37398231cfaca4c7c216c9d66d5b899ec1d7ac617c40c7261906a45fc01617a021e5da3bd8d4182695b5cb785a28237cbb167590e34718e56d8aab8"
+ };
+
+ // test vectors from http://www.di-mgt.com.au/hmac_sha3_testvectors.html
+ final static byte[][] macKeys =
+ {
+ Hex.decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
+ Hex.decode("4a656665"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
+ Hex.decode("0102030405060708090a0b0c0d0e0f10111213141516171819"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaa"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaa"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+ };
+
+ final static String[] macData =
+ {
+ "4869205468657265",
+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
+ "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" +
+ "dddddddddddddddddddddddddddddddddddd",
+ "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" +
+ "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" +
+ "65204b6579202d2048617368204b6579204669727374",
+ "5468697320697320612074657374207573696e672061206c6172676572207468" +
+ "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" +
+ "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" +
+ "647320746f20626520686173686564206265666f7265206265696e6720757365" +
+ "642062792074686520484d414320616c676f726974686d2e",
+ "5468697320697320612074657374207573696e672061206c6172676572207468" +
+ "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" +
+ "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" +
+ "647320746f20626520686173686564206265666f7265206265696e6720757365\n" +
+ "642062792074686520484d414320616c676f726974686d2e"
+ };
+
+ final static String[] mac224 =
+ {
+ "b73d595a2ba9af815e9f2b4e53e78581ebd34a80b3bbaac4e702c4cc",
+ "e824fec96c074f22f99235bb942da1982664ab692ca8501053cbd414",
+ "770df38c99d6e2bacd68056dcfe07d4c89ae20b2686a6185e1faa449",
+ "305a8f2dfb94bad28861a03cbc4d590febe775c58cb4961c28428a0b",
+ "e7a52dfa45f95a217c100066b239aa8ad519be9b35d667268b1b57ff",
+ "ba13009405a929f398b348885caa5419191bb948ada32194afc84104",
+ "92649468be236c3c72c189909c063b13f994be05749dc91310db639e"
+ };
+
+ final static String[] mac256 =
+ {
+ "9663d10c73ee294054dc9faf95647cb99731d12210ff7075fb3d3395abfb9821",
+ "aa9aed448c7abc8b5e326ffa6a01cdedf7b4b831881468c044ba8dd4566369a1",
+ "95f43e50f8df80a21977d51a8db3ba572dcd71db24687e6f86f47c1139b26260",
+ "6331ba9b4af5804a68725b3663eb74814494b63c6093e35fb320a85d507936fd",
+ "b4d0cdee7ec2ba81a88b86918958312300a15622377929a054a9ce3ae1fac2b6",
+ "1fdc8cb4e27d07c10d897dec39c217792a6e64fa9c63a77ce42ad106ef284e02",
+ "fdaa10a0299aecff9bb411cf2d7748a4022e4a26be3fb5b11b33d8c2b7ef5484"
+ };
+
+ final static String[] mac384 =
+ {
+ "892dfdf5d51e4679bf320cd16d4c9dc6f749744608e003add7fba894acff87361efa4e5799be06b6461f43b60ae97048",
+ "5af5c9a77a23a6a93d80649e562ab77f4f3552e3c5caffd93bdf8b3cfc6920e3023fc26775d9df1f3c94613146ad2c9d",
+ "4243c29f2201992ff96441e3b91ff81d8c601d706fbc83252684a4bc51101ca9b2c06ddd03677303c502ac5331752a3c",
+ "b730724d3d4090cda1be799f63acbbe389fef7792fc18676fa5453aab398664650ed029c3498bbe8056f06c658e1e693",
+ "d62482ef601d7847439b55236e9679388ffcd53c62cd126f39be6ea63de762e26cd5974cb9a8de401b786b5555040f6f",
+ "4860ea191ac34994cf88957afe5a836ef36e4cc1a66d75bf77defb7576122d75f60660e4cf731c6effac06402787e2b9",
+ "fe9357e3cfa538eb0373a2ce8f1e26ad6590afdaf266f1300522e8896d27e73f654d0631c8fa598d4bb82af6b744f4f5"
+ };
+
+ final static String[] mac512 =
+ {
+ "8852c63be8cfc21541a4ee5e5a9a852fc2f7a9adec2ff3a13718ab4ed81aaea0b87b7eb397323548e261a64e7fc75198f6663a11b22cd957f7c8ec858a1c7755",
+ "c2962e5bbe1238007852f79d814dbbecd4682e6f097d37a363587c03bfa2eb0859d8d9c701e04cececfd3dd7bfd438f20b8b648e01bf8c11d26824b96cebbdcb",
+ "eb0ed9580e0ec11fc66cbb646b1be904eaff6da4556d9334f65ee4b2c85739157bae9027c51505e49d1bb81cfa55e6822db55262d5a252c088a29a5e95b84a66",
+ "b46193bb59f4f696bf702597616da91e2a4558a593f4b015e69141ba81e1e50ea580834c2b87f87baa25a3a03bfc9bb389847f2dc820beae69d30c4bb75369cb",
+ "d05888a6ebf8460423ea7bc85ea4ffda847b32df32291d2ce115fd187707325c7ce4f71880d91008084ce24a38795d20e6a28328a0f0712dc38253370da3ebb5",
+ "2c6b9748d35c4c8db0b4407dd2ed2381f133bdbd1dfaa69e30051eb6badfcca64299b88ae05fdbd3dd3dd7fe627e42e39e48b0fe8c7f1e85f2dbd52c2d753572",
+ "6adc502f14e27812402fc81a807b28bf8a53c87bea7a1df6256bf66f5de1a4cb741407ad15ab8abc136846057f881969fbb159c321c904bfb557b77afb7778c8"
+ };
+
+ final static KeyParameter truncKey = new KeyParameter(Hex.decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"));
+ final static byte[] truncData = Hex.decode("546573742057697468205472756e636174696f6e");
+
+ final static byte[] trunc224 = Hex.decode("f52bbcfd654264e7133085c5e69b72c3");
+ final static byte[] trunc256 = Hex.decode("745e7e687f8335280d54202ef13cecc6");
+ final static byte[] trunc384 = Hex.decode("fa9aea2bc1e181e47cbb8c3df243814d");
+ final static byte[] trunc512 = Hex.decode("04c929fead434bba190dacfa554ce3f5");
+
+ final static byte[] xtremeData = Hex.decode("61626364656667686263646566676869636465666768696a6465666768696a6b65666768696a6b6c666768696a6b6c6d6768696a6b6c6d6e68696a6b6c6d6e6f");
+
+ KeccakDigestTest()
+ {
+ }
+
+ public String getName()
+ {
+ return "Keccak";
+ }
+
+ private void testDigest(Digest digest, String[] expected)
+ {
+ byte[] hash = new byte[digest.getDigestSize()];
+
+ for (int i = 0; i != messages.length; i++)
+ {
+ if (messages.length != 0)
+ {
+ byte[] data = Hex.decode(messages[i]);
+
+ digest.update(data, 0, data.length);
+ }
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[i]), hash))
+ {
+ fail("Keccak mismatch on " + digest.getAlgorithmName() + " index " + i);
+ }
+ }
+
+ byte[] k64 = new byte[1024 * 64];
+
+ for (int i = 0; i != k64.length; i++)
+ {
+ k64[i] = (byte)'a';
+ }
+
+ digest.update(k64, 0, k64.length);
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length]), hash))
+ {
+ fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k a");
+ }
+
+ for (int i = 0; i != k64.length; i++)
+ {
+ digest.update((byte)'a');
+ }
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length]), hash))
+ {
+ fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k a single");
+ }
+
+
+ for (int i = 0; i != k64.length; i++)
+ {
+ k64[i] = (byte)('a' + (i % 26));
+ }
+
+ digest.update(k64, 0, k64.length);
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length + 1]), hash))
+ {
+ fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k alpha");
+ }
+
+ for (int i = 0; i != 64; i++)
+ {
+ digest.update(k64[i * 1024]);
+ digest.update(k64, i * 1024 + 1, 1023);
+ }
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length + 1]), hash))
+ {
+ fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k chunked alpha");
+ }
+
+ testDigestDoFinal(digest);
+
+ //
+ // extremely long data test
+ //
+// System.out.println("Starting very long");
+// for (int i = 0; i != 16384; i++)
+// {
+// for (int j = 0; j != 1024; j++)
+// {
+// digest.update(xtremeData, 0, xtremeData.length);
+// }
+// }
+//
+// digest.doFinal(hash, 0);
+//
+// if (!Arrays.areEqual(Hex.decode(expected[messages.length + 2]), hash))
+// {
+// fail("Keccak mismatch on " + digest.getAlgorithmName() + " extreme data test");
+// }
+// System.out.println("Done");
+ }
+
+ private void testDigestDoFinal(Digest digest)
+ {
+ byte[] hash = new byte[digest.getDigestSize()];
+ digest.doFinal(hash, 0);
+
+ for (int i = 0; i <= digest.getDigestSize(); ++i)
+ {
+ byte[] cmp = new byte[2 * digest.getDigestSize()];
+ System.arraycopy(hash, 0, cmp, i, hash.length);
+
+ byte[] buf = new byte[2 * digest.getDigestSize()];
+ digest.doFinal(buf, i);
+
+ if (!Arrays.areEqual(cmp, buf))
+ {
+ fail("Keccak offset doFinal on " + digest.getAlgorithmName());
+ }
+ }
+ }
+
+ private void testMac(Digest digest, byte[][] keys, String[] data, String[] expected, byte[] truncExpected)
+ {
+ Mac mac = new HMac(digest);
+
+ for (int i = 0; i != keys.length; i++)
+ {
+ mac.init(new KeyParameter(keys[i]));
+
+ byte[] mData = Hex.decode(data[i]);
+
+ mac.update(mData, 0, mData.length);
+
+ byte[] macV = new byte[mac.getMacSize()];
+
+ mac.doFinal(macV, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[i]), macV))
+ {
+ fail("Keccak HMAC mismatch on " + digest.getAlgorithmName());
+ }
+ }
+
+ mac = new HMac(digest);
+
+ mac.init(truncKey);
+
+ mac.update(truncData, 0, truncData.length);
+
+ byte[] macV = new byte[mac.getMacSize()];
+
+ mac.doFinal(macV, 0);
+
+ for (int i = 0; i != truncExpected.length; i++)
+ {
+ if (macV[i] != truncExpected[i])
+ {
+ fail("mismatch on truncated HMAC for " + digest.getAlgorithmName());
+ }
+ }
+ }
+
+ public void performTest() throws Exception
+ {
+ testDigest(new KeccakDigest(), digests288);
+ testDigest(new KeccakDigest(224), digests224);
+ testDigest(new KeccakDigest(256), digests256);
+ testDigest(new KeccakDigest(384), digests384);
+ testDigest(new KeccakDigest(512), digests512);
+
+ testMac(new KeccakDigest(224), macKeys, macData, mac224, trunc224);
+ testMac(new KeccakDigest(256), macKeys, macData, mac256, trunc256);
+ testMac(new KeccakDigest(384), macKeys, macData, mac384, trunc384);
+ testMac(new KeccakDigest(512), macKeys, macData, mac512, trunc512);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new KeccakDigest((KeccakDigest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new KeccakDigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RNGUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RNGUtils.java
new file mode 100644
index 00000000..166c49da
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RNGUtils.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.crypto.test;
+
+import java.util.Random;
+
+class RNGUtils
+{
+ public static int nextInt(Random rng, int n)
+ {
+ return rng.nextInt(n);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java
new file mode 100644
index 00000000..a3ec2eba
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java
@@ -0,0 +1,305 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.BufferedReader;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * SHAKE Digest Test
+ */
+public class SHAKEDigestTest
+ extends SimpleTest
+{
+ static class MySHAKEDigest extends SHAKEDigest
+ {
+ MySHAKEDigest(int bitLength)
+ {
+ super(bitLength);
+ }
+
+ int myDoFinal(byte[] out, int outOff, int outLen, byte partialByte, int partialBits)
+ {
+ return doFinal(out, outOff, outLen, partialByte, partialBits);
+ }
+ }
+
+ SHAKEDigestTest()
+ {
+ }
+
+ public String getName()
+ {
+ return "SHAKE";
+ }
+
+ public void performTest() throws Exception
+ {
+ testVectors();
+ }
+
+ public void testVectors() throws Exception
+ {
+ BufferedReader r = new BufferedReader(new InputStreamReader(
+ getClass().getResourceAsStream("SHAKETestVectors.txt")));
+
+ String line;
+ while (null != (line = readLine(r)))
+ {
+ if (line.length() != 0)
+ {
+ TestVector v = readTestVector(r, line);
+ runTestVector(v);
+ }
+ }
+
+ r.close();
+ }
+
+ private MySHAKEDigest createDigest(String algorithm) throws Exception
+ {
+ if (algorithm.startsWith("SHAKE-"))
+ {
+ int bits = parseDecimal(algorithm.substring("SHAKE-".length()));
+ return new MySHAKEDigest(bits);
+ }
+ throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
+ }
+
+ private byte[] decodeBinary(String block)
+ {
+ int bits = block.length();
+ int fullBytes = bits / 8;
+ int totalBytes = (bits + 7) / 8;
+ byte[] result = new byte[totalBytes];
+
+ for (int i = 0; i < fullBytes; ++i)
+ {
+ String byteStr = reverse(block.substring(i * 8, (i + 1) * 8));
+ result[i] = (byte)parseBinary(byteStr);
+ }
+
+ if (totalBytes > fullBytes)
+ {
+ String byteStr = reverse(block.substring(fullBytes * 8));
+ result[fullBytes] = (byte)parseBinary(byteStr);
+ }
+
+ return result;
+ }
+
+ private int parseBinary(String s)
+ {
+ return Integer.parseInt(s, 2);
+ }
+
+ private int parseDecimal(String s)
+ {
+ return Integer.parseInt(s);
+ }
+
+ private String readBlock(BufferedReader r) throws IOException
+ {
+ StringBuffer b = new StringBuffer();
+ String line;
+ while ((line = readBlockLine(r)) != null)
+ {
+ b.append(line);
+ }
+ return b.toString();
+ }
+
+ private String readBlockLine(BufferedReader r) throws IOException
+ {
+ String line = readLine(r);
+ if (line == null || line.length() == 0)
+ {
+ return null;
+ }
+
+ char[] chars = line.toCharArray();
+
+ int pos = 0;
+ for (int i = 0; i != chars.length; i++)
+ {
+ if (chars[i] != ' ')
+ {
+ chars[pos++] = chars[i];
+ }
+ }
+
+ return new String(chars, 0, pos);
+ }
+
+ private TestVector readTestVector(BufferedReader r, String header) throws IOException
+ {
+ String[] parts = splitAround(header, TestVector.SAMPLE_OF);
+
+ String algorithm = parts[0];
+ int bits = parseDecimal(stripFromChar(parts[1], '-'));
+
+ skipUntil(r, TestVector.MSG_HEADER);
+ String messageBlock = readBlock(r);
+ if (messageBlock.length() != bits)
+ {
+ throw new IllegalStateException("Test vector length mismatch");
+ }
+ byte[] message = decodeBinary(messageBlock);
+
+ skipUntil(r, TestVector.OUTPUT_HEADER);
+ byte[] output = Hex.decode(readBlock(r));
+
+ return new TestVector(algorithm, bits, message, output);
+ }
+
+ private String readLine(BufferedReader r) throws IOException
+ {
+ String line = r.readLine();
+ return line == null ? null : stripFromChar(line, '#').trim();
+ }
+
+ private String requireLine(BufferedReader r) throws IOException
+ {
+ String line = readLine(r);
+ if (line == null)
+ {
+ throw new EOFException();
+ }
+ return line;
+ }
+
+ private String reverse(String s)
+ {
+ return new StringBuffer(s).reverse().toString();
+ }
+
+ private void runTestVector(TestVector v) throws Exception
+ {
+ int bits = v.getBits();
+ int partialBits = bits % 8;
+
+ byte[] expected = v.getOutput();
+
+// System.out.println(v.getAlgorithm() + " " + bits + "-bit");
+// System.out.println(Hex.toHexString(v.getMessage()).toUpperCase());
+// System.out.println(Hex.toHexString(expected).toUpperCase());
+
+ int outLen = expected.length;
+
+ MySHAKEDigest d = createDigest(v.getAlgorithm());
+ byte[] output = new byte[outLen];
+
+ byte[] m = v.getMessage();
+ if (partialBits == 0)
+ {
+ d.update(m, 0, m.length);
+ d.doFinal(output, 0, outLen);
+ }
+ else
+ {
+ d.update(m, 0, m.length - 1);
+ d.myDoFinal(output, 0, outLen, m[m.length - 1], partialBits);
+ }
+
+ if (!Arrays.areEqual(expected, output))
+ {
+ fail(v.getAlgorithm() + " " + v.getBits() + "-bit test vector hash mismatch");
+// System.err.println(v.getAlgorithm() + " " + v.getBits() + "-bit test vector hash mismatch");
+// System.err.println(Hex.toHexString(output).toUpperCase());
+ }
+ }
+
+ private void skipUntil(BufferedReader r, String header) throws IOException
+ {
+ String line;
+ do
+ {
+ line = requireLine(r);
+ }
+ while (line.length() == 0);
+ if (!line.equals(header))
+ {
+ throw new IOException("Expected: " + header);
+ }
+ }
+
+ private String[] splitAround(String s, String separator)
+ {
+ List strings = new ArrayList();
+
+ String remaining = s;
+ int index;
+
+ while ((index = remaining.indexOf(separator)) > 0)
+ {
+ strings.add(remaining.substring(0, index));
+ remaining = remaining.substring(index + separator.length());
+ }
+ strings.add(remaining);
+
+ return (String[])strings.toArray(new String[strings.size()]);
+ }
+
+ private String stripFromChar(String s, char c)
+ {
+ int i = s.indexOf(c);
+ if (i >= 0)
+ {
+ s = s.substring(0, i);
+ }
+ return s;
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHAKEDigestTest());
+ }
+
+ private static class TestVector
+ {
+ private static String SAMPLE_OF = " sample of ";
+ private static String MSG_HEADER = "Msg as bit string";
+ private static String OUTPUT_HEADER = "Output val is";
+
+ private String algorithm;
+ private int bits;
+ private byte[] message;
+ private byte[] output;
+
+ private TestVector(String algorithm, int bits, byte[] message, byte[] output)
+ {
+ this.algorithm = algorithm;
+ this.bits = bits;
+ this.message = message;
+ this.output = output;
+ }
+
+ public String getAlgorithm()
+ {
+ return algorithm;
+ }
+
+ public int getBits()
+ {
+ return bits;
+ }
+
+ public byte[] getMessage()
+ {
+ return message;
+ }
+
+ public byte[] getOutput()
+ {
+ return output;
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SM4Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM4Test.java
new file mode 100644
index 00000000..7c12e5b1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM4Test.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.engines.SM4Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * SM4 tester, vectors from <a href="http://eprint.iacr.org/2008/329.pdf">http://eprint.iacr.org/2008/329.pdf</a>
+ */
+public class SM4Test
+ extends CipherTest
+{
+ static SimpleTest[] tests = {
+ new BlockCipherVectorTest(0, new SM4Engine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba9876543210")),
+ "0123456789abcdeffedcba9876543210",
+ "681edf34d206965e86b3e94f536e4246")
+ };
+
+ SM4Test()
+ {
+ super(tests, new SM4Engine(), new KeyParameter(new byte[16]));
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ test1000000();
+ }
+
+ private void test1000000()
+ {
+ byte[] plain = Hex.decode("0123456789abcdeffedcba9876543210");
+ byte[] key = Hex.decode("0123456789abcdeffedcba9876543210");
+ byte[] cipher = Hex.decode("595298c7c6fd271f0402f804c33d3f66");
+ byte[] buf = new byte[16];
+
+ BlockCipher engine = new SM4Engine();
+
+ engine.init(true, new KeyParameter(key));
+
+ System.arraycopy(plain, 0, buf, 0, buf.length);
+
+ for (int i = 0; i != 1000000; i++)
+ {
+ engine.processBlock(buf, 0, buf, 0);
+ }
+
+ if (!areEqual(cipher, buf))
+ {
+ fail("1000000 encryption test failed");
+ }
+
+ engine.init(false, new KeyParameter(key));
+
+ for (int i = 0; i != 1000000; i++)
+ {
+ engine.processBlock(buf, 0, buf, 0);
+ }
+
+ if (!areEqual(plain, buf))
+ {
+ fail("1000000 decryption test failed");
+ }
+ }
+
+ public String getName()
+ {
+ return "SM4";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SM4Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/TnepresTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/TnepresTest.java
new file mode 100644
index 00000000..6b873587
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/TnepresTest.java
@@ -0,0 +1,144 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.engines.TnepresEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors based on Floppy 4 of the Serpent AES submission.
+ */
+public class TnepresTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new TnepresEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000000000000000000", "8910494504181950f98dd998a82b6749"),
+ new BlockCipherVectorTest(1, new TnepresEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "80000000000000000000000000000000", "10b5ffb720b8cb9002a1142b0ba2e94a"),
+ new BlockCipherVectorTest(2, new TnepresEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000008000000000000000000000", "4f057a42d8d5bd9746e434680ddcd5e5"),
+ new BlockCipherVectorTest(3, new TnepresEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000400000000000", "99407bf8582ef12550886ef5b6f169b9"),
+ new BlockCipherVectorTest(4, new TnepresEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "40000000000000000000000000000000", "d522a3b8d6d89d4d2a124fdd88f36896"),
+ new BlockCipherVectorTest(5, new TnepresEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00000000000200000000000000000000", "189b8ec3470085b3da97e82ca8964e32"),
+ new BlockCipherVectorTest(6, new TnepresEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00000000000000000000008000000000", "f77d868cf760b9143a89809510ccb099"),
+ new BlockCipherVectorTest(7, new TnepresEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "08000000000000000000000000000000", "d43b7b981b829342fce0e3ec6f5f4c82"),
+ new BlockCipherVectorTest(8, new TnepresEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000100000000000000", "0bf30e1a0c33ccf6d5293177886912a7"),
+ new BlockCipherVectorTest(9, new TnepresEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000000000000000001", "6a7f3b805d2ddcba49b89770ade5e507"),
+ new BlockCipherVectorTest(10, new TnepresEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "49afbfad9d5a34052cd8ffa5986bd2dd"),
+ new BlockCipherVectorTest(11, new TnepresEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000004000000000000000000000")),
+ "00000000000000000000000000000000", "ba8829b1de058c4b48615d851fc74f17"),
+ new BlockCipherVectorTest(12, new TnepresEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000100000000")),
+ "00000000000000000000000000000000", "89f64377bf1e8a46c8247044e8056a98"),
+/*
+ new BlockCipherMonteCarloTest(13, 10000, new TnepresEngine(),
+ new KeyParameter(Hex.decode("47f5f881daab9b67b43bd1342e339c19")),
+ "7a4f7db38c52a8b711b778a38d203b6b", "003380e19f10065740394f48e2fe80b7"),
+*/
+ new BlockCipherMonteCarloTest(13, 100, new TnepresEngine(),
+ new KeyParameter(Hex.decode("47f5f881daab9b67b43bd1342e339c19")),
+ "7a4f7db38c52a8b711b778a38d203b6b", "4db75303d815c2f7cc6ca935d1c5a046"),
+/*
+ new BlockCipherMonteCarloTest(14, 10000, new TnepresEngine(),
+ new KeyParameter(Hex.decode("31fba879ebc5e80df35e6fa33eaf92d6")),
+ "70a05e12f74589009692a337f53ff614", "afb5425426906db26b70bdf842ac5400"),
+*/
+ new BlockCipherMonteCarloTest(14, 100, new TnepresEngine(),
+ new KeyParameter(Hex.decode("31fba879ebc5e80df35e6fa33eaf92d6")),
+ "70a05e12f74589009692a337f53ff614", "fc53a50f4d3bc9836001893d2f41742d"),
+/*
+ new BlockCipherMonteCarloTest(15, 10000, new TnepresEngine(),
+ new KeyParameter(Hex.decode("bde6dd392307984695aee80e574f9977caae9aa78eda53e8")),
+ "9cc523d034a93740a0aa4e2054bb34d8", "1949d506ada7de1f1344986e8ea049b2"),
+*/
+ new BlockCipherMonteCarloTest(15, 100, new TnepresEngine(),
+ new KeyParameter(Hex.decode("bde6dd392307984695aee80e574f9977caae9aa78eda53e8")),
+ "9cc523d034a93740a0aa4e2054bb34d8", "77117e6a9e80f40b2a36b7d755573c2d"),
+/*
+ new BlockCipherMonteCarloTest(16, 10000, new TnepresEngine(),
+ new KeyParameter(Hex.decode("60f6f8ad4290699dc50921a1bbcca92da914e7d9cf01a9317c79c0af8f2487a1")),
+ "ee1a61106fae2d381d686cbf854bab65", "e57f45559027cb1f2ed9603d814e1c34"),
+*/
+ new BlockCipherMonteCarloTest(16, 100, new TnepresEngine(),
+ new KeyParameter(Hex.decode("60f6f8ad4290699dc50921a1bbcca92da914e7d9cf01a9317c79c0af8f2487a1")),
+ "ee1a61106fae2d381d686cbf854bab65", "dcd7f13ea0dcdfd0139d1a42e2ffb84b")
+ };
+
+ TnepresTest()
+ {
+ super(tests, new TnepresEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ doCbcMonte(new byte[16], new byte[16], new byte[16], Hex.decode("9ea101ecebaa41c712bcb0d9bab3e2e4"));
+ doCbcMonte(Hex.decode("9ea101ecebaa41c712bcb0d9bab3e2e4"), Hex.decode("9ea101ecebaa41c712bcb0d9bab3e2e4"), Hex.decode("b4813d8a66244188b9e92c75913fa2f4"), Hex.decode("f86b2c265b9c75869f31e2c684c13e9f"));
+ }
+
+ private void doCbcMonte(byte[] key, byte[] iv, byte[] pt, byte[] expected)
+ {
+ BlockCipher c = new TnepresEngine();
+
+ byte[] ct = new byte[16];
+
+ System.arraycopy(iv, 0, ct, 0, 16);
+
+ for (int i = 0; i < 10000; i++)
+ {
+ for (int k = 0; k != iv.length; k++)
+ {
+ iv[k] ^= pt[k];
+ }
+ System.arraycopy(ct, 0, pt, 0, 16);
+
+ c.init(true, new KeyParameter(key));
+
+ c.processBlock(iv, 0, ct, 0);
+
+ System.arraycopy(ct, 0, iv, 0, 16);
+ }
+
+ if (!Arrays.areEqual(expected, ct))
+ {
+ fail("CBC monte test failed");
+ }
+ }
+
+ public String getName()
+ {
+ return "Tnepres";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new TnepresTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPListener.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPListener.java
new file mode 100644
index 00000000..81f86e57
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPListener.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.util.Properties;
+
+public interface CAVPListener
+{
+ public void setup();
+
+ public void receiveStart(String name);
+
+ public void receiveCAVPVectors(String name, Properties config, Properties vectors);
+
+ public void receiveCommentLine(String commentLine);
+
+ public void receiveEnd();
+
+ public void tearDown();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPReader.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPReader.java
new file mode 100644
index 00000000..9cd33afc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPReader.java
@@ -0,0 +1,152 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA224Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA384Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.macs.CMac;
+import org.bouncycastle.crypto.macs.HMac;
+
+public class CAVPReader
+{
+
+ private static final Pattern COMMENT_PATTERN = Pattern.compile("^\\s*\\#\\s*(.*)$");
+ private static final Pattern CONFIG_PATTERN = Pattern.compile("^\\s*+\\[\\s*+(.*?)\\s*+=\\s*+(.*?)\\s*+\\]\\s*+$");
+ private static final Pattern VECTOR_PATTERN = Pattern.compile("^\\s*+(.*?)\\s*+=\\s*+(.*?)\\s*+$");
+ private static final Pattern EMPTY_PATTERN = Pattern.compile("^\\s*+$");
+ static final Pattern PATTERN_FOR_R = Pattern.compile("(\\d+)_BITS");
+ private final CAVPListener listener;
+ private String name;
+ private BufferedReader lineReader;
+
+
+ public CAVPReader(CAVPListener listener)
+ {
+ this.listener = listener;
+ }
+
+ public void setInput(String name, Reader reader)
+ {
+ this.name = name;
+ this.lineReader = new BufferedReader(reader);
+ }
+
+ public void readAll()
+ throws IOException
+ {
+
+ listener.setup();
+
+ Properties config = new Properties();
+
+ boolean startNewVector = true;
+
+ Properties vectors = new Properties();
+
+ while (true)
+ {
+ final String line = lineReader.readLine();
+ if (line == null)
+ {
+ listener.receiveEnd();
+ break;
+ }
+
+ final Matcher commentMatcher = COMMENT_PATTERN.matcher(line);
+ if (commentMatcher.matches())
+ {
+ listener.receiveCommentLine(commentMatcher.group(1));
+ continue;
+ }
+
+ final Matcher configMatcher = CONFIG_PATTERN.matcher(line);
+ if (configMatcher.matches())
+ {
+ config.put(configMatcher.group(1), configMatcher.group(2));
+ continue;
+ }
+
+ final Matcher vectorMatcher = VECTOR_PATTERN.matcher(line);
+ if (vectorMatcher.matches())
+ {
+ vectors.put(vectorMatcher.group(1), vectorMatcher.group(2));
+ startNewVector = false;
+ continue;
+ }
+
+ final Matcher emptyMatcher = EMPTY_PATTERN.matcher(line);
+ if (emptyMatcher.matches())
+ {
+ if (startNewVector)
+ {
+ continue;
+ }
+
+ listener.receiveCAVPVectors(name, config, vectors);
+ vectors = new Properties();
+ startNewVector = true;
+ }
+ }
+
+ listener.tearDown();
+ }
+
+ static Mac createPRF(Properties config)
+ {
+ final Mac prf;
+ if (config.getProperty("PRF").matches("CMAC_AES\\d\\d\\d"))
+ {
+ BlockCipher blockCipher = new AESFastEngine();
+ prf = new CMac(blockCipher);
+ }
+ else if (config.getProperty("PRF").matches("CMAC_TDES\\d"))
+ {
+ BlockCipher blockCipher = new DESedeEngine();
+ prf = new CMac(blockCipher);
+ }
+ else if (config.getProperty("PRF").matches("HMAC_SHA1"))
+ {
+ Digest digest = new SHA1Digest();
+ prf = new HMac(digest);
+ }
+ else if (config.getProperty("PRF").matches("HMAC_SHA224"))
+ {
+ Digest digest = new SHA224Digest();
+ prf = new HMac(digest);
+ }
+ else if (config.getProperty("PRF").matches("HMAC_SHA256"))
+ {
+ Digest digest = new SHA256Digest();
+ prf = new HMac(digest);
+ }
+ else if (config.getProperty("PRF").matches("HMAC_SHA384"))
+ {
+ Digest digest = new SHA384Digest();
+ prf = new HMac(digest);
+ }
+ else if (config.getProperty("PRF").matches("HMAC_SHA512"))
+ {
+ Digest digest = new SHA512Digest();
+ prf = new HMac(digest);
+ }
+ else
+ {
+ throw new IllegalStateException("Unknown Mac for PRF");
+ }
+ return prf;
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFCounterTests.java
new file mode 100644
index 00000000..81f10824
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFCounterTests.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+import java.util.regex.Matcher;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.KDFCounterBytesGenerator;
+import org.bouncycastle.crypto.params.KDFCounterParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.TestFailedException;
+
+public final class KDFCounterTests
+ implements CAVPListener
+{
+ private PrintWriter out;
+
+ public void receiveCAVPVectors(String name, Properties config,
+ Properties vectors)
+ {
+
+ // create Mac based PRF from PRF property, create the KDF
+ final Mac prf = CAVPReader.createPRF(config);
+ final KDFCounterBytesGenerator gen = new KDFCounterBytesGenerator(prf);
+
+
+ Matcher matcherForR = CAVPReader.PATTERN_FOR_R.matcher(config.getProperty("RLEN"));
+ if (!matcherForR.matches())
+ {
+ throw new IllegalStateException("RLEN value should always match");
+ }
+ final int r = Integer.parseInt(matcherForR.group(1));
+
+ final int count = Integer.parseInt(vectors.getProperty("COUNT"));
+ final int l = Integer.parseInt(vectors.getProperty("L"));
+ final byte[] ki = Hex.decode(vectors.getProperty("KI"));
+
+ //Three variants of this KDF are possible, with the counter before the fixed data, after the fixed data, or in the middle of the fixed data.
+ if (config.getProperty("CTRLOCATION").matches("BEFORE_FIXED"))
+ {
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFCounterParameters params = new KDFCounterParameters(ki, null, fixedInputData, r);
+ gen.init(params);
+ }
+ else if (config.getProperty("CTRLOCATION").matches("AFTER_FIXED"))
+ {
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFCounterParameters params = new KDFCounterParameters(ki, fixedInputData, null, r);
+ gen.init(params);
+ }
+ else if (config.getProperty("CTRLOCATION").matches("MIDDLE_FIXED"))
+ {
+ final byte[] DataBeforeCtrData = Hex.decode(vectors.getProperty("DataBeforeCtrData"));
+ final byte[] DataAfterCtrData = Hex.decode(vectors.getProperty("DataAfterCtrData"));
+ final KDFCounterParameters params = new KDFCounterParameters(ki, DataBeforeCtrData, DataAfterCtrData, r);
+ gen.init(params);
+ }
+ else
+ {
+ return; // Unknown CTRLOCATION
+ }
+
+
+ final byte[] koGenerated = new byte[l / 8];
+ gen.generateBytes(koGenerated, 0, koGenerated.length);
+
+ final byte[] koVectors = Hex.decode(vectors.getProperty("KO"));
+
+ compareKO(name, config, count, koGenerated, koVectors);
+ }
+
+ private static void compareKO(
+ String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!Arrays.areEqual(calculatedOKM, testOKM))
+ {
+ throw new TestFailedException(new SimpleTestResult(
+ false, name + " using " + config + " test " + test + " failed"));
+
+ }
+ }
+
+ public void receiveCommentLine(String commentLine)
+ {
+ // out.println("# " + commentLine);
+ }
+
+ public void receiveStart(String name)
+ {
+ // do nothing
+ }
+
+ public void receiveEnd()
+ {
+ out.println(" *** *** *** ");
+ }
+
+ public void setup()
+ {
+ try
+ {
+ out = new PrintWriter(new FileWriter("KDFCTR.gen"));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void tearDown()
+ {
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java
new file mode 100644
index 00000000..5b3df023
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java
@@ -0,0 +1,107 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+import java.util.regex.Matcher;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.KDFDoublePipelineIterationBytesGenerator;
+import org.bouncycastle.crypto.params.KDFDoublePipelineIterationParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.TestFailedException;
+
+public final class KDFDoublePipelineCounterTests
+ implements CAVPListener
+{
+ private PrintWriter out;
+
+ public void receiveCAVPVectors(String name, Properties config,
+ Properties vectors)
+ {
+ // out.println(" === " + name + " === ");
+ // out.println(" --- config --- ");
+ // out.println(config);
+ // out.println(" --- vectors --- ");
+ // out.println(vectors);
+
+ // always skip AFTER_FIXED
+ if (!config.getProperty("CTRLOCATION").matches("AFTER_ITER"))
+ {
+ return;
+ }
+
+ // create Mac based PRF from PRF property, create the KDF
+ final Mac prf = CAVPReader.createPRF(config);
+ final KDFDoublePipelineIterationBytesGenerator gen = new KDFDoublePipelineIterationBytesGenerator(prf);
+
+
+ Matcher matcherForR = CAVPReader.PATTERN_FOR_R.matcher(config.getProperty("RLEN"));
+ if (!matcherForR.matches())
+ {
+ throw new IllegalStateException("RLEN value should always match");
+ }
+ final int r = Integer.parseInt(matcherForR.group(1));
+
+ final int count = Integer.parseInt(vectors.getProperty("COUNT"));
+ final int l = Integer.parseInt(vectors.getProperty("L"));
+ final byte[] ki = Hex.decode(vectors.getProperty("KI"));
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFDoublePipelineIterationParameters params = KDFDoublePipelineIterationParameters.createWithCounter(ki, fixedInputData, r);
+ gen.init(params);
+
+ final byte[] koGenerated = new byte[l / 8];
+ gen.generateBytes(koGenerated, 0, koGenerated.length);
+
+ final byte[] koVectors = Hex.decode(vectors.getProperty("KO"));
+
+ compareKO(name, config, count, koGenerated, koVectors);
+ }
+
+ private static void compareKO(
+ String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!Arrays.areEqual(calculatedOKM, testOKM))
+ {
+ throw new TestFailedException(new SimpleTestResult(
+ false, name + " using " + config + " test " + test + " failed"));
+
+ }
+ }
+
+ public void receiveCommentLine(String commentLine)
+ {
+ // out.println("# " + commentLine);
+ }
+
+ public void receiveStart(String name)
+ {
+ // do nothing
+ }
+
+ public void receiveEnd()
+ {
+ out.println(" *** *** *** ");
+ }
+
+ public void setup()
+ {
+ try
+ {
+ out = new PrintWriter(new FileWriter("KDFDblPipelineCounter.gen"));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void tearDown()
+ {
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java
new file mode 100644
index 00000000..3923f9a1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java
@@ -0,0 +1,88 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.KDFDoublePipelineIterationBytesGenerator;
+import org.bouncycastle.crypto.params.KDFDoublePipelineIterationParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.TestFailedException;
+
+public final class KDFDoublePipelineIterationNoCounterTests
+ implements CAVPListener
+{
+ private PrintWriter out;
+
+ public void receiveCAVPVectors(String name, Properties config,
+ Properties vectors)
+ {
+
+
+ // create Mac based PRF from PRF property, create the KDF
+ final Mac prf = CAVPReader.createPRF(config);
+ final KDFDoublePipelineIterationBytesGenerator gen = new KDFDoublePipelineIterationBytesGenerator(prf);
+
+ final int count = Integer.parseInt(vectors.getProperty("COUNT"));
+ final int l = Integer.parseInt(vectors.getProperty("L"));
+ final byte[] ki = Hex.decode(vectors.getProperty("KI"));
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFDoublePipelineIterationParameters params = KDFDoublePipelineIterationParameters.createWithoutCounter(ki, fixedInputData);
+ gen.init(params);
+
+ final byte[] koGenerated = new byte[l / 8];
+ gen.generateBytes(koGenerated, 0, koGenerated.length);
+
+ final byte[] koVectors = Hex.decode(vectors.getProperty("KO"));
+
+ compareKO(name, config, count, koGenerated, koVectors);
+ }
+
+ private static void compareKO(
+ String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!Arrays.areEqual(calculatedOKM, testOKM))
+ {
+ throw new TestFailedException(new SimpleTestResult(
+ false, name + " using " + config + " test " + test + " failed"));
+
+ }
+ }
+
+ public void receiveCommentLine(String commentLine)
+ {
+ // out.println("# " + commentLine);
+ }
+
+ public void receiveStart(String name)
+ {
+ // do nothing
+ }
+
+ public void receiveEnd()
+ {
+ out.println(" *** *** *** ");
+ }
+
+ public void setup()
+ {
+ try
+ {
+ out = new PrintWriter(new FileWriter("KDFDblPipelineNoCounter.gen"));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void tearDown()
+ {
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackCounterTests.java
new file mode 100644
index 00000000..6f8a0fde
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackCounterTests.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+import java.util.regex.Matcher;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.KDFFeedbackBytesGenerator;
+import org.bouncycastle.crypto.params.KDFFeedbackParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.TestFailedException;
+
+public final class KDFFeedbackCounterTests
+ implements CAVPListener
+{
+ private PrintWriter out;
+
+ public void receiveCAVPVectors(String name, Properties config,
+ Properties vectors)
+ {
+ // out.println(" === " + name + " === ");
+ // out.println(" --- config --- ");
+ // out.println(config);
+ // out.println(" --- vectors --- ");
+ // out.println(vectors);
+
+ // always skip AFTER_FIXED
+ if (!config.getProperty("CTRLOCATION").matches("AFTER_ITER"))
+ {
+ return;
+ }
+
+ // create Mac based PRF from PRF property, create the KDF
+ final Mac prf = CAVPReader.createPRF(config);
+ final KDFFeedbackBytesGenerator gen = new KDFFeedbackBytesGenerator(prf);
+
+
+ Matcher matcherForR = CAVPReader.PATTERN_FOR_R.matcher(config.getProperty("RLEN"));
+ if (!matcherForR.matches())
+ {
+ throw new IllegalStateException("RLEN value should always match");
+ }
+ final int r = Integer.parseInt(matcherForR.group(1));
+
+ final int count = Integer.parseInt(vectors.getProperty("COUNT"));
+ final int l = Integer.parseInt(vectors.getProperty("L"));
+ final byte[] ki = Hex.decode(vectors.getProperty("KI"));
+ final byte[] iv = Hex.decode(vectors.getProperty("IV"));
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFFeedbackParameters params = KDFFeedbackParameters.createWithCounter(ki, iv, fixedInputData, r);
+ gen.init(params);
+
+ final byte[] koGenerated = new byte[l / 8];
+ gen.generateBytes(koGenerated, 0, koGenerated.length);
+
+ final byte[] koVectors = Hex.decode(vectors.getProperty("KO"));
+
+ compareKO(name, config, count, koGenerated, koVectors);
+ }
+
+ private static void compareKO(
+ String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!Arrays.areEqual(calculatedOKM, testOKM))
+ {
+ throw new TestFailedException(new SimpleTestResult(
+ false, name + " using " + config + " test " + test + " failed"));
+
+ }
+ }
+
+ public void receiveCommentLine(String commentLine)
+ {
+ // out.println("# " + commentLine);
+ }
+
+ public void receiveStart(String name)
+ {
+ // do nothing
+ }
+
+ public void receiveEnd()
+ {
+ out.println(" *** *** *** ");
+ }
+
+ public void setup()
+ {
+ try
+ {
+ out = new PrintWriter(new FileWriter("KDFFeedbackCounter.gen"));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void tearDown()
+ {
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java
new file mode 100644
index 00000000..cd7d8b80
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java
@@ -0,0 +1,89 @@
+package org.bouncycastle.crypto.test.cavp;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.KDFFeedbackBytesGenerator;
+import org.bouncycastle.crypto.params.KDFFeedbackParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.TestFailedException;
+
+public final class KDFFeedbackNoCounterTests
+ implements CAVPListener
+{
+ private PrintWriter out;
+
+ public void receiveCAVPVectors(String name, Properties config,
+ Properties vectors)
+ {
+
+
+ // create Mac based PRF from PRF property, create the KDF
+ final Mac prf = CAVPReader.createPRF(config);
+ final KDFFeedbackBytesGenerator gen = new KDFFeedbackBytesGenerator(prf);
+
+ final int count = Integer.parseInt(vectors.getProperty("COUNT"));
+ final int l = Integer.parseInt(vectors.getProperty("L"));
+ final byte[] ki = Hex.decode(vectors.getProperty("KI"));
+ final byte[] iv = Hex.decode(vectors.getProperty("IV"));
+ final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData"));
+ final KDFFeedbackParameters params = KDFFeedbackParameters.createWithoutCounter(ki, iv, fixedInputData);
+ gen.init(params);
+
+ final byte[] koGenerated = new byte[l / 8];
+ gen.generateBytes(koGenerated, 0, koGenerated.length);
+
+ final byte[] koVectors = Hex.decode(vectors.getProperty("KO"));
+
+ compareKO(name, config, count, koGenerated, koVectors);
+ }
+
+ private static void compareKO(
+ String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!Arrays.areEqual(calculatedOKM, testOKM))
+ {
+ throw new TestFailedException(new SimpleTestResult(
+ false, name + " using " + config + " test " + test + " failed"));
+
+ }
+ }
+
+ public void receiveCommentLine(String commentLine)
+ {
+// out.println("# " + commentLine);
+ }
+
+ public void receiveStart(String name)
+ {
+ // do nothing
+ }
+
+ public void receiveEnd()
+ {
+ out.println(" *** *** *** ");
+ }
+
+ public void setup()
+ {
+ try
+ {
+ out = new PrintWriter(new FileWriter("KDFFeedbackNoCounter.gen"));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void tearDown()
+ {
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueInputStream.java
new file mode 100644
index 00000000..34bb035a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueInputStream.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.InputStream;
+
+public class ByteQueueInputStream
+ extends InputStream
+{
+ private ByteQueue buffer;
+
+ public ByteQueueInputStream()
+ {
+ buffer = new ByteQueue();
+ }
+
+ public void addBytes(byte[] bytes)
+ {
+ buffer.addData(bytes, 0, bytes.length);
+ }
+
+ public int peek(byte[] buf)
+ {
+ int bytesToRead = Math.min(buffer.available(), buf.length);
+ buffer.read(buf, 0, bytesToRead, 0);
+ return bytesToRead;
+ }
+
+ public int read()
+ {
+ if (buffer.available() == 0)
+ {
+ return -1;
+ }
+ return buffer.removeData(1, 0)[0] & 0xFF;
+ }
+
+ public int read(byte[] b)
+ {
+ return read(b, 0, b.length);
+ }
+
+ public int read(byte[] b, int off, int len)
+ {
+ int bytesToRead = Math.min(buffer.available(), len);
+ buffer.removeData(b, off, bytesToRead, 0);
+ return bytesToRead;
+ }
+
+ public long skip(long n)
+ {
+ int bytesToRemove = Math.min((int)n, buffer.available());
+ buffer.removeData(bytesToRemove);
+ return bytesToRemove;
+ }
+
+ public int available()
+ {
+ return buffer.available();
+ }
+
+ public void close()
+ {
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java
new file mode 100644
index 00000000..aa3b36a4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.crypto.tls.ByteQueue;
+
+public class ByteQueueOutputStream
+ extends OutputStream
+{
+ private ByteQueue buffer;
+
+ public ByteQueueOutputStream()
+ {
+ buffer = new ByteQueue();
+ }
+
+ public ByteQueue getBuffer()
+ {
+ return buffer;
+ }
+
+ public void write(int b) throws IOException
+ {
+ buffer.addData(new byte[]{ (byte)b }, 0, 1);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ buffer.addData(b, off, len);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/ByteQueueInputStreamTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/ByteQueueInputStreamTest.java
new file mode 100644
index 00000000..de4d5598
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/ByteQueueInputStreamTest.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.crypto.tls.test;
+
+import org.bouncycastle.crypto.tls.ByteQueueInputStream;
+import org.bouncycastle.util.Arrays;
+
+import junit.framework.TestCase;
+
+public class ByteQueueInputStreamTest
+ extends TestCase
+{
+ public void testAvailable()
+ {
+ ByteQueueInputStream in = new ByteQueueInputStream();
+
+ // buffer is empty
+ assertEquals(0, in.available());
+
+ // after adding once
+ in.addBytes(new byte[10]);
+ assertEquals(10, in.available());
+
+ // after adding more than once
+ in.addBytes(new byte[5]);
+ assertEquals(15, in.available());
+
+ // after reading a single byte
+ in.read();
+ assertEquals(14, in.available());
+
+ // after reading into a byte array
+ in.read(new byte[4]);
+ assertEquals(10, in.available());
+
+ in.close();// so Eclipse doesn't whine about a resource leak
+ }
+
+ public void testSkip()
+ {
+ ByteQueueInputStream in = new ByteQueueInputStream();
+
+ // skip when buffer is empty
+ assertEquals(0, in.skip(10));
+
+ // skip equal to available
+ in.addBytes(new byte[2]);
+ assertEquals(2, in.skip(2));
+ assertEquals(0, in.available());
+
+ // skip less than available
+ in.addBytes(new byte[10]);
+ assertEquals(5, in.skip(5));
+ assertEquals(5, in.available());
+
+ // skip more than available
+ assertEquals(5, in.skip(20));
+ assertEquals(0, in.available());
+
+ in.close();// so Eclipse doesn't whine about a resource leak
+ }
+
+ public void testRead()
+ {
+ ByteQueueInputStream in = new ByteQueueInputStream();
+ in.addBytes(new byte[]{ 0x01, 0x02 });
+ in.addBytes(new byte[]{ 0x03 });
+
+ assertEquals(0x01, in.read());
+ assertEquals(0x02, in.read());
+ assertEquals(0x03, in.read());
+ assertEquals(-1, in.read());
+
+ in.close();// so Eclipse doesn't whine about a resource leak
+ }
+
+ public void testReadArray()
+ {
+ ByteQueueInputStream in = new ByteQueueInputStream();
+ in.addBytes(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 });
+
+ byte[] buffer = new byte[5];
+
+ // read less than available into specified position
+ assertEquals(1, in.read(buffer, 2, 1));
+ assertArrayEquals(new byte[]{ 0x00, 0x00, 0x01, 0x00, 0x00 }, buffer);
+
+ // read equal to available
+ assertEquals(5, in.read(buffer));
+ assertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer);
+
+ // read more than available
+ in.addBytes(new byte[]{ 0x01, 0x02, 0x03 });
+ assertEquals(3, in.read(buffer));
+ assertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x05, 0x06 }, buffer);
+
+ in.close();// so Eclipse doesn't whine about a resource leak
+ }
+
+ public void testPeek()
+ {
+ ByteQueueInputStream in = new ByteQueueInputStream();
+
+ byte[] buffer = new byte[5];
+
+ // peek more than available
+ assertEquals(0, in.peek(buffer));
+ assertArrayEquals(new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x00 }, buffer);
+
+ // peek less than available
+ in.addBytes(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 });
+ assertEquals(5, in.peek(buffer));
+ assertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05 }, buffer);
+ assertEquals(6, in.available());
+
+ // peek equal to available
+ in.read();
+ assertEquals(5, in.peek(buffer));
+ assertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer);
+ assertEquals(5, in.available());
+
+ in.close();// so Eclipse doesn't whine about a resource leak
+ }
+
+ private static void assertArrayEquals(byte[] a, byte[] b)
+ {
+ assertTrue(Arrays.areEqual(a, b));
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestClientProtocol.java
new file mode 100644
index 00000000..84109cdb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestClientProtocol.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.DTLSClientProtocol;
+import org.bouncycastle.crypto.tls.DigitallySigned;
+
+class DTLSTestClientProtocol extends DTLSClientProtocol
+{
+ protected final TlsTestConfig config;
+
+ public DTLSTestClientProtocol(SecureRandom secureRandom, TlsTestConfig config)
+ {
+ super(secureRandom);
+
+ this.config = config;
+ }
+
+ protected byte[] generateCertificateVerify(ClientHandshakeState state, DigitallySigned certificateVerify)
+ throws IOException
+ {
+ if (certificateVerify.getAlgorithm() != null && config.clientAuthSigAlgClaimed != null)
+ {
+ certificateVerify = new DigitallySigned(config.clientAuthSigAlgClaimed, certificateVerify.getSignature());
+ }
+
+ return super.generateCertificateVerify(state, certificateVerify);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestServerProtocol.java
new file mode 100644
index 00000000..9244eeb8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestServerProtocol.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.DTLSServerProtocol;
+
+class DTLSTestServerProtocol extends DTLSServerProtocol
+{
+ protected final TlsTestConfig config;
+
+ public DTLSTestServerProtocol(SecureRandom secureRandom, TlsTestConfig config)
+ {
+ super(secureRandom);
+
+ this.config = config;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolNonBlockingTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolNonBlockingTest.java
new file mode 100644
index 00000000..564bb742
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolNonBlockingTest.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+import org.bouncycastle.crypto.tls.TlsProtocol;
+import org.bouncycastle.crypto.tls.TlsServerProtocol;
+import org.bouncycastle.util.Arrays;
+
+import junit.framework.TestCase;
+
+public class TlsProtocolNonBlockingTest
+ extends TestCase
+{
+ public void testClientServerFragmented() throws IOException
+ {
+ // tests if it's really non-blocking when partial records arrive
+ testClientServer(true);
+ }
+
+ public void testClientServerNonFragmented() throws IOException
+ {
+ testClientServer(false);
+ }
+
+ private static void testClientServer(boolean fragment) throws IOException
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(secureRandom);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(secureRandom);
+
+ clientProtocol.connect(new MockTlsClient(null));
+ serverProtocol.accept(new MockTlsServer());
+
+ // pump handshake
+ boolean hadDataFromServer = true;
+ boolean hadDataFromClient = true;
+ while (hadDataFromServer || hadDataFromClient)
+ {
+ hadDataFromServer = pumpData(serverProtocol, clientProtocol, fragment);
+ hadDataFromClient = pumpData(clientProtocol, serverProtocol, fragment);
+ }
+
+ // send data in both directions
+ byte[] data = new byte[1024];
+ secureRandom.nextBytes(data);
+ writeAndRead(clientProtocol, serverProtocol, data, fragment);
+ writeAndRead(serverProtocol, clientProtocol, data, fragment);
+
+ // close the connection
+ clientProtocol.close();
+ pumpData(clientProtocol, serverProtocol, fragment);
+ checkClosed(serverProtocol);
+ checkClosed(clientProtocol);
+ }
+
+ private static void writeAndRead(TlsProtocol writer, TlsProtocol reader, byte[] data, boolean fragment)
+ throws IOException
+ {
+ int dataSize = data.length;
+ writer.offerOutput(data, 0, dataSize);
+ pumpData(writer, reader, fragment);
+
+ assertEquals(dataSize, reader.getAvailableInputBytes());
+ byte[] readData = new byte[dataSize];
+ reader.readInput(readData, 0, dataSize);
+ assertArrayEquals(data, readData);
+ }
+
+ private static boolean pumpData(TlsProtocol from, TlsProtocol to, boolean fragment) throws IOException
+ {
+ int byteCount = from.getAvailableOutputBytes();
+ if (byteCount == 0)
+ {
+ return false;
+ }
+
+ if (fragment)
+ {
+ while (from.getAvailableOutputBytes() > 0)
+ {
+ byte[] buffer = new byte[1];
+ from.readOutput(buffer, 0, 1);
+ to.offerInput(buffer);
+ }
+ }
+ else
+ {
+ byte[] buffer = new byte[byteCount];
+ from.readOutput(buffer, 0, buffer.length);
+ to.offerInput(buffer);
+ }
+
+ return true;
+ }
+
+ private static void checkClosed(TlsProtocol protocol)
+ {
+ assertTrue(protocol.isClosed());
+
+ try
+ {
+ protocol.offerInput(new byte[10]);
+ fail("Input was accepted after close");
+ }
+ catch (IOException e)
+ {
+ }
+
+ try
+ {
+ protocol.offerOutput(new byte[10], 0, 10);
+ fail("Output was accepted after close");
+ }
+ catch (IOException e)
+ {
+ }
+ }
+
+ private static void assertArrayEquals(byte[] a, byte[] b)
+ {
+ assertTrue(Arrays.areEqual(a, b));
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientProtocol.java
new file mode 100644
index 00000000..2f6f751e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientProtocol.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.DigitallySigned;
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+
+class TlsTestClientProtocol extends TlsClientProtocol
+{
+ protected final TlsTestConfig config;
+
+ public TlsTestClientProtocol(InputStream input, OutputStream output, SecureRandom secureRandom, TlsTestConfig config)
+ {
+ super(input, output, secureRandom);
+
+ this.config = config;
+ }
+
+ protected void sendCertificateVerifyMessage(DigitallySigned certificateVerify) throws IOException
+ {
+ if (certificateVerify.getAlgorithm() != null && config.clientAuthSigAlgClaimed != null)
+ {
+ certificateVerify = new DigitallySigned(config.clientAuthSigAlgClaimed, certificateVerify.getSignature());
+ }
+
+ super.sendCertificateVerifyMessage(certificateVerify);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerProtocol.java
new file mode 100644
index 00000000..0bfc985b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerProtocol.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.TlsServerProtocol;
+
+class TlsTestServerProtocol extends TlsServerProtocol
+{
+ protected final TlsTestConfig config;
+
+ public TlsTestServerProtocol(InputStream input, OutputStream output, SecureRandom secureRandom, TlsTestConfig config)
+ {
+ super(input, output, secureRandom);
+
+ this.config = config;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/DERMacData.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/DERMacData.java
new file mode 100644
index 00000000..d895a1fd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/DERMacData.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.crypto.util;
+
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Builder and holder class for preparing SP 800-56A compliant MacData. Elements in the data are encoded
+ * as DER objects with empty octet strings used to represent nulls in compulsory fields.
+ */
+public final class DERMacData
+{
+ public enum Type
+ {
+ UNILATERALU("KC_1_U"),
+ UNILATERALV("KC_1_V"),
+ BILATERALU("KC_2_U"),
+ BILATERALV("KC_2_V");
+
+ private final String enc;
+
+ Type(String enc)
+ {
+ this.enc = enc;
+ }
+
+ public byte[] getHeader()
+ {
+ return Strings.toByteArray(enc);
+ }
+ }
+
+ /**
+ * Builder to create OtherInfo
+ */
+ public static final class Builder
+ {
+ private final Type type;
+
+ private ASN1OctetString idU;
+ private ASN1OctetString idV;
+ private ASN1OctetString ephemDataU;
+ private ASN1OctetString ephemDataV;
+ private byte[] text;
+
+ /**
+ * Create a basic builder with just the compulsory fields.
+ *
+ * @param type the MAC header
+ * @param idU sender party ID.
+ * @param idV receiver party ID.
+ * @param ephemDataU ephemeral data from sender.
+ * @param ephemDataV ephemeral data from receiver.
+ */
+ public Builder(Type type, byte[] idU, byte[] idV, byte[] ephemDataU, byte[] ephemDataV)
+ {
+ this.type = type;
+ this.idU = DerUtil.getOctetString(idU);
+ this.idV = DerUtil.getOctetString(idV);
+ this.ephemDataU = DerUtil.getOctetString(ephemDataU);
+ this.ephemDataV = DerUtil.getOctetString(ephemDataV);
+ }
+
+ /**
+ * Add optional text.
+ *
+ * @param text optional agreed text to add to the MAC.
+ * @return the current builder instance.
+ */
+ public Builder withText(byte[] text)
+ {
+ this.text = DerUtil.toByteArray(new DERTaggedObject(false, 0, DerUtil.getOctetString(text)));
+
+ return this;
+ }
+
+ public DERMacData build()
+ {
+ switch (type)
+ {
+ case UNILATERALU:
+ case BILATERALU:
+ return new DERMacData(concatenate(type.getHeader(),
+ DerUtil.toByteArray(idU), DerUtil.toByteArray(idV),
+ DerUtil.toByteArray(ephemDataU), DerUtil.toByteArray(ephemDataV), text));
+ case UNILATERALV:
+ case BILATERALV:
+ return new DERMacData(concatenate(type.getHeader(),
+ DerUtil.toByteArray(idV), DerUtil.toByteArray(idU),
+ DerUtil.toByteArray(ephemDataV), DerUtil.toByteArray(ephemDataU), text));
+ }
+
+ throw new IllegalStateException("Unknown type encountered in build"); // should never happen
+ }
+
+ private byte[] concatenate(byte[] header, byte[] id1, byte[] id2, byte[] ed1, byte[] ed2, byte[] text)
+ {
+ return Arrays.concatenate(Arrays.concatenate(header, id1, id2), Arrays.concatenate(ed1, ed2, text));
+ }
+ }
+
+ private final byte[] macData;
+
+ private DERMacData(byte[] macData)
+ {
+ this.macData = macData;
+ }
+
+ public byte[] getMacData()
+ {
+ return Arrays.clone(macData);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/DEROtherInfo.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/DEROtherInfo.java
new file mode 100644
index 00000000..4e821f7e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/DEROtherInfo.java
@@ -0,0 +1,109 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * Builder and holder class for preparing SP 800-56A compliant OtherInfo. The data is ultimately encoded as a DER SEQUENCE.
+ * Empty octet strings are used to represent nulls in compulsory fields.
+ */
+public class DEROtherInfo
+{
+ /**
+ * Builder to create OtherInfo
+ */
+ public static final class Builder
+ {
+ private final AlgorithmIdentifier algorithmID;
+ private final ASN1OctetString partyUVInfo;
+ private final ASN1OctetString partyVInfo;
+
+ private ASN1TaggedObject suppPubInfo;
+ private ASN1TaggedObject suppPrivInfo;
+
+ /**
+ * Create a basic builder with just the compulsory fields.
+ *
+ * @param algorithmID the algorithm associated with this invocation of the KDF.
+ * @param partyUInfo sender party info.
+ * @param partyVInfo receiver party info.
+ */
+ public Builder(AlgorithmIdentifier algorithmID, byte[] partyUInfo, byte[] partyVInfo)
+ {
+ this.algorithmID = algorithmID;
+ this.partyUVInfo = DerUtil.getOctetString(partyUInfo);
+ this.partyVInfo = DerUtil.getOctetString(partyVInfo);
+ }
+
+ /**
+ * Add optional supplementary public info (DER tagged, implicit, 0).
+ *
+ * @param suppPubInfo supplementary public info.
+ * @return the current builder instance.
+ */
+ public Builder withSuppPubInfo(byte[] suppPubInfo)
+ {
+ this.suppPubInfo = new DERTaggedObject(false, 0, DerUtil.getOctetString(suppPubInfo));
+
+ return this;
+ }
+
+ /**
+ * Add optional supplementary private info (DER tagged, implicit, 1).
+ *
+ * @param suppPrivInfo supplementary private info.
+ * @return the current builder instance.
+ */
+ public Builder withSuppPrivInfo(byte[] suppPrivInfo)
+ {
+ this.suppPrivInfo = new DERTaggedObject(false, 1, DerUtil.getOctetString(suppPrivInfo));
+
+ return this;
+ }
+
+ /**
+ * Build the KTSOtherInfo.
+ *
+ * @return an KTSOtherInfo containing the data.
+ */
+ public DEROtherInfo build()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(algorithmID);
+ v.add(partyUVInfo);
+ v.add(partyVInfo);
+
+ if (suppPubInfo != null)
+ {
+ v.add(suppPubInfo);
+ }
+
+ if (suppPrivInfo != null)
+ {
+ v.add(suppPrivInfo);
+ }
+
+ return new DEROtherInfo(new DERSequence(v));
+ }
+ }
+
+ private final DERSequence sequence;
+
+ private DEROtherInfo(DERSequence sequence)
+ {
+ this.sequence = sequence;
+ }
+
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return sequence.getEncoded();
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/DerUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/DerUtil.java
new file mode 100644
index 00000000..324c5ae4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/DerUtil.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.util.Arrays;
+
+class DerUtil
+{
+ static ASN1OctetString getOctetString(byte[] data)
+ {
+ if (data == null)
+ {
+ return new DEROctetString(new byte[0]);
+ }
+
+ return new DEROctetString(Arrays.clone(data));
+ }
+
+ static byte[] toByteArray(ASN1Primitive primitive)
+ {
+ try
+ {
+ return primitive.getEncoded();
+ }
+ catch (final IOException e)
+ {
+ throw new IllegalStateException("Cannot get encoding: " + e.getMessage())
+ {
+ public Throwable getCause()
+ {
+ return e;
+ }
+ };
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1Key.java b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1Key.java
new file mode 100644
index 00000000..7f7581c0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1Key.java
@@ -0,0 +1,67 @@
+package org.bouncycastle.jcajce;
+
+import org.bouncycastle.crypto.CharToByteConverter;
+
+/**
+ * A password based key for use with PBKDF1 as defined in PKCS#5.
+ */
+public class PBKDF1Key
+ implements PBKDFKey
+{
+ private final char[] password;
+ private final CharToByteConverter converter;
+
+ /**
+ * Basic constructor for a password based key with generation parameters for PBKDF1.
+ *
+ * @param password password to use.
+ * @param converter the converter to use to turn the char array into octets.
+ */
+ public PBKDF1Key(char[] password, CharToByteConverter converter)
+ {
+ this.password = new char[password.length];
+ this.converter = converter;
+
+ System.arraycopy(password, 0, this.password, 0, password.length);
+ }
+
+ /**
+ * Return a reference to the char[] array holding the password.
+ *
+ * @return a reference to the password array.
+ */
+ public char[] getPassword()
+ {
+ return password;
+ }
+
+ /**
+ * Return the password based key derivation function this key is for,
+ *
+ * @return the string "PBKDF1"
+ */
+ public String getAlgorithm()
+ {
+ return "PBKDF1";
+ }
+
+ /**
+ * Return the format encoding.
+ *
+ * @return the type name representing a char[] to byte[] conversion.
+ */
+ public String getFormat()
+ {
+ return converter.getType();
+ }
+
+ /**
+ * Return the password converted to bytes.
+ *
+ * @return the password converted to a byte array.
+ */
+ public byte[] getEncoded()
+ {
+ return converter.convert(password);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1KeyWithParameters.java b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1KeyWithParameters.java
new file mode 100644
index 00000000..d0899c1d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1KeyWithParameters.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.jcajce;
+
+import javax.crypto.interfaces.PBEKey;
+
+import org.bouncycastle.crypto.CharToByteConverter;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A password based key for use with PBKDF1 as defined in PKCS#5 with full PBE parameters.
+ */
+public class PBKDF1KeyWithParameters
+ extends PBKDF1Key
+ implements PBEKey
+{
+ private final byte[] salt;
+ private final int iterationCount;
+
+ /**
+ * Basic constructor for a password based key with generation parameters for PBKDF1.
+ *
+ * @param password password to use.
+ * @param converter the converter to use to turn the char array into octets.
+ * @param salt salt for generation algorithm
+ * @param iterationCount iteration count for generation algorithm.
+ */
+ public PBKDF1KeyWithParameters(char[] password, CharToByteConverter converter, byte[] salt, int iterationCount)
+ {
+ super(password, converter);
+
+ this.salt = Arrays.clone(salt);
+ this.iterationCount = iterationCount;
+ }
+
+ /**
+ * Return the salt to use in the key derivation function.
+ *
+ * @return the salt to use in the KDF.
+ */
+ public byte[] getSalt()
+ {
+ return salt;
+ }
+
+ /**
+ * Return the iteration count to use in the key derivation function.
+ *
+ * @return the iteration count to use in the KDF.
+ */
+ public int getIterationCount()
+ {
+ return iterationCount;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2Key.java b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2Key.java
new file mode 100644
index 00000000..d9dc6e75
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2Key.java
@@ -0,0 +1,65 @@
+package org.bouncycastle.jcajce;
+
+import org.bouncycastle.crypto.CharToByteConverter;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A password based key for use with PBKDF2 as defined in PKCS#5.
+ */
+public class PBKDF2Key
+ implements PBKDFKey
+{
+ private final char[] password;
+ private final CharToByteConverter converter;
+
+ /**
+ * Basic constructor for a password based key using PBKDF - secret key generation parameters will be passed separately..
+ *
+ * @param password password to use.
+ */
+ public PBKDF2Key(char[] password, CharToByteConverter converter)
+ {
+ this.password = Arrays.clone(password);
+ this.converter = converter;
+ }
+
+ /**
+ * Return a reference to the char[] array holding the password.
+ *
+ * @return a reference to the password array.
+ */
+ public char[] getPassword()
+ {
+ return password;
+ }
+
+ /**
+ * Return the password based key derivation function this key is for,
+ *
+ * @return the string "PBKDF2"
+ */
+ public String getAlgorithm()
+ {
+ return "PBKDF2";
+ }
+
+ /**
+ * Return the format encoding.
+ *
+ * @return the type name representing a char[] to byte[] conversion.
+ */
+ public String getFormat()
+ {
+ return converter.getType();
+ }
+
+ /**
+ * Return the password converted to bytes.
+ *
+ * @return the password converted to a byte array.
+ */
+ public byte[] getEncoded()
+ {
+ return converter.convert(password);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2KeyWithParameters.java b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2KeyWithParameters.java
new file mode 100644
index 00000000..b356cfbd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2KeyWithParameters.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.jcajce;
+
+import javax.crypto.interfaces.PBEKey;
+
+import org.bouncycastle.crypto.CharToByteConverter;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A password based key for use with PBKDF2 as defined in PKCS#5 with full PBE parameters.
+ */
+public class PBKDF2KeyWithParameters
+ extends PBKDF2Key
+ implements PBEKey
+{
+ private final byte[] salt;
+ private final int iterationCount;
+
+ /**
+ * Basic constructor for a password based key with generation parameters using FIPS PBKDF.
+ *
+ * @param password password to use.
+ * @param converter converter to use for transforming characters into bytes.
+ * @param salt salt for generation algorithm
+ * @param iterationCount iteration count for generation algorithm.
+ */
+ public PBKDF2KeyWithParameters(char[] password, CharToByteConverter converter, byte[] salt, int iterationCount)
+ {
+ super(password, converter);
+
+ this.salt = Arrays.clone(salt);
+ this.iterationCount = iterationCount;
+ }
+
+ /**
+ * Return the salt to use in the key derivation function.
+ *
+ * @return the salt to use in the KDF.
+ */
+ public byte[] getSalt()
+ {
+ return salt;
+ }
+
+ /**
+ * Return the iteration count to use in the key derivation function.
+ *
+ * @return the iteration count to use in the KDF.
+ */
+ public int getIterationCount()
+ {
+ return iterationCount;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDFKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDFKey.java
new file mode 100644
index 00000000..ae3d2ebd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDFKey.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.jcajce;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Base interface for keys associated with various password based key derivation functions (PBKDF).
+ */
+public interface PBKDFKey
+ extends SecretKey
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
new file mode 100644
index 00000000..6af71e80
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
@@ -0,0 +1,167 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ec;
+
+import java.io.IOException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X962Parameters;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.math.ec.ECCurve;
+
+public class AlgorithmParametersSpi
+ extends java.security.AlgorithmParametersSpi
+{
+ private ECParameterSpec ecParameterSpec;
+ private String curveName;
+
+ protected boolean isASN1FormatString(String format)
+ {
+ return format == null || format.equals("ASN.1");
+ }
+
+ @Override
+ protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec)
+ throws InvalidParameterSpecException
+ {
+ if (algorithmParameterSpec instanceof ECGenParameterSpec)
+ {
+ ECGenParameterSpec ecGenParameterSpec = (ECGenParameterSpec)algorithmParameterSpec;
+ X9ECParameters params = ECUtils.getDomainParametersFromGenSpec(ecGenParameterSpec);
+
+ if (params == null)
+ {
+ throw new InvalidParameterSpecException("EC curve name not recognized: " + ecGenParameterSpec.getName());
+ }
+ curveName = ecGenParameterSpec.getName();
+ ecParameterSpec = EC5Util.convertToSpec(params);
+ }
+ else if (algorithmParameterSpec instanceof ECParameterSpec)
+ {
+ curveName = null;
+ ecParameterSpec = (ECParameterSpec)algorithmParameterSpec;
+ }
+ else
+ {
+ throw new InvalidParameterSpecException("AlgorithmParameterSpec class not recognized: " + algorithmParameterSpec.getClass().getName());
+ }
+ }
+
+ @Override
+ protected void engineInit(byte[] bytes)
+ throws IOException
+ {
+ engineInit(bytes, "ASN.1");
+ }
+
+ @Override
+ protected void engineInit(byte[] bytes, String format)
+ throws IOException
+ {
+ if (isASN1FormatString(format))
+ {
+ X962Parameters params = X962Parameters.getInstance(bytes);
+
+ ECCurve curve = EC5Util.getCurve(BouncyCastleProvider.CONFIGURATION, params);
+
+ if (params.isNamedCurve())
+ {
+ curveName = ECNamedCurveTable.getName(ASN1ObjectIdentifier.getInstance(params.getParameters()));
+ }
+
+ ecParameterSpec = EC5Util.convertToSpec(params, curve);
+ }
+ else
+ {
+ throw new IOException("Unknown encoded parameters format in AlgorithmParameters object: " + format);
+ }
+ }
+
+ @Override
+ protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> paramSpec)
+ throws InvalidParameterSpecException
+ {
+ if (ECParameterSpec.class.isAssignableFrom(paramSpec) || paramSpec == AlgorithmParameterSpec.class)
+ {
+ return (T)ecParameterSpec;
+ }
+ else if (ECGenParameterSpec.class.isAssignableFrom(paramSpec))
+ {
+ if (curveName != null)
+ {
+ ASN1ObjectIdentifier namedCurveOid = ECUtil.getNamedCurveOid(curveName);
+
+ if (namedCurveOid != null)
+ {
+ return (T)new ECGenParameterSpec(namedCurveOid.getId());
+ }
+ return (T)new ECGenParameterSpec(curveName);
+ }
+ else
+ {
+ ASN1ObjectIdentifier namedCurveOid = ECUtil.getNamedCurveOid(EC5Util.convertSpec(ecParameterSpec, false));
+
+ if (namedCurveOid != null)
+ {
+ return (T)new ECGenParameterSpec(namedCurveOid.getId());
+ }
+ }
+ }
+ throw new InvalidParameterSpecException("EC AlgorithmParameters cannot convert to " + paramSpec.getName());
+ }
+
+ @Override
+ protected byte[] engineGetEncoded()
+ throws IOException
+ {
+ return engineGetEncoded("ASN.1");
+ }
+
+ @Override
+ protected byte[] engineGetEncoded(String format)
+ throws IOException
+ {
+ if (isASN1FormatString(format))
+ {
+ X962Parameters params;
+
+ if (ecParameterSpec == null) // implicitly CA
+ {
+ params = new X962Parameters(DERNull.INSTANCE);
+ }
+ else if (curveName != null)
+ {
+ params = new X962Parameters(ECUtil.getNamedCurveOid(curveName));
+ }
+ else
+ {
+ org.bouncycastle.jce.spec.ECParameterSpec ecSpec = EC5Util.convertSpec(ecParameterSpec, false);
+ X9ECParameters ecP = new X9ECParameters(
+ ecSpec.getCurve(),
+ ecSpec.getG(),
+ ecSpec.getN(),
+ ecSpec.getH(),
+ ecSpec.getSeed());
+
+ params = new X962Parameters(ecP);
+ }
+
+ return params.getEncoded();
+ }
+
+ throw new IOException("Unknown parameters format in AlgorithmParameters object: " + format);
+ }
+
+ @Override
+ protected String engineToString()
+ {
+ return "EC AlgorithmParameters ";
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java
new file mode 100644
index 00000000..dc92e0e4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ec;
+
+import java.security.spec.ECGenParameterSpec;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+
+class ECUtils
+{
+ static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec)
+ {
+ return getDomainParametersFromName(genSpec.getName());
+ }
+
+ static X9ECParameters getDomainParametersFromName(String curveName)
+ {
+ X9ECParameters domainParameters;
+ try
+ {
+ if (curveName.charAt(0) >= '0' && curveName.charAt(0) <= '2')
+ {
+ ASN1ObjectIdentifier oidID = new ASN1ObjectIdentifier(curveName);
+ domainParameters = ECUtil.getNamedCurveByOid(oidID);
+ }
+ else
+ {
+ if (curveName.indexOf(' ') > 0)
+ {
+ curveName = curveName.substring(curveName.indexOf(' ') + 1);
+ domainParameters = ECUtil.getNamedCurveByName(curveName);
+ }
+ else
+ {
+ domainParameters = ECUtil.getNamedCurveByName(curveName);
+ }
+ }
+ }
+ catch (IllegalArgumentException ex)
+ {
+ domainParameters = ECUtil.getNamedCurveByName(curveName);
+ }
+ return domainParameters;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
new file mode 100644
index 00000000..8dc0d2dd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
@@ -0,0 +1,314 @@
+package org.bouncycastle.jcajce.provider.asymmetric.util;
+
+import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.gnu.GNUObjectIdentifiers;
+import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.agreement.kdf.DHKDFParameters;
+import org.bouncycastle.crypto.agreement.kdf.DHKEKGenerator;
+import org.bouncycastle.crypto.params.DESParameters;
+import org.bouncycastle.crypto.params.KDFParameters;
+import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.Strings;
+
+public abstract class BaseAgreementSpi
+ extends KeyAgreementSpi
+{
+ private static final Map<String, ASN1ObjectIdentifier> defaultOids = new HashMap<String, ASN1ObjectIdentifier>();
+ private static final Map<String, Integer> keySizes = new HashMap<String, Integer>();
+ private static final Map<String, String> nameTable = new HashMap<String, String>();
+
+ private static final Hashtable oids = new Hashtable();
+ private static final Hashtable des = new Hashtable();
+
+ static
+ {
+ Integer i64 = Integers.valueOf(64);
+ Integer i128 = Integers.valueOf(128);
+ Integer i192 = Integers.valueOf(192);
+ Integer i256 = Integers.valueOf(256);
+
+ keySizes.put("DES", i64);
+ keySizes.put("DESEDE", i192);
+ keySizes.put("BLOWFISH", i128);
+ keySizes.put("AES", i256);
+
+ keySizes.put(NISTObjectIdentifiers.id_aes128_ECB.getId(), i128);
+ keySizes.put(NISTObjectIdentifiers.id_aes192_ECB.getId(), i192);
+ keySizes.put(NISTObjectIdentifiers.id_aes256_ECB.getId(), i256);
+ keySizes.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), i128);
+ keySizes.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), i192);
+ keySizes.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), i256);
+ keySizes.put(NISTObjectIdentifiers.id_aes128_CFB.getId(), i128);
+ keySizes.put(NISTObjectIdentifiers.id_aes192_CFB.getId(), i192);
+ keySizes.put(NISTObjectIdentifiers.id_aes256_CFB.getId(), i256);
+ keySizes.put(NISTObjectIdentifiers.id_aes128_OFB.getId(), i128);
+ keySizes.put(NISTObjectIdentifiers.id_aes192_OFB.getId(), i192);
+ keySizes.put(NISTObjectIdentifiers.id_aes256_OFB.getId(), i256);
+ keySizes.put(NISTObjectIdentifiers.id_aes128_wrap.getId(), i128);
+ keySizes.put(NISTObjectIdentifiers.id_aes192_wrap.getId(), i192);
+ keySizes.put(NISTObjectIdentifiers.id_aes256_wrap.getId(), i256);
+ keySizes.put(NISTObjectIdentifiers.id_aes128_CCM.getId(), i128);
+ keySizes.put(NISTObjectIdentifiers.id_aes192_CCM.getId(), i192);
+ keySizes.put(NISTObjectIdentifiers.id_aes256_CCM.getId(), i256);
+ keySizes.put(NISTObjectIdentifiers.id_aes128_GCM.getId(), i128);
+ keySizes.put(NISTObjectIdentifiers.id_aes192_GCM.getId(), i192);
+ keySizes.put(NISTObjectIdentifiers.id_aes256_GCM.getId(), i256);
+ keySizes.put(NTTObjectIdentifiers.id_camellia128_wrap.getId(), i128);
+ keySizes.put(NTTObjectIdentifiers.id_camellia192_wrap.getId(), i192);
+ keySizes.put(NTTObjectIdentifiers.id_camellia256_wrap.getId(), i256);
+ keySizes.put(KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap.getId(), i128);
+
+ keySizes.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), i192);
+ keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), i192);
+ keySizes.put(OIWObjectIdentifiers.desCBC.getId(), i64);
+
+ keySizes.put(PKCSObjectIdentifiers.id_hmacWithSHA1.getId(), Integers.valueOf(160));
+ keySizes.put(PKCSObjectIdentifiers.id_hmacWithSHA256.getId(), i256);
+ keySizes.put(PKCSObjectIdentifiers.id_hmacWithSHA384.getId(), Integers.valueOf(384));
+ keySizes.put(PKCSObjectIdentifiers.id_hmacWithSHA512.getId(), Integers.valueOf(512));
+
+ defaultOids.put("DESEDE", PKCSObjectIdentifiers.des_EDE3_CBC);
+ defaultOids.put("AES", NISTObjectIdentifiers.id_aes256_CBC);
+ defaultOids.put("CAMELLIA", NTTObjectIdentifiers.id_camellia256_cbc);
+ defaultOids.put("SEED", KISAObjectIdentifiers.id_seedCBC);
+ defaultOids.put("DES", OIWObjectIdentifiers.desCBC);
+
+ nameTable.put(MiscObjectIdentifiers.cast5CBC.getId(), "CAST5");
+ nameTable.put(MiscObjectIdentifiers.as_sys_sec_alg_ideaCBC.getId(), "IDEA");
+ nameTable.put(MiscObjectIdentifiers.cryptlib_algorithm_blowfish_ECB.getId(), "Blowfish");
+ nameTable.put(MiscObjectIdentifiers.cryptlib_algorithm_blowfish_CBC.getId(), "Blowfish");
+ nameTable.put(MiscObjectIdentifiers.cryptlib_algorithm_blowfish_CFB.getId(), "Blowfish");
+ nameTable.put(MiscObjectIdentifiers.cryptlib_algorithm_blowfish_OFB.getId(), "Blowfish");
+ nameTable.put(OIWObjectIdentifiers.desECB.getId(), "DES");
+ nameTable.put(OIWObjectIdentifiers.desCBC.getId(), "DES");
+ nameTable.put(OIWObjectIdentifiers.desCFB.getId(), "DES");
+ nameTable.put(OIWObjectIdentifiers.desOFB.getId(), "DES");
+ nameTable.put(OIWObjectIdentifiers.desEDE.getId(), "DESede");
+ nameTable.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), "DESede");
+ nameTable.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), "DESede");
+ nameTable.put(PKCSObjectIdentifiers.id_alg_CMSRC2wrap.getId(), "RC2");
+ nameTable.put(PKCSObjectIdentifiers.id_hmacWithSHA1.getId(), "HmacSHA1");
+ nameTable.put(PKCSObjectIdentifiers.id_hmacWithSHA224.getId(), "HmacSHA224");
+ nameTable.put(PKCSObjectIdentifiers.id_hmacWithSHA256.getId(), "HmacSHA256");
+ nameTable.put(PKCSObjectIdentifiers.id_hmacWithSHA384.getId(), "HmacSHA384");
+ nameTable.put(PKCSObjectIdentifiers.id_hmacWithSHA512.getId(), "HmacSHA512");
+ nameTable.put(NTTObjectIdentifiers.id_camellia128_cbc.getId(), "Camellia");
+ nameTable.put(NTTObjectIdentifiers.id_camellia192_cbc.getId(), "Camellia");
+ nameTable.put(NTTObjectIdentifiers.id_camellia256_cbc.getId(), "Camellia");
+ nameTable.put(NTTObjectIdentifiers.id_camellia128_wrap.getId(), "Camellia");
+ nameTable.put(NTTObjectIdentifiers.id_camellia192_wrap.getId(), "Camellia");
+ nameTable.put(NTTObjectIdentifiers.id_camellia256_wrap.getId(), "Camellia");
+ nameTable.put(KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap.getId(), "SEED");
+ nameTable.put(KISAObjectIdentifiers.id_seedCBC.getId(), "SEED");
+ nameTable.put(KISAObjectIdentifiers.id_seedMAC.getId(), "SEED");
+ nameTable.put(CryptoProObjectIdentifiers.gostR28147_gcfb.getId(), "GOST28147");
+
+ nameTable.put(NISTObjectIdentifiers.id_aes128_wrap.getId(), "AES");
+ nameTable.put(NISTObjectIdentifiers.id_aes128_CCM.getId(), "AES");
+ nameTable.put(NISTObjectIdentifiers.id_aes128_CCM.getId(), "AES");
+
+ oids.put("DESEDE", PKCSObjectIdentifiers.des_EDE3_CBC);
+ oids.put("AES", NISTObjectIdentifiers.id_aes256_CBC);
+ oids.put("DES", OIWObjectIdentifiers.desCBC);
+
+ des.put("DES", "DES");
+ des.put("DESEDE", "DES");
+ des.put(OIWObjectIdentifiers.desCBC.getId(), "DES");
+ des.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), "DES");
+ des.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), "DES");
+ }
+
+ private final String kaAlgorithm;
+ private final DerivationFunction kdf;
+
+ protected BigInteger result;
+ protected byte[] ukmParameters;
+
+ public BaseAgreementSpi(String kaAlgorithm, DerivationFunction kdf)
+ {
+ this.kaAlgorithm = kaAlgorithm;
+ this.kdf = kdf;
+ }
+
+ protected static String getAlgorithm(String algDetails)
+ {
+ if (algDetails.indexOf('[') > 0)
+ {
+ return algDetails.substring(0, algDetails.indexOf('['));
+ }
+
+ if (algDetails.startsWith(NISTObjectIdentifiers.aes.getId()))
+ {
+ return "AES";
+ }
+ if (algDetails.startsWith(GNUObjectIdentifiers.Serpent.getId()))
+ {
+ return "Serpent";
+ }
+
+ String name = (String)nameTable.get(Strings.toUpperCase(algDetails));
+
+ if (name != null)
+ {
+ return name;
+ }
+
+ return algDetails;
+ }
+
+ protected static int getKeySize(String algDetails)
+ {
+ if (algDetails.indexOf('[') > 0)
+ {
+ return (Integer.parseInt(algDetails.substring(algDetails.indexOf('[') + 1, algDetails.indexOf(']'))) + 7) / 8;
+ }
+
+ String algKey = Strings.toUpperCase(algDetails);
+ if (!keySizes.containsKey(algKey))
+ {
+ return -1;
+ }
+
+ return ((Integer)keySizes.get(algKey)).intValue();
+ }
+
+ protected static byte[] trimZeroes(byte[] secret)
+ {
+ if (secret[0] != 0)
+ {
+ return secret;
+ }
+ else
+ {
+ int ind = 0;
+ while (ind < secret.length && secret[ind] == 0)
+ {
+ ind++;
+ }
+
+ byte[] rv = new byte[secret.length - ind];
+
+ System.arraycopy(secret, ind, rv, 0, rv.length);
+
+ return rv;
+ }
+ }
+
+ protected byte[] engineGenerateSecret()
+ throws IllegalStateException
+ {
+ if (kdf != null)
+ {
+ throw new UnsupportedOperationException(
+ "KDF can only be used when algorithm is known");
+ }
+
+ return bigIntToBytes(result);
+ }
+
+ protected int engineGenerateSecret(
+ byte[] sharedSecret,
+ int offset)
+ throws IllegalStateException, ShortBufferException
+ {
+ byte[] secret = engineGenerateSecret();
+
+ if (sharedSecret.length - offset < secret.length)
+ {
+ throw new ShortBufferException(kaAlgorithm + " key agreement: need " + secret.length + " bytes");
+ }
+
+ System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
+
+ return secret.length;
+ }
+
+ protected SecretKey engineGenerateSecret(
+ String algorithm)
+ throws NoSuchAlgorithmException
+ {
+ byte[] secret = bigIntToBytes(result);
+ String algKey = Strings.toUpperCase(algorithm);
+ String oidAlgorithm = algorithm;
+
+ if (oids.containsKey(algKey))
+ {
+ oidAlgorithm = ((ASN1ObjectIdentifier)oids.get(algKey)).getId();
+ }
+
+ int keySize = getKeySize(oidAlgorithm);
+
+ if (kdf != null)
+ {
+ if (keySize < 0)
+ {
+ throw new NoSuchAlgorithmException("unknown algorithm encountered: " + oidAlgorithm);
+ }
+ byte[] keyBytes = new byte[keySize / 8];
+
+ if (kdf instanceof DHKEKGenerator)
+ {
+ ASN1ObjectIdentifier oid;
+ try
+ {
+ oid = new ASN1ObjectIdentifier(oidAlgorithm);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new NoSuchAlgorithmException("no OID for algorithm: " + oidAlgorithm);
+ }
+ DHKDFParameters params = new DHKDFParameters(oid, keySize, secret, ukmParameters);
+
+ kdf.init(params);
+ }
+ else
+ {
+ KDFParameters params = new KDFParameters(secret, ukmParameters);
+
+ kdf.init(params);
+ }
+
+ kdf.generateBytes(keyBytes, 0, keyBytes.length);
+
+ secret = keyBytes;
+ }
+ else
+ {
+ if (keySize > 0)
+ {
+ byte[] keyBytes = new byte[keySize / 8];
+
+ System.arraycopy(secret, 0, keyBytes, 0, keyBytes.length);
+
+ secret = keyBytes;
+ }
+ }
+
+ if (des.containsKey(oidAlgorithm))
+ {
+ DESParameters.setOddParity(secret);
+ }
+
+ return new SecretKeySpec(secret, getAlgorithm(algorithm));
+ }
+
+ protected abstract byte[] bigIntToBytes(BigInteger result);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DESUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DESUtil.java
new file mode 100644
index 00000000..2409fb03
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DESUtil.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.jcajce.provider.asymmetric.util;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.util.Strings;
+
+public class DESUtil
+{
+ private static final Set<String> des = new HashSet<String>();
+
+ static
+ {
+ des.add("DES");
+ des.add("DESEDE");
+ des.add(OIWObjectIdentifiers.desCBC.getId());
+ des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
+ des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
+ des.add(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId());
+ }
+
+ public static boolean isDES(String algorithmID)
+ {
+ String name = Strings.toUpperCase(algorithmID);
+
+ return des.contains(name);
+ }
+
+ /**
+ * DES Keys use the LSB as the odd parity bit. This can
+ * be used to check for corrupt keys.
+ *
+ * @param bytes the byte array to set the parity on.
+ */
+ public static void setOddParity(
+ byte[] bytes)
+ {
+ for (int i = 0; i < bytes.length; i++)
+ {
+ int b = bytes[i];
+ bytes[i] = (byte)((b & 0xfe) |
+ ((((b >> 1) ^
+ (b >> 2) ^
+ (b >> 3) ^
+ (b >> 4) ^
+ (b >> 5) ^
+ (b >> 6) ^
+ (b >> 7)) ^ 0x01) & 0x01));
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Blake2b.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Blake2b.java
new file mode 100644
index 00000000..0eb978a9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Blake2b.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.jcajce.provider.digest;
+
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.crypto.digests.Blake2bDigest;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+
+public class Blake2b
+{
+ private Blake2b()
+ {
+
+ }
+
+ static public class Blake2b512
+ extends BCMessageDigest
+ implements Cloneable
+ {
+ public Blake2b512()
+ {
+ super(new Blake2bDigest(512));
+ }
+
+ public Object clone()
+ throws CloneNotSupportedException
+ {
+ Blake2b512 d = (Blake2b512)super.clone();
+ d.digest = new Blake2bDigest((Blake2bDigest)digest);
+
+ return d;
+ }
+ }
+
+ static public class Blake2b384
+ extends BCMessageDigest
+ implements Cloneable
+ {
+ public Blake2b384()
+ {
+ super(new Blake2bDigest(384));
+ }
+
+ public Object clone()
+ throws CloneNotSupportedException
+ {
+ Blake2b384 d = (Blake2b384)super.clone();
+ d.digest = new Blake2bDigest((Blake2bDigest)digest);
+
+ return d;
+ }
+ }
+
+ static public class Blake2b256
+ extends BCMessageDigest
+ implements Cloneable
+ {
+ public Blake2b256()
+ {
+ super(new Blake2bDigest(256));
+ }
+
+ public Object clone()
+ throws CloneNotSupportedException
+ {
+ Blake2b256 d = (Blake2b256)super.clone();
+ d.digest = new Blake2bDigest((Blake2bDigest)digest);
+
+ return d;
+ }
+ }
+
+ static public class Blake2b160
+ extends BCMessageDigest
+ implements Cloneable
+ {
+ public Blake2b160()
+ {
+ super(new Blake2bDigest(160));
+ }
+
+ public Object clone()
+ throws CloneNotSupportedException
+ {
+ Blake2b160 d = (Blake2b160)super.clone();
+ d.digest = new Blake2bDigest((Blake2bDigest)digest);
+
+ return d;
+ }
+ }
+
+ public static class Mappings
+ extends DigestAlgorithmProvider
+ {
+ private static final String PREFIX = Blake2b.class.getName();
+
+ public Mappings()
+ {
+ }
+
+ public void configure(ConfigurableProvider provider)
+ {
+ provider.addAlgorithm("MessageDigest.BLAKE2B-512", PREFIX + "$Blake2b512");
+ provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2b512, "BLAKE2B-512");
+
+ provider.addAlgorithm("MessageDigest.BLAKE2B-384", PREFIX + "$Blake2b384");
+ provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2b384, "BLAKE2B-384");
+
+ provider.addAlgorithm("MessageDigest.BLAKE2B-256", PREFIX + "$Blake2b256");
+ provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2b256, "BLAKE2B-256");
+
+ provider.addAlgorithm("MessageDigest.BLAKE2B-160", PREFIX + "$Blake2b160");
+ provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2b160, "BLAKE2B-160");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Keccak.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Keccak.java
new file mode 100644
index 00000000..f8b989bf
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Keccak.java
@@ -0,0 +1,195 @@
+package org.bouncycastle.jcajce.provider.digest;
+
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.digests.KeccakDigest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+
+public class Keccak
+{
+ private Keccak()
+ {
+
+ }
+
+ static public class DigestKeccak
+ extends BCMessageDigest
+ implements Cloneable
+ {
+ public DigestKeccak(int size)
+ {
+ super(new KeccakDigest(size));
+ }
+
+ public Object clone()
+ throws CloneNotSupportedException
+ {
+ BCMessageDigest d = (BCMessageDigest)super.clone();
+ d.digest = new KeccakDigest((KeccakDigest)digest);
+
+ return d;
+ }
+ }
+
+ static public class Digest224
+ extends DigestKeccak
+ {
+ public Digest224()
+ {
+ super(224);
+ }
+ }
+
+ static public class Digest256
+ extends DigestKeccak
+ {
+ public Digest256()
+ {
+ super(256);
+ }
+ }
+
+ static public class Digest288
+ extends DigestKeccak
+ {
+ public Digest288()
+ {
+ super(288);
+ }
+ }
+
+ static public class Digest384
+ extends DigestKeccak
+ {
+ public Digest384()
+ {
+ super(384);
+ }
+ }
+
+ static public class Digest512
+ extends DigestKeccak
+ {
+ public Digest512()
+ {
+ super(512);
+ }
+ }
+
+ public static class HashMac224
+ extends BaseMac
+ {
+ public HashMac224()
+ {
+ super(new HMac(new KeccakDigest(224)));
+ }
+ }
+
+ public static class HashMac256
+ extends BaseMac
+ {
+ public HashMac256()
+ {
+ super(new HMac(new KeccakDigest(256)));
+ }
+ }
+
+ public static class HashMac288
+ extends BaseMac
+ {
+ public HashMac288()
+ {
+ super(new HMac(new KeccakDigest(288)));
+ }
+ }
+
+ public static class HashMac384
+ extends BaseMac
+ {
+ public HashMac384()
+ {
+ super(new HMac(new KeccakDigest(384)));
+ }
+ }
+
+ public static class HashMac512
+ extends BaseMac
+ {
+ public HashMac512()
+ {
+ super(new HMac(new KeccakDigest(512)));
+ }
+ }
+
+ public static class KeyGenerator224
+ extends BaseKeyGenerator
+ {
+ public KeyGenerator224()
+ {
+ super("HMACKECCAK224", 224, new CipherKeyGenerator());
+ }
+ }
+
+ public static class KeyGenerator256
+ extends BaseKeyGenerator
+ {
+ public KeyGenerator256()
+ {
+ super("HMACKECCAK256", 256, new CipherKeyGenerator());
+ }
+ }
+
+ public static class KeyGenerator288
+ extends BaseKeyGenerator
+ {
+ public KeyGenerator288()
+ {
+ super("HMACKECCAK288", 288, new CipherKeyGenerator());
+ }
+ }
+
+ public static class KeyGenerator384
+ extends BaseKeyGenerator
+ {
+ public KeyGenerator384()
+ {
+ super("HMACKECCAK384", 384, new CipherKeyGenerator());
+ }
+ }
+
+ public static class KeyGenerator512
+ extends BaseKeyGenerator
+ {
+ public KeyGenerator512()
+ {
+ super("HMACKECCAK512", 512, new CipherKeyGenerator());
+ }
+ }
+
+ public static class Mappings
+ extends DigestAlgorithmProvider
+ {
+ private static final String PREFIX = Keccak.class.getName();
+
+ public Mappings()
+ {
+ }
+
+ public void configure(ConfigurableProvider provider)
+ {
+ provider.addAlgorithm("MessageDigest.KECCAK-224", PREFIX + "$Digest224");
+ provider.addAlgorithm("MessageDigest.KECCAK-288", PREFIX + "$Digest288");
+ provider.addAlgorithm("MessageDigest.KECCAK-256", PREFIX + "$Digest256");
+ provider.addAlgorithm("MessageDigest.KECCAK-384", PREFIX + "$Digest384");
+ provider.addAlgorithm("MessageDigest.KECCAK-512", PREFIX + "$Digest512");
+
+ addHMACAlgorithm(provider, "KECCAK224", PREFIX + "$HashMac224", PREFIX + "$KeyGenerator224");
+ addHMACAlgorithm(provider, "KECCAK256", PREFIX + "$HashMac256", PREFIX + "$KeyGenerator256");
+ addHMACAlgorithm(provider, "KECCAK288", PREFIX + "$HashMac288", PREFIX + "$KeyGenerator288");
+ addHMACAlgorithm(provider, "KECCAK384", PREFIX + "$HashMac384", PREFIX + "$KeyGenerator384");
+ addHMACAlgorithm(provider, "KECCAK512", PREFIX + "$HashMac512", PREFIX + "$KeyGenerator512");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java
new file mode 100644
index 00000000..5ccc8ff0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java
@@ -0,0 +1,78 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.cms.GCMParameters;
+import org.bouncycastle.util.Integers;
+
+class GcmSpecUtil
+{
+ static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec");
+
+ static boolean gcmSpecExists()
+ {
+ return gcmSpecClass != null;
+ }
+
+ static boolean isGcmSpec(AlgorithmParameterSpec paramSpec)
+ {
+ return gcmSpecClass != null && gcmSpecClass.isInstance(paramSpec);
+ }
+
+ static boolean isGcmSpec(Class paramSpecClass)
+ {
+ return gcmSpecClass == paramSpecClass;
+ }
+
+ static AlgorithmParameterSpec extractGcmSpec(ASN1Primitive spec)
+ throws InvalidParameterSpecException
+ {
+ try
+ {
+ GCMParameters gcmParams = GCMParameters.getInstance(spec);
+ Constructor constructor = gcmSpecClass.getConstructor(new Class[]{Integer.TYPE, byte[].class});
+
+ return (AlgorithmParameterSpec)constructor.newInstance(new Object[] { Integers.valueOf(gcmParams.getIcvLen() * 8), gcmParams.getNonce() });
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new InvalidParameterSpecException("No constructor found!"); // should never happen
+ }
+ catch (Exception e)
+ {
+ throw new InvalidParameterSpecException("Construction failed: " + e.getMessage()); // should never happen
+ }
+ }
+
+ static GCMParameters extractGcmParameters(AlgorithmParameterSpec paramSpec)
+ throws InvalidParameterSpecException
+ {
+ try
+ {
+ Method tLen = gcmSpecClass.getDeclaredMethod("getTLen", new Class[0]);
+ Method iv= gcmSpecClass.getDeclaredMethod("getIV", new Class[0]);
+
+ return new GCMParameters((byte[])iv.invoke(paramSpec, new Object[0]), ((Integer)tLen.invoke(paramSpec, new Object[0])).intValue() / 8);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidParameterSpecException("Cannot process GCMParameterSpec");
+ }
+ }
+
+ private static Class lookup(String className)
+ {
+ try
+ {
+ return GcmSpecUtil.class.getClassLoader().loadClass(className);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/OpenSSLPBKDF.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/OpenSSLPBKDF.java
new file mode 100644
index 00000000..5888adfa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/OpenSSLPBKDF.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseSecretKeyFactory;
+import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
+import org.bouncycastle.util.Strings;
+
+public final class OpenSSLPBKDF
+{
+ private OpenSSLPBKDF()
+ {
+ }
+
+ public static class PBKDF
+ extends BaseSecretKeyFactory
+ {
+ public PBKDF()
+ {
+ super("PBKDF-OpenSSL", null);
+ }
+
+ protected SecretKey engineGenerateSecret(
+ KeySpec keySpec)
+ throws InvalidKeySpecException
+ {
+ if (keySpec instanceof PBEKeySpec)
+ {
+ PBEKeySpec pbeSpec = (PBEKeySpec)keySpec;
+
+ if (pbeSpec.getSalt() == null)
+ {
+ throw new InvalidKeySpecException("missing required salt");
+ }
+
+ if (pbeSpec.getIterationCount() <= 0)
+ {
+ throw new InvalidKeySpecException("positive iteration count required: "
+ + pbeSpec.getIterationCount());
+ }
+
+ if (pbeSpec.getKeyLength() <= 0)
+ {
+ throw new InvalidKeySpecException("positive key length required: "
+ + pbeSpec.getKeyLength());
+ }
+
+ if (pbeSpec.getPassword().length == 0)
+ {
+ throw new IllegalArgumentException("password empty");
+ }
+
+ OpenSSLPBEParametersGenerator pGen = new OpenSSLPBEParametersGenerator();
+
+ pGen.init(Strings.toByteArray(pbeSpec.getPassword()), pbeSpec.getSalt());
+
+ return new SecretKeySpec(((KeyParameter)pGen.generateDerivedParameters(pbeSpec.getKeyLength())).getKey(), "OpenSSLPBKDF");
+ }
+
+ throw new InvalidKeySpecException("Invalid KeySpec");
+ }
+ }
+
+ public static class Mappings
+ extends AlgorithmProvider
+ {
+ private static final String PREFIX = OpenSSLPBKDF.class.getName();
+
+ public Mappings()
+ {
+ }
+
+ public void configure(ConfigurableProvider provider)
+ {
+ provider.addAlgorithm("SecretKeyFactory.PBKDF-OPENSSL", PREFIX + "$PBKDF");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java
new file mode 100644
index 00000000..05e037ab
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java
@@ -0,0 +1,162 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.spec.IvParameterSpec;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.engines.SM4Engine;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
+import org.bouncycastle.crypto.macs.CMac;
+import org.bouncycastle.crypto.macs.GMac;
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
+
+public final class SM4
+{
+ private SM4()
+ {
+ }
+
+ public static class ECB
+ extends BaseBlockCipher
+ {
+ public ECB()
+ {
+ super(new BlockCipherProvider()
+ {
+ public BlockCipher get()
+ {
+ return new SM4Engine();
+ }
+ });
+ }
+ }
+
+ public static class KeyGen
+ extends BaseKeyGenerator
+ {
+ public KeyGen()
+ {
+ super("SM4", 128, new CipherKeyGenerator());
+ }
+ }
+
+ public static class CMAC
+ extends BaseMac
+ {
+ public CMAC()
+ {
+ super(new CMac(new SM4Engine()));
+ }
+ }
+
+ public static class GMAC
+ extends BaseMac
+ {
+ public GMAC()
+ {
+ super(new GMac(new GCMBlockCipher(new SM4Engine())));
+ }
+ }
+
+ public static class Poly1305
+ extends BaseMac
+ {
+ public Poly1305()
+ {
+ super(new org.bouncycastle.crypto.macs.Poly1305(new SM4Engine()));
+ }
+ }
+
+ public static class Poly1305KeyGen
+ extends BaseKeyGenerator
+ {
+ public Poly1305KeyGen()
+ {
+ super("Poly1305-SM4", 256, new Poly1305KeyGenerator());
+ }
+ }
+
+ public static class AlgParamGen
+ extends BaseAlgorithmParameterGenerator
+ {
+ protected void engineInit(
+ AlgorithmParameterSpec genParamSpec,
+ SecureRandom random)
+ throws InvalidAlgorithmParameterException
+ {
+ throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for SM4 parameter generation.");
+ }
+
+ protected AlgorithmParameters engineGenerateParameters()
+ {
+ byte[] iv = new byte[16];
+
+ if (random == null)
+ {
+ random = new SecureRandom();
+ }
+
+ random.nextBytes(iv);
+
+ AlgorithmParameters params;
+
+ try
+ {
+ params = createParametersInstance("SM4");
+ params.init(new IvParameterSpec(iv));
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e.getMessage());
+ }
+
+ return params;
+ }
+ }
+
+ public static class AlgParams
+ extends IvAlgorithmParameters
+ {
+ protected String engineToString()
+ {
+ return "SM4 IV";
+ }
+ }
+
+ public static class Mappings
+ extends SymmetricAlgorithmProvider
+ {
+ private static final String PREFIX = SM4.class.getName();
+
+ public Mappings()
+ {
+ }
+
+ public void configure(ConfigurableProvider provider)
+ {
+ provider.addAlgorithm("AlgorithmParameters.SM4", PREFIX + "$AlgParams");
+
+ provider.addAlgorithm("AlgorithmParameterGenerator.SM4", PREFIX + "$AlgParamGen");
+
+ provider.addAlgorithm("Cipher.SM4", PREFIX + "$ECB");
+
+ provider.addAlgorithm("KeyGenerator.SM4", PREFIX + "$KeyGen");
+
+ addCMacAlgorithm(provider, "SM4", PREFIX + "$CMAC", PREFIX + "$KeyGen");
+ addGMacAlgorithm(provider, "SM4", PREFIX + "$GMAC", PREFIX + "$KeyGen");
+ addPoly1305Algorithm(provider, "SM4", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KTSParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KTSParameterSpec.java
new file mode 100644
index 00000000..a6d1a8d2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KTSParameterSpec.java
@@ -0,0 +1,157 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Parameter spec for doing KTS based wrapping via the Cipher API.
+ */
+public class KTSParameterSpec
+ implements AlgorithmParameterSpec
+{
+ private final String wrappingKeyAlgorithm;
+ private final int keySizeInBits;
+ private final AlgorithmParameterSpec parameterSpec;
+ private final AlgorithmIdentifier kdfAlgorithm;
+ private byte[] otherInfo;
+
+ /**
+ * Builder class for creating a KTSParameterSpec.
+ */
+ public static final class Builder
+ {
+ private final String algorithmName;
+ private final int keySizeInBits;
+
+ private AlgorithmParameterSpec parameterSpec;
+ private AlgorithmIdentifier kdfAlgorithm;
+ private byte[] otherInfo;
+
+ /**
+ * Basic builder.
+ *
+ * @param algorithmName the algorithm name for the secret key we use for wrapping.
+ * @param keySizeInBits the size of the wrapping key we want to produce in bits.
+ */
+ public Builder(String algorithmName, int keySizeInBits)
+ {
+ this(algorithmName, keySizeInBits, null);
+ }
+
+ /**
+ * Basic builder.
+ *
+ * @param algorithmName the algorithm name for the secret key we use for wrapping.
+ * @param keySizeInBits the size of the wrapping key we want to produce in bits.
+ * @param otherInfo the otherInfo/IV encoding to be applied to the KDF.
+ */
+ public Builder(String algorithmName, int keySizeInBits, byte[] otherInfo)
+ {
+ this.algorithmName = algorithmName;
+ this.keySizeInBits = keySizeInBits;
+ this.kdfAlgorithm = new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf3, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
+ this.otherInfo = (otherInfo == null) ? new byte[0] : Arrays.clone(otherInfo);
+ }
+
+ /**
+ * Set the algorithm parameter spec to be used with the wrapper.
+ *
+ * @param parameterSpec the algorithm parameter spec to be used in wrapping/unwrapping.
+ * @return the current Builder instance.
+ */
+ public Builder withParameterSpec(AlgorithmParameterSpec parameterSpec)
+ {
+ this.parameterSpec = parameterSpec;
+
+ return this;
+ }
+
+ /**
+ * Set the KDF algorithm and digest algorithm for wrap key generation.
+ *
+ * @param kdfAlgorithm the KDF algorithm to apply.
+ * @return the current Builder instance.
+ */
+ public Builder withKdfAlgorithm(AlgorithmIdentifier kdfAlgorithm)
+ {
+ this.kdfAlgorithm = kdfAlgorithm;
+
+ return this;
+ }
+
+ /**
+ * Build the new parameter spec.
+ *
+ * @return a new parameter spec configured according to the builder state.
+ */
+ public KTSParameterSpec build()
+ {
+ return new KTSParameterSpec(algorithmName, keySizeInBits, parameterSpec, kdfAlgorithm, otherInfo);
+ }
+ }
+
+ private KTSParameterSpec(
+ String wrappingKeyAlgorithm, int keySizeInBits,
+ AlgorithmParameterSpec parameterSpec, AlgorithmIdentifier kdfAlgorithm, byte[] otherInfo)
+ {
+ this.wrappingKeyAlgorithm = wrappingKeyAlgorithm;
+ this.keySizeInBits = keySizeInBits;
+ this.parameterSpec = parameterSpec;
+ this.kdfAlgorithm = kdfAlgorithm;
+ this.otherInfo = otherInfo;
+ }
+
+ /**
+ * Return the name of the algorithm for the wrapping key this key spec should use.
+ *
+ * @return the key algorithm.
+ */
+ public String getKeyAlgorithmName()
+ {
+ return wrappingKeyAlgorithm;
+ }
+
+ /**
+ * Return the size of the key (in bits) for the wrapping key this key spec should use.
+ *
+ * @return length in bits of the key to be calculated.
+ */
+ public int getKeySize()
+ {
+ return keySizeInBits;
+ }
+
+ /**
+ * Return the algorithm parameter spec to be applied with the private key when the encapsulation is decrypted.
+ *
+ * @return the algorithm parameter spec to be used with the private key.
+ */
+ public AlgorithmParameterSpec getParameterSpec()
+ {
+ return parameterSpec;
+ }
+
+ /**
+ * Return the AlgorithmIdentifier for the KDF to do key derivation after extracting the secret.
+ *
+ * @return the AlgorithmIdentifier for the SecretKeyFactory's KDF.
+ */
+ public AlgorithmIdentifier getKdfAlgorithm()
+ {
+ return kdfAlgorithm;
+ }
+
+ /**
+ * Return the otherInfo data for initialising the KDF.
+ *
+ * @return the otherInfo data.
+ */
+ public byte[] getOtherInfo()
+ {
+ return Arrays.clone(otherInfo);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/MQVParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/MQVParameterSpec.java
new file mode 100644
index 00000000..907abfb4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/MQVParameterSpec.java
@@ -0,0 +1,70 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.util.Arrays;
+
+public class MQVParameterSpec
+ implements AlgorithmParameterSpec
+{
+ private final PublicKey ephemeralPublicKey;
+ private final PrivateKey ephemeralPrivateKey;
+ private final PublicKey otherPartyEphemeralKey;
+ private final byte[] userKeyingMaterial;
+
+ public MQVParameterSpec(PublicKey ephemeralPublicKey, PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey, byte[] userKeyingMaterial)
+ {
+ this.ephemeralPublicKey = ephemeralPublicKey;
+ this.ephemeralPrivateKey = ephemeralPrivateKey;
+ this.otherPartyEphemeralKey = otherPartyEphemeralKey;
+ this.userKeyingMaterial = Arrays.clone(userKeyingMaterial);
+ }
+
+ public MQVParameterSpec(PublicKey ephemeralPublicKey, PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey)
+ {
+ this(ephemeralPublicKey, ephemeralPrivateKey, otherPartyEphemeralKey, null);
+ }
+
+ public MQVParameterSpec(KeyPair ephemeralKeyPair, PublicKey otherPartyEphemeralKey, byte[] userKeyingMaterial)
+ {
+ this(ephemeralKeyPair.getPublic(), ephemeralKeyPair.getPrivate(), otherPartyEphemeralKey, userKeyingMaterial);
+ }
+
+ public MQVParameterSpec(PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey, byte[] userKeyingMaterial)
+ {
+ this(null, ephemeralPrivateKey, otherPartyEphemeralKey, userKeyingMaterial);
+ }
+
+ public MQVParameterSpec(KeyPair ephemeralKeyPair, PublicKey otherPartyEphemeralKey)
+ {
+ this(ephemeralKeyPair.getPublic(), ephemeralKeyPair.getPrivate(), otherPartyEphemeralKey, null);
+ }
+
+ public MQVParameterSpec(PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey)
+ {
+ this(null, ephemeralPrivateKey, otherPartyEphemeralKey, null);
+ }
+
+ public PrivateKey getEphemeralPrivateKey()
+ {
+ return ephemeralPrivateKey;
+ }
+
+ public PublicKey getEphemeralPublicKey()
+ {
+ return ephemeralPublicKey;
+ }
+
+ public PublicKey getOtherPartyEphemeralKey()
+ {
+ return otherPartyEphemeralKey;
+ }
+
+ public byte[] getUserKeyingMaterial()
+ {
+ return Arrays.clone(userKeyingMaterial);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java
new file mode 100644
index 00000000..d8135310
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.util.Arrays;
+
+public class UserKeyingMaterialSpec
+ implements AlgorithmParameterSpec
+{
+ private final byte[] userKeyingMaterial;
+
+ public UserKeyingMaterialSpec(byte[] userKeyingMaterial)
+ {
+ this.userKeyingMaterial = Arrays.clone(userKeyingMaterial);
+ }
+
+ public byte[] getUserKeyingMaterial()
+ {
+ return Arrays.clone(userKeyingMaterial);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/util/AlgorithmParametersUtils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/util/AlgorithmParametersUtils.java
new file mode 100644
index 00000000..4a1a92a5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/util/AlgorithmParametersUtils.java
@@ -0,0 +1,68 @@
+/***************************************************************/
+/****** DO NOT EDIT THIS CLASS bc-java SOURCE FILE ******/
+/***************************************************************/
+package org.bouncycastle.jcajce.util;
+
+import java.io.IOException;
+import java.security.AlgorithmParameters;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Primitive;
+
+/**
+ * General JCA/JCE utility methods.
+ */
+public class AlgorithmParametersUtils
+{
+
+
+ private AlgorithmParametersUtils()
+ {
+
+ }
+
+ /**
+ * Extract an ASN.1 encodable from an AlgorithmParameters object.
+ *
+ * @param params the object to get the encoding used to create the return value.
+ * @return an ASN.1 object representing the primitives making up the params parameter.
+ * @throws IOException if an encoding cannot be extracted.
+ */
+ public static ASN1Encodable extractParameters(AlgorithmParameters params)
+ throws IOException
+ {
+ // we try ASN.1 explicitly first just in case and then role back to the default.
+ ASN1Encodable asn1Params;
+ try
+ {
+ asn1Params = ASN1Primitive.fromByteArray(params.getEncoded("ASN.1"));
+ }
+ catch (Exception ex)
+ {
+ asn1Params = ASN1Primitive.fromByteArray(params.getEncoded());
+ }
+
+ return asn1Params;
+ }
+
+ /**
+ * Load an AlgorithmParameters object with the passed in ASN.1 encodable - if possible.
+ *
+ * @param params the AlgorithmParameters object to be initialised.
+ * @param sParams the ASN.1 encodable to initialise params with.
+ * @throws IOException if the parameters cannot be initialised.
+ */
+ public static void loadParameters(AlgorithmParameters params, ASN1Encodable sParams)
+ throws IOException
+ {
+ // we try ASN.1 explicitly first just in case and then role back to the default.
+ try
+ {
+ params.init(sParams.toASN1Primitive().getEncoded(), "ASN.1");
+ }
+ catch (Exception ex)
+ {
+ params.init(sParams.toASN1Primitive().getEncoded());
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java
new file mode 100644
index 00000000..1bf8b31b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java
@@ -0,0 +1,55 @@
+package org.bouncycastle.jcajce.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.gnu.GNUObjectIdentifiers;
+import org.bouncycastle.asn1.iso.ISOIECObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+
+public class MessageDigestUtils
+{
+ private static Map<ASN1ObjectIdentifier, String> digestOidMap = new HashMap<ASN1ObjectIdentifier, String>();
+
+ static
+ {
+ digestOidMap.put(PKCSObjectIdentifiers.md2, "MD2");
+ digestOidMap.put(PKCSObjectIdentifiers.md4, "MD4");
+ digestOidMap.put(PKCSObjectIdentifiers.md5, "MD5");
+ digestOidMap.put(OIWObjectIdentifiers.idSHA1, "SHA-1");
+ digestOidMap.put(NISTObjectIdentifiers.id_sha224, "SHA-224");
+ digestOidMap.put(NISTObjectIdentifiers.id_sha256, "SHA-256");
+ digestOidMap.put(NISTObjectIdentifiers.id_sha384, "SHA-384");
+ digestOidMap.put(NISTObjectIdentifiers.id_sha512, "SHA-512");
+ digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD-128");
+ digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD-160");
+ digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD-128");
+ digestOidMap.put(ISOIECObjectIdentifiers.ripemd128, "RIPEMD-128");
+ digestOidMap.put(ISOIECObjectIdentifiers.ripemd160, "RIPEMD-160");
+ digestOidMap.put(CryptoProObjectIdentifiers.gostR3411, "GOST3411");
+ digestOidMap.put(GNUObjectIdentifiers.Tiger_192, "Tiger");
+ digestOidMap.put(ISOIECObjectIdentifiers.whirlpool, "Whirlpool");
+ }
+
+ /**
+ * Attempt to find a standard JCA name for the digest represented by the passed in OID.
+ *
+ * @param digestAlgOID the OID of the digest algorithm of interest.
+ * @return a string representing the standard name - the OID as a string if none available.
+ */
+ public static String getDigestName(ASN1ObjectIdentifier digestAlgOID)
+ {
+ String name = (String)digestOidMap.get(digestAlgOID); // for pre 1.5 JDK
+ if (name != null)
+ {
+ return name;
+ }
+
+ return digestAlgOID.getId();
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECIESVectorTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECIESVectorTest.java
new file mode 100644
index 00000000..95589561
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECIESVectorTest.java
@@ -0,0 +1,236 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.Security;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import javax.crypto.Cipher;
+
+import org.bouncycastle.crypto.prng.FixedSecureRandom;
+import org.bouncycastle.jce.interfaces.ECPrivateKey;
+import org.bouncycastle.jce.interfaces.ECPublicKey;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.IESParameterSpec;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test for ECIES - Elliptic Curve Integrated Encryption Scheme
+ */
+public class ECIESVectorTest
+ extends SimpleTest
+{
+ static byte[] message = Hex.decode("0102030405060708090a0b0c0d0e0f10111213141516");
+
+ static byte[] derivation1 = Hex.decode("202122232425262728292a2b2c2d2e2f");
+ static byte[] derivation2 = Hex.decode("202122232425262728292a2b2c2d2e2f404142434445464748");
+
+ static byte[] encoding1 = Hex.decode("af");
+ static byte[] encoding2 = Hex.decode("303132333435363738393a3b3c3d3e3f");
+ static byte[] encoding3 = Hex.decode("101112131415161718191a1b1c1d1e1f303132333435363738393a3b3c3d3e3f");
+
+ static byte[] p256_1_pub = Base64.decode("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGVoxUX5AiyggqzcaXG3yG6cH6PKSX6fVOnCo5SKolfR8kwc6S8zmADXlpnjzMLNUVvGDL805VKIXNJHijq4+gw==");
+ static byte[] p256_1_pri = Base64.decode("MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg+dn4oLSJcx5lxZhVxJCip13O/OblrNzNyCj2b9sNbQegCgYIKoZIzj0DAQehRANCAAQZWjFRfkCLKCCrNxpcbfIbpwfo8pJfp9U6cKjlIqiV9HyTBzpLzOYANeWmePMws1RW8YMvzTlUohc0keKOrj6D");
+ static byte[] p256_1_eph = Hex.decode("35ee388194396b5febbddb7e3618eaaba44f3bae766dac70f75ae7b84b210948");
+
+ static byte[] p256_1_no_params = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae43dfbe605c746ba0c546c0c25ba5a00304587fbed07a35ca06415cf6ad4d6a69d01122c99e2c854fe818f");
+
+ static byte[] p256_1_with_params11 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a33c5d1948ee93ea5dc7577d42d7c2e41ab08f85835");
+ static byte[] p256_1_with_params12 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a3394725300acb7855f71a149d3f51a2e6fbd7f5389");
+ static byte[] p256_1_with_params13 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a33920872328f8d05ec561ba8c6e58d82b79c360078");
+ static byte[] p256_1_with_params21 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df4499a4f4b6726651fb5e88178f01d831d9e5be0c1");
+ static byte[] p256_1_with_params22 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df457c384a0e6402a5bb073120f08c706b49a4775d0");
+ static byte[] p256_1_with_params23 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df4039427c24c55a2627df0ebea00f9d1eded9dd64b");
+
+ static byte[] p256_2_pub = Base64.decode("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAER4gx8nnRAkP+u76j0COZD81Cu9CTw3vczLnu1DG7ObI/VCzrDzJuswfzNWmxOFYXiXmZMAAkkEFA40nDSGOoqA==");
+ static byte[] p256_2_pri = Base64.decode("MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgkflw6MQj4RbyuPFHoDE3i1dEaROS9uDSjGvrvPjqL7mgCgYIKoZIzj0DAQehRANCAARHiDHyedECQ/67vqPQI5kPzUK70JPDe9zMue7UMbs5sj9ULOsPMm6zB/M1abE4VheJeZkwACSQQUDjScNIY6io");
+ static byte[] p256_2_eph = Hex.decode("0c37e1e0559a60d0b9c5b7b139a3f2df022b23abbd194bd95c8eff0aab1fd544");
+
+ static byte[] p256_2_no_params = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dacd99a5cadfe82652a18c6058fc5ef7ffca626235fdbf7e2537f494d049d6f4e53d3d3df489cefb31101e1");
+
+ static byte[] p256_2_with_params11 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dace0e423753f63e054824edbc298a5645d7a4010cc7e65947090625dc0d750c50b2d563f264718d95818d4");
+ static byte[] p256_2_with_params12 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dace0e423753f63e054824edbc298a5645d7a4010cc7e6502850f65c7a2c466444dea69d95a2df9a4b6cf2c");
+ static byte[] p256_2_with_params13 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dace0e423753f63e054824edbc298a5645d7a4010cc7e651188a654e47322a60e53ea20a9e02f0e78bf8a32");
+ static byte[] p256_2_with_params21 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dacdbdbe5e5f72245f4b5c28ee1ae21442868aef592d4047c8149babf4e7faffb5474bd6d84d6b9f1c9faf5");
+ static byte[] p256_2_with_params22 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dacdbdbe5e5f72245f4b5c28ee1ae21442868aef592d40471190bd9b4bddd63983e4963378c9f55c703a7e6");
+ static byte[] p256_2_with_params23 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dacdbdbe5e5f72245f4b5c28ee1ae21442868aef592d404ca4307e077a693c6410815c7985b0678a42cbd84");
+
+ static byte[] p521_1_pub = Base64.decode("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBLNtLEbQBq2YGA2Q+eCwulIDggr1dCj8CqY/Yj/HuzicYGmVkNpr/gRfZHX4FRrh9HsGrS7tW+UA1pCQmn3p3aeEADiaXGIybDBsnuU20xntomQG/d/OHDkEaPee9nFNbi9Oha7BHTA/x2yyLMhQHeUlMgjz0DQqRxrwGJmvI85eNZpM=");
+ static byte[] p521_1_pri = Base64.decode("MIH3AgEAMBAGByqGSM49AgEGBSuBBAAjBIHfMIHcAgEBBEIA1QAfnUvK1fAaENAArV0YnNGvnu2H1vGHNgsG3QV7p16gjd6UOgmGCZVM9SLlRH3fi9K1Xreyl4sQH3NeY+ZnInqgBwYFK4EEACOhgYkDgYYABAEs20sRtAGrZgYDZD54LC6UgOCCvV0KPwKpj9iP8e7OJxgaZWQ2mv+BF9kdfgVGuH0ewatLu1b5QDWkJCafendp4QAOJpcYjJsMGye5TbTGe2iZAb9384cOQRo9572cU1uL06FrsEdMD/HbLIsyFAd5SUyCPPQNCpHGvAYma8jzl41mkw==");
+ static byte[] p521_1_eph = Hex.decode("d25c44819712555824360eef5afda947373ec462c2a0fd5bebbe0b0e4e40b4ebf483bc5b5eeb84542fc226dc2e5307ceec59fe7c557522b77589210b434295aabfc7");
+
+ static byte[] p521_1_no_params = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7ee2102423e67f5ee7f46e87a26370b7734548f3baee7729c5ff1c1638b93f068628320c7c56b7a68fb86d");
+
+ static byte[] p521_1_with_params11 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e49225382108fb5bdd6b4ad57fee63c2819d10c1bc7517bd0a5070c123d4e24d66f9f0bf21ec105629cb2");
+ static byte[] p521_1_with_params12 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e49225382108fb5bdd6b4ad57fee63c2819d10c1bc751a51c6c78d905b0b7702d2edf89d0cf30f0d21c93");
+ static byte[] p521_1_with_params13 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e49225382108fb5bdd6b4ad57fee63c2819d10c1bc7513a9fb7f21f78ef3467b54153b8e2998de5f46724");
+ static byte[] p521_1_with_params21 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e1e0e7ac9ade1646519a365ef578f0bbe3556a818c83a0ce6226db7a3f3964e7a18955f9150fb030dcb09");
+ static byte[] p521_1_with_params22 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e1e0e7ac9ade1646519a365ef578f0bbe3556a818c83ade9180b72d7d2e9218395ea7d83c00cf03c6b39d");
+ static byte[] p521_1_with_params23 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e1e0e7ac9ade1646519a365ef578f0bbe3556a818c83a605f2a4ef486f9215e546d618eb7624443ee1a7d");
+
+ static byte[] p521_2_pub = Base64.decode("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQACXj0hmGm68PQxI12y5sKMn4mN6mIMOEn1l6PBOeGtk8QuJHt1AS3X5DcPorJTxhuhdcdDHA3if1utfAw9hl7tCIBQELwgw2Vo3gYkdbG7Hj6O9kf8WpqIEJ3UsC4S0hITMY1knO41BaO4UdO+nUiN+PZPRRSqFwm3C5v3fffLSGbpIQ=");
+ static byte[] p521_2_pri = Base64.decode("MIH3AgEAMBAGByqGSM49AgEGBSuBBAAjBIHfMIHcAgEBBEIB6s6VR/8WgiOvSjHT/n5Y91wESyI9e4GABUNWMXMPhNspwb9++7fyanbeliHNz9K61SjDG7rLXs90K1iRvHPJwmygBwYFK4EEACOhgYkDgYYABAAJePSGYabrw9DEjXbLmwoyfiY3qYgw4SfWXo8E54a2TxC4ke3UBLdfkNw+islPGG6F1x0McDeJ/W618DD2GXu0IgFAQvCDDZWjeBiR1sbsePo72R/xamogQndSwLhLSEhMxjWSc7jUFo7hR076dSI349k9FFKoXCbcLm/d998tIZukhA==");
+ static byte[] p521_2_eph = Hex.decode("c9ee178c7f200a51a0053931c697001205f8923fd04b538c3fd5df2be43a6f304581f57138906bba0be631f7e0fd2804d00b5cb053559f080c13c913358f6e1fcc6d");
+
+ static byte[] p521_2_no_params = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace65922842dba0351c1cebde53ee4ba9afadbd3a03a88507046f62c7383f96142c09df58be5b0a661893d41");
+
+ static byte[] p521_2_with_params11 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace674ccf084d69e0bc9a728548d887a162ddf53a0002ed034b51a5406b831ce9ea713be7df54934b77dcc84");
+ static byte[] p521_2_with_params12 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace674ccf084d69e0bc9a728548d887a162ddf53a0002ed0767534663184e99c0958451ea417729e29e02a60");
+ static byte[] p521_2_with_params13 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace674ccf084d69e0bc9a728548d887a162ddf53a0002ed0517e1040bf7d57f2c8f9c3ce69e5a05a9a292700");
+ static byte[] p521_2_with_params21 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace68a9e28bb5d49c07d5f5f59a27311619103aea50e48ddc66e0f20686c8d0c329db510facc1c014f800920");
+ static byte[] p521_2_with_params22 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace68a9e28bb5d49c07d5f5f59a27311619103aea50e48ddbc970a66c80886782713e25ece75a23dec995121");
+ static byte[] p521_2_with_params23 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace68a9e28bb5d49c07d5f5f59a27311619103aea50e48dd7c3d8a51a95f88813d04a0c681355f6e86bb44e5");
+
+ static byte[] old_p256_1_no_params = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae43dfbe605c746ba0c546c0c25ba5a00304587fbed07a3ebe44837c43b86025cc08e333b1631a8227b8d49");
+
+ static byte[] old_p256_1_with_params11 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a332ce23d272262d47a528057eb5b0b11631707b28f");
+ static byte[] old_p256_1_with_params12 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a3331e3e514868c771270632c9a9b852f2060d415da");
+ static byte[] old_p256_1_with_params13 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a3353e10e6260e937bcdd244201a0de1d3941439076");
+ static byte[] old_p256_1_with_params21 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df4912a36ccc3c49de7d3d6b1fb312aeacf2d0b30e7");
+ static byte[] old_p256_1_with_params22 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df4171c056b90a3badd0bb689403ab214546b5f6411");
+ static byte[] old_p256_1_with_params23 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df4ad1a6a561fe0582495cd14ac4494e4610ecddef1");
+
+ ECIESVectorTest()
+ {
+ }
+
+ public String getName()
+ {
+ return "ECIESVectorTest";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ KeyFactory ecFact = KeyFactory.getInstance("EC", "BC");
+
+ KeyPair keyPair = new KeyPair(ecFact.generatePublic(new X509EncodedKeySpec(p256_1_pub)), ecFact.generatePrivate(new PKCS8EncodedKeySpec(p256_1_pri)));
+
+ doTestNoParams("ECIES with P-256 None", keyPair, "ECIES", p256_1_eph, p256_1_no_params);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding1, 128), p256_1_with_params11);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding2, 128), p256_1_with_params12);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding3, 128), p256_1_with_params13);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding1, 128), p256_1_with_params21);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding2, 128), p256_1_with_params22);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding3, 128), p256_1_with_params23);
+
+ doTestNoParams("ECIES with P-256 None", keyPair, "OldECIES", p256_1_eph, old_p256_1_no_params);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding1, 128), old_p256_1_with_params11);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding2, 128), old_p256_1_with_params12);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding3, 128), old_p256_1_with_params13);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding1, 128), old_p256_1_with_params21);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding2, 128), old_p256_1_with_params22);
+ doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding3, 128), old_p256_1_with_params23);
+
+ keyPair = new KeyPair(ecFact.generatePublic(new X509EncodedKeySpec(p256_2_pub)), ecFact.generatePrivate(new PKCS8EncodedKeySpec(p256_2_pri)));
+
+ doTestNoParams("ECIES with P-256 None", keyPair, "ECIES", p256_2_eph, p256_2_no_params);
+ doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation1, encoding1, 128), p256_2_with_params11);
+ doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation1, encoding2, 128), p256_2_with_params12);
+ doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation1, encoding3, 128), p256_2_with_params13);
+ doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation2, encoding1, 128), p256_2_with_params21);
+ doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation2, encoding2, 128), p256_2_with_params22);
+ doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation2, encoding3, 128), p256_2_with_params23);
+
+ keyPair = new KeyPair(ecFact.generatePublic(new X509EncodedKeySpec(p521_1_pub)), ecFact.generatePrivate(new PKCS8EncodedKeySpec(p521_1_pri)));
+
+ doTestNoParams("ECIES with P-521 None", keyPair, "ECIES", p521_1_eph, p521_1_no_params);
+ doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation1, encoding1, 128), p521_1_with_params11);
+ doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation1, encoding2, 128), p521_1_with_params12);
+ doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation1, encoding3, 128), p521_1_with_params13);
+ doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation2, encoding1, 128), p521_1_with_params21);
+ doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation2, encoding2, 128), p521_1_with_params22);
+ doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation2, encoding3, 128), p521_1_with_params23);
+
+ keyPair = new KeyPair(ecFact.generatePublic(new X509EncodedKeySpec(p521_2_pub)), ecFact.generatePrivate(new PKCS8EncodedKeySpec(p521_2_pri)));
+
+ doTestNoParams("ECIES with default", keyPair, "ECIES", p521_2_eph, p521_2_no_params);
+ doTestWithParams("ECIES with P-521 KP2 P11", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation1, encoding1, 128), p521_2_with_params11);
+ doTestWithParams("ECIES with P-521 KP2 P12", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation1, encoding2, 128), p521_2_with_params12);
+ doTestWithParams("ECIES with P-521 KP2 P13", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation1, encoding3, 128), p521_2_with_params13);
+ doTestWithParams("ECIES with P-521 KP2 P21", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation2, encoding1, 128), p521_2_with_params21);
+ doTestWithParams("ECIES with P-521 KP2 P21", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation2, encoding2, 128), p521_2_with_params22);
+ doTestWithParams("ECIES with P-521 KP2 P21", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation2, encoding3, 128), p521_2_with_params23);
+ }
+
+ public void doTestNoParams(
+ String testname,
+ KeyPair keyPair,
+ String cipher,
+ byte[] ephPrivateValue,
+ byte[] expected)
+ throws Exception
+ {
+
+
+ byte[] out1, out2;
+
+ // Generate static key pair
+ ECPublicKey Pub = (ECPublicKey)keyPair.getPublic();
+ ECPrivateKey Priv = (ECPrivateKey)keyPair.getPrivate();
+
+ Cipher c1 = Cipher.getInstance(cipher);
+ Cipher c2 = Cipher.getInstance(cipher);
+
+ // Testing with null parameters and DHAES mode off
+ c1.init(Cipher.ENCRYPT_MODE, Pub, new FixedSecureRandom(ephPrivateValue));
+ c2.init(Cipher.DECRYPT_MODE, Priv, new FixedSecureRandom(ephPrivateValue));
+ out1 = c1.doFinal(message, 0, message.length);
+
+ if (!areEqual(out1, expected))
+ {
+ fail(testname + " test failed encrypt with null parameters, DHAES mode false.");
+ }
+
+ out2 = c2.doFinal(out1, 0, out1.length);
+ if (!areEqual(out2, message))
+ {
+ fail(testname + " test failed decrypt with null parameters, DHAES mode false.");
+ }
+ }
+
+ public void doTestWithParams(
+ String testname,
+ KeyPair keyPair,
+ String cipher,
+ byte[] ephPrivateValue,
+ IESParameterSpec p,
+ byte[] expected)
+ throws Exception
+ {
+ byte[] out1, out2;
+
+ // Generate static key pair
+ ECPublicKey Pub = (ECPublicKey)keyPair.getPublic();
+ ECPrivateKey Priv = (ECPrivateKey)keyPair.getPrivate();
+
+ Cipher c1 = Cipher.getInstance(cipher);
+ Cipher c2 = Cipher.getInstance(cipher);
+
+ // Testing with given parameters and DHAES mode off
+ c1.init(Cipher.ENCRYPT_MODE, Pub, p, new FixedSecureRandom(ephPrivateValue));
+ c2.init(Cipher.DECRYPT_MODE, Priv, p, new FixedSecureRandom(ephPrivateValue));
+ out1 = c1.doFinal(message, 0, message.length);
+
+ if (!areEqual(out1, expected))
+ {
+ fail(testname + " test failed encrypt with non-null parameters, DHAES mode false.");
+ }
+ out2 = c2.doFinal(out1, 0, out1.length);
+ if (!areEqual(out2, message))
+ {
+ fail(testname + " test failed decrypt with non-null parameters, DHAES mode false.");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ Security.addProvider(new BouncyCastleProvider());
+
+ runTest(new ECIESVectorTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/KeccakTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/KeccakTest.java
new file mode 100644
index 00000000..ed1659b3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/KeccakTest.java
@@ -0,0 +1,136 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.security.MessageDigest;
+import java.security.Security;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class KeccakTest
+ extends SimpleTest
+{
+ final static String provider = "BC";
+
+ static private byte[] nullMsg = new byte[0];
+
+ static private String[][] nullVectors =
+ {
+ { "KECCAK-224", "f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd" },
+ { "KECCAK-256", "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" },
+ { "KECCAK-384", "2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff" },
+ { "KECCAK-512", "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e" },
+ };
+
+ static private byte[] shortMsg = Hex.decode("54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67");
+
+ static private String[][] shortVectors =
+ {
+ { "KECCAK-224", "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe" },
+ { "KECCAK-256", "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15" },
+ { "KECCAK-384", "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3" },
+ { "KECCAK-512", "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609" },
+ };
+
+ public String getName()
+ {
+ return "KECCAK";
+ }
+
+ void test(String algorithm, byte[] message, String expected)
+ throws Exception
+ {
+ MessageDigest digest = MessageDigest.getInstance(algorithm, provider);
+
+ byte[] result = digest.digest(message);
+ byte[] result2 = digest.digest(message);
+
+ // test zero results valid
+ if (!MessageDigest.isEqual(result, Hex.decode(expected)))
+ {
+ fail("null result not equal for " + algorithm);
+ }
+
+ // test one digest the same message with the same instance
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail("Result object 1 not equal");
+ }
+
+ if (!MessageDigest.isEqual(result, Hex.decode(expected)))
+ {
+ fail("Result object 1 not equal");
+ }
+
+ // test two, single byte updates
+ for (int i = 0; i < message.length; i++)
+ {
+ digest.update(message[i]);
+ }
+ result2 = digest.digest();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail("Result object 2 not equal");
+ }
+
+ // test three, two half updates
+ digest.update(message, 0, message.length/2);
+ digest.update(message, message.length/2, message.length-message.length/2);
+ result2 = digest.digest();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail("Result object 3 not equal");
+ }
+
+ // test four, clone test
+ digest.update(message, 0, message.length/2);
+ MessageDigest d = (MessageDigest)digest.clone();
+ digest.update(message, message.length/2, message.length-message.length/2);
+ result2 = digest.digest();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail("Result object 4(a) not equal");
+ }
+
+ d.update(message, message.length/2, message.length-message.length/2);
+ result2 = d.digest();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail("Result object 4(b) not equal");
+ }
+
+ // test five, check reset() method
+ digest.update(message, 0, message.length/2);
+ digest.reset();
+ digest.update(message, 0, message.length/2);
+ digest.update(message, message.length/2, message.length-message.length/2);
+ result2 = digest.digest();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail("Result object 5 not equal");
+ }
+
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ for (int i = 0; i != nullVectors.length; i++)
+ {
+ test(nullVectors[i][0], nullMsg, nullVectors[i][1]);
+ test(shortVectors[i][0], shortMsg, shortVectors[i][1]);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ Security.addProvider(new BouncyCastleProvider());
+
+ runTest(new KeccakTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM4Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM4Test.java
new file mode 100644
index 00000000..428a9cfe
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM4Test.java
@@ -0,0 +1,153 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.security.Key;
+import java.security.Security;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * basic test class for SM4
+ */
+public class SM4Test
+ extends BaseBlockCipherTest
+{
+ static String[] cipherTests =
+ {
+ "128",
+ "0123456789abcdeffedcba9876543210",
+ "0123456789abcdeffedcba9876543210",
+ "681edf34d206965e86b3e94f536e4246"
+ };
+
+ public SM4Test()
+ {
+ super("SM4");
+ }
+
+ public void test(
+ int strength,
+ byte[] keyBytes,
+ byte[] input,
+ byte[] output)
+ throws Exception
+ {
+ Key key;
+ Cipher in, out;
+ CipherInputStream cIn;
+ CipherOutputStream cOut;
+ ByteArrayInputStream bIn;
+ ByteArrayOutputStream bOut;
+
+ key = new SecretKeySpec(keyBytes, "SM4");
+
+ in = Cipher.getInstance("SM4/ECB/NoPadding", "BC");
+ out = Cipher.getInstance("SM4/ECB/NoPadding", "BC");
+
+ try
+ {
+ out.init(Cipher.ENCRYPT_MODE, key);
+ }
+ catch (Exception e)
+ {
+ fail("SM4 failed initialisation - " + e.toString(), e);
+ }
+
+ try
+ {
+ in.init(Cipher.DECRYPT_MODE, key);
+ }
+ catch (Exception e)
+ {
+ fail("SM4 failed initialisation - " + e.toString(), e);
+ }
+
+ //
+ // encryption pass
+ //
+ bOut = new ByteArrayOutputStream();
+
+ cOut = new CipherOutputStream(bOut, out);
+
+ try
+ {
+ for (int i = 0; i != input.length / 2; i++)
+ {
+ cOut.write(input[i]);
+ }
+ cOut.write(input, input.length / 2, input.length - input.length / 2);
+ cOut.close();
+ }
+ catch (IOException e)
+ {
+ fail("SM4 failed encryption - " + e.toString(), e);
+ }
+
+ byte[] bytes;
+
+ bytes = bOut.toByteArray();
+
+ if (!areEqual(bytes, output))
+ {
+ fail("SM4 failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes)));
+ }
+
+ //
+ // decryption pass
+ //
+ bIn = new ByteArrayInputStream(bytes);
+
+ cIn = new CipherInputStream(bIn, in);
+
+ try
+ {
+ DataInputStream dIn = new DataInputStream(cIn);
+
+ bytes = new byte[input.length];
+
+ for (int i = 0; i != input.length / 2; i++)
+ {
+ bytes[i] = (byte)dIn.read();
+ }
+ dIn.readFully(bytes, input.length / 2, bytes.length - input.length / 2);
+ }
+ catch (Exception e)
+ {
+ fail("SM4 failed encryption - " + e.toString(), e);
+ }
+
+ if (!areEqual(bytes, input))
+ {
+ fail("SM4 failed decryption - expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(bytes)));
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ for (int i = 0; i != cipherTests.length; i += 4)
+ {
+ test(Integer.parseInt(cipherTests[i]),
+ Hex.decode(cipherTests[i + 1]),
+ Hex.decode(cipherTests[i + 2]),
+ Hex.decode(cipherTests[i + 3]));
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ Security.addProvider(new BouncyCastleProvider());
+
+ runTest(new SM4Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/nist/AllTests.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/nist/AllTests.java
new file mode 100644
index 00000000..434631a0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/nist/AllTests.java
@@ -0,0 +1,47 @@
+package org.bouncycastle.jce.provider.test.nist;
+
+import java.security.Security;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+public class AllTests
+ extends TestCase
+{
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("CertPath Tests");
+
+ suite.addTestSuite(NistCertPathTest.class);
+ suite.addTestSuite(NistCertPathReviewerTest.class);
+
+ return new BCTestSetup(suite);
+ }
+
+ static class BCTestSetup
+ extends TestSetup
+ {
+ public BCTestSetup(Test test)
+ {
+ super(test);
+ }
+
+ protected void setUp()
+ {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ protected void tearDown()
+ {
+ Security.removeProvider("BC");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/rsa3/AllTests.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/rsa3/AllTests.java
new file mode 100644
index 00000000..c29e5ecf
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/rsa3/AllTests.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.jce.provider.test.rsa3;
+
+import java.security.Security;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+public class AllTests
+ extends TestCase
+{
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("Forgery Tests");
+
+ suite.addTestSuite(RSA3CertTest.class);
+
+ return new BCTestSetup(suite);
+ }
+
+ static class BCTestSetup
+ extends TestSetup
+ {
+ public BCTestSetup(Test test)
+ {
+ super(test);
+ }
+
+ protected void setUp()
+ {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ protected void tearDown()
+ {
+ Security.removeProvider("BC");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/Primes.java b/bcprov/src/main/java/org/bouncycastle/math/Primes.java
new file mode 100644
index 00000000..dc4c017c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/Primes.java
@@ -0,0 +1,674 @@
+package org.bouncycastle.math;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * Utility methods for generating primes and testing for primality.
+ */
+public abstract class Primes
+{
+ public static final int SMALL_FACTOR_LIMIT = 211;
+
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+ private static final BigInteger THREE = BigInteger.valueOf(3);
+
+ /**
+ * Used to return the output from the
+ * {@linkplain Primes#enhancedMRProbablePrimeTest(BigInteger, SecureRandom, int) Enhanced
+ * Miller-Rabin Probabilistic Primality Test}
+ */
+ public static class MROutput
+ {
+ private static MROutput probablyPrime()
+ {
+ return new MROutput(false, null);
+ }
+
+ private static MROutput provablyCompositeWithFactor(BigInteger factor)
+ {
+ return new MROutput(true, factor);
+ }
+
+ private static MROutput provablyCompositeNotPrimePower()
+ {
+ return new MROutput(true, null);
+ }
+
+ private boolean provablyComposite;
+ private BigInteger factor;
+
+ private MROutput(boolean provablyComposite, BigInteger factor)
+ {
+ this.provablyComposite = provablyComposite;
+ this.factor = factor;
+ }
+
+ public BigInteger getFactor()
+ {
+ return factor;
+ }
+
+ public boolean isProvablyComposite()
+ {
+ return provablyComposite;
+ }
+
+ public boolean isNotPrimePower()
+ {
+ return provablyComposite && factor == null;
+ }
+ }
+
+ /**
+ * Used to return the output from the
+ * {@linkplain Primes#generateSTRandomPrime(Digest, int, byte[]) Shawe-Taylor Random_Prime
+ * Routine}
+ */
+ public static class STOutput
+ {
+ private BigInteger prime;
+ private byte[] primeSeed;
+ private int primeGenCounter;
+
+ private STOutput(BigInteger prime, byte[] primeSeed, int primeGenCounter)
+ {
+ this.prime = prime;
+ this.primeSeed = primeSeed;
+ this.primeGenCounter = primeGenCounter;
+ }
+
+ public BigInteger getPrime()
+ {
+ return prime;
+ }
+
+ public byte[] getPrimeSeed()
+ {
+ return primeSeed;
+ }
+
+ public int getPrimeGenCounter()
+ {
+ return primeGenCounter;
+ }
+ }
+
+ /**
+ * FIPS 186-4 C.6 Shawe-Taylor Random_Prime Routine
+ *
+ * Construct a provable prime number using a hash function.
+ *
+ * @param hash
+ * the {@link Digest} instance to use (as "Hash()"). Cannot be null.
+ * @param length
+ * the length (in bits) of the prime to be generated. Must be at least 2.
+ * @param inputSeed
+ * the seed to be used for the generation of the requested prime. Cannot be null or
+ * empty.
+ * @return an {@link STOutput} instance containing the requested prime.
+ */
+ public static STOutput generateSTRandomPrime(Digest hash, int length, byte[] inputSeed)
+ {
+ if (hash == null)
+ {
+ throw new IllegalArgumentException("'hash' cannot be null");
+ }
+ if (length < 2)
+ {
+ throw new IllegalArgumentException("'length' must be >= 2");
+ }
+ if (inputSeed == null || inputSeed.length == 0)
+ {
+ throw new IllegalArgumentException("'inputSeed' cannot be null or empty");
+ }
+
+ return implSTRandomPrime(hash, length, Arrays.clone(inputSeed));
+ }
+
+ /**
+ * FIPS 186-4 C.3.2 Enhanced Miller-Rabin Probabilistic Primality Test
+ *
+ * Run several iterations of the Miller-Rabin algorithm with randomly-chosen bases. This is an
+ * alternative to {@link #isMRProbablePrime(BigInteger, SecureRandom, int)} that provides more
+ * information about a composite candidate, which may be useful when generating or validating
+ * RSA moduli.
+ *
+ * @param candidate
+ * the {@link BigInteger} instance to test for primality.
+ * @param random
+ * the source of randomness to use to choose bases.
+ * @param iterations
+ * the number of randomly-chosen bases to perform the test for.
+ * @return an {@link MROutput} instance that can be further queried for details.
+ */
+ public static MROutput enhancedMRProbablePrimeTest(BigInteger candidate, SecureRandom random, int iterations)
+ {
+ checkCandidate(candidate, "candidate");
+
+ if (random == null)
+ {
+ throw new IllegalArgumentException("'random' cannot be null");
+ }
+ if (iterations < 1)
+ {
+ throw new IllegalArgumentException("'iterations' must be > 0");
+ }
+
+ if (candidate.bitLength() == 2)
+ {
+ return MROutput.probablyPrime();
+ }
+ if (!candidate.testBit(0))
+ {
+ return MROutput.provablyCompositeWithFactor(TWO);
+ }
+
+ BigInteger w = candidate;
+ BigInteger wSubOne = candidate.subtract(ONE);
+ BigInteger wSubTwo = candidate.subtract(TWO);
+
+ int a = wSubOne.getLowestSetBit();
+ BigInteger m = wSubOne.shiftRight(a);
+
+ for (int i = 0; i < iterations; ++i)
+ {
+ BigInteger b = BigIntegers.createRandomInRange(TWO, wSubTwo, random);
+ BigInteger g = b.gcd(w);
+
+ if (g.compareTo(ONE) > 0)
+ {
+ return MROutput.provablyCompositeWithFactor(g);
+ }
+
+ BigInteger z = b.modPow(m, w);
+
+ if (z.equals(ONE) || z.equals(wSubOne))
+ {
+ continue;
+ }
+
+ boolean primeToBase = false;
+
+ BigInteger x = z;
+ for (int j = 1; j < a; ++j)
+ {
+ z = z.modPow(TWO, w);
+
+ if (z.equals(wSubOne))
+ {
+ primeToBase = true;
+ break;
+ }
+
+ if (z.equals(ONE))
+ {
+ break;
+ }
+
+ x = z;
+ }
+
+ if (!primeToBase)
+ {
+ if (!z.equals(ONE))
+ {
+ x = z;
+ z = z.modPow(TWO, w);
+
+ if (!z.equals(ONE))
+ {
+ x = z;
+ }
+ }
+
+ g = x.subtract(ONE).gcd(w);
+
+ if (g.compareTo(ONE) > 0)
+ {
+ return MROutput.provablyCompositeWithFactor(g);
+ }
+
+ return MROutput.provablyCompositeNotPrimePower();
+ }
+ }
+
+ return MROutput.probablyPrime();
+ }
+
+ /**
+ * A fast check for small divisors, up to some implementation-specific limit.
+ *
+ * @param candidate
+ * the {@link BigInteger} instance to test for division by small factors.
+ *
+ * @return <code>true</code> if the candidate is found to have any small factors,
+ * <code>false</code> otherwise.
+ */
+ public static boolean hasAnySmallFactors(BigInteger candidate)
+ {
+ checkCandidate(candidate, "candidate");
+
+ return implHasAnySmallFactors(candidate);
+ }
+
+ /**
+ * FIPS 186-4 C.3.1 Miller-Rabin Probabilistic Primality Test
+ *
+ * Run several iterations of the Miller-Rabin algorithm with randomly-chosen bases.
+ *
+ * @param candidate
+ * the {@link BigInteger} instance to test for primality.
+ * @param random
+ * the source of randomness to use to choose bases.
+ * @param iterations
+ * the number of randomly-chosen bases to perform the test for.
+ * @return <code>false</code> if any witness to compositeness is found amongst the chosen bases
+ * (so <code>candidate</code> is definitely NOT prime), or else <code>true</code>
+ * (indicating primality with some probability dependent on the number of iterations
+ * that were performed).
+ */
+ public static boolean isMRProbablePrime(BigInteger candidate, SecureRandom random, int iterations)
+ {
+ checkCandidate(candidate, "candidate");
+
+ if (random == null)
+ {
+ throw new IllegalArgumentException("'random' cannot be null");
+ }
+ if (iterations < 1)
+ {
+ throw new IllegalArgumentException("'iterations' must be > 0");
+ }
+
+ if (candidate.bitLength() == 2)
+ {
+ return true;
+ }
+ if (!candidate.testBit(0))
+ {
+ return false;
+ }
+
+ BigInteger w = candidate;
+ BigInteger wSubOne = candidate.subtract(ONE);
+ BigInteger wSubTwo = candidate.subtract(TWO);
+
+ int a = wSubOne.getLowestSetBit();
+ BigInteger m = wSubOne.shiftRight(a);
+
+ for (int i = 0; i < iterations; ++i)
+ {
+ BigInteger b = BigIntegers.createRandomInRange(TWO, wSubTwo, random);
+
+ if (!implMRProbablePrimeToBase(w, wSubOne, m, a, b))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * FIPS 186-4 C.3.1 Miller-Rabin Probabilistic Primality Test (to a fixed base).
+ *
+ * Run a single iteration of the Miller-Rabin algorithm against the specified base.
+ *
+ * @param candidate
+ * the {@link BigInteger} instance to test for primality.
+ * @param base
+ * the base value to use for this iteration.
+ * @return <code>false</code> if the specified base is a witness to compositeness (so
+ * <code>candidate</code> is definitely NOT prime), or else <code>true</code>.
+ */
+ public static boolean isMRProbablePrimeToBase(BigInteger candidate, BigInteger base)
+ {
+ checkCandidate(candidate, "candidate");
+ checkCandidate(base, "base");
+
+ if (base.compareTo(candidate.subtract(ONE)) >= 0)
+ {
+ throw new IllegalArgumentException("'base' must be < ('candidate' - 1)");
+ }
+
+ if (candidate.bitLength() == 2)
+ {
+ return true;
+ }
+
+ BigInteger w = candidate;
+ BigInteger wSubOne = candidate.subtract(ONE);
+
+ int a = wSubOne.getLowestSetBit();
+ BigInteger m = wSubOne.shiftRight(a);
+
+ return implMRProbablePrimeToBase(w, wSubOne, m, a, base);
+ }
+
+ private static void checkCandidate(BigInteger n, String name)
+ {
+ if (n == null || n.signum() < 1 || n.bitLength() < 2)
+ {
+ throw new IllegalArgumentException("'" + name + "' must be non-null and >= 2");
+ }
+ }
+
+ private static boolean implHasAnySmallFactors(BigInteger x)
+ {
+ /*
+ * Bundle trial divisors into ~32-bit moduli then use fast tests on the ~32-bit remainders.
+ */
+ int m = 2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23;
+ int r = x.mod(BigInteger.valueOf(m)).intValue();
+ if ((r % 2) == 0 || (r % 3) == 0 || (r % 5) == 0 || (r % 7) == 0 || (r % 11) == 0 || (r % 13) == 0
+ || (r % 17) == 0 || (r % 19) == 0 || (r % 23) == 0)
+ {
+ return true;
+ }
+
+ m = 29 * 31 * 37 * 41 * 43;
+ r = x.mod(BigInteger.valueOf(m)).intValue();
+ if ((r % 29) == 0 || (r % 31) == 0 || (r % 37) == 0 || (r % 41) == 0 || (r % 43) == 0)
+ {
+ return true;
+ }
+
+ m = 47 * 53 * 59 * 61 * 67;
+ r = x.mod(BigInteger.valueOf(m)).intValue();
+ if ((r % 47) == 0 || (r % 53) == 0 || (r % 59) == 0 || (r % 61) == 0 || (r % 67) == 0)
+ {
+ return true;
+ }
+
+ m = 71 * 73 * 79 * 83;
+ r = x.mod(BigInteger.valueOf(m)).intValue();
+ if ((r % 71) == 0 || (r % 73) == 0 || (r % 79) == 0 || (r % 83) == 0)
+ {
+ return true;
+ }
+
+ m = 89 * 97 * 101 * 103;
+ r = x.mod(BigInteger.valueOf(m)).intValue();
+ if ((r % 89) == 0 || (r % 97) == 0 || (r % 101) == 0 || (r % 103) == 0)
+ {
+ return true;
+ }
+
+ m = 107 * 109 * 113 * 127;
+ r = x.mod(BigInteger.valueOf(m)).intValue();
+ if ((r % 107) == 0 || (r % 109) == 0 || (r % 113) == 0 || (r % 127) == 0)
+ {
+ return true;
+ }
+
+ m = 131 * 137 * 139 * 149;
+ r = x.mod(BigInteger.valueOf(m)).intValue();
+ if ((r % 131) == 0 || (r % 137) == 0 || (r % 139) == 0 || (r % 149) == 0)
+ {
+ return true;
+ }
+
+ m = 151 * 157 * 163 * 167;
+ r = x.mod(BigInteger.valueOf(m)).intValue();
+ if ((r % 151) == 0 || (r % 157) == 0 || (r % 163) == 0 || (r % 167) == 0)
+ {
+ return true;
+ }
+
+ m = 173 * 179 * 181 * 191;
+ r = x.mod(BigInteger.valueOf(m)).intValue();
+ if ((r % 173) == 0 || (r % 179) == 0 || (r % 181) == 0 || (r % 191) == 0)
+ {
+ return true;
+ }
+
+ m = 193 * 197 * 199 * 211;
+ r = x.mod(BigInteger.valueOf(m)).intValue();
+ if ((r % 193) == 0 || (r % 197) == 0 || (r % 199) == 0 || (r % 211) == 0)
+ {
+ return true;
+ }
+
+ /*
+ * NOTE: Unit tests depend on SMALL_FACTOR_LIMIT matching the
+ * highest small factor tested here.
+ */
+ return false;
+ }
+
+ private static boolean implMRProbablePrimeToBase(BigInteger w, BigInteger wSubOne, BigInteger m, int a, BigInteger b)
+ {
+ BigInteger z = b.modPow(m, w);
+
+ if (z.equals(ONE) || z.equals(wSubOne))
+ {
+ return true;
+ }
+
+ boolean result = false;
+
+ for (int j = 1; j < a; ++j)
+ {
+ z = z.modPow(TWO, w);
+
+ if (z.equals(wSubOne))
+ {
+ result = true;
+ break;
+ }
+
+ if (z.equals(ONE))
+ {
+ return false;
+ }
+ }
+
+ return result;
+ }
+
+ private static STOutput implSTRandomPrime(Digest d, int length, byte[] primeSeed)
+ {
+ int dLen = d.getDigestSize();
+
+ if (length < 33)
+ {
+ int primeGenCounter = 0;
+
+ byte[] c0 = new byte[dLen];
+ byte[] c1 = new byte[dLen];
+
+ for (;;)
+ {
+ hash(d, primeSeed, c0, 0);
+ inc(primeSeed, 1);
+
+ hash(d, primeSeed, c1, 0);
+ inc(primeSeed, 1);
+
+ int c = extract32(c0) ^ extract32(c1);
+ c &= (-1 >>> (32 - length));
+ c |= (1 << (length - 1)) | 1;
+
+ ++primeGenCounter;
+
+ long c64 = c & 0xFFFFFFFFL;
+ if (isPrime32(c64))
+ {
+ return new STOutput(BigInteger.valueOf(c64), primeSeed, primeGenCounter);
+ }
+
+ if (primeGenCounter > (4 * length))
+ {
+ throw new IllegalStateException("Too many iterations in Shawe-Taylor Random_Prime Routine");
+ }
+ }
+ }
+
+ STOutput rec = implSTRandomPrime(d, (length + 3) / 2, primeSeed);
+
+ BigInteger c0 = rec.getPrime();
+ primeSeed = rec.getPrimeSeed();
+ int primeGenCounter = rec.getPrimeGenCounter();
+
+ int outlen = 8 * dLen;
+ int iterations = (length - 1) / outlen;
+
+ int oldCounter = primeGenCounter;
+
+ BigInteger x = hashGen(d, primeSeed, iterations + 1);
+ x = x.mod(ONE.shiftLeft(length - 1)).setBit(length - 1);
+
+ BigInteger c0x2 = c0.shiftLeft(1);
+ BigInteger tx2 = x.subtract(ONE).divide(c0x2).add(ONE).shiftLeft(1);
+ int dt = 0;
+
+ BigInteger c = tx2.multiply(c0).add(ONE);
+
+ /*
+ * TODO Since the candidate primes are generated by constant steps ('c0x2'), sieving could
+ * be used here in place of the 'hasAnySmallFactors' approach.
+ */
+ for (;;)
+ {
+ if (c.bitLength() > length)
+ {
+ tx2 = ONE.shiftLeft(length - 1).subtract(ONE).divide(c0x2).add(ONE).shiftLeft(1);
+ c = tx2.multiply(c0).add(ONE);
+ }
+
+ ++primeGenCounter;
+
+ /*
+ * This is an optimization of the original algorithm, using trial division to screen out
+ * many non-primes quickly.
+ *
+ * NOTE: 'primeSeed' is still incremented as if we performed the full check!
+ */
+ if (!implHasAnySmallFactors(c))
+ {
+ BigInteger a = hashGen(d, primeSeed, iterations + 1);
+ a = a.mod(c.subtract(THREE)).add(TWO);
+
+ tx2 = tx2.add(BigInteger.valueOf(dt));
+ dt = 0;
+
+ BigInteger z = a.modPow(tx2, c);
+
+ if (c.gcd(z.subtract(ONE)).equals(ONE) && z.modPow(c0, c).equals(ONE))
+ {
+ return new STOutput(c, primeSeed, primeGenCounter);
+ }
+ }
+ else
+ {
+ inc(primeSeed, iterations + 1);
+ }
+
+ if (primeGenCounter >= ((4 * length) + oldCounter))
+ {
+ throw new IllegalStateException("Too many iterations in Shawe-Taylor Random_Prime Routine");
+ }
+
+ dt += 2;
+ c = c.add(c0x2);
+ }
+ }
+
+ private static int extract32(byte[] bs)
+ {
+ int result = 0;
+
+ int count = Math.min(4, bs.length);
+ for (int i = 0; i < count; ++i)
+ {
+ int b = bs[bs.length - (i + 1)] & 0xFF;
+ result |= (b << (8 * i));
+ }
+
+ return result;
+ }
+
+ private static void hash(Digest d, byte[] input, byte[] output, int outPos)
+ {
+ d.update(input, 0, input.length);
+ d.doFinal(output, outPos);
+ }
+
+ private static BigInteger hashGen(Digest d, byte[] seed, int count)
+ {
+ int dLen = d.getDigestSize();
+ int pos = count * dLen;
+ byte[] buf = new byte[pos];
+ for (int i = 0; i < count; ++i)
+ {
+ pos -= dLen;
+ hash(d, seed, buf, pos);
+ inc(seed, 1);
+ }
+ return new BigInteger(1, buf);
+ }
+
+ private static void inc(byte[] seed, int c)
+ {
+ int pos = seed.length;
+ while (c > 0 && --pos >= 0)
+ {
+ c += (seed[pos] & 0xFF);
+ seed[pos] = (byte)c;
+ c >>>= 8;
+ }
+ }
+
+ private static boolean isPrime32(long x)
+ {
+ if (x >>> 32 != 0L)
+ {
+ throw new IllegalArgumentException("Size limit exceeded");
+ }
+
+ /*
+ * Use wheel factorization with 2, 3, 5 to select trial divisors.
+ */
+
+ if (x <= 5L)
+ {
+ return x == 2L || x == 3L || x == 5L;
+ }
+
+ if ((x & 1L) == 0L || (x % 3L) == 0L || (x % 5L) == 0L)
+ {
+ return false;
+ }
+
+ long[] ds = new long[]{ 1L, 7L, 11L, 13L, 17L, 19L, 23L, 29L };
+ long base = 0L;
+ for (int pos = 1;; pos = 0)
+ {
+ /*
+ * Trial division by wheel-selected divisors
+ */
+ while (pos < ds.length)
+ {
+ long d = base + ds[pos];
+ if (x % d == 0L)
+ {
+ return x < 30L;
+ }
+ ++pos;
+ }
+
+ base += 30L;
+
+ if (base * base >= x)
+ {
+ return true;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Curve.java
new file mode 100644
index 00000000..8d555f63
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Curve.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecP128R1Curve extends ECCurve.AbstractFp
+{
+ public static final BigInteger q = new BigInteger(1,
+ Hex.decode("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF"));
+
+ private static final int SecP128R1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+ protected SecP128R1Point infinity;
+
+ public SecP128R1Curve()
+ {
+ super(q);
+
+ this.infinity = new SecP128R1Point(this, null, null);
+
+ this.a = fromBigInteger(new BigInteger(1,
+ Hex.decode("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC")));
+ this.b = fromBigInteger(new BigInteger(1,
+ Hex.decode("E87579C11079F43DD824993C2CEE5ED3")));
+ this.order = new BigInteger(1, Hex.decode("FFFFFFFE0000000075A30D1B9038A115"));
+ this.cofactor = BigInteger.valueOf(1);
+
+ this.coord = SecP128R1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecP128R1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_JACOBIAN:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public int getFieldSize()
+ {
+ return q.bitLength();
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecP128R1FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecP128R1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecP128R1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Field.java
new file mode 100644
index 00000000..171b4927
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Field.java
@@ -0,0 +1,220 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat128;
+import org.bouncycastle.math.raw.Nat256;
+
+public class SecP128R1Field
+{
+ private static final long M = 0xFFFFFFFFL;
+
+ // 2^128 - 2^97 - 1
+ static final int[] P = new int[] { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFD };
+ static final int[] PExt = new int[] { 0x00000001, 0x00000000, 0x00000000, 0x00000004, 0xFFFFFFFE,
+ 0xFFFFFFFF, 0x00000003, 0xFFFFFFFC };
+ private static final int[] PExtInv = new int[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFB,
+ 0x00000001, 0x00000000, 0xFFFFFFFC, 0x00000003 };
+ private static final int P3 = 0xFFFFFFFD;
+ private static final int PExt7 = 0xFFFFFFFC;
+
+ public static void add(int[] x, int[] y, int[] z)
+ {
+ int c = Nat128.add(x, y, z);
+ if (c != 0 || (z[3] == P3 && Nat128.gte(z, P)))
+ {
+ addPInvTo(z);
+ }
+ }
+
+ public static void addExt(int[] xx, int[] yy, int[] zz)
+ {
+ int c = Nat256.add(xx, yy, zz);
+ if (c != 0 || (zz[7] == PExt7 && Nat256.gte(zz, PExt)))
+ {
+ Nat.addTo(PExtInv.length, PExtInv, zz);
+ }
+ }
+
+ public static void addOne(int[] x, int[] z)
+ {
+ int c = Nat.inc(4, x, z);
+ if (c != 0 || (z[3] == P3 && Nat128.gte(z, P)))
+ {
+ addPInvTo(z);
+ }
+ }
+
+ public static int[] fromBigInteger(BigInteger x)
+ {
+ int[] z = Nat128.fromBigInteger(x);
+ if (z[3] == P3 && Nat128.gte(z, P))
+ {
+ Nat128.subFrom(P, z);
+ }
+ return z;
+ }
+
+ public static void half(int[] x, int[] z)
+ {
+ if ((x[0] & 1) == 0)
+ {
+ Nat.shiftDownBit(4, x, 0, z);
+ }
+ else
+ {
+ int c = Nat128.add(x, P, z);
+ Nat.shiftDownBit(4, z, c);
+ }
+ }
+
+ public static void multiply(int[] x, int[] y, int[] z)
+ {
+ int[] tt = Nat128.createExt();
+ Nat128.mul(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
+ {
+ int c = Nat128.mulAddTo(x, y, zz);
+ if (c != 0 || (zz[7] == PExt7 && Nat256.gte(zz, PExt)))
+ {
+ Nat.addTo(PExtInv.length, PExtInv, zz);
+ }
+ }
+
+ public static void negate(int[] x, int[] z)
+ {
+ if (Nat128.isZero(x))
+ {
+ Nat128.zero(z);
+ }
+ else
+ {
+ Nat128.sub(P, x, z);
+ }
+ }
+
+ public static void reduce(int[] xx, int[] z)
+ {
+ long x0 = xx[0] & M, x1 = xx[1] & M, x2 = xx[2] & M, x3 = xx[3] & M;
+ long x4 = xx[4] & M, x5 = xx[5] & M, x6 = xx[6] & M, x7 = xx[7] & M;
+
+ x3 += x7; x6 += (x7 << 1);
+ x2 += x6; x5 += (x6 << 1);
+ x1 += x5; x4 += (x5 << 1);
+ x0 += x4; x3 += (x4 << 1);
+
+ z[0] = (int)x0; x1 += (x0 >>> 32);
+ z[1] = (int)x1; x2 += (x1 >>> 32);
+ z[2] = (int)x2; x3 += (x2 >>> 32);
+ z[3] = (int)x3;
+
+ reduce32((int)(x3 >>> 32), z);
+ }
+
+ public static void reduce32(int x, int[] z)
+ {
+ while (x != 0)
+ {
+ long c, x4 = x & M;
+
+ c = (z[0] & M) + x4;
+ z[0] = (int)c; c >>= 32;
+ if (c != 0)
+ {
+ c += (z[1] & M);
+ z[1] = (int)c; c >>= 32;
+ c += (z[2] & M);
+ z[2] = (int)c; c >>= 32;
+ }
+ c += (z[3] & M) + (x4 << 1);
+ z[3] = (int)c; c >>= 32;
+
+// assert c >= 0 && c <= 2;
+
+ x = (int)c;
+ }
+ }
+
+ public static void square(int[] x, int[] z)
+ {
+ int[] tt = Nat128.createExt();
+ Nat128.square(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareN(int[] x, int n, int[] z)
+ {
+// assert n > 0;
+
+ int[] tt = Nat128.createExt();
+ Nat128.square(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ Nat128.square(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static void subtract(int[] x, int[] y, int[] z)
+ {
+ int c = Nat128.sub(x, y, z);
+ if (c != 0)
+ {
+ subPInvFrom(z);
+ }
+ }
+
+ public static void subtractExt(int[] xx, int[] yy, int[] zz)
+ {
+ int c = Nat.sub(10, xx, yy, zz);
+ if (c != 0)
+ {
+ Nat.subFrom(PExtInv.length, PExtInv, zz);
+ }
+ }
+
+ public static void twice(int[] x, int[] z)
+ {
+ int c = Nat.shiftUpBit(4, x, 0, z);
+ if (c != 0 || (z[3] == P3 && Nat128.gte(z, P)))
+ {
+ addPInvTo(z);
+ }
+ }
+
+ private static void addPInvTo(int[] z)
+ {
+ long c = (z[0] & M) + 1;
+ z[0] = (int)c; c >>= 32;
+ if (c != 0)
+ {
+ c += (z[1] & M);
+ z[1] = (int)c; c >>= 32;
+ c += (z[2] & M);
+ z[2] = (int)c; c >>= 32;
+ }
+ c += (z[3] & M) + 2;
+ z[3] = (int)c;
+ }
+
+ private static void subPInvFrom(int[] z)
+ {
+ long c = (z[0] & M) - 1;
+ z[0] = (int)c; c >>= 32;
+ if (c != 0)
+ {
+ c += (z[1] & M);
+ z[1] = (int)c; c >>= 32;
+ c += (z[2] & M);
+ z[2] = (int)c; c >>= 32;
+ }
+ c += (z[3] & M) - 2;
+ z[3] = (int)c;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1FieldElement.java
new file mode 100644
index 00000000..91d999d6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1FieldElement.java
@@ -0,0 +1,199 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Mod;
+import org.bouncycastle.math.raw.Nat128;
+import org.bouncycastle.util.Arrays;
+
+public class SecP128R1FieldElement extends ECFieldElement
+{
+ public static final BigInteger Q = SecP128R1Curve.q;
+
+ protected int[] x;
+
+ public SecP128R1FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0)
+ {
+ throw new IllegalArgumentException("x value invalid for SecP128R1FieldElement");
+ }
+
+ this.x = SecP128R1Field.fromBigInteger(x);
+ }
+
+ public SecP128R1FieldElement()
+ {
+ this.x = Nat128.create();
+ }
+
+ protected SecP128R1FieldElement(int[] x)
+ {
+ this.x = x;
+ }
+
+ public boolean isZero()
+ {
+ return Nat128.isZero(x);
+ }
+
+ public boolean isOne()
+ {
+ return Nat128.isOne(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return Nat128.getBit(x, 0) == 1;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat128.toBigInteger(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecP128R1Field";
+ }
+
+ public int getFieldSize()
+ {
+ return Q.bitLength();
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ int[] z = Nat128.create();
+ SecP128R1Field.add(x, ((SecP128R1FieldElement)b).x, z);
+ return new SecP128R1FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ int[] z = Nat128.create();
+ SecP128R1Field.addOne(x, z);
+ return new SecP128R1FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ int[] z = Nat128.create();
+ SecP128R1Field.subtract(x, ((SecP128R1FieldElement)b).x, z);
+ return new SecP128R1FieldElement(z);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ int[] z = Nat128.create();
+ SecP128R1Field.multiply(x, ((SecP128R1FieldElement)b).x, z);
+ return new SecP128R1FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+// return multiply(b.invert());
+ int[] z = Nat128.create();
+ Mod.invert(SecP128R1Field.P, ((SecP128R1FieldElement)b).x, z);
+ SecP128R1Field.multiply(z, x, z);
+ return new SecP128R1FieldElement(z);
+ }
+
+ public ECFieldElement negate()
+ {
+ int[] z = Nat128.create();
+ SecP128R1Field.negate(x, z);
+ return new SecP128R1FieldElement(z);
+ }
+
+ public ECFieldElement square()
+ {
+ int[] z = Nat128.create();
+ SecP128R1Field.square(x, z);
+ return new SecP128R1FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+// return new SecP128R1FieldElement(toBigInteger().modInverse(Q));
+ int[] z = Nat128.create();
+ Mod.invert(SecP128R1Field.P, x, z);
+ return new SecP128R1FieldElement(z);
+ }
+
+ // D.1.4 91
+ /**
+ * return a sqrt root - the routine verifies that the calculation returns the right value - if
+ * none exists it returns null.
+ */
+ public ECFieldElement sqrt()
+ {
+ /*
+ * Raise this element to the exponent 2^126 - 2^95
+ *
+ * Breaking up the exponent's binary representation into "repunits", we get:
+ * { 31 1s } { 95 0s }
+ *
+ * Therefore we need an addition chain containing 31 (the length of the repunit) We use:
+ * 1, 2, 4, 8, 10, 20, 30, [31]
+ */
+
+ int[] x1 = this.x;
+ if (Nat128.isZero(x1) || Nat128.isOne(x1))
+ {
+ return this;
+ }
+
+ int[] x2 = Nat128.create();
+ SecP128R1Field.square(x1, x2);
+ SecP128R1Field.multiply(x2, x1, x2);
+ int[] x4 = Nat128.create();
+ SecP128R1Field.squareN(x2, 2, x4);
+ SecP128R1Field.multiply(x4, x2, x4);
+ int[] x8 = Nat128.create();
+ SecP128R1Field.squareN(x4, 4, x8);
+ SecP128R1Field.multiply(x8, x4, x8);
+ int[] x10 = x4;
+ SecP128R1Field.squareN(x8, 2, x10);
+ SecP128R1Field.multiply(x10, x2, x10);
+ int[] x20 = x2;
+ SecP128R1Field.squareN(x10, 10, x20);
+ SecP128R1Field.multiply(x20, x10, x20);
+ int[] x30 = x8;
+ SecP128R1Field.squareN(x20, 10, x30);
+ SecP128R1Field.multiply(x30, x10, x30);
+ int[] x31 = x10;
+ SecP128R1Field.square(x30, x31);
+ SecP128R1Field.multiply(x31, x1, x31);
+
+ int[] t1 = x31;
+ SecP128R1Field.squareN(t1, 95, t1);
+
+ int[] t2 = x30;
+ SecP128R1Field.square(t1, t2);
+
+ return Nat128.eq(x1, t2) ? new SecP128R1FieldElement(t1) : null;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecP128R1FieldElement))
+ {
+ return false;
+ }
+
+ SecP128R1FieldElement o = (SecP128R1FieldElement)other;
+ return Nat128.eq(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return Q.hashCode() ^ Arrays.hashCode(x, 0, 4);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Point.java
new file mode 100644
index 00000000..b7934da7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Point.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat128;
+
+public class SecP128R1Point extends ECPoint.AbstractFp
+{
+ /**
+ * Create a point which encodes with point compression.
+ *
+ * @param curve
+ * the curve to use
+ * @param x
+ * affine x co-ordinate
+ * @param y
+ * affine y co-ordinate
+ *
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecP128R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * Create a point that encodes with or without point compresion.
+ *
+ * @param curve
+ * the curve to use
+ * @param x
+ * affine x co-ordinate
+ * @param y
+ * affine y co-ordinate
+ * @param withCompression
+ * if true encode with point compression
+ *
+ * @deprecated per-point compression property will be removed, refer
+ * {@link #getEncoded(boolean)}
+ */
+ public SecP128R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecP128R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecP128R1Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+ if (this == b)
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ SecP128R1FieldElement X1 = (SecP128R1FieldElement)this.x, Y1 = (SecP128R1FieldElement)this.y;
+ SecP128R1FieldElement X2 = (SecP128R1FieldElement)b.getXCoord(), Y2 = (SecP128R1FieldElement)b.getYCoord();
+
+ SecP128R1FieldElement Z1 = (SecP128R1FieldElement)this.zs[0];
+ SecP128R1FieldElement Z2 = (SecP128R1FieldElement)b.getZCoord(0);
+
+ int c;
+ int[] tt1 = Nat128.createExt();
+ int[] t2 = Nat128.create();
+ int[] t3 = Nat128.create();
+ int[] t4 = Nat128.create();
+
+ boolean Z1IsOne = Z1.isOne();
+ int[] U2, S2;
+ if (Z1IsOne)
+ {
+ U2 = X2.x;
+ S2 = Y2.x;
+ }
+ else
+ {
+ S2 = t3;
+ SecP128R1Field.square(Z1.x, S2);
+
+ U2 = t2;
+ SecP128R1Field.multiply(S2, X2.x, U2);
+
+ SecP128R1Field.multiply(S2, Z1.x, S2);
+ SecP128R1Field.multiply(S2, Y2.x, S2);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ int[] U1, S1;
+ if (Z2IsOne)
+ {
+ U1 = X1.x;
+ S1 = Y1.x;
+ }
+ else
+ {
+ S1 = t4;
+ SecP128R1Field.square(Z2.x, S1);
+
+ U1 = tt1;
+ SecP128R1Field.multiply(S1, X1.x, U1);
+
+ SecP128R1Field.multiply(S1, Z2.x, S1);
+ SecP128R1Field.multiply(S1, Y1.x, S1);
+ }
+
+ int[] H = Nat128.create();
+ SecP128R1Field.subtract(U1, U2, H);
+
+ int[] R = t2;
+ SecP128R1Field.subtract(S1, S2, R);
+
+ // Check if b == this or b == -this
+ if (Nat128.isZero(H))
+ {
+ if (Nat128.isZero(R))
+ {
+ // this == b, i.e. this must be doubled
+ return this.twice();
+ }
+
+ // this == -b, i.e. the result is the point at infinity
+ return curve.getInfinity();
+ }
+
+ int[] HSquared = t3;
+ SecP128R1Field.square(H, HSquared);
+
+ int[] G = Nat128.create();
+ SecP128R1Field.multiply(HSquared, H, G);
+
+ int[] V = t3;
+ SecP128R1Field.multiply(HSquared, U1, V);
+
+ SecP128R1Field.negate(G, G);
+ Nat128.mul(S1, G, tt1);
+
+ c = Nat128.addBothTo(V, V, G);
+ SecP128R1Field.reduce32(c, G);
+
+ SecP128R1FieldElement X3 = new SecP128R1FieldElement(t4);
+ SecP128R1Field.square(R, X3.x);
+ SecP128R1Field.subtract(X3.x, G, X3.x);
+
+ SecP128R1FieldElement Y3 = new SecP128R1FieldElement(G);
+ SecP128R1Field.subtract(V, X3.x, Y3.x);
+ SecP128R1Field.multiplyAddToExt(Y3.x, R, tt1);
+ SecP128R1Field.reduce(tt1, Y3.x);
+
+ SecP128R1FieldElement Z3 = new SecP128R1FieldElement(H);
+ if (!Z1IsOne)
+ {
+ SecP128R1Field.multiply(Z3.x, Z1.x, Z3.x);
+ }
+ if (!Z2IsOne)
+ {
+ SecP128R1Field.multiply(Z3.x, Z2.x, Z3.x);
+ }
+
+ ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
+
+ return new SecP128R1Point(curve, X3, Y3, zs, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ SecP128R1FieldElement Y1 = (SecP128R1FieldElement)this.y;
+ if (Y1.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ SecP128R1FieldElement X1 = (SecP128R1FieldElement)this.x, Z1 = (SecP128R1FieldElement)this.zs[0];
+
+ int c;
+ int[] t1 = Nat128.create();
+ int[] t2 = Nat128.create();
+
+ int[] Y1Squared = Nat128.create();
+ SecP128R1Field.square(Y1.x, Y1Squared);
+
+ int[] T = Nat128.create();
+ SecP128R1Field.square(Y1Squared, T);
+
+ boolean Z1IsOne = Z1.isOne();
+
+ int[] Z1Squared = Z1.x;
+ if (!Z1IsOne)
+ {
+ Z1Squared = t2;
+ SecP128R1Field.square(Z1.x, Z1Squared);
+ }
+
+ SecP128R1Field.subtract(X1.x, Z1Squared, t1);
+
+ int[] M = t2;
+ SecP128R1Field.add(X1.x, Z1Squared, M);
+ SecP128R1Field.multiply(M, t1, M);
+ c = Nat128.addBothTo(M, M, M);
+ SecP128R1Field.reduce32(c, M);
+
+ int[] S = Y1Squared;
+ SecP128R1Field.multiply(Y1Squared, X1.x, S);
+ c = Nat.shiftUpBits(4, S, 2, 0);
+ SecP128R1Field.reduce32(c, S);
+
+ c = Nat.shiftUpBits(4, T, 3, 0, t1);
+ SecP128R1Field.reduce32(c, t1);
+
+ SecP128R1FieldElement X3 = new SecP128R1FieldElement(T);
+ SecP128R1Field.square(M, X3.x);
+ SecP128R1Field.subtract(X3.x, S, X3.x);
+ SecP128R1Field.subtract(X3.x, S, X3.x);
+
+ SecP128R1FieldElement Y3 = new SecP128R1FieldElement(S);
+ SecP128R1Field.subtract(S, X3.x, Y3.x);
+ SecP128R1Field.multiply(Y3.x, M, Y3.x);
+ SecP128R1Field.subtract(Y3.x, t1, Y3.x);
+
+ SecP128R1FieldElement Z3 = new SecP128R1FieldElement(M);
+ SecP128R1Field.twice(Y1.x, Z3.x);
+ if (!Z1IsOne)
+ {
+ SecP128R1Field.multiply(Z3.x, Z1.x, Z3.x);
+ }
+
+ return new SecP128R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this == b)
+ {
+ return threeTimes();
+ }
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECFieldElement Y1 = this.y;
+ if (Y1.isZero())
+ {
+ return b;
+ }
+
+ return twice().add(b);
+ }
+
+ public ECPoint threeTimes()
+ {
+ if (this.isInfinity() || this.y.isZero())
+ {
+ return this;
+ }
+
+ // NOTE: Be careful about recursions between twicePlus and threeTimes
+ return twice().add(this);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ return new SecP128R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Curve.java
new file mode 100644
index 00000000..9285e7c8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Curve.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecP160K1Curve extends ECCurve.AbstractFp
+{
+ public static final BigInteger q = SecP160R2Curve.q;
+
+ private static final int SECP160K1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+ protected SecP160K1Point infinity;
+
+ public SecP160K1Curve()
+ {
+ super(q);
+
+ this.infinity = new SecP160K1Point(this, null, null);
+
+ this.a = fromBigInteger(ECConstants.ZERO);
+ this.b = fromBigInteger(BigInteger.valueOf(7));
+ this.order = new BigInteger(1, Hex.decode("0100000000000000000001B8FA16DFAB9ACA16B6B3"));
+ this.cofactor = BigInteger.valueOf(1);
+ this.coord = SECP160K1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecP160K1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_JACOBIAN:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public int getFieldSize()
+ {
+ return q.bitLength();
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecP160R2FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecP160K1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecP160K1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Point.java
new file mode 100644
index 00000000..37a520a1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Point.java
@@ -0,0 +1,298 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat160;
+
+public class SecP160K1Point extends ECPoint.AbstractFp
+{
+ /**
+ * Create a point which encodes with point compression.
+ *
+ * @param curve
+ * the curve to use
+ * @param x
+ * affine x co-ordinate
+ * @param y
+ * affine y co-ordinate
+ *
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecP160K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * Create a point that encodes with or without point compresion.
+ *
+ * @param curve
+ * the curve to use
+ * @param x
+ * affine x co-ordinate
+ * @param y
+ * affine y co-ordinate
+ * @param withCompression
+ * if true encode with point compression
+ *
+ * @deprecated per-point compression property will be removed, refer
+ * {@link #getEncoded(boolean)}
+ */
+ public SecP160K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecP160K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs,
+ boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecP160K1Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ // B.3 pg 62
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+ if (this == b)
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.x, Y1 = (SecP160R2FieldElement)this.y;
+ SecP160R2FieldElement X2 = (SecP160R2FieldElement)b.getXCoord(), Y2 = (SecP160R2FieldElement)b.getYCoord();
+
+ SecP160R2FieldElement Z1 = (SecP160R2FieldElement)this.zs[0];
+ SecP160R2FieldElement Z2 = (SecP160R2FieldElement)b.getZCoord(0);
+
+ int c;
+ int[] tt1 = Nat160.createExt();
+ int[] t2 = Nat160.create();
+ int[] t3 = Nat160.create();
+ int[] t4 = Nat160.create();
+
+ boolean Z1IsOne = Z1.isOne();
+ int[] U2, S2;
+ if (Z1IsOne)
+ {
+ U2 = X2.x;
+ S2 = Y2.x;
+ }
+ else
+ {
+ S2 = t3;
+ SecP160R2Field.square(Z1.x, S2);
+
+ U2 = t2;
+ SecP160R2Field.multiply(S2, X2.x, U2);
+
+ SecP160R2Field.multiply(S2, Z1.x, S2);
+ SecP160R2Field.multiply(S2, Y2.x, S2);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ int[] U1, S1;
+ if (Z2IsOne)
+ {
+ U1 = X1.x;
+ S1 = Y1.x;
+ }
+ else
+ {
+ S1 = t4;
+ SecP160R2Field.square(Z2.x, S1);
+
+ U1 = tt1;
+ SecP160R2Field.multiply(S1, X1.x, U1);
+
+ SecP160R2Field.multiply(S1, Z2.x, S1);
+ SecP160R2Field.multiply(S1, Y1.x, S1);
+ }
+
+ int[] H = Nat160.create();
+ SecP160R2Field.subtract(U1, U2, H);
+
+ int[] R = t2;
+ SecP160R2Field.subtract(S1, S2, R);
+
+ // Check if b == this or b == -this
+ if (Nat160.isZero(H))
+ {
+ if (Nat160.isZero(R))
+ {
+ // this == b, i.e. this must be doubled
+ return this.twice();
+ }
+
+ // this == -b, i.e. the result is the point at infinity
+ return curve.getInfinity();
+ }
+
+ int[] HSquared = t3;
+ SecP160R2Field.square(H, HSquared);
+
+ int[] G = Nat160.create();
+ SecP160R2Field.multiply(HSquared, H, G);
+
+ int[] V = t3;
+ SecP160R2Field.multiply(HSquared, U1, V);
+
+ SecP160R2Field.negate(G, G);
+ Nat160.mul(S1, G, tt1);
+
+ c = Nat160.addBothTo(V, V, G);
+ SecP160R2Field.reduce32(c, G);
+
+ SecP160R2FieldElement X3 = new SecP160R2FieldElement(t4);
+ SecP160R2Field.square(R, X3.x);
+ SecP160R2Field.subtract(X3.x, G, X3.x);
+
+ SecP160R2FieldElement Y3 = new SecP160R2FieldElement(G);
+ SecP160R2Field.subtract(V, X3.x, Y3.x);
+ SecP160R2Field.multiplyAddToExt(Y3.x, R, tt1);
+ SecP160R2Field.reduce(tt1, Y3.x);
+
+ SecP160R2FieldElement Z3 = new SecP160R2FieldElement(H);
+ if (!Z1IsOne)
+ {
+ SecP160R2Field.multiply(Z3.x, Z1.x, Z3.x);
+ }
+ if (!Z2IsOne)
+ {
+ SecP160R2Field.multiply(Z3.x, Z2.x, Z3.x);
+ }
+
+ ECFieldElement[] zs = new ECFieldElement[] { Z3 };
+
+ return new SecP160K1Point(curve, X3, Y3, zs, this.withCompression);
+ }
+
+ // B.3 pg 62
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ SecP160R2FieldElement Y1 = (SecP160R2FieldElement)this.y;
+ if (Y1.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.x, Z1 = (SecP160R2FieldElement)this.zs[0];
+
+ int c;
+
+ int[] Y1Squared = Nat160.create();
+ SecP160R2Field.square(Y1.x, Y1Squared);
+
+ int[] T = Nat160.create();
+ SecP160R2Field.square(Y1Squared, T);
+
+ int[] M = Nat160.create();
+ SecP160R2Field.square(X1.x, M);
+ c = Nat160.addBothTo(M, M, M);
+ SecP160R2Field.reduce32(c, M);
+
+ int[] S = Y1Squared;
+ SecP160R2Field.multiply(Y1Squared, X1.x, S);
+ c = Nat.shiftUpBits(5, S, 2, 0);
+ SecP160R2Field.reduce32(c, S);
+
+ int[] t1 = Nat160.create();
+ c = Nat.shiftUpBits(5, T, 3, 0, t1);
+ SecP160R2Field.reduce32(c, t1);
+
+ SecP160R2FieldElement X3 = new SecP160R2FieldElement(T);
+ SecP160R2Field.square(M, X3.x);
+ SecP160R2Field.subtract(X3.x, S, X3.x);
+ SecP160R2Field.subtract(X3.x, S, X3.x);
+
+ SecP160R2FieldElement Y3 = new SecP160R2FieldElement(S);
+ SecP160R2Field.subtract(S, X3.x, Y3.x);
+ SecP160R2Field.multiply(Y3.x, M, Y3.x);
+ SecP160R2Field.subtract(Y3.x, t1, Y3.x);
+
+ SecP160R2FieldElement Z3 = new SecP160R2FieldElement(M);
+ SecP160R2Field.twice(Y1.x, Z3.x);
+ if (!Z1.isOne())
+ {
+ SecP160R2Field.multiply(Z3.x, Z1.x, Z3.x);
+ }
+
+ return new SecP160K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this == b)
+ {
+ return threeTimes();
+ }
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECFieldElement Y1 = this.y;
+ if (Y1.isZero())
+ {
+ return b;
+ }
+
+ return twice().add(b);
+ }
+
+ public ECPoint threeTimes()
+ {
+ if (this.isInfinity() || this.y.isZero())
+ {
+ return this;
+ }
+
+ // NOTE: Be careful about recursions between twicePlus and threeTimes
+ return twice().add(this);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ return new SecP160K1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Curve.java
new file mode 100644
index 00000000..fd39a4a1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Curve.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecP160R1Curve extends ECCurve.AbstractFp
+{
+ public static final BigInteger q = new BigInteger(1,
+ Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF"));
+
+ private static final int SecP160R1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+ protected SecP160R1Point infinity;
+
+ public SecP160R1Curve()
+ {
+ super(q);
+
+ this.infinity = new SecP160R1Point(this, null, null);
+
+ this.a = fromBigInteger(new BigInteger(1,
+ Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC")));
+ this.b = fromBigInteger(new BigInteger(1,
+ Hex.decode("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45")));
+ this.order = new BigInteger(1, Hex.decode("0100000000000000000001F4C8F927AED3CA752257"));
+ this.cofactor = BigInteger.valueOf(1);
+
+ this.coord = SecP160R1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecP160R1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_JACOBIAN:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public int getFieldSize()
+ {
+ return q.bitLength();
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecP160R1FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecP160R1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecP160R1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Field.java
new file mode 100644
index 00000000..91ba0e58
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Field.java
@@ -0,0 +1,187 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat160;
+
+public class SecP160R1Field
+{
+ private static final long M = 0xFFFFFFFFL;
+
+ // 2^160 - 2^31 - 1
+ static final int[] P = new int[] { 0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
+ static final int[] PExt = new int[] { 0x00000001, 0x40000001, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFE, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+ private static final int[] PExtInv = new int[]{ 0xFFFFFFFF, 0xBFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x00000001, 0x00000001 };
+ private static final int P4 = 0xFFFFFFFF;
+ private static final int PExt9 = 0xFFFFFFFF;
+ private static final int PInv = 0x80000001;
+
+ public static void add(int[] x, int[] y, int[] z)
+ {
+ int c = Nat160.add(x, y, z);
+ if (c != 0 || (z[4] == P4 && Nat160.gte(z, P)))
+ {
+ Nat.addWordTo(5, PInv, z);
+ }
+ }
+
+ public static void addExt(int[] xx, int[] yy, int[] zz)
+ {
+ int c = Nat.add(10, xx, yy, zz);
+ if (c != 0 || (zz[9] == PExt9 && Nat.gte(10, zz, PExt)))
+ {
+ if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0)
+ {
+ Nat.incAt(10, zz, PExtInv.length);
+ }
+ }
+ }
+
+ public static void addOne(int[] x, int[] z)
+ {
+ int c = Nat.inc(5, x, z);
+ if (c != 0 || (z[4] == P4 && Nat160.gte(z, P)))
+ {
+ Nat.addWordTo(5, PInv, z);
+ }
+ }
+
+ public static int[] fromBigInteger(BigInteger x)
+ {
+ int[] z = Nat160.fromBigInteger(x);
+ if (z[4] == P4 && Nat160.gte(z, P))
+ {
+ Nat160.subFrom(P, z);
+ }
+ return z;
+ }
+
+ public static void half(int[] x, int[] z)
+ {
+ if ((x[0] & 1) == 0)
+ {
+ Nat.shiftDownBit(5, x, 0, z);
+ }
+ else
+ {
+ int c = Nat160.add(x, P, z);
+ Nat.shiftDownBit(5, z, c);
+ }
+ }
+
+ public static void multiply(int[] x, int[] y, int[] z)
+ {
+ int[] tt = Nat160.createExt();
+ Nat160.mul(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
+ {
+ int c = Nat160.mulAddTo(x, y, zz);
+ if (c != 0 || (zz[9] == PExt9 && Nat.gte(10, zz, PExt)))
+ {
+ if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0)
+ {
+ Nat.incAt(10, zz, PExtInv.length);
+ }
+ }
+ }
+
+ public static void negate(int[] x, int[] z)
+ {
+ if (Nat160.isZero(x))
+ {
+ Nat160.zero(z);
+ }
+ else
+ {
+ Nat160.sub(P, x, z);
+ }
+ }
+
+ public static void reduce(int[] xx, int[] z)
+ {
+ long x5 = xx[5] & M, x6 = xx[6] & M, x7 = xx[7] & M, x8 = xx[8] & M, x9 = xx[9] & M;
+
+ long c = 0;
+ c += (xx[0] & M) + x5 + (x5 << 31);
+ z[0] = (int)c; c >>>= 32;
+ c += (xx[1] & M) + x6 + (x6 << 31);
+ z[1] = (int)c; c >>>= 32;
+ c += (xx[2] & M) + x7 + (x7 << 31);
+ z[2] = (int)c; c >>>= 32;
+ c += (xx[3] & M) + x8 + (x8 << 31);
+ z[3] = (int)c; c >>>= 32;
+ c += (xx[4] & M) + x9 + (x9 << 31);
+ z[4] = (int)c; c >>>= 32;
+
+// assert c >>> 32 == 0;
+
+ reduce32((int)c, z);
+ }
+
+ public static void reduce32(int x, int[] z)
+ {
+ if ((x != 0 && Nat160.mulWordsAdd(PInv, x, z, 0) != 0)
+ || (z[4] == P4 && Nat160.gte(z, P)))
+ {
+ Nat.addWordTo(5, PInv, z);
+ }
+ }
+
+ public static void square(int[] x, int[] z)
+ {
+ int[] tt = Nat160.createExt();
+ Nat160.square(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareN(int[] x, int n, int[] z)
+ {
+// assert n > 0;
+
+ int[] tt = Nat160.createExt();
+ Nat160.square(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ Nat160.square(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static void subtract(int[] x, int[] y, int[] z)
+ {
+ int c = Nat160.sub(x, y, z);
+ if (c != 0)
+ {
+ Nat.subWordFrom(5, PInv, z);
+ }
+ }
+
+ public static void subtractExt(int[] xx, int[] yy, int[] zz)
+ {
+ int c = Nat.sub(10, xx, yy, zz);
+ if (c != 0)
+ {
+ if (Nat.subFrom(PExtInv.length, PExtInv, zz) != 0)
+ {
+ Nat.decAt(10, zz, PExtInv.length);
+ }
+ }
+ }
+
+ public static void twice(int[] x, int[] z)
+ {
+ int c = Nat.shiftUpBit(5, x, 0, z);
+ if (c != 0 || (z[4] == P4 && Nat160.gte(z, P)))
+ {
+ Nat.addWordTo(5, PInv, z);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1FieldElement.java
new file mode 100644
index 00000000..1b411b0c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1FieldElement.java
@@ -0,0 +1,202 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Mod;
+import org.bouncycastle.math.raw.Nat160;
+import org.bouncycastle.util.Arrays;
+
+public class SecP160R1FieldElement extends ECFieldElement
+{
+ public static final BigInteger Q = SecP160R1Curve.q;
+
+ protected int[] x;
+
+ public SecP160R1FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0)
+ {
+ throw new IllegalArgumentException("x value invalid for SecP160R1FieldElement");
+ }
+
+ this.x = SecP160R1Field.fromBigInteger(x);
+ }
+
+ public SecP160R1FieldElement()
+ {
+ this.x = Nat160.create();
+ }
+
+ protected SecP160R1FieldElement(int[] x)
+ {
+ this.x = x;
+ }
+
+ public boolean isZero()
+ {
+ return Nat160.isZero(x);
+ }
+
+ public boolean isOne()
+ {
+ return Nat160.isOne(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return Nat160.getBit(x, 0) == 1;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat160.toBigInteger(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecP160R1Field";
+ }
+
+ public int getFieldSize()
+ {
+ return Q.bitLength();
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ int[] z = Nat160.create();
+ SecP160R1Field.add(x, ((SecP160R1FieldElement)b).x, z);
+ return new SecP160R1FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ int[] z = Nat160.create();
+ SecP160R1Field.addOne(x, z);
+ return new SecP160R1FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ int[] z = Nat160.create();
+ SecP160R1Field.subtract(x, ((SecP160R1FieldElement)b).x, z);
+ return new SecP160R1FieldElement(z);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ int[] z = Nat160.create();
+ SecP160R1Field.multiply(x, ((SecP160R1FieldElement)b).x, z);
+ return new SecP160R1FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+// return multiply(b.invert());
+ int[] z = Nat160.create();
+ Mod.invert(SecP160R1Field.P, ((SecP160R1FieldElement)b).x, z);
+ SecP160R1Field.multiply(z, x, z);
+ return new SecP160R1FieldElement(z);
+ }
+
+ public ECFieldElement negate()
+ {
+ int[] z = Nat160.create();
+ SecP160R1Field.negate(x, z);
+ return new SecP160R1FieldElement(z);
+ }
+
+ public ECFieldElement square()
+ {
+ int[] z = Nat160.create();
+ SecP160R1Field.square(x, z);
+ return new SecP160R1FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+// return new SecP160R1FieldElement(toBigInteger().modInverse(Q));
+ int[] z = Nat160.create();
+ Mod.invert(SecP160R1Field.P, x, z);
+ return new SecP160R1FieldElement(z);
+ }
+
+ // D.1.4 91
+ /**
+ * return a sqrt root - the routine verifies that the calculation returns the right value - if
+ * none exists it returns null.
+ */
+ public ECFieldElement sqrt()
+ {
+ /*
+ * Raise this element to the exponent 2^158 - 2^29
+ *
+ * Breaking up the exponent's binary representation into "repunits", we get:
+ * { 129 1s } { 29 0s }
+ *
+ * Therefore we need an addition chain containing 129 (the length of the repunit) We use:
+ * 1, 2, 4, 8, 16, 32, 64, 128, [129]
+ */
+
+ int[] x1 = this.x;
+ if (Nat160.isZero(x1) || Nat160.isOne(x1))
+ {
+ return this;
+ }
+
+ int[] x2 = Nat160.create();
+ SecP160R1Field.square(x1, x2);
+ SecP160R1Field.multiply(x2, x1, x2);
+ int[] x4 = Nat160.create();
+ SecP160R1Field.squareN(x2, 2, x4);
+ SecP160R1Field.multiply(x4, x2, x4);
+ int[] x8 = x2;
+ SecP160R1Field.squareN(x4, 4, x8);
+ SecP160R1Field.multiply(x8, x4, x8);
+ int[] x16 = x4;
+ SecP160R1Field.squareN(x8, 8, x16);
+ SecP160R1Field.multiply(x16, x8, x16);
+ int[] x32 = x8;
+ SecP160R1Field.squareN(x16, 16, x32);
+ SecP160R1Field.multiply(x32, x16, x32);
+ int[] x64 = x16;
+ SecP160R1Field.squareN(x32, 32, x64);
+ SecP160R1Field.multiply(x64, x32, x64);
+ int[] x128 = x32;
+ SecP160R1Field.squareN(x64, 64, x128);
+ SecP160R1Field.multiply(x128, x64, x128);
+ int[] x129 = x64;
+ SecP160R1Field.square(x128, x129);
+ SecP160R1Field.multiply(x129, x1, x129);
+
+ int[] t1 = x129;
+ SecP160R1Field.squareN(t1, 29, t1);
+
+ int[] t2 = x128;
+ SecP160R1Field.square(t1, t2);
+
+ return Nat160.eq(x1, t2) ? new SecP160R1FieldElement(t1) : null;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecP160R1FieldElement))
+ {
+ return false;
+ }
+
+ SecP160R1FieldElement o = (SecP160R1FieldElement)other;
+ return Nat160.eq(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return Q.hashCode() ^ Arrays.hashCode(x, 0, 5);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Point.java
new file mode 100644
index 00000000..42aaa777
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Point.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat160;
+
+public class SecP160R1Point extends ECPoint.AbstractFp
+{
+ /**
+ * Create a point which encodes with point compression.
+ *
+ * @param curve
+ * the curve to use
+ * @param x
+ * affine x co-ordinate
+ * @param y
+ * affine y co-ordinate
+ *
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecP160R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * Create a point that encodes with or without point compresion.
+ *
+ * @param curve
+ * the curve to use
+ * @param x
+ * affine x co-ordinate
+ * @param y
+ * affine y co-ordinate
+ * @param withCompression
+ * if true encode with point compression
+ *
+ * @deprecated per-point compression property will be removed, refer
+ * {@link #getEncoded(boolean)}
+ */
+ public SecP160R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecP160R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecP160R1Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+ if (this == b)
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ SecP160R1FieldElement X1 = (SecP160R1FieldElement)this.x, Y1 = (SecP160R1FieldElement)this.y;
+ SecP160R1FieldElement X2 = (SecP160R1FieldElement)b.getXCoord(), Y2 = (SecP160R1FieldElement)b.getYCoord();
+
+ SecP160R1FieldElement Z1 = (SecP160R1FieldElement)this.zs[0];
+ SecP160R1FieldElement Z2 = (SecP160R1FieldElement)b.getZCoord(0);
+
+ int c;
+ int[] tt1 = Nat160.createExt();
+ int[] t2 = Nat160.create();
+ int[] t3 = Nat160.create();
+ int[] t4 = Nat160.create();
+
+ boolean Z1IsOne = Z1.isOne();
+ int[] U2, S2;
+ if (Z1IsOne)
+ {
+ U2 = X2.x;
+ S2 = Y2.x;
+ }
+ else
+ {
+ S2 = t3;
+ SecP160R1Field.square(Z1.x, S2);
+
+ U2 = t2;
+ SecP160R1Field.multiply(S2, X2.x, U2);
+
+ SecP160R1Field.multiply(S2, Z1.x, S2);
+ SecP160R1Field.multiply(S2, Y2.x, S2);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ int[] U1, S1;
+ if (Z2IsOne)
+ {
+ U1 = X1.x;
+ S1 = Y1.x;
+ }
+ else
+ {
+ S1 = t4;
+ SecP160R1Field.square(Z2.x, S1);
+
+ U1 = tt1;
+ SecP160R1Field.multiply(S1, X1.x, U1);
+
+ SecP160R1Field.multiply(S1, Z2.x, S1);
+ SecP160R1Field.multiply(S1, Y1.x, S1);
+ }
+
+ int[] H = Nat160.create();
+ SecP160R1Field.subtract(U1, U2, H);
+
+ int[] R = t2;
+ SecP160R1Field.subtract(S1, S2, R);
+
+ // Check if b == this or b == -this
+ if (Nat160.isZero(H))
+ {
+ if (Nat160.isZero(R))
+ {
+ // this == b, i.e. this must be doubled
+ return this.twice();
+ }
+
+ // this == -b, i.e. the result is the point at infinity
+ return curve.getInfinity();
+ }
+
+ int[] HSquared = t3;
+ SecP160R1Field.square(H, HSquared);
+
+ int[] G = Nat160.create();
+ SecP160R1Field.multiply(HSquared, H, G);
+
+ int[] V = t3;
+ SecP160R1Field.multiply(HSquared, U1, V);
+
+ SecP160R1Field.negate(G, G);
+ Nat160.mul(S1, G, tt1);
+
+ c = Nat160.addBothTo(V, V, G);
+ SecP160R1Field.reduce32(c, G);
+
+ SecP160R1FieldElement X3 = new SecP160R1FieldElement(t4);
+ SecP160R1Field.square(R, X3.x);
+ SecP160R1Field.subtract(X3.x, G, X3.x);
+
+ SecP160R1FieldElement Y3 = new SecP160R1FieldElement(G);
+ SecP160R1Field.subtract(V, X3.x, Y3.x);
+ SecP160R1Field.multiplyAddToExt(Y3.x, R, tt1);
+ SecP160R1Field.reduce(tt1, Y3.x);
+
+ SecP160R1FieldElement Z3 = new SecP160R1FieldElement(H);
+ if (!Z1IsOne)
+ {
+ SecP160R1Field.multiply(Z3.x, Z1.x, Z3.x);
+ }
+ if (!Z2IsOne)
+ {
+ SecP160R1Field.multiply(Z3.x, Z2.x, Z3.x);
+ }
+
+ ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
+
+ return new SecP160R1Point(curve, X3, Y3, zs, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ SecP160R1FieldElement Y1 = (SecP160R1FieldElement)this.y;
+ if (Y1.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ SecP160R1FieldElement X1 = (SecP160R1FieldElement)this.x, Z1 = (SecP160R1FieldElement)this.zs[0];
+
+ int c;
+ int[] t1 = Nat160.create();
+ int[] t2 = Nat160.create();
+
+ int[] Y1Squared = Nat160.create();
+ SecP160R1Field.square(Y1.x, Y1Squared);
+
+ int[] T = Nat160.create();
+ SecP160R1Field.square(Y1Squared, T);
+
+ boolean Z1IsOne = Z1.isOne();
+
+ int[] Z1Squared = Z1.x;
+ if (!Z1IsOne)
+ {
+ Z1Squared = t2;
+ SecP160R1Field.square(Z1.x, Z1Squared);
+ }
+
+ SecP160R1Field.subtract(X1.x, Z1Squared, t1);
+
+ int[] M = t2;
+ SecP160R1Field.add(X1.x, Z1Squared, M);
+ SecP160R1Field.multiply(M, t1, M);
+ c = Nat160.addBothTo(M, M, M);
+ SecP160R1Field.reduce32(c, M);
+
+ int[] S = Y1Squared;
+ SecP160R1Field.multiply(Y1Squared, X1.x, S);
+ c = Nat.shiftUpBits(5, S, 2, 0);
+ SecP160R1Field.reduce32(c, S);
+
+ c = Nat.shiftUpBits(5, T, 3, 0, t1);
+ SecP160R1Field.reduce32(c, t1);
+
+ SecP160R1FieldElement X3 = new SecP160R1FieldElement(T);
+ SecP160R1Field.square(M, X3.x);
+ SecP160R1Field.subtract(X3.x, S, X3.x);
+ SecP160R1Field.subtract(X3.x, S, X3.x);
+
+ SecP160R1FieldElement Y3 = new SecP160R1FieldElement(S);
+ SecP160R1Field.subtract(S, X3.x, Y3.x);
+ SecP160R1Field.multiply(Y3.x, M, Y3.x);
+ SecP160R1Field.subtract(Y3.x, t1, Y3.x);
+
+ SecP160R1FieldElement Z3 = new SecP160R1FieldElement(M);
+ SecP160R1Field.twice(Y1.x, Z3.x);
+ if (!Z1IsOne)
+ {
+ SecP160R1Field.multiply(Z3.x, Z1.x, Z3.x);
+ }
+
+ return new SecP160R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this == b)
+ {
+ return threeTimes();
+ }
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECFieldElement Y1 = this.y;
+ if (Y1.isZero())
+ {
+ return b;
+ }
+
+ return twice().add(b);
+ }
+
+ public ECPoint threeTimes()
+ {
+ if (this.isInfinity() || this.y.isZero())
+ {
+ return this;
+ }
+
+ // NOTE: Be careful about recursions between twicePlus and threeTimes
+ return twice().add(this);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ return new SecP160R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Curve.java
new file mode 100644
index 00000000..3e1cbbb4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Curve.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecP160R2Curve extends ECCurve.AbstractFp
+{
+ public static final BigInteger q = new BigInteger(1,
+ Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73"));
+
+ private static final int SecP160R2_DEFAULT_COORDS = COORD_JACOBIAN;
+
+ protected SecP160R2Point infinity;
+
+ public SecP160R2Curve()
+ {
+ super(q);
+
+ this.infinity = new SecP160R2Point(this, null, null);
+
+ this.a = fromBigInteger(new BigInteger(1,
+ Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70")));
+ this.b = fromBigInteger(new BigInteger(1,
+ Hex.decode("B4E134D3FB59EB8BAB57274904664D5AF50388BA")));
+ this.order = new BigInteger(1, Hex.decode("0100000000000000000000351EE786A818F3A1A16B"));
+ this.cofactor = BigInteger.valueOf(1);
+
+ this.coord = SecP160R2_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecP160R2Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_JACOBIAN:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public int getFieldSize()
+ {
+ return q.bitLength();
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecP160R2FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecP160R2Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecP160R2Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Field.java
new file mode 100644
index 00000000..70c5e0c9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Field.java
@@ -0,0 +1,177 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat160;
+
+public class SecP160R2Field
+{
+ // 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1
+ static final int[] P = new int[]{ 0xFFFFAC73, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+ static final int[] PExt = new int[]{ 0x1B44BBA9, 0x0000A71A, 0x00000001, 0x00000000, 0x00000000,
+ 0xFFFF58E6, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+ private static final int[] PExtInv = new int[]{ 0xE4BB4457, 0xFFFF58E5, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x0000A719, 0x00000002 };
+ private static final int P4 = 0xFFFFFFFF;
+ private static final int PExt9 = 0xFFFFFFFF;
+ private static final int PInv33 = 0x538D;
+
+ public static void add(int[] x, int[] y, int[] z)
+ {
+ int c = Nat160.add(x, y, z);
+ if (c != 0 || (z[4] == P4 && Nat160.gte(z, P)))
+ {
+ Nat.add33To(5, PInv33, z);
+ }
+ }
+
+ public static void addExt(int[] xx, int[] yy, int[] zz)
+ {
+ int c = Nat.add(10, xx, yy, zz);
+ if (c != 0 || (zz[9] == PExt9 && Nat.gte(10, zz, PExt)))
+ {
+ if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0)
+ {
+ Nat.incAt(10, zz, PExtInv.length);
+ }
+ }
+ }
+
+ public static void addOne(int[] x, int[] z)
+ {
+ int c = Nat.inc(5, x, z);
+ if (c != 0 || (z[4] == P4 && Nat160.gte(z, P)))
+ {
+ Nat.add33To(5, PInv33, z);
+ }
+ }
+
+ public static int[] fromBigInteger(BigInteger x)
+ {
+ int[] z = Nat160.fromBigInteger(x);
+ if (z[4] == P4 && Nat160.gte(z, P))
+ {
+ Nat160.subFrom(P, z);
+ }
+ return z;
+ }
+
+ public static void half(int[] x, int[] z)
+ {
+ if ((x[0] & 1) == 0)
+ {
+ Nat.shiftDownBit(5, x, 0, z);
+ }
+ else
+ {
+ int c = Nat160.add(x, P, z);
+ Nat.shiftDownBit(5, z, c);
+ }
+ }
+
+ public static void multiply(int[] x, int[] y, int[] z)
+ {
+ int[] tt = Nat160.createExt();
+ Nat160.mul(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
+ {
+ int c = Nat160.mulAddTo(x, y, zz);
+ if (c != 0 || (zz[9] == PExt9 && Nat.gte(10, zz, PExt)))
+ {
+ if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0)
+ {
+ Nat.incAt(10, zz, PExtInv.length);
+ }
+ }
+ }
+
+ public static void negate(int[] x, int[] z)
+ {
+ if (Nat160.isZero(x))
+ {
+ Nat160.zero(z);
+ }
+ else
+ {
+ Nat160.sub(P, x, z);
+ }
+ }
+
+ public static void reduce(int[] xx, int[] z)
+ {
+ long cc = Nat160.mul33Add(PInv33, xx, 5, xx, 0, z, 0);
+ int c = Nat160.mul33DWordAdd(PInv33, cc, z, 0);
+
+ // assert c == 0 || c == 1;
+
+ if (c != 0 || (z[4] == P4 && Nat160.gte(z, P)))
+ {
+ Nat.add33To(5, PInv33, z);
+ }
+ }
+
+ public static void reduce32(int x, int[] z)
+ {
+ if ((x != 0 && Nat160.mul33WordAdd(PInv33, x, z, 0) != 0)
+ || (z[4] == P4 && Nat160.gte(z, P)))
+ {
+ Nat.add33To(5, PInv33, z);
+ }
+ }
+
+ public static void square(int[] x, int[] z)
+ {
+ int[] tt = Nat160.createExt();
+ Nat160.square(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareN(int[] x, int n, int[] z)
+ {
+// assert n > 0;
+
+ int[] tt = Nat160.createExt();
+ Nat160.square(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ Nat160.square(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static void subtract(int[] x, int[] y, int[] z)
+ {
+ int c = Nat160.sub(x, y, z);
+ if (c != 0)
+ {
+ Nat.sub33From(5, PInv33, z);
+ }
+ }
+
+ public static void subtractExt(int[] xx, int[] yy, int[] zz)
+ {
+ int c = Nat.sub(10, xx, yy, zz);
+ if (c != 0)
+ {
+ if (Nat.subFrom(PExtInv.length, PExtInv, zz) != 0)
+ {
+ Nat.decAt(10, zz, PExtInv.length);
+ }
+ }
+ }
+
+ public static void twice(int[] x, int[] z)
+ {
+ int c = Nat.shiftUpBit(5, x, 0, z);
+ if (c != 0 || (z[4] == P4 && Nat160.gte(z, P)))
+ {
+ Nat.add33To(5, PInv33, z);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2FieldElement.java
new file mode 100644
index 00000000..37171360
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2FieldElement.java
@@ -0,0 +1,217 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Mod;
+import org.bouncycastle.math.raw.Nat160;
+import org.bouncycastle.util.Arrays;
+
+public class SecP160R2FieldElement extends ECFieldElement
+{
+ public static final BigInteger Q = SecP160R2Curve.q;
+
+ protected int[] x;
+
+ public SecP160R2FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0)
+ {
+ throw new IllegalArgumentException("x value invalid for SecP160R2FieldElement");
+ }
+
+ this.x = SecP160R2Field.fromBigInteger(x);
+ }
+
+ public SecP160R2FieldElement()
+ {
+ this.x = Nat160.create();
+ }
+
+ protected SecP160R2FieldElement(int[] x)
+ {
+ this.x = x;
+ }
+
+ public boolean isZero()
+ {
+ return Nat160.isZero(x);
+ }
+
+ public boolean isOne()
+ {
+ return Nat160.isOne(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return Nat160.getBit(x, 0) == 1;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat160.toBigInteger(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecP160R2Field";
+ }
+
+ public int getFieldSize()
+ {
+ return Q.bitLength();
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ int[] z = Nat160.create();
+ SecP160R2Field.add(x, ((SecP160R2FieldElement)b).x, z);
+ return new SecP160R2FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ int[] z = Nat160.create();
+ SecP160R2Field.addOne(x, z);
+ return new SecP160R2FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ int[] z = Nat160.create();
+ SecP160R2Field.subtract(x, ((SecP160R2FieldElement)b).x, z);
+ return new SecP160R2FieldElement(z);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ int[] z = Nat160.create();
+ SecP160R2Field.multiply(x, ((SecP160R2FieldElement)b).x, z);
+ return new SecP160R2FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+// return multiply(b.invert());
+ int[] z = Nat160.create();
+ Mod.invert(SecP160R2Field.P, ((SecP160R2FieldElement)b).x, z);
+ SecP160R2Field.multiply(z, x, z);
+ return new SecP160R2FieldElement(z);
+ }
+
+ public ECFieldElement negate()
+ {
+ int[] z = Nat160.create();
+ SecP160R2Field.negate(x, z);
+ return new SecP160R2FieldElement(z);
+ }
+
+ public ECFieldElement square()
+ {
+ int[] z = Nat160.create();
+ SecP160R2Field.square(x, z);
+ return new SecP160R2FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+// return new SecP160R2FieldElement(toBigInteger().modInverse(Q));
+ int[] z = Nat160.create();
+ Mod.invert(SecP160R2Field.P, x, z);
+ return new SecP160R2FieldElement(z);
+ }
+
+ // D.1.4 91
+ /**
+ * return a sqrt root - the routine verifies that the calculation returns the right value - if
+ * none exists it returns null.
+ */
+ public ECFieldElement sqrt()
+ {
+ /*
+ * Raise this element to the exponent 2^158 - 2^30 - 2^12 - 2^10 - 2^7 - 2^6 - 2^5 - 2^1 - 2^0
+ *
+ * Breaking up the exponent's binary representation into "repunits", we get: { 127 1s } { 1
+ * 0s } { 17 1s } { 1 0s } { 1 1s } { 1 0s } { 2 1s } { 3 0s } { 3 1s } { 1 0s } { 1 1s }
+ *
+ * Therefore we need an addition chain containing 1, 2, 3, 17, 127 (the lengths of the repunits)
+ * We use: [1], [2], [3], 4, 7, 14, [17], 31, 62, 124, [127]
+ */
+
+ int[] x1 = this.x;
+ if (Nat160.isZero(x1) || Nat160.isOne(x1))
+ {
+ return this;
+ }
+
+ int[] x2 = Nat160.create();
+ SecP160R2Field.square(x1, x2);
+ SecP160R2Field.multiply(x2, x1, x2);
+ int[] x3 = Nat160.create();
+ SecP160R2Field.square(x2, x3);
+ SecP160R2Field.multiply(x3, x1, x3);
+ int[] x4 = Nat160.create();
+ SecP160R2Field.square(x3, x4);
+ SecP160R2Field.multiply(x4, x1, x4);
+ int[] x7 = Nat160.create();
+ SecP160R2Field.squareN(x4, 3, x7);
+ SecP160R2Field.multiply(x7, x3, x7);
+ int[] x14 = x4;
+ SecP160R2Field.squareN(x7, 7, x14);
+ SecP160R2Field.multiply(x14, x7, x14);
+ int[] x17 = x7;
+ SecP160R2Field.squareN(x14, 3, x17);
+ SecP160R2Field.multiply(x17, x3, x17);
+ int[] x31 = Nat160.create();
+ SecP160R2Field.squareN(x17, 14, x31);
+ SecP160R2Field.multiply(x31, x14, x31);
+ int[] x62 = x14;
+ SecP160R2Field.squareN(x31, 31, x62);
+ SecP160R2Field.multiply(x62, x31, x62);
+ int[] x124 = x31;
+ SecP160R2Field.squareN(x62, 62, x124);
+ SecP160R2Field.multiply(x124, x62, x124);
+ int[] x127 = x62;
+ SecP160R2Field.squareN(x124, 3, x127);
+ SecP160R2Field.multiply(x127, x3, x127);
+
+ int[] t1 = x127;
+ SecP160R2Field.squareN(t1, 18, t1);
+ SecP160R2Field.multiply(t1, x17, t1);
+ SecP160R2Field.squareN(t1, 2, t1);
+ SecP160R2Field.multiply(t1, x1, t1);
+ SecP160R2Field.squareN(t1, 3, t1);
+ SecP160R2Field.multiply(t1, x2, t1);
+ SecP160R2Field.squareN(t1, 6, t1);
+ SecP160R2Field.multiply(t1, x3, t1);
+ SecP160R2Field.squareN(t1, 2, t1);
+ SecP160R2Field.multiply(t1, x1, t1);
+
+ int[] t2 = x2;
+ SecP160R2Field.square(t1, t2);
+
+ return Nat160.eq(x1, t2) ? new SecP160R2FieldElement(t1) : null;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecP160R2FieldElement))
+ {
+ return false;
+ }
+
+ SecP160R2FieldElement o = (SecP160R2FieldElement)other;
+ return Nat160.eq(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return Q.hashCode() ^ Arrays.hashCode(x, 0, 5);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Point.java
new file mode 100644
index 00000000..49350b87
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Point.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat160;
+
+public class SecP160R2Point extends ECPoint.AbstractFp
+{
+ /**
+ * Create a point which encodes with point compression.
+ *
+ * @param curve
+ * the curve to use
+ * @param x
+ * affine x co-ordinate
+ * @param y
+ * affine y co-ordinate
+ *
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecP160R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * Create a point that encodes with or without point compresion.
+ *
+ * @param curve
+ * the curve to use
+ * @param x
+ * affine x co-ordinate
+ * @param y
+ * affine y co-ordinate
+ * @param withCompression
+ * if true encode with point compression
+ *
+ * @deprecated per-point compression property will be removed, refer
+ * {@link #getEncoded(boolean)}
+ */
+ public SecP160R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecP160R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecP160R2Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+ if (this == b)
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.x, Y1 = (SecP160R2FieldElement)this.y;
+ SecP160R2FieldElement X2 = (SecP160R2FieldElement)b.getXCoord(), Y2 = (SecP160R2FieldElement)b.getYCoord();
+
+ SecP160R2FieldElement Z1 = (SecP160R2FieldElement)this.zs[0];
+ SecP160R2FieldElement Z2 = (SecP160R2FieldElement)b.getZCoord(0);
+
+ int c;
+ int[] tt1 = Nat160.createExt();
+ int[] t2 = Nat160.create();
+ int[] t3 = Nat160.create();
+ int[] t4 = Nat160.create();
+
+ boolean Z1IsOne = Z1.isOne();
+ int[] U2, S2;
+ if (Z1IsOne)
+ {
+ U2 = X2.x;
+ S2 = Y2.x;
+ }
+ else
+ {
+ S2 = t3;
+ SecP160R2Field.square(Z1.x, S2);
+
+ U2 = t2;
+ SecP160R2Field.multiply(S2, X2.x, U2);
+
+ SecP160R2Field.multiply(S2, Z1.x, S2);
+ SecP160R2Field.multiply(S2, Y2.x, S2);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ int[] U1, S1;
+ if (Z2IsOne)
+ {
+ U1 = X1.x;
+ S1 = Y1.x;
+ }
+ else
+ {
+ S1 = t4;
+ SecP160R2Field.square(Z2.x, S1);
+
+ U1 = tt1;
+ SecP160R2Field.multiply(S1, X1.x, U1);
+
+ SecP160R2Field.multiply(S1, Z2.x, S1);
+ SecP160R2Field.multiply(S1, Y1.x, S1);
+ }
+
+ int[] H = Nat160.create();
+ SecP160R2Field.subtract(U1, U2, H);
+
+ int[] R = t2;
+ SecP160R2Field.subtract(S1, S2, R);
+
+ // Check if b == this or b == -this
+ if (Nat160.isZero(H))
+ {
+ if (Nat160.isZero(R))
+ {
+ // this == b, i.e. this must be doubled
+ return this.twice();
+ }
+
+ // this == -b, i.e. the result is the point at infinity
+ return curve.getInfinity();
+ }
+
+ int[] HSquared = t3;
+ SecP160R2Field.square(H, HSquared);
+
+ int[] G = Nat160.create();
+ SecP160R2Field.multiply(HSquared, H, G);
+
+ int[] V = t3;
+ SecP160R2Field.multiply(HSquared, U1, V);
+
+ SecP160R2Field.negate(G, G);
+ Nat160.mul(S1, G, tt1);
+
+ c = Nat160.addBothTo(V, V, G);
+ SecP160R2Field.reduce32(c, G);
+
+ SecP160R2FieldElement X3 = new SecP160R2FieldElement(t4);
+ SecP160R2Field.square(R, X3.x);
+ SecP160R2Field.subtract(X3.x, G, X3.x);
+
+ SecP160R2FieldElement Y3 = new SecP160R2FieldElement(G);
+ SecP160R2Field.subtract(V, X3.x, Y3.x);
+ SecP160R2Field.multiplyAddToExt(Y3.x, R, tt1);
+ SecP160R2Field.reduce(tt1, Y3.x);
+
+ SecP160R2FieldElement Z3 = new SecP160R2FieldElement(H);
+ if (!Z1IsOne)
+ {
+ SecP160R2Field.multiply(Z3.x, Z1.x, Z3.x);
+ }
+ if (!Z2IsOne)
+ {
+ SecP160R2Field.multiply(Z3.x, Z2.x, Z3.x);
+ }
+
+ ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
+
+ return new SecP160R2Point(curve, X3, Y3, zs, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ SecP160R2FieldElement Y1 = (SecP160R2FieldElement)this.y;
+ if (Y1.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.x, Z1 = (SecP160R2FieldElement)this.zs[0];
+
+ int c;
+ int[] t1 = Nat160.create();
+ int[] t2 = Nat160.create();
+
+ int[] Y1Squared = Nat160.create();
+ SecP160R2Field.square(Y1.x, Y1Squared);
+
+ int[] T = Nat160.create();
+ SecP160R2Field.square(Y1Squared, T);
+
+ boolean Z1IsOne = Z1.isOne();
+
+ int[] Z1Squared = Z1.x;
+ if (!Z1IsOne)
+ {
+ Z1Squared = t2;
+ SecP160R2Field.square(Z1.x, Z1Squared);
+ }
+
+ SecP160R2Field.subtract(X1.x, Z1Squared, t1);
+
+ int[] M = t2;
+ SecP160R2Field.add(X1.x, Z1Squared, M);
+ SecP160R2Field.multiply(M, t1, M);
+ c = Nat160.addBothTo(M, M, M);
+ SecP160R2Field.reduce32(c, M);
+
+ int[] S = Y1Squared;
+ SecP160R2Field.multiply(Y1Squared, X1.x, S);
+ c = Nat.shiftUpBits(5, S, 2, 0);
+ SecP160R2Field.reduce32(c, S);
+
+ c = Nat.shiftUpBits(5, T, 3, 0, t1);
+ SecP160R2Field.reduce32(c, t1);
+
+ SecP160R2FieldElement X3 = new SecP160R2FieldElement(T);
+ SecP160R2Field.square(M, X3.x);
+ SecP160R2Field.subtract(X3.x, S, X3.x);
+ SecP160R2Field.subtract(X3.x, S, X3.x);
+
+ SecP160R2FieldElement Y3 = new SecP160R2FieldElement(S);
+ SecP160R2Field.subtract(S, X3.x, Y3.x);
+ SecP160R2Field.multiply(Y3.x, M, Y3.x);
+ SecP160R2Field.subtract(Y3.x, t1, Y3.x);
+
+ SecP160R2FieldElement Z3 = new SecP160R2FieldElement(M);
+ SecP160R2Field.twice(Y1.x, Z3.x);
+ if (!Z1IsOne)
+ {
+ SecP160R2Field.multiply(Z3.x, Z1.x, Z3.x);
+ }
+
+ return new SecP160R2Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this == b)
+ {
+ return threeTimes();
+ }
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECFieldElement Y1 = this.y;
+ if (Y1.isZero())
+ {
+ return b;
+ }
+
+ return twice().add(b);
+ }
+
+ public ECPoint threeTimes()
+ {
+ if (this.isInfinity() || this.y.isZero())
+ {
+ return this;
+ }
+
+ // NOTE: Be careful about recursions between twicePlus and threeTimes
+ return twice().add(this);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ return new SecP160R2Point(curve, this.x, this.y.negate(), this.zs, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113Field.java
new file mode 100644
index 00000000..17483b02
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113Field.java
@@ -0,0 +1,226 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Interleave;
+import org.bouncycastle.math.raw.Nat128;
+
+public class SecT113Field
+{
+ private static final long M49 = -1L >>> 15;
+ private static final long M57 = -1L >>> 7;
+
+ public static void add(long[] x, long[] y, long[] z)
+ {
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
+ }
+
+ public static void addExt(long[] xx, long[] yy, long[] zz)
+ {
+ zz[0] = xx[0] ^ yy[0];
+ zz[1] = xx[1] ^ yy[1];
+ zz[2] = xx[2] ^ yy[2];
+ zz[3] = xx[3] ^ yy[3];
+ }
+
+ public static void addOne(long[] x, long[] z)
+ {
+ z[0] = x[0] ^ 1L;
+ z[1] = x[1];
+ }
+
+ public static long[] fromBigInteger(BigInteger x)
+ {
+ long[] z = Nat128.fromBigInteger64(x);
+ reduce15(z, 0);
+ return z;
+ }
+
+ public static void invert(long[] x, long[] z)
+ {
+ if (Nat128.isZero64(x))
+ {
+ throw new IllegalStateException();
+ }
+
+ // Itoh-Tsujii inversion
+
+ long[] t0 = Nat128.create64();
+ long[] t1 = Nat128.create64();
+
+ square(x, t0);
+ multiply(t0, x, t0);
+ square(t0, t0);
+ multiply(t0, x, t0);
+ squareN(t0, 3, t1);
+ multiply(t1, t0, t1);
+ square(t1, t1);
+ multiply(t1, x, t1);
+ squareN(t1, 7, t0);
+ multiply(t0, t1, t0);
+ squareN(t0, 14, t1);
+ multiply(t1, t0, t1);
+ squareN(t1, 28, t0);
+ multiply(t0, t1, t0);
+ squareN(t0, 56, t1);
+ multiply(t1, t0, t1);
+ square(t1, z);
+ }
+
+ public static void multiply(long[] x, long[] y, long[] z)
+ {
+ long[] tt = Nat128.createExt64();
+ implMultiply(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(long[] x, long[] y, long[] zz)
+ {
+ long[] tt = Nat128.createExt64();
+ implMultiply(x, y, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void reduce(long[] xx, long[] z)
+ {
+ long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3];
+
+ x1 ^= (x3 << 15) ^ (x3 << 24);
+ x2 ^= (x3 >>> 49) ^ (x3 >>> 40);
+
+ x0 ^= (x2 << 15) ^ (x2 << 24);
+ x1 ^= (x2 >>> 49) ^ (x2 >>> 40);
+
+ long t = x1 >>> 49;
+ z[0] = x0 ^ t ^ (t << 9);
+ z[1] = x1 & M49;
+ }
+
+ public static void reduce15(long[] z, int zOff)
+ {
+ long z1 = z[zOff + 1], t = z1 >>> 49;
+ z[zOff ] ^= t ^ (t << 9);
+ z[zOff + 1] = z1 & M49;
+ }
+
+ public static void sqrt(long[] x, long[] z)
+ {
+ long u0 = Interleave.unshuffle(x[0]), u1 = Interleave.unshuffle(x[1]);
+ long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ long c0 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ z[0] = e0 ^ (c0 << 57) ^ (c0 << 5);
+ z[1] = (c0 >>> 7) ^ (c0 >>> 59);
+ }
+
+ public static void square(long[] x, long[] z)
+ {
+ long[] tt = Nat128.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareAddToExt(long[] x, long[] zz)
+ {
+ long[] tt = Nat128.createExt64();
+ implSquare(x, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void squareN(long[] x, int n, long[] z)
+ {
+// assert n > 0;
+
+ long[] tt = Nat128.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ implSquare(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static int trace(long[] x)
+ {
+ // Non-zero-trace bits: 0
+ return (int)(x[0]) & 1;
+ }
+
+ protected static void implMultiply(long[] x, long[] y, long[] zz)
+ {
+ /*
+ * "Three-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein.
+ */
+
+ long f0 = x[0], f1 = x[1];
+ f1 = ((f0 >>> 57) ^ (f1 << 7)) & M57;
+ f0 &= M57;
+
+ long g0 = y[0], g1 = y[1];
+ g1 = ((g0 >>> 57) ^ (g1 << 7)) & M57;
+ g0 &= M57;
+
+ long[] H = new long[6];
+
+ implMulw(f0, g0, H, 0); // H(0) 57/56 bits
+ implMulw(f1, g1, H, 2); // H(INF) 57/54 bits
+ implMulw(f0 ^ f1, g0 ^ g1, H, 4); // H(1) 57/56 bits
+
+ long r = H[1] ^ H[2];
+ long z0 = H[0],
+ z3 = H[3],
+ z1 = H[4] ^ z0 ^ r,
+ z2 = H[5] ^ z3 ^ r;
+
+ zz[0] = z0 ^ (z1 << 57);
+ zz[1] = (z1 >>> 7) ^ (z2 << 50);
+ zz[2] = (z2 >>> 14) ^ (z3 << 43);
+ zz[3] = (z3 >>> 21);
+ }
+
+ protected static void implMulw(long x, long y, long[] z, int zOff)
+ {
+// assert x >>> 57 == 0;
+// assert y >>> 57 == 0;
+
+ long[] u = new long[8];
+// u[0] = 0;
+ u[1] = y;
+ u[2] = u[1] << 1;
+ u[3] = u[2] ^ y;
+ u[4] = u[2] << 1;
+ u[5] = u[4] ^ y;
+ u[6] = u[3] << 1;
+ u[7] = u[6] ^ y;
+
+ int j = (int)x;
+ long g, h = 0, l = u[j & 7];
+ int k = 48;
+ do
+ {
+ j = (int)(x >>> k);
+ g = u[j & 7]
+ ^ u[(j >>> 3) & 7] << 3
+ ^ u[(j >>> 6) & 7] << 6;
+ l ^= (g << k);
+ h ^= (g >>> -k);
+ }
+ while ((k -= 9) > 0);
+
+ h ^= ((x & 0x0100804020100800L) & ((y << 7) >> 63)) >>> 8;
+
+// assert h >>> 49 == 0;
+
+ z[zOff ] = l & M57;
+ z[zOff + 1] = (l >>> 57) ^ (h << 7);
+ }
+
+ protected static void implSquare(long[] x, long[] zz)
+ {
+ Interleave.expand64To128(x[0], zz, 0);
+ Interleave.expand64To128(x[1], zz, 2);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113FieldElement.java
new file mode 100644
index 00000000..6fda7ef1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113FieldElement.java
@@ -0,0 +1,221 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Nat128;
+import org.bouncycastle.util.Arrays;
+
+public class SecT113FieldElement extends ECFieldElement
+{
+ protected long[] x;
+
+ public SecT113FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.bitLength() > 113)
+ {
+ throw new IllegalArgumentException("x value invalid for SecT113FieldElement");
+ }
+
+ this.x = SecT113Field.fromBigInteger(x);
+ }
+
+ public SecT113FieldElement()
+ {
+ this.x = Nat128.create64();
+ }
+
+ protected SecT113FieldElement(long[] x)
+ {
+ this.x = x;
+ }
+
+// public int bitLength()
+// {
+// return x.degree();
+// }
+
+ public boolean isOne()
+ {
+ return Nat128.isOne64(x);
+ }
+
+ public boolean isZero()
+ {
+ return Nat128.isZero64(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return (x[0] & 1L) != 0L;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat128.toBigInteger64(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecT113Field";
+ }
+
+ public int getFieldSize()
+ {
+ return 113;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ long[] z = Nat128.create64();
+ SecT113Field.add(x, ((SecT113FieldElement)b).x, z);
+ return new SecT113FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ long[] z = Nat128.create64();
+ SecT113Field.addOne(x, z);
+ return new SecT113FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ long[] z = Nat128.create64();
+ SecT113Field.multiply(x, ((SecT113FieldElement)b).x, z);
+ return new SecT113FieldElement(z);
+ }
+
+ public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ return multiplyPlusProduct(b, x, y);
+ }
+
+ public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x, bx = ((SecT113FieldElement)b).x;
+ long[] xx = ((SecT113FieldElement)x).x, yx = ((SecT113FieldElement)y).x;
+
+ long[] tt = Nat128.createExt64();
+ SecT113Field.multiplyAddToExt(ax, bx, tt);
+ SecT113Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat128.create64();
+ SecT113Field.reduce(tt, z);
+ return new SecT113FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+ return multiply(b.invert());
+ }
+
+ public ECFieldElement negate()
+ {
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ long[] z = Nat128.create64();
+ SecT113Field.square(x, z);
+ return new SecT113FieldElement(z);
+ }
+
+ public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ return squarePlusProduct(x, y);
+ }
+
+ public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x;
+ long[] xx = ((SecT113FieldElement)x).x, yx = ((SecT113FieldElement)y).x;
+
+ long[] tt = Nat128.createExt64();
+ SecT113Field.squareAddToExt(ax, tt);
+ SecT113Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat128.create64();
+ SecT113Field.reduce(tt, z);
+ return new SecT113FieldElement(z);
+ }
+
+ public ECFieldElement squarePow(int pow)
+ {
+ if (pow < 1)
+ {
+ return this;
+ }
+
+ long[] z = Nat128.create64();
+ SecT113Field.squareN(x, pow, z);
+ return new SecT113FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+ long[] z = Nat128.create64();
+ SecT113Field.invert(x, z);
+ return new SecT113FieldElement(z);
+ }
+
+ public ECFieldElement sqrt()
+ {
+ long[] z = Nat128.create64();
+ SecT113Field.sqrt(x, z);
+ return new SecT113FieldElement(z);
+ }
+
+ public int getRepresentation()
+ {
+ return ECFieldElement.F2m.TPB;
+ }
+
+ public int getM()
+ {
+ return 113;
+ }
+
+ public int getK1()
+ {
+ return 9;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecT113FieldElement))
+ {
+ return false;
+ }
+
+ SecT113FieldElement o = (SecT113FieldElement)other;
+ return Nat128.eq64(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return 113009 ^ Arrays.hashCode(x, 0, 2);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Curve.java
new file mode 100644
index 00000000..7c12d0a3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Curve.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT113R1Curve extends AbstractF2m
+{
+ private static final int SecT113R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT113R1Point infinity;
+
+ public SecT113R1Curve()
+ {
+ super(113, 9, 0, 0);
+
+ this.infinity = new SecT113R1Point(this, null, null);
+
+ this.a = fromBigInteger(new BigInteger(1, Hex.decode("003088250CA6E7C7FE649CE85820F7")));
+ this.b = fromBigInteger(new BigInteger(1, Hex.decode("00E8BEE4D3E2260744188BE0E9C723")));
+ this.order = new BigInteger(1, Hex.decode("0100000000000000D9CCEC8A39E56F"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT113R1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT113R1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 113;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT113FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT113R1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT113R1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 113;
+ }
+
+ public boolean isTrinomial()
+ {
+ return true;
+ }
+
+ public int getK1()
+ {
+ return 9;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Point.java
new file mode 100644
index 00000000..28226c28
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Point.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT113R1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT113R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT113R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT113R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT113R1Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+ X3 = L.square().add(L).add(X1).add(curve.getA());
+ if (X3.isZero())
+ {
+ return new SecT113R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+ return new SecT113R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT113R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement a = curve.getA();
+ ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq);
+ ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq);
+ if (T.isZero())
+ {
+ return new SecT113R1Point(curve, T, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT113R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+ ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+ ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+ return new SecT113R1Point(curve, A, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT113R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT113R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Curve.java
new file mode 100644
index 00000000..209987bc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Curve.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT113R2Curve extends AbstractF2m
+{
+ private static final int SecT113R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT113R2Point infinity;
+
+ public SecT113R2Curve()
+ {
+ super(113, 9, 0, 0);
+
+ this.infinity = new SecT113R2Point(this, null, null);
+
+ this.a = fromBigInteger(new BigInteger(1, Hex.decode("00689918DBEC7E5A0DD6DFC0AA55C7")));
+ this.b = fromBigInteger(new BigInteger(1, Hex.decode("0095E9A9EC9B297BD4BF36E059184F")));
+ this.order = new BigInteger(1, Hex.decode("010000000000000108789B2496AF93"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT113R2_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT113R2Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 113;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT113FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT113R2Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT113R2Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 113;
+ }
+
+ public boolean isTrinomial()
+ {
+ return true;
+ }
+
+ public int getK1()
+ {
+ return 9;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Point.java
new file mode 100644
index 00000000..6b7a2de3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Point.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT113R2Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT113R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT113R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT113R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT113R2Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+ X3 = L.square().add(L).add(X1).add(curve.getA());
+ if (X3.isZero())
+ {
+ return new SecT113R2Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+ return new SecT113R2Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT113R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement a = curve.getA();
+ ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq);
+ ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq);
+ if (T.isZero())
+ {
+ return new SecT113R2Point(curve, T, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT113R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+ ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+ ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+ return new SecT113R2Point(curve, A, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT113R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT113R2Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131Field.java
new file mode 100644
index 00000000..ab25c5df
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131Field.java
@@ -0,0 +1,332 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Interleave;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat192;
+
+public class SecT131Field
+{
+ private static final long M03 = -1L >>> 61;
+ private static final long M44 = -1L >>> 20;
+
+ private static final long[] ROOT_Z = new long[]{ 0x26BC4D789AF13523L, 0x26BC4D789AF135E2L, 0x6L };
+
+ public static void add(long[] x, long[] y, long[] z)
+ {
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
+ z[2] = x[2] ^ y[2];
+ }
+
+ public static void addExt(long[] xx, long[] yy, long[] zz)
+ {
+ zz[0] = xx[0] ^ yy[0];
+ zz[1] = xx[1] ^ yy[1];
+ zz[2] = xx[2] ^ yy[2];
+ zz[3] = xx[3] ^ yy[3];
+ zz[4] = xx[4] ^ yy[4];
+ }
+
+ public static void addOne(long[] x, long[] z)
+ {
+ z[0] = x[0] ^ 1L;
+ z[1] = x[1];
+ z[2] = x[2];
+ }
+
+ public static long[] fromBigInteger(BigInteger x)
+ {
+ long[] z = Nat192.fromBigInteger64(x);
+ reduce61(z, 0);
+ return z;
+ }
+
+ public static void invert(long[] x, long[] z)
+ {
+ if (Nat192.isZero64(x))
+ {
+ throw new IllegalStateException();
+ }
+
+ // Itoh-Tsujii inversion
+
+ long[] t0 = Nat192.create64();
+ long[] t1 = Nat192.create64();
+
+ square(x, t0);
+ multiply(t0, x, t0);
+ squareN(t0, 2, t1);
+ multiply(t1, t0, t1);
+ squareN(t1, 4, t0);
+ multiply(t0, t1, t0);
+ squareN(t0, 8, t1);
+ multiply(t1, t0, t1);
+ squareN(t1, 16, t0);
+ multiply(t0, t1, t0);
+ squareN(t0, 32, t1);
+ multiply(t1, t0, t1);
+ square(t1, t1);
+ multiply(t1, x, t1);
+ squareN(t1, 65, t0);
+ multiply(t0, t1, t0);
+ square(t0, z);
+ }
+
+ public static void multiply(long[] x, long[] y, long[] z)
+ {
+ long[] tt = Nat192.createExt64();
+ implMultiply(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(long[] x, long[] y, long[] zz)
+ {
+ long[] tt = Nat192.createExt64();
+ implMultiply(x, y, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void reduce(long[] xx, long[] z)
+ {
+ long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4];
+
+ x1 ^= (x4 << 61) ^ (x4 << 63);
+ x2 ^= (x4 >>> 3) ^ (x4 >>> 1) ^ x4 ^ (x4 << 5);
+ x3 ^= (x4 >>> 59);
+
+ x0 ^= (x3 << 61) ^ (x3 << 63);
+ x1 ^= (x3 >>> 3) ^ (x3 >>> 1) ^ x3 ^ (x3 << 5);
+ x2 ^= (x3 >>> 59);
+
+ long t = x2 >>> 3;
+ z[0] = x0 ^ t ^ (t << 2) ^ (t << 3) ^ (t << 8);
+ z[1] = x1 ^ (t >>> 56);
+ z[2] = x2 & M03;
+ }
+
+ public static void reduce61(long[] z, int zOff)
+ {
+ long z2 = z[zOff + 2], t = z2 >>> 3;
+ z[zOff ] ^= t ^ (t << 2) ^ (t << 3) ^ (t << 8);
+ z[zOff + 1] ^= (t >>> 56);
+ z[zOff + 2] = z2 & M03;
+ }
+
+ public static void sqrt(long[] x, long[] z)
+ {
+ long[] odd = Nat192.create64();
+
+ long u0, u1;
+ u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]);
+ long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ odd[0] = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ u0 = Interleave.unshuffle(x[2]);
+ long e1 = (u0 & 0x00000000FFFFFFFFL);
+ odd[1] = (u0 >>> 32);
+
+ multiply(odd, ROOT_Z, z);
+
+ z[0] ^= e0;
+ z[1] ^= e1;
+ }
+
+ public static void square(long[] x, long[] z)
+ {
+ long[] tt = Nat.create64(5);
+ implSquare(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareAddToExt(long[] x, long[] zz)
+ {
+ long[] tt = Nat.create64(5);
+ implSquare(x, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void squareN(long[] x, int n, long[] z)
+ {
+// assert n > 0;
+
+ long[] tt = Nat.create64(5);
+ implSquare(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ implSquare(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static int trace(long[] x)
+ {
+ // Non-zero-trace bits: 0, 123, 129
+ return (int)(x[0] ^ (x[1] >>> 59) ^ (x[2] >>> 1)) & 1;
+ }
+
+ protected static void implCompactExt(long[] zz)
+ {
+ long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5];
+ zz[0] = z0 ^ (z1 << 44);
+ zz[1] = (z1 >>> 20) ^ (z2 << 24);
+ zz[2] = (z2 >>> 40) ^ (z3 << 4)
+ ^ (z4 << 48);
+ zz[3] = (z3 >>> 60) ^ (z5 << 28)
+ ^ (z4 >>> 16);
+ zz[4] = (z5 >>> 36);
+ zz[5] = 0;
+ }
+
+ protected static void implMultiply(long[] x, long[] y, long[] zz)
+ {
+ /*
+ * "Five-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein.
+ */
+
+ long f0 = x[0], f1 = x[1], f2 = x[2];
+ f2 = ((f1 >>> 24) ^ (f2 << 40)) & M44;
+ f1 = ((f0 >>> 44) ^ (f1 << 20)) & M44;
+ f0 &= M44;
+
+ long g0 = y[0], g1 = y[1], g2 = y[2];
+ g2 = ((g1 >>> 24) ^ (g2 << 40)) & M44;
+ g1 = ((g0 >>> 44) ^ (g1 << 20)) & M44;
+ g0 &= M44;
+
+ long[] H = new long[10];
+
+ implMulw(f0, g0, H, 0); // H(0) 44/43 bits
+ implMulw(f2, g2, H, 2); // H(INF) 44/41 bits
+
+ long t0 = f0 ^ f1 ^ f2;
+ long t1 = g0 ^ g1 ^ g2;
+
+ implMulw(t0, t1, H, 4); // H(1) 44/43 bits
+
+ long t2 = (f1 << 1) ^ (f2 << 2);
+ long t3 = (g1 << 1) ^ (g2 << 2);
+
+ implMulw(f0 ^ t2, g0 ^ t3, H, 6); // H(t) 44/45 bits
+ implMulw(t0 ^ t2, t1 ^ t3, H, 8); // H(t + 1) 44/45 bits
+
+ long t4 = H[6] ^ H[8];
+ long t5 = H[7] ^ H[9];
+
+ // assert t5 >>> 44 == 0;
+
+ // Calculate V
+ long v0 = (t4 << 1) ^ H[6];
+ long v1 = t4 ^ (t5 << 1) ^ H[7];
+ long v2 = t5;
+
+ // Calculate U
+ long u0 = H[0];
+ long u1 = H[1] ^ H[0] ^ H[4];
+ long u2 = H[1] ^ H[5];
+
+ // Calculate W
+ long w0 = u0 ^ v0 ^ (H[2] << 4) ^ (H[2] << 1);
+ long w1 = u1 ^ v1 ^ (H[3] << 4) ^ (H[3] << 1);
+ long w2 = u2 ^ v2;
+
+ // Propagate carries
+ w1 ^= (w0 >>> 44); w0 &= M44;
+ w2 ^= (w1 >>> 44); w1 &= M44;
+
+ // assert (w0 & 1L) == 0;
+
+ // Divide W by t
+
+ w0 = (w0 >>> 1) ^ ((w1 & 1L) << 43);
+ w1 = (w1 >>> 1) ^ ((w2 & 1L) << 43);
+ w2 = (w2 >>> 1);
+
+ // Divide W by (t + 1)
+
+ w0 ^= (w0 << 1);
+ w0 ^= (w0 << 2);
+ w0 ^= (w0 << 4);
+ w0 ^= (w0 << 8);
+ w0 ^= (w0 << 16);
+ w0 ^= (w0 << 32);
+
+ w0 &= M44; w1 ^= (w0 >>> 43);
+
+ w1 ^= (w1 << 1);
+ w1 ^= (w1 << 2);
+ w1 ^= (w1 << 4);
+ w1 ^= (w1 << 8);
+ w1 ^= (w1 << 16);
+ w1 ^= (w1 << 32);
+
+ w1 &= M44; w2 ^= (w1 >>> 43);
+
+ w2 ^= (w2 << 1);
+ w2 ^= (w2 << 2);
+ w2 ^= (w2 << 4);
+ w2 ^= (w2 << 8);
+ w2 ^= (w2 << 16);
+ w2 ^= (w2 << 32);
+
+ // assert w2 >>> 42 == 0;
+
+ zz[0] = u0;
+ zz[1] = u1 ^ w0 ^ H[2];
+ zz[2] = u2 ^ w1 ^ w0 ^ H[3];
+ zz[3] = w2 ^ w1;
+ zz[4] = w2 ^ H[2];
+ zz[5] = H[3];
+
+ implCompactExt(zz);
+ }
+
+ protected static void implMulw(long x, long y, long[] z, int zOff)
+ {
+// assert x >>> 45 == 0;
+// assert y >>> 45 == 0;
+
+ long[] u = new long[8];
+// u[0] = 0;
+ u[1] = y;
+ u[2] = u[1] << 1;
+ u[3] = u[2] ^ y;
+ u[4] = u[2] << 1;
+ u[5] = u[4] ^ y;
+ u[6] = u[3] << 1;
+ u[7] = u[6] ^ y;
+
+ int j = (int)x;
+ long g, h = 0, l = u[j & 7]
+ ^ u[(j >>> 3) & 7] << 3
+ ^ u[(j >>> 6) & 7] << 6;
+ int k = 33;
+ do
+ {
+ j = (int)(x >>> k);
+ g = u[j & 7]
+ ^ u[(j >>> 3) & 7] << 3
+ ^ u[(j >>> 6) & 7] << 6
+ ^ u[(j >>> 9) & 7] << 9;
+ l ^= (g << k);
+ h ^= (g >>> -k);
+ }
+ while ((k -= 12) > 0);
+
+// assert h >>> 25 == 0;
+
+ z[zOff ] = l & M44;
+ z[zOff + 1] = (l >>> 44) ^ (h << 20);
+ }
+
+ protected static void implSquare(long[] x, long[] zz)
+ {
+ Interleave.expand64To128(x[0], zz, 0);
+ Interleave.expand64To128(x[1], zz, 2);
+
+ zz[4] = Interleave.expand8to16((int)x[2]) & 0xFFFFFFFFL;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131FieldElement.java
new file mode 100644
index 00000000..5dc18c5e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131FieldElement.java
@@ -0,0 +1,222 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat192;
+import org.bouncycastle.util.Arrays;
+
+public class SecT131FieldElement extends ECFieldElement
+{
+ protected long[] x;
+
+ public SecT131FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.bitLength() > 131)
+ {
+ throw new IllegalArgumentException("x value invalid for SecT131FieldElement");
+ }
+
+ this.x = SecT131Field.fromBigInteger(x);
+ }
+
+ public SecT131FieldElement()
+ {
+ this.x = Nat192.create64();
+ }
+
+ protected SecT131FieldElement(long[] x)
+ {
+ this.x = x;
+ }
+
+// public int bitLength()
+// {
+// return x.degree();
+// }
+
+ public boolean isOne()
+ {
+ return Nat192.isOne64(x);
+ }
+
+ public boolean isZero()
+ {
+ return Nat192.isZero64(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return (x[0] & 1L) != 0L;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat192.toBigInteger64(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecT131Field";
+ }
+
+ public int getFieldSize()
+ {
+ return 131;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ long[] z = Nat192.create64();
+ SecT131Field.add(x, ((SecT131FieldElement)b).x, z);
+ return new SecT131FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ long[] z = Nat192.create64();
+ SecT131Field.addOne(x, z);
+ return new SecT131FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ long[] z = Nat192.create64();
+ SecT131Field.multiply(x, ((SecT131FieldElement)b).x, z);
+ return new SecT131FieldElement(z);
+ }
+
+ public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ return multiplyPlusProduct(b, x, y);
+ }
+
+ public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x, bx = ((SecT131FieldElement)b).x;
+ long[] xx = ((SecT131FieldElement)x).x, yx = ((SecT131FieldElement)y).x;
+
+ long[] tt = Nat.create64(5);
+ SecT131Field.multiplyAddToExt(ax, bx, tt);
+ SecT131Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat192.create64();
+ SecT131Field.reduce(tt, z);
+ return new SecT131FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+ return multiply(b.invert());
+ }
+
+ public ECFieldElement negate()
+ {
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ long[] z = Nat192.create64();
+ SecT131Field.square(x, z);
+ return new SecT131FieldElement(z);
+ }
+
+ public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ return squarePlusProduct(x, y);
+ }
+
+ public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x;
+ long[] xx = ((SecT131FieldElement)x).x, yx = ((SecT131FieldElement)y).x;
+
+ long[] tt = Nat.create64(5);
+ SecT131Field.squareAddToExt(ax, tt);
+ SecT131Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat192.create64();
+ SecT131Field.reduce(tt, z);
+ return new SecT131FieldElement(z);
+ }
+
+ public ECFieldElement squarePow(int pow)
+ {
+ if (pow < 1)
+ {
+ return this;
+ }
+
+ long[] z = Nat192.create64();
+ SecT131Field.squareN(x, pow, z);
+ return new SecT131FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+ long[] z = Nat192.create64();
+ SecT131Field.invert(x, z);
+ return new SecT131FieldElement(z);
+ }
+
+ public ECFieldElement sqrt()
+ {
+ long[] z = Nat192.create64();
+ SecT131Field.sqrt(x, z);
+ return new SecT131FieldElement(z);
+ }
+
+ public int getRepresentation()
+ {
+ return ECFieldElement.F2m.PPB;
+ }
+
+ public int getM()
+ {
+ return 131;
+ }
+
+ public int getK1()
+ {
+ return 2;
+ }
+
+ public int getK2()
+ {
+ return 3;
+ }
+
+ public int getK3()
+ {
+ return 8;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecT131FieldElement))
+ {
+ return false;
+ }
+
+ SecT131FieldElement o = (SecT131FieldElement)other;
+ return Nat192.eq64(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return 131832 ^ Arrays.hashCode(x, 0, 3);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Curve.java
new file mode 100644
index 00000000..6b216adf
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Curve.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT131R1Curve extends AbstractF2m
+{
+ private static final int SecT131R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT131R1Point infinity;
+
+ public SecT131R1Curve()
+ {
+ super(131, 2, 3, 8);
+
+ this.infinity = new SecT131R1Point(this, null, null);
+
+ this.a = fromBigInteger(new BigInteger(1, Hex.decode("07A11B09A76B562144418FF3FF8C2570B8")));
+ this.b = fromBigInteger(new BigInteger(1, Hex.decode("0217C05610884B63B9C6C7291678F9D341")));
+ this.order = new BigInteger(1, Hex.decode("0400000000000000023123953A9464B54D"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT131R1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT131R1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 131;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT131FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT131R1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT131R1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 131;
+ }
+
+ public boolean isTrinomial()
+ {
+ return false;
+ }
+
+ public int getK1()
+ {
+ return 2;
+ }
+
+ public int getK2()
+ {
+ return 3;
+ }
+
+ public int getK3()
+ {
+ return 8;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Point.java
new file mode 100644
index 00000000..4a276dd5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Point.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT131R1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT131R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT131R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT131R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT131R1Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+ X3 = L.square().add(L).add(X1).add(curve.getA());
+ if (X3.isZero())
+ {
+ return new SecT131R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+ return new SecT131R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT131R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement a = curve.getA();
+ ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq);
+ ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq);
+ if (T.isZero())
+ {
+ return new SecT131R1Point(curve, T, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT131R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+ ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+ ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+ return new SecT131R1Point(curve, A, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT131R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT131R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Curve.java
new file mode 100644
index 00000000..8d19e375
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Curve.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT131R2Curve extends AbstractF2m
+{
+ private static final int SecT131R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT131R2Point infinity;
+
+ public SecT131R2Curve()
+ {
+ super(131, 2, 3, 8);
+
+ this.infinity = new SecT131R2Point(this, null, null);
+
+ this.a = fromBigInteger(new BigInteger(1, Hex.decode("03E5A88919D7CAFCBF415F07C2176573B2")));
+ this.b = fromBigInteger(new BigInteger(1, Hex.decode("04B8266A46C55657AC734CE38F018F2192")));
+ this.order = new BigInteger(1, Hex.decode("0400000000000000016954A233049BA98F"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT131R2_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT131R2Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 131;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT131FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT131R2Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT131R2Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 131;
+ }
+
+ public boolean isTrinomial()
+ {
+ return false;
+ }
+
+ public int getK1()
+ {
+ return 2;
+ }
+
+ public int getK2()
+ {
+ return 3;
+ }
+
+ public int getK3()
+ {
+ return 8;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Point.java
new file mode 100644
index 00000000..7e85c63a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Point.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT131R2Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT131R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT131R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT131R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT131R2Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+ X3 = L.square().add(L).add(X1).add(curve.getA());
+ if (X3.isZero())
+ {
+ return new SecT131R2Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+ return new SecT131R2Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT131R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement a = curve.getA();
+ ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq);
+ ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq);
+ if (T.isZero())
+ {
+ return new SecT131R2Point(curve, T, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT131R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+ ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+ ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+ return new SecT131R2Point(curve, A, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT131R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT131R2Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163Field.java
new file mode 100644
index 00000000..12eca28d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163Field.java
@@ -0,0 +1,341 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Interleave;
+import org.bouncycastle.math.raw.Nat192;
+
+public class SecT163Field
+{
+ private static final long M35 = -1L >>> 29;
+ private static final long M55 = -1L >>> 9;
+
+ private static final long[] ROOT_Z = new long[]{ 0xB6DB6DB6DB6DB6B0L, 0x492492492492DB6DL, 0x492492492L };
+
+ public static void add(long[] x, long[] y, long[] z)
+ {
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
+ z[2] = x[2] ^ y[2];
+ }
+
+ public static void addExt(long[] xx, long[] yy, long[] zz)
+ {
+ zz[0] = xx[0] ^ yy[0];
+ zz[1] = xx[1] ^ yy[1];
+ zz[2] = xx[2] ^ yy[2];
+ zz[3] = xx[3] ^ yy[3];
+ zz[4] = xx[4] ^ yy[4];
+ zz[5] = xx[5] ^ yy[5];
+ }
+
+ public static void addOne(long[] x, long[] z)
+ {
+ z[0] = x[0] ^ 1L;
+ z[1] = x[1];
+ z[2] = x[2];
+ }
+
+ public static long[] fromBigInteger(BigInteger x)
+ {
+ long[] z = Nat192.fromBigInteger64(x);
+ reduce29(z, 0);
+ return z;
+ }
+
+ public static void invert(long[] x, long[] z)
+ {
+ if (Nat192.isZero64(x))
+ {
+ throw new IllegalStateException();
+ }
+
+ // Itoh-Tsujii inversion with bases { 2, 3 }
+
+ long[] t0 = Nat192.create64();
+ long[] t1 = Nat192.create64();
+
+ square(x, t0);
+
+ // 3 | 162
+ squareN(t0, 1, t1);
+ multiply(t0, t1, t0);
+ squareN(t1, 1, t1);
+ multiply(t0, t1, t0);
+
+ // 3 | 54
+ squareN(t0, 3, t1);
+ multiply(t0, t1, t0);
+ squareN(t1, 3, t1);
+ multiply(t0, t1, t0);
+
+ // 3 | 18
+ squareN(t0, 9, t1);
+ multiply(t0, t1, t0);
+ squareN(t1, 9, t1);
+ multiply(t0, t1, t0);
+
+ // 3 | 6
+ squareN(t0, 27, t1);
+ multiply(t0, t1, t0);
+ squareN(t1, 27, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 2
+ squareN(t0, 81, t1);
+ multiply(t0, t1, z);
+ }
+
+ public static void multiply(long[] x, long[] y, long[] z)
+ {
+ long[] tt = Nat192.createExt64();
+ implMultiply(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(long[] x, long[] y, long[] zz)
+ {
+ long[] tt = Nat192.createExt64();
+ implMultiply(x, y, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void reduce(long[] xx, long[] z)
+ {
+ long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4], x5 = xx[5];
+
+ x2 ^= (x5 << 29) ^ (x5 << 32) ^ (x5 << 35) ^ (x5 << 36);
+ x3 ^= (x5 >>> 35) ^ (x5 >>> 32) ^ (x5 >>> 29) ^ (x5 >>> 28);
+
+ x1 ^= (x4 << 29) ^ (x4 << 32) ^ (x4 << 35) ^ (x4 << 36);
+ x2 ^= (x4 >>> 35) ^ (x4 >>> 32) ^ (x4 >>> 29) ^ (x4 >>> 28);
+
+ x0 ^= (x3 << 29) ^ (x3 << 32) ^ (x3 << 35) ^ (x3 << 36);
+ x1 ^= (x3 >>> 35) ^ (x3 >>> 32) ^ (x3 >>> 29) ^ (x3 >>> 28);
+
+ long t = x2 >>> 35;
+ z[0] = x0 ^ t ^ (t << 3) ^ (t << 6) ^ (t << 7);
+ z[1] = x1;
+ z[2] = x2 & M35;
+ }
+
+ public static void reduce29(long[] z, int zOff)
+ {
+ long z2 = z[zOff + 2], t = z2 >>> 35;
+ z[zOff ] ^= t ^ (t << 3) ^ (t << 6) ^ (t << 7);
+ z[zOff + 2] = z2 & M35;
+ }
+
+ public static void sqrt(long[] x, long[] z)
+ {
+ long[] odd = Nat192.create64();
+
+ long u0, u1;
+ u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]);
+ long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ odd[0] = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ u0 = Interleave.unshuffle(x[2]);
+ long e1 = (u0 & 0x00000000FFFFFFFFL);
+ odd[1] = (u0 >>> 32);
+
+ multiply(odd, ROOT_Z, z);
+
+ z[0] ^= e0;
+ z[1] ^= e1;
+ }
+
+ public static void square(long[] x, long[] z)
+ {
+ long[] tt = Nat192.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareAddToExt(long[] x, long[] zz)
+ {
+ long[] tt = Nat192.createExt64();
+ implSquare(x, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void squareN(long[] x, int n, long[] z)
+ {
+// assert n > 0;
+
+ long[] tt = Nat192.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ implSquare(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static int trace(long[] x)
+ {
+ // Non-zero-trace bits: 0, 157
+ return (int)(x[0] ^ (x[2] >>> 29)) & 1;
+ }
+
+ protected static void implCompactExt(long[] zz)
+ {
+ long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5];
+ zz[0] = z0 ^ (z1 << 55);
+ zz[1] = (z1 >>> 9) ^ (z2 << 46);
+ zz[2] = (z2 >>> 18) ^ (z3 << 37);
+ zz[3] = (z3 >>> 27) ^ (z4 << 28);
+ zz[4] = (z4 >>> 36) ^ (z5 << 19);
+ zz[5] = (z5 >>> 45);
+ }
+
+ protected static void implMultiply(long[] x, long[] y, long[] zz)
+ {
+ /*
+ * "Five-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein.
+ */
+
+ long f0 = x[0], f1 = x[1], f2 = x[2];
+ f2 = ((f1 >>> 46) ^ (f2 << 18));
+ f1 = ((f0 >>> 55) ^ (f1 << 9)) & M55;
+ f0 &= M55;
+
+ long g0 = y[0], g1 = y[1], g2 = y[2];
+ g2 = ((g1 >>> 46) ^ (g2 << 18));
+ g1 = ((g0 >>> 55) ^ (g1 << 9)) & M55;
+ g0 &= M55;
+
+ long[] H = new long[10];
+
+ implMulw(f0, g0, H, 0); // H(0) 55/54 bits
+ implMulw(f2, g2, H, 2); // H(INF) 55/50 bits
+
+ long t0 = f0 ^ f1 ^ f2;
+ long t1 = g0 ^ g1 ^ g2;
+
+ implMulw(t0, t1, H, 4); // H(1) 55/54 bits
+
+ long t2 = (f1 << 1) ^ (f2 << 2);
+ long t3 = (g1 << 1) ^ (g2 << 2);
+
+ implMulw(f0 ^ t2, g0 ^ t3, H, 6); // H(t) 55/56 bits
+ implMulw(t0 ^ t2, t1 ^ t3, H, 8); // H(t + 1) 55/56 bits
+
+ long t4 = H[6] ^ H[8];
+ long t5 = H[7] ^ H[9];
+
+// assert t5 >>> 55 == 0;
+
+ // Calculate V
+ long v0 = (t4 << 1) ^ H[6];
+ long v1 = t4 ^ (t5 << 1) ^ H[7];
+ long v2 = t5;
+
+ // Calculate U
+ long u0 = H[0];
+ long u1 = H[1] ^ H[0] ^ H[4];
+ long u2 = H[1] ^ H[5];
+
+ // Calculate W
+ long w0 = u0 ^ v0 ^ (H[2] << 4) ^ (H[2] << 1);
+ long w1 = u1 ^ v1 ^ (H[3] << 4) ^ (H[3] << 1);
+ long w2 = u2 ^ v2;
+
+ // Propagate carries
+ w1 ^= (w0 >>> 55); w0 &= M55;
+ w2 ^= (w1 >>> 55); w1 &= M55;
+
+// assert (w0 & 1L) == 0;
+
+ // Divide W by t
+
+ w0 = (w0 >>> 1) ^ ((w1 & 1L) << 54);
+ w1 = (w1 >>> 1) ^ ((w2 & 1L) << 54);
+ w2 = (w2 >>> 1);
+
+ // Divide W by (t + 1)
+
+ w0 ^= (w0 << 1);
+ w0 ^= (w0 << 2);
+ w0 ^= (w0 << 4);
+ w0 ^= (w0 << 8);
+ w0 ^= (w0 << 16);
+ w0 ^= (w0 << 32);
+
+ w0 &= M55; w1 ^= (w0 >>> 54);
+
+ w1 ^= (w1 << 1);
+ w1 ^= (w1 << 2);
+ w1 ^= (w1 << 4);
+ w1 ^= (w1 << 8);
+ w1 ^= (w1 << 16);
+ w1 ^= (w1 << 32);
+
+ w1 &= M55; w2 ^= (w1 >>> 54);
+
+ w2 ^= (w2 << 1);
+ w2 ^= (w2 << 2);
+ w2 ^= (w2 << 4);
+ w2 ^= (w2 << 8);
+ w2 ^= (w2 << 16);
+ w2 ^= (w2 << 32);
+
+// assert w2 >>> 52 == 0;
+
+ zz[0] = u0;
+ zz[1] = u1 ^ w0 ^ H[2];
+ zz[2] = u2 ^ w1 ^ w0 ^ H[3];
+ zz[3] = w2 ^ w1;
+ zz[4] = w2 ^ H[2];
+ zz[5] = H[3];
+
+ implCompactExt(zz);
+ }
+
+ protected static void implMulw(long x, long y, long[] z, int zOff)
+ {
+// assert x >>> 56 == 0;
+// assert y >>> 56 == 0;
+
+ long[] u = new long[8];
+// u[0] = 0;
+ u[1] = y;
+ u[2] = u[1] << 1;
+ u[3] = u[2] ^ y;
+ u[4] = u[2] << 1;
+ u[5] = u[4] ^ y;
+ u[6] = u[3] << 1;
+ u[7] = u[6] ^ y;
+
+ int j = (int)x;
+ long g, h = 0, l = u[j & 3];
+ int k = 47;
+ do
+ {
+ j = (int)(x >>> k);
+ g = u[j & 7]
+ ^ u[(j >>> 3) & 7] << 3
+ ^ u[(j >>> 6) & 7] << 6;
+ l ^= (g << k);
+ h ^= (g >>> -k);
+ }
+ while ((k -= 9) > 0);
+
+// assert h >>> 47 == 0;
+
+ z[zOff ] = l & M55;
+ z[zOff + 1] = (l >>> 55) ^ (h << 9);
+ }
+
+ protected static void implSquare(long[] x, long[] zz)
+ {
+ Interleave.expand64To128(x[0], zz, 0);
+ Interleave.expand64To128(x[1], zz, 2);
+
+ long x2 = x[2];
+ zz[4] = Interleave.expand32to64((int)x2);
+ zz[5] = Interleave.expand8to16((int)(x2 >>> 32)) & 0xFFFFFFFFL;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163FieldElement.java
new file mode 100644
index 00000000..7a95c222
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163FieldElement.java
@@ -0,0 +1,221 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Nat192;
+import org.bouncycastle.util.Arrays;
+
+public class SecT163FieldElement extends ECFieldElement
+{
+ protected long[] x;
+
+ public SecT163FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.bitLength() > 163)
+ {
+ throw new IllegalArgumentException("x value invalid for SecT163FieldElement");
+ }
+
+ this.x = SecT163Field.fromBigInteger(x);
+ }
+
+ public SecT163FieldElement()
+ {
+ this.x = Nat192.create64();
+ }
+
+ protected SecT163FieldElement(long[] x)
+ {
+ this.x = x;
+ }
+
+// public int bitLength()
+// {
+// return x.degree();
+// }
+
+ public boolean isOne()
+ {
+ return Nat192.isOne64(x);
+ }
+
+ public boolean isZero()
+ {
+ return Nat192.isZero64(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return (x[0] & 1L) != 0L;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat192.toBigInteger64(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecT163Field";
+ }
+
+ public int getFieldSize()
+ {
+ return 163;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ long[] z = Nat192.create64();
+ SecT163Field.add(x, ((SecT163FieldElement)b).x, z);
+ return new SecT163FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ long[] z = Nat192.create64();
+ SecT163Field.addOne(x, z);
+ return new SecT163FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ long[] z = Nat192.create64();
+ SecT163Field.multiply(x, ((SecT163FieldElement)b).x, z);
+ return new SecT163FieldElement(z);
+ }
+
+ public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ return multiplyPlusProduct(b, x, y);
+ }
+
+ public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x, bx = ((SecT163FieldElement)b).x;
+ long[] xx = ((SecT163FieldElement)x).x, yx = ((SecT163FieldElement)y).x;
+
+ long[] tt = Nat192.createExt64();
+ SecT163Field.multiplyAddToExt(ax, bx, tt);
+ SecT163Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat192.create64();
+ SecT163Field.reduce(tt, z);
+ return new SecT163FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+ return multiply(b.invert());
+ }
+
+ public ECFieldElement negate()
+ {
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ long[] z = Nat192.create64();
+ SecT163Field.square(x, z);
+ return new SecT163FieldElement(z);
+ }
+
+ public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ return squarePlusProduct(x, y);
+ }
+
+ public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x;
+ long[] xx = ((SecT163FieldElement)x).x, yx = ((SecT163FieldElement)y).x;
+
+ long[] tt = Nat192.createExt64();
+ SecT163Field.squareAddToExt(ax, tt);
+ SecT163Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat192.create64();
+ SecT163Field.reduce(tt, z);
+ return new SecT163FieldElement(z);
+ }
+
+ public ECFieldElement squarePow(int pow)
+ {
+ if (pow < 1)
+ {
+ return this;
+ }
+
+ long[] z = Nat192.create64();
+ SecT163Field.squareN(x, pow, z);
+ return new SecT163FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+ long[] z = Nat192.create64();
+ SecT163Field.invert(x, z);
+ return new SecT163FieldElement(z);
+ }
+
+ public ECFieldElement sqrt()
+ {
+ long[] z = Nat192.create64();
+ SecT163Field.sqrt(x, z);
+ return new SecT163FieldElement(z);
+ }
+
+ public int getRepresentation()
+ {
+ return ECFieldElement.F2m.PPB;
+ }
+
+ public int getM()
+ {
+ return 163;
+ }
+
+ public int getK1()
+ {
+ return 3;
+ }
+
+ public int getK2()
+ {
+ return 6;
+ }
+
+ public int getK3()
+ {
+ return 7;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecT163FieldElement))
+ {
+ return false;
+ }
+
+ SecT163FieldElement o = (SecT163FieldElement)other;
+ return Nat192.eq64(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return 163763 ^ Arrays.hashCode(x, 0, 3);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Curve.java
new file mode 100644
index 00000000..387cc555
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Curve.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECMultiplier;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.WTauNafMultiplier;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT163K1Curve extends AbstractF2m
+{
+ private static final int SecT163K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT163K1Point infinity;
+
+ public SecT163K1Curve()
+ {
+ super(163, 3, 6, 7);
+
+ this.infinity = new SecT163K1Point(this, null, null);
+
+ this.a = fromBigInteger(BigInteger.valueOf(1));
+ this.b = this.a;
+ this.order = new BigInteger(1, Hex.decode("04000000000000000000020108A2E0CC0D99F8A5EF"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT163K1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT163K1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected ECMultiplier createDefaultMultiplier()
+ {
+ return new WTauNafMultiplier();
+ }
+
+ public int getFieldSize()
+ {
+ return 163;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT163FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT163K1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT163K1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return true;
+ }
+
+ public int getM()
+ {
+ return 163;
+ }
+
+ public boolean isTrinomial()
+ {
+ return false;
+ }
+
+ public int getK1()
+ {
+ return 3;
+ }
+
+ public int getK2()
+ {
+ return 6;
+ }
+
+ public int getK3()
+ {
+ return 7;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Point.java
new file mode 100644
index 00000000..9c483195
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Point.java
@@ -0,0 +1,314 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT163K1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT163K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT163K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT163K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT163K1Point(null, this.getAffineXCoord(), this.getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+// X3 = L.square().add(L).add(X1).add(curve.getA());
+ X3 = L.square().add(L).add(X1).addOne();
+ if (X3.isZero())
+ {
+// return new SecT163K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT163K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+// return new SecT163K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT163K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT163K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq);
+ if (T.isZero())
+ {
+// return new SecT163K1Point(curve, T, curve.getB().sqrt(), withCompression);
+ return new SecT163K1Point(curve, T, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement t1 = L1.add(X1).square();
+ ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(X3);
+
+ return new SecT163K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ // NOTE: twicePlus() only optimized for lambda-affine argument
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement A = L2.multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+// return new SecT163K1Point(curve, A, curve.getB().sqrt(), withCompression);
+ return new SecT163K1Point(curve, A, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT163K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT163K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Curve.java
new file mode 100644
index 00000000..88b14c0b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Curve.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT163R1Curve extends AbstractF2m
+{
+ private static final int SecT163R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT163R1Point infinity;
+
+ public SecT163R1Curve()
+ {
+ super(163, 3, 6, 7);
+
+ this.infinity = new SecT163R1Point(this, null, null);
+
+ this.a = fromBigInteger(new BigInteger(1, Hex.decode("07B6882CAAEFA84F9554FF8428BD88E246D2782AE2")));
+ this.b = fromBigInteger(new BigInteger(1, Hex.decode("0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9")));
+ this.order = new BigInteger(1, Hex.decode("03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT163R1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT163R1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 163;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT163FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT163R1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT163R1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 163;
+ }
+
+ public boolean isTrinomial()
+ {
+ return false;
+ }
+
+ public int getK1()
+ {
+ return 3;
+ }
+
+ public int getK2()
+ {
+ return 6;
+ }
+
+ public int getK3()
+ {
+ return 7;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Point.java
new file mode 100644
index 00000000..1c3355ee
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Point.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT163R1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT163R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT163R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT163R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT163R1Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+ X3 = L.square().add(L).add(X1).add(curve.getA());
+ if (X3.isZero())
+ {
+ return new SecT163R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+ return new SecT163R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT163R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement a = curve.getA();
+ ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq);
+ ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq);
+ if (T.isZero())
+ {
+ return new SecT163R1Point(curve, T, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT163R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+ ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+ ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+ return new SecT163R1Point(curve, A, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT163R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT163R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Curve.java
new file mode 100644
index 00000000..44054fee
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Curve.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT163R2Curve extends AbstractF2m
+{
+ private static final int SecT163R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT163R2Point infinity;
+
+ public SecT163R2Curve()
+ {
+ super(163, 3, 6, 7);
+
+ this.infinity = new SecT163R2Point(this, null, null);
+
+ this.a = fromBigInteger(BigInteger.valueOf(1));
+ this.b = fromBigInteger(new BigInteger(1, Hex.decode("020A601907B8C953CA1481EB10512F78744A3205FD")));
+ this.order = new BigInteger(1, Hex.decode("040000000000000000000292FE77E70C12A4234C33"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT163R2_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT163R2Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 163;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT163FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT163R2Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT163R2Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 163;
+ }
+
+ public boolean isTrinomial()
+ {
+ return false;
+ }
+
+ public int getK1()
+ {
+ return 3;
+ }
+
+ public int getK2()
+ {
+ return 6;
+ }
+
+ public int getK3()
+ {
+ return 7;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Point.java
new file mode 100644
index 00000000..dcadede8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Point.java
@@ -0,0 +1,309 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT163R2Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT163R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT163R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT163R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT163R2Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+// X3 = L.square().add(L).add(X1).add(curve.getA());
+ X3 = L.square().add(L).add(X1).addOne();
+ if (X3.isZero())
+ {
+ return new SecT163R2Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+ return new SecT163R2Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT163R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq);
+ if (T.isZero())
+ {
+ return new SecT163R2Point(curve, T, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT163R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement A = L2.multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+ return new SecT163R2Point(curve, A, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT163R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT163R2Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193Field.java
new file mode 100644
index 00000000..2e5e1866
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193Field.java
@@ -0,0 +1,306 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Interleave;
+import org.bouncycastle.math.raw.Nat256;
+
+public class SecT193Field
+{
+ private static final long M01 = 1L;
+ private static final long M49 = -1L >>> 15;
+
+ public static void add(long[] x, long[] y, long[] z)
+ {
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
+ z[2] = x[2] ^ y[2];
+ z[3] = x[3] ^ y[3];
+ }
+
+ public static void addExt(long[] xx, long[] yy, long[] zz)
+ {
+ zz[0] = xx[0] ^ yy[0];
+ zz[1] = xx[1] ^ yy[1];
+ zz[2] = xx[2] ^ yy[2];
+ zz[3] = xx[3] ^ yy[3];
+ zz[4] = xx[4] ^ yy[4];
+ zz[5] = xx[5] ^ yy[5];
+ zz[6] = xx[6] ^ yy[6];
+ }
+
+ public static void addOne(long[] x, long[] z)
+ {
+ z[0] = x[0] ^ 1L;
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ }
+
+ public static long[] fromBigInteger(BigInteger x)
+ {
+ long[] z = Nat256.fromBigInteger64(x);
+ reduce63(z, 0);
+ return z;
+ }
+
+ public static void invert(long[] x, long[] z)
+ {
+ if (Nat256.isZero64(x))
+ {
+ throw new IllegalStateException();
+ }
+
+ // Itoh-Tsujii inversion with bases { 2, 3 }
+
+ long[] t0 = Nat256.create64();
+ long[] t1 = Nat256.create64();
+
+ square(x, t0);
+
+ // 3 | 192
+ squareN(t0, 1, t1);
+ multiply(t0, t1, t0);
+ squareN(t1, 1, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 64
+ squareN(t0, 3, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 32
+ squareN(t0, 6, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 16
+ squareN(t0, 12, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 8
+ squareN(t0, 24, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 4
+ squareN(t0, 48, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 2
+ squareN(t0, 96, t1);
+ multiply(t0, t1, z);
+ }
+
+ public static void multiply(long[] x, long[] y, long[] z)
+ {
+ long[] tt = Nat256.createExt64();
+ implMultiply(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(long[] x, long[] y, long[] zz)
+ {
+ long[] tt = Nat256.createExt64();
+ implMultiply(x, y, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void reduce(long[] xx, long[] z)
+ {
+ long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4], x5 = xx[5], x6 = xx[6];
+
+ x2 ^= (x6 << 63);
+ x3 ^= (x6 >>> 1) ^ (x6 << 14);
+ x4 ^= (x6 >>> 50);
+
+ x1 ^= (x5 << 63);
+ x2 ^= (x5 >>> 1) ^ (x5 << 14);
+ x3 ^= (x5 >>> 50);
+
+ x0 ^= (x4 << 63);
+ x1 ^= (x4 >>> 1) ^ (x4 << 14);
+ x2 ^= (x4 >>> 50);
+
+ long t = x3 >>> 1;
+ z[0] = x0 ^ t ^ (t << 15);
+ z[1] = x1 ^ (t >>> 49);
+ z[2] = x2;
+ z[3] = x3 & M01;
+ }
+
+ public static void reduce63(long[] z, int zOff)
+ {
+ long z3 = z[zOff + 3], t = z3 >>> 1;
+ z[zOff ] ^= t ^ (t << 15);
+ z[zOff + 1] ^= (t >>> 49);
+ z[zOff + 3] = z3 & M01;
+ }
+
+ public static void sqrt(long[] x, long[] z)
+ {
+ long u0, u1;
+ u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]);
+ long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ long c0 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ u0 = Interleave.unshuffle(x[2]);
+ long e1 = (u0 & 0x00000000FFFFFFFFL) ^ (x[3] << 32);
+ long c1 = (u0 >>> 32);
+
+ z[0] = e0 ^ (c0 << 8);
+ z[1] = e1 ^ (c1 << 8) ^ (c0 >>> 56) ^ (c0 << 33);
+ z[2] = (c1 >>> 56) ^ (c1 << 33) ^ (c0 >>> 31);
+ z[3] = (c1 >>> 31);
+ }
+
+ public static void square(long[] x, long[] z)
+ {
+ long[] tt = Nat256.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareAddToExt(long[] x, long[] zz)
+ {
+ long[] tt = Nat256.createExt64();
+ implSquare(x, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void squareN(long[] x, int n, long[] z)
+ {
+// assert n > 0;
+
+ long[] tt = Nat256.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ implSquare(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static int trace(long[] x)
+ {
+ // Non-zero-trace bits: 0
+ return (int)(x[0]) & 1;
+ }
+
+ protected static void implCompactExt(long[] zz)
+ {
+ long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5], z6 = zz[6], z7 = zz[7];
+ zz[0] = z0 ^ (z1 << 49);
+ zz[1] = (z1 >>> 15) ^ (z2 << 34);
+ zz[2] = (z2 >>> 30) ^ (z3 << 19);
+ zz[3] = (z3 >>> 45) ^ (z4 << 4)
+ ^ (z5 << 53);
+ zz[4] = (z4 >>> 60) ^ (z6 << 38)
+ ^ (z5 >>> 11);
+ zz[5] = (z6 >>> 26) ^ (z7 << 23);
+ zz[6] = (z7 >>> 41);
+ zz[7] = 0;
+ }
+
+ protected static void implExpand(long[] x, long[] z)
+ {
+ long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+ z[0] = x0 & M49;
+ z[1] = ((x0 >>> 49) ^ (x1 << 15)) & M49;
+ z[2] = ((x1 >>> 34) ^ (x2 << 30)) & M49;
+ z[3] = ((x2 >>> 19) ^ (x3 << 45));
+ }
+
+ protected static void implMultiply(long[] x, long[] y, long[] zz)
+ {
+ /*
+ * "Two-level seven-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein.
+ */
+
+ long[] f = new long[4], g = new long[4];
+ implExpand(x, f);
+ implExpand(y, g);
+
+ implMulwAcc(f[0], g[0], zz, 0);
+ implMulwAcc(f[1], g[1], zz, 1);
+ implMulwAcc(f[2], g[2], zz, 2);
+ implMulwAcc(f[3], g[3], zz, 3);
+
+ // U *= (1 - t^n)
+ for (int i = 5; i > 0; --i)
+ {
+ zz[i] ^= zz[i - 1];
+ }
+
+ implMulwAcc(f[0] ^ f[1], g[0] ^ g[1], zz, 1);
+ implMulwAcc(f[2] ^ f[3], g[2] ^ g[3], zz, 3);
+
+ // V *= (1 - t^2n)
+ for (int i = 7; i > 1; --i)
+ {
+ zz[i] ^= zz[i - 2];
+ }
+
+ // Double-length recursion
+ {
+ long c0 = f[0] ^ f[2], c1 = f[1] ^ f[3];
+ long d0 = g[0] ^ g[2], d1 = g[1] ^ g[3];
+ implMulwAcc(c0 ^ c1, d0 ^ d1, zz, 3);
+ long[] t = new long[3];
+ implMulwAcc(c0, d0, t, 0);
+ implMulwAcc(c1, d1, t, 1);
+ long t0 = t[0], t1 = t[1], t2 = t[2];
+ zz[2] ^= t0;
+ zz[3] ^= t0 ^ t1;
+ zz[4] ^= t2 ^ t1;
+ zz[5] ^= t2;
+ }
+
+ implCompactExt(zz);
+ }
+
+ protected static void implMulwAcc(long x, long y, long[] z, int zOff)
+ {
+// assert x >>> 49 == 0;
+// assert y >>> 49 == 0;
+
+ long[] u = new long[8];
+// u[0] = 0;
+ u[1] = y;
+ u[2] = u[1] << 1;
+ u[3] = u[2] ^ y;
+ u[4] = u[2] << 1;
+ u[5] = u[4] ^ y;
+ u[6] = u[3] << 1;
+ u[7] = u[6] ^ y;
+
+ int j = (int)x;
+ long g, h = 0, l = u[j & 7]
+ ^ (u[(j >>> 3) & 7] << 3);
+ int k = 36;
+ do
+ {
+ j = (int)(x >>> k);
+ g = u[j & 7]
+ ^ u[(j >>> 3) & 7] << 3
+ ^ u[(j >>> 6) & 7] << 6
+ ^ u[(j >>> 9) & 7] << 9
+ ^ u[(j >>> 12) & 7] << 12;
+ l ^= (g << k);
+ h ^= (g >>> -k);
+ }
+ while ((k -= 15) > 0);
+
+// assert h >>> 33 == 0;
+
+ z[zOff ] ^= l & M49;
+ z[zOff + 1] ^= (l >>> 49) ^ (h << 15);
+ }
+
+ protected static void implSquare(long[] x, long[] zz)
+ {
+ Interleave.expand64To128(x[0], zz, 0);
+ Interleave.expand64To128(x[1], zz, 2);
+ Interleave.expand64To128(x[2], zz, 4);
+ zz[6] = (x[3] & M01);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193FieldElement.java
new file mode 100644
index 00000000..d7a712db
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193FieldElement.java
@@ -0,0 +1,221 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Nat256;
+import org.bouncycastle.util.Arrays;
+
+public class SecT193FieldElement extends ECFieldElement
+{
+ protected long[] x;
+
+ public SecT193FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.bitLength() > 193)
+ {
+ throw new IllegalArgumentException("x value invalid for SecT193FieldElement");
+ }
+
+ this.x = SecT193Field.fromBigInteger(x);
+ }
+
+ public SecT193FieldElement()
+ {
+ this.x = Nat256.create64();
+ }
+
+ protected SecT193FieldElement(long[] x)
+ {
+ this.x = x;
+ }
+
+// public int bitLength()
+// {
+// return x.degree();
+// }
+
+ public boolean isOne()
+ {
+ return Nat256.isOne64(x);
+ }
+
+ public boolean isZero()
+ {
+ return Nat256.isZero64(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return (x[0] & 1L) != 0L;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat256.toBigInteger64(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecT193Field";
+ }
+
+ public int getFieldSize()
+ {
+ return 193;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ long[] z = Nat256.create64();
+ SecT193Field.add(x, ((SecT193FieldElement)b).x, z);
+ return new SecT193FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ long[] z = Nat256.create64();
+ SecT193Field.addOne(x, z);
+ return new SecT193FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ long[] z = Nat256.create64();
+ SecT193Field.multiply(x, ((SecT193FieldElement)b).x, z);
+ return new SecT193FieldElement(z);
+ }
+
+ public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ return multiplyPlusProduct(b, x, y);
+ }
+
+ public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x, bx = ((SecT193FieldElement)b).x;
+ long[] xx = ((SecT193FieldElement)x).x, yx = ((SecT193FieldElement)y).x;
+
+ long[] tt = Nat256.createExt64();
+ SecT193Field.multiplyAddToExt(ax, bx, tt);
+ SecT193Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat256.create64();
+ SecT193Field.reduce(tt, z);
+ return new SecT193FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+ return multiply(b.invert());
+ }
+
+ public ECFieldElement negate()
+ {
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ long[] z = Nat256.create64();
+ SecT193Field.square(x, z);
+ return new SecT193FieldElement(z);
+ }
+
+ public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ return squarePlusProduct(x, y);
+ }
+
+ public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x;
+ long[] xx = ((SecT193FieldElement)x).x, yx = ((SecT193FieldElement)y).x;
+
+ long[] tt = Nat256.createExt64();
+ SecT193Field.squareAddToExt(ax, tt);
+ SecT193Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat256.create64();
+ SecT193Field.reduce(tt, z);
+ return new SecT193FieldElement(z);
+ }
+
+ public ECFieldElement squarePow(int pow)
+ {
+ if (pow < 1)
+ {
+ return this;
+ }
+
+ long[] z = Nat256.create64();
+ SecT193Field.squareN(x, pow, z);
+ return new SecT193FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+ long[] z = Nat256.create64();
+ SecT193Field.invert(x, z);
+ return new SecT193FieldElement(z);
+ }
+
+ public ECFieldElement sqrt()
+ {
+ long[] z = Nat256.create64();
+ SecT193Field.sqrt(x, z);
+ return new SecT193FieldElement(z);
+ }
+
+ public int getRepresentation()
+ {
+ return ECFieldElement.F2m.TPB;
+ }
+
+ public int getM()
+ {
+ return 193;
+ }
+
+ public int getK1()
+ {
+ return 15;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecT193FieldElement))
+ {
+ return false;
+ }
+
+ SecT193FieldElement o = (SecT193FieldElement)other;
+ return Nat256.eq64(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return 1930015 ^ Arrays.hashCode(x, 0, 4);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Curve.java
new file mode 100644
index 00000000..2dd53d14
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Curve.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT193R1Curve extends AbstractF2m
+{
+ private static final int SecT193R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT193R1Point infinity;
+
+ public SecT193R1Curve()
+ {
+ super(193, 15, 0, 0);
+
+ this.infinity = new SecT193R1Point(this, null, null);
+
+ this.a = fromBigInteger(new BigInteger(1, Hex.decode("0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01")));
+ this.b = fromBigInteger(new BigInteger(1, Hex.decode("00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814")));
+ this.order = new BigInteger(1, Hex.decode("01000000000000000000000000C7F34A778F443ACC920EBA49"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT193R1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT193R1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 193;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT193FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT193R1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT193R1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 193;
+ }
+
+ public boolean isTrinomial()
+ {
+ return true;
+ }
+
+ public int getK1()
+ {
+ return 15;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Point.java
new file mode 100644
index 00000000..9997b8e1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Point.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT193R1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT193R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT193R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT193R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT193R1Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+ X3 = L.square().add(L).add(X1).add(curve.getA());
+ if (X3.isZero())
+ {
+ return new SecT193R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+ return new SecT193R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT193R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement a = curve.getA();
+ ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq);
+ ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq);
+ if (T.isZero())
+ {
+ return new SecT193R1Point(curve, T, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT193R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+ ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+ ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+ return new SecT193R1Point(curve, A, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT193R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT193R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Curve.java
new file mode 100644
index 00000000..334ce3a8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Curve.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT193R2Curve extends AbstractF2m
+{
+ private static final int SecT193R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT193R2Point infinity;
+
+ public SecT193R2Curve()
+ {
+ super(193, 15, 0, 0);
+
+ this.infinity = new SecT193R2Point(this, null, null);
+
+ this.a = fromBigInteger(new BigInteger(1, Hex.decode("0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B")));
+ this.b = fromBigInteger(new BigInteger(1, Hex.decode("00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE")));
+ this.order = new BigInteger(1, Hex.decode("010000000000000000000000015AAB561B005413CCD4EE99D5"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT193R2_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT193R2Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 193;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT193FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT193R2Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT193R2Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 193;
+ }
+
+ public boolean isTrinomial()
+ {
+ return true;
+ }
+
+ public int getK1()
+ {
+ return 15;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Point.java
new file mode 100644
index 00000000..f3bbb706
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Point.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT193R2Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT193R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT193R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT193R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT193R2Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+ X3 = L.square().add(L).add(X1).add(curve.getA());
+ if (X3.isZero())
+ {
+ return new SecT193R2Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+ return new SecT193R2Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT193R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement a = curve.getA();
+ ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq);
+ ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq);
+ if (T.isZero())
+ {
+ return new SecT193R2Point(curve, T, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT193R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+ ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+ ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+ return new SecT193R2Point(curve, A, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT193R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT193R2Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233Field.java
new file mode 100644
index 00000000..e5ab93a5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233Field.java
@@ -0,0 +1,318 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Interleave;
+import org.bouncycastle.math.raw.Nat256;
+
+public class SecT233Field
+{
+ private static final long M41 = -1L >>> 23;
+ private static final long M59 = -1L >>> 5;
+
+ public static void add(long[] x, long[] y, long[] z)
+ {
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
+ z[2] = x[2] ^ y[2];
+ z[3] = x[3] ^ y[3];
+ }
+
+ public static void addExt(long[] xx, long[] yy, long[] zz)
+ {
+ zz[0] = xx[0] ^ yy[0];
+ zz[1] = xx[1] ^ yy[1];
+ zz[2] = xx[2] ^ yy[2];
+ zz[3] = xx[3] ^ yy[3];
+ zz[4] = xx[4] ^ yy[4];
+ zz[5] = xx[5] ^ yy[5];
+ zz[6] = xx[6] ^ yy[6];
+ zz[7] = xx[7] ^ yy[7];
+ }
+
+ public static void addOne(long[] x, long[] z)
+ {
+ z[0] = x[0] ^ 1L;
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ }
+
+ public static long[] fromBigInteger(BigInteger x)
+ {
+ long[] z = Nat256.fromBigInteger64(x);
+ reduce23(z, 0);
+ return z;
+ }
+
+ public static void invert(long[] x, long[] z)
+ {
+ if (Nat256.isZero64(x))
+ {
+ throw new IllegalStateException();
+ }
+
+ // Itoh-Tsujii inversion
+
+ long[] t0 = Nat256.create64();
+ long[] t1 = Nat256.create64();
+
+ square(x, t0);
+ multiply(t0, x, t0);
+ square(t0, t0);
+ multiply(t0, x, t0);
+ squareN(t0, 3, t1);
+ multiply(t1, t0, t1);
+ square(t1, t1);
+ multiply(t1, x, t1);
+ squareN(t1, 7, t0);
+ multiply(t0, t1, t0);
+ squareN(t0, 14, t1);
+ multiply(t1, t0, t1);
+ square(t1, t1);
+ multiply(t1, x, t1);
+ squareN(t1, 29, t0);
+ multiply(t0, t1, t0);
+ squareN(t0, 58, t1);
+ multiply(t1, t0, t1);
+ squareN(t1, 116, t0);
+ multiply(t0, t1, t0);
+ square(t0, z);
+ }
+
+ public static void multiply(long[] x, long[] y, long[] z)
+ {
+ long[] tt = Nat256.createExt64();
+ implMultiply(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(long[] x, long[] y, long[] zz)
+ {
+ long[] tt = Nat256.createExt64();
+ implMultiply(x, y, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void reduce(long[] xx, long[] z)
+ {
+ long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3];
+ long x4 = xx[4], x5 = xx[5], x6 = xx[6], x7 = xx[7];
+
+ x3 ^= (x7 << 23);
+ x4 ^= (x7 >>> 41) ^ (x7 << 33);
+ x5 ^= (x7 >>> 31);
+
+ x2 ^= (x6 << 23);
+ x3 ^= (x6 >>> 41) ^ (x6 << 33);
+ x4 ^= (x6 >>> 31);
+
+ x1 ^= (x5 << 23);
+ x2 ^= (x5 >>> 41) ^ (x5 << 33);
+ x3 ^= (x5 >>> 31);
+
+ x0 ^= (x4 << 23);
+ x1 ^= (x4 >>> 41) ^ (x4 << 33);
+ x2 ^= (x4 >>> 31);
+
+ long t = x3 >>> 41;
+ z[0] = x0 ^ t;
+ z[1] = x1 ^ (t << 10);
+ z[2] = x2;
+ z[3] = x3 & M41;
+ }
+
+ public static void reduce23(long[] z, int zOff)
+ {
+ long z3 = z[zOff + 3], t = z3 >>> 41;
+ z[zOff ] ^= t;
+ z[zOff + 1] ^= (t << 10);
+ z[zOff + 3] = z3 & M41;
+ }
+
+ public static void square(long[] x, long[] z)
+ {
+ long[] tt = Nat256.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareAddToExt(long[] x, long[] zz)
+ {
+ long[] tt = Nat256.createExt64();
+ implSquare(x, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void squareN(long[] x, int n, long[] z)
+ {
+// assert n > 0;
+
+ long[] tt = Nat256.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ implSquare(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static void sqrt(long[] x, long[] z)
+ {
+ long u0, u1;
+ u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]);
+ long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ long c0 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ u0 = Interleave.unshuffle(x[2]); u1 = Interleave.unshuffle(x[3]);
+ long e1 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ long c1 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ long c2;
+ c2 = (c1 >>> 27);
+ c1 ^= (c0 >>> 27) | (c1 << 37);
+ c0 ^= (c0 << 37);
+
+ long[] tt = Nat256.createExt64();
+
+ int[] shifts = { 32, 117, 191 };
+ for (int i = 0; i < shifts.length; ++i)
+ {
+ int w = shifts[i] >>> 6, s = shifts[i] & 63;
+// assert s != 0;
+ tt[w ] ^= (c0 << s);
+ tt[w + 1] ^= (c1 << s) | (c0 >>> -s);
+ tt[w + 2] ^= (c2 << s) | (c1 >>> -s);
+ tt[w + 3] ^= (c2 >>> -s);
+ }
+
+ reduce(tt, z);
+
+ z[0] ^= e0;
+ z[1] ^= e1;
+ }
+
+ public static int trace(long[] x)
+ {
+ // Non-zero-trace bits: 0, 159
+ return (int)(x[0] ^ (x[2] >>> 31)) & 1;
+ }
+
+ protected static void implCompactExt(long[] zz)
+ {
+ long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5], z6 = zz[6], z7 = zz[7];
+ zz[0] = z0 ^ (z1 << 59);
+ zz[1] = (z1 >>> 5) ^ (z2 << 54);
+ zz[2] = (z2 >>> 10) ^ (z3 << 49);
+ zz[3] = (z3 >>> 15) ^ (z4 << 44);
+ zz[4] = (z4 >>> 20) ^ (z5 << 39);
+ zz[5] = (z5 >>> 25) ^ (z6 << 34);
+ zz[6] = (z6 >>> 30) ^ (z7 << 29);
+ zz[7] = (z7 >>> 35);
+ }
+
+ protected static void implExpand(long[] x, long[] z)
+ {
+ long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+ z[0] = x0 & M59;
+ z[1] = ((x0 >>> 59) ^ (x1 << 5)) & M59;
+ z[2] = ((x1 >>> 54) ^ (x2 << 10)) & M59;
+ z[3] = ((x2 >>> 49) ^ (x3 << 15));
+ }
+
+ protected static void implMultiply(long[] x, long[] y, long[] zz)
+ {
+ /*
+ * "Two-level seven-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein.
+ */
+
+ long[] f = new long[4], g = new long[4];
+ implExpand(x, f);
+ implExpand(y, g);
+
+ implMulwAcc(f[0], g[0], zz, 0);
+ implMulwAcc(f[1], g[1], zz, 1);
+ implMulwAcc(f[2], g[2], zz, 2);
+ implMulwAcc(f[3], g[3], zz, 3);
+
+ // U *= (1 - t^n)
+ for (int i = 5; i > 0; --i)
+ {
+ zz[i] ^= zz[i - 1];
+ }
+
+ implMulwAcc(f[0] ^ f[1], g[0] ^ g[1], zz, 1);
+ implMulwAcc(f[2] ^ f[3], g[2] ^ g[3], zz, 3);
+
+ // V *= (1 - t^2n)
+ for (int i = 7; i > 1; --i)
+ {
+ zz[i] ^= zz[i - 2];
+ }
+
+ // Double-length recursion
+ {
+ long c0 = f[0] ^ f[2], c1 = f[1] ^ f[3];
+ long d0 = g[0] ^ g[2], d1 = g[1] ^ g[3];
+ implMulwAcc(c0 ^ c1, d0 ^ d1, zz, 3);
+ long[] t = new long[3];
+ implMulwAcc(c0, d0, t, 0);
+ implMulwAcc(c1, d1, t, 1);
+ long t0 = t[0], t1 = t[1], t2 = t[2];
+ zz[2] ^= t0;
+ zz[3] ^= t0 ^ t1;
+ zz[4] ^= t2 ^ t1;
+ zz[5] ^= t2;
+ }
+
+ implCompactExt(zz);
+ }
+
+ protected static void implMulwAcc(long x, long y, long[] z, int zOff)
+ {
+// assert x >>> 59 == 0;
+// assert y >>> 59 == 0;
+
+ long[] u = new long[8];
+// u[0] = 0;
+ u[1] = y;
+ u[2] = u[1] << 1;
+ u[3] = u[2] ^ y;
+ u[4] = u[2] << 1;
+ u[5] = u[4] ^ y;
+ u[6] = u[3] << 1;
+ u[7] = u[6] ^ y;
+
+ int j = (int)x;
+ long g, h = 0, l = u[j & 7]
+ ^ (u[(j >>> 3) & 7] << 3);
+ int k = 54;
+ do
+ {
+ j = (int)(x >>> k);
+ g = u[j & 7]
+ ^ u[(j >>> 3) & 7] << 3;
+ l ^= (g << k);
+ h ^= (g >>> -k);
+ }
+ while ((k -= 6) > 0);
+
+// assert h >>> 53 == 0;
+
+ z[zOff ] ^= l & M59;
+ z[zOff + 1] ^= (l >>> 59) ^ (h << 5);
+ }
+
+ protected static void implSquare(long[] x, long[] zz)
+ {
+ Interleave.expand64To128(x[0], zz, 0);
+ Interleave.expand64To128(x[1], zz, 2);
+ Interleave.expand64To128(x[2], zz, 4);
+
+ long x3 = x[3];
+ zz[6] = Interleave.expand32to64((int)x3);
+ zz[7] = Interleave.expand16to32((int)(x3 >>> 32)) & 0xFFFFFFFFL;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233FieldElement.java
new file mode 100644
index 00000000..73706517
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233FieldElement.java
@@ -0,0 +1,221 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Nat256;
+import org.bouncycastle.util.Arrays;
+
+public class SecT233FieldElement extends ECFieldElement
+{
+ protected long[] x;
+
+ public SecT233FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.bitLength() > 233)
+ {
+ throw new IllegalArgumentException("x value invalid for SecT233FieldElement");
+ }
+
+ this.x = SecT233Field.fromBigInteger(x);
+ }
+
+ public SecT233FieldElement()
+ {
+ this.x = Nat256.create64();
+ }
+
+ protected SecT233FieldElement(long[] x)
+ {
+ this.x = x;
+ }
+
+// public int bitLength()
+// {
+// return x.degree();
+// }
+
+ public boolean isOne()
+ {
+ return Nat256.isOne64(x);
+ }
+
+ public boolean isZero()
+ {
+ return Nat256.isZero64(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return (x[0] & 1L) != 0L;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat256.toBigInteger64(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecT233Field";
+ }
+
+ public int getFieldSize()
+ {
+ return 233;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ long[] z = Nat256.create64();
+ SecT233Field.add(x, ((SecT233FieldElement)b).x, z);
+ return new SecT233FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ long[] z = Nat256.create64();
+ SecT233Field.addOne(x, z);
+ return new SecT233FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ long[] z = Nat256.create64();
+ SecT233Field.multiply(x, ((SecT233FieldElement)b).x, z);
+ return new SecT233FieldElement(z);
+ }
+
+ public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ return multiplyPlusProduct(b, x, y);
+ }
+
+ public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x, bx = ((SecT233FieldElement)b).x;
+ long[] xx = ((SecT233FieldElement)x).x, yx = ((SecT233FieldElement)y).x;
+
+ long[] tt = Nat256.createExt64();
+ SecT233Field.multiplyAddToExt(ax, bx, tt);
+ SecT233Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat256.create64();
+ SecT233Field.reduce(tt, z);
+ return new SecT233FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+ return multiply(b.invert());
+ }
+
+ public ECFieldElement negate()
+ {
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ long[] z = Nat256.create64();
+ SecT233Field.square(x, z);
+ return new SecT233FieldElement(z);
+ }
+
+ public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ return squarePlusProduct(x, y);
+ }
+
+ public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x;
+ long[] xx = ((SecT233FieldElement)x).x, yx = ((SecT233FieldElement)y).x;
+
+ long[] tt = Nat256.createExt64();
+ SecT233Field.squareAddToExt(ax, tt);
+ SecT233Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat256.create64();
+ SecT233Field.reduce(tt, z);
+ return new SecT233FieldElement(z);
+ }
+
+ public ECFieldElement squarePow(int pow)
+ {
+ if (pow < 1)
+ {
+ return this;
+ }
+
+ long[] z = Nat256.create64();
+ SecT233Field.squareN(x, pow, z);
+ return new SecT233FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+ long[] z = Nat256.create64();
+ SecT233Field.invert(x, z);
+ return new SecT233FieldElement(z);
+ }
+
+ public ECFieldElement sqrt()
+ {
+ long[] z = Nat256.create64();
+ SecT233Field.sqrt(x, z);
+ return new SecT233FieldElement(z);
+ }
+
+ public int getRepresentation()
+ {
+ return ECFieldElement.F2m.TPB;
+ }
+
+ public int getM()
+ {
+ return 233;
+ }
+
+ public int getK1()
+ {
+ return 74;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecT233FieldElement))
+ {
+ return false;
+ }
+
+ SecT233FieldElement o = (SecT233FieldElement)other;
+ return Nat256.eq64(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return 2330074 ^ Arrays.hashCode(x, 0, 4);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Curve.java
new file mode 100644
index 00000000..2e83d1c9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Curve.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECMultiplier;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.WTauNafMultiplier;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT233K1Curve extends AbstractF2m
+{
+ private static final int SecT233K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT233K1Point infinity;
+
+ public SecT233K1Curve()
+ {
+ super(233, 74, 0, 0);
+
+ this.infinity = new SecT233K1Point(this, null, null);
+
+ this.a = fromBigInteger(BigInteger.valueOf(0));
+ this.b = fromBigInteger(BigInteger.valueOf(1));
+ this.order = new BigInteger(1, Hex.decode("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF"));
+ this.cofactor = BigInteger.valueOf(4);
+
+ this.coord = SecT233K1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT233K1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected ECMultiplier createDefaultMultiplier()
+ {
+ return new WTauNafMultiplier();
+ }
+
+ public int getFieldSize()
+ {
+ return 233;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT233FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT233K1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT233K1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return true;
+ }
+
+ public int getM()
+ {
+ return 233;
+ }
+
+ public boolean isTrinomial()
+ {
+ return true;
+ }
+
+ public int getK1()
+ {
+ return 74;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Point.java
new file mode 100644
index 00000000..90a9701c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Point.java
@@ -0,0 +1,323 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT233K1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT233K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT233K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT233K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT233K1Point(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+// X3 = L.square().add(L).add(X1).add(curve.getA());
+ X3 = L.square().add(L).add(X1);
+ if (X3.isZero())
+ {
+// return new SecT233K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT233K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+// return new SecT233K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT233K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT233K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement T;
+ if (Z1IsOne)
+ {
+ T = L1.square().add(L1);
+ }
+ else
+ {
+ T = L1.add(Z1).multiply(L1);
+ }
+
+ if (T.isZero())
+ {
+// return new SecT233K1Point(curve, T, curve.getB().sqrt(), withCompression);
+ return new SecT233K1Point(curve, T, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement t1 = L1.add(X1).square();
+ ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.square();
+ ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3).add(Z3);
+
+ return new SecT233K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ // NOTE: twicePlus() only optimized for lambda-affine argument
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement T = L1Sq.add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement A = L2plus1.multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+// return new SecT233K1Point(curve, A, curve.getB().sqrt(), withCompression);
+ return new SecT233K1Point(curve, A, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT233K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT233K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Curve.java
new file mode 100644
index 00000000..7a9c5226
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Curve.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT233R1Curve extends AbstractF2m
+{
+ private static final int SecT233R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT233R1Point infinity;
+
+ public SecT233R1Curve()
+ {
+ super(233, 74, 0, 0);
+
+ this.infinity = new SecT233R1Point(this, null, null);
+
+ this.a = fromBigInteger(BigInteger.valueOf(1));
+ this.b = fromBigInteger(new BigInteger(1, Hex.decode("0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD")));
+ this.order = new BigInteger(1, Hex.decode("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT233R1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT233R1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 233;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT233FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT233R1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT233R1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 233;
+ }
+
+ public boolean isTrinomial()
+ {
+ return true;
+ }
+
+ public int getK1()
+ {
+ return 74;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Point.java
new file mode 100644
index 00000000..2c01a583
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Point.java
@@ -0,0 +1,309 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT233R1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT233R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT233R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT233R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT233R1Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+// X3 = L.square().add(L).add(X1).add(curve.getA());
+ X3 = L.square().add(L).add(X1).addOne();
+ if (X3.isZero())
+ {
+ return new SecT233R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+ return new SecT233R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT233R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq);
+ if (T.isZero())
+ {
+ return new SecT233R1Point(curve, T, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT233R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement A = L2.multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+ return new SecT233R1Point(curve, A, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT233R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT233R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239Field.java
new file mode 100644
index 00000000..5f5bf3fd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239Field.java
@@ -0,0 +1,329 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Interleave;
+import org.bouncycastle.math.raw.Nat256;
+
+public class SecT239Field
+{
+ private static final long M47 = -1L >>> 17;
+ private static final long M60 = -1L >>> 4;
+
+ public static void add(long[] x, long[] y, long[] z)
+ {
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
+ z[2] = x[2] ^ y[2];
+ z[3] = x[3] ^ y[3];
+ }
+
+ public static void addExt(long[] xx, long[] yy, long[] zz)
+ {
+ zz[0] = xx[0] ^ yy[0];
+ zz[1] = xx[1] ^ yy[1];
+ zz[2] = xx[2] ^ yy[2];
+ zz[3] = xx[3] ^ yy[3];
+ zz[4] = xx[4] ^ yy[4];
+ zz[5] = xx[5] ^ yy[5];
+ zz[6] = xx[6] ^ yy[6];
+ zz[7] = xx[7] ^ yy[7];
+ }
+
+ public static void addOne(long[] x, long[] z)
+ {
+ z[0] = x[0] ^ 1L;
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ }
+
+ public static long[] fromBigInteger(BigInteger x)
+ {
+ long[] z = Nat256.fromBigInteger64(x);
+ reduce17(z, 0);
+ return z;
+ }
+
+ public static void invert(long[] x, long[] z)
+ {
+ if (Nat256.isZero64(x))
+ {
+ throw new IllegalStateException();
+ }
+
+ // Itoh-Tsujii inversion
+
+ long[] t0 = Nat256.create64();
+ long[] t1 = Nat256.create64();
+
+ square(x, t0);
+ multiply(t0, x, t0);
+ square(t0, t0);
+ multiply(t0, x, t0);
+ squareN(t0, 3, t1);
+ multiply(t1, t0, t1);
+ square(t1, t1);
+ multiply(t1, x, t1);
+ squareN(t1, 7, t0);
+ multiply(t0, t1, t0);
+ squareN(t0, 14, t1);
+ multiply(t1, t0, t1);
+ square(t1, t1);
+ multiply(t1, x, t1);
+ squareN(t1, 29, t0);
+ multiply(t0, t1, t0);
+ square(t0, t0);
+ multiply(t0, x, t0);
+ squareN(t0, 59, t1);
+ multiply(t1, t0, t1);
+ square(t1, t1);
+ multiply(t1, x, t1);
+ squareN(t1, 119, t0);
+ multiply(t0, t1, t0);
+ square(t0, z);
+ }
+
+ public static void multiply(long[] x, long[] y, long[] z)
+ {
+ long[] tt = Nat256.createExt64();
+ implMultiply(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(long[] x, long[] y, long[] zz)
+ {
+ long[] tt = Nat256.createExt64();
+ implMultiply(x, y, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void reduce(long[] xx, long[] z)
+ {
+ long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3];
+ long x4 = xx[4], x5 = xx[5], x6 = xx[6], x7 = xx[7];
+
+ x3 ^= (x7 << 17);
+ x4 ^= (x7 >>> 47);
+ x5 ^= (x7 << 47);
+ x6 ^= (x7 >>> 17);
+
+ x2 ^= (x6 << 17);
+ x3 ^= (x6 >>> 47);
+ x4 ^= (x6 << 47);
+ x5 ^= (x6 >>> 17);
+
+ x1 ^= (x5 << 17);
+ x2 ^= (x5 >>> 47);
+ x3 ^= (x5 << 47);
+ x4 ^= (x5 >>> 17);
+
+ x0 ^= (x4 << 17);
+ x1 ^= (x4 >>> 47);
+ x2 ^= (x4 << 47);
+ x3 ^= (x4 >>> 17);
+
+ long t = x3 >>> 47;
+ z[0] = x0 ^ t;
+ z[1] = x1;
+ z[2] = x2 ^ (t << 30);
+ z[3] = x3 & M47;
+ }
+
+ public static void reduce17(long[] z, int zOff)
+ {
+ long z3 = z[zOff + 3], t = z3 >>> 47;
+ z[zOff ] ^= t;
+ z[zOff + 2] ^= (t << 30);
+ z[zOff + 3] = z3 & M47;
+ }
+
+ public static void sqrt(long[] x, long[] z)
+ {
+ long u0, u1;
+ u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]);
+ long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ long c0 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ u0 = Interleave.unshuffle(x[2]); u1 = Interleave.unshuffle(x[3]);
+ long e1 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ long c1 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ long c2, c3;
+ c3 = (c1 >>> 49);
+ c2 = (c0 >>> 49) | (c1 << 15);
+ c1 ^= (c0 << 15);
+
+ long[] tt = Nat256.createExt64();
+
+ int[] shifts = { 39, 120 };
+ for (int i = 0; i < shifts.length; ++i)
+ {
+ int w = shifts[i] >>> 6, s = shifts[i] & 63;
+// assert s != 0;
+ tt[w ] ^= (c0 << s);
+ tt[w + 1] ^= (c1 << s) | (c0 >>> -s);
+ tt[w + 2] ^= (c2 << s) | (c1 >>> -s);
+ tt[w + 3] ^= (c3 << s) | (c2 >>> -s);
+ tt[w + 4] ^= (c3 >>> -s);
+ }
+
+ reduce(tt, z);
+
+ z[0] ^= e0;
+ z[1] ^= e1;
+ }
+
+ public static void square(long[] x, long[] z)
+ {
+ long[] tt = Nat256.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareAddToExt(long[] x, long[] zz)
+ {
+ long[] tt = Nat256.createExt64();
+ implSquare(x, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void squareN(long[] x, int n, long[] z)
+ {
+// assert n > 0;
+
+ long[] tt = Nat256.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ implSquare(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static int trace(long[] x)
+ {
+ // Non-zero-trace bits: 0, 81, 162
+ return (int)(x[0] ^ (x[1] >>> 17) ^ (x[2] >>> 34)) & 1;
+ }
+
+ protected static void implCompactExt(long[] zz)
+ {
+ long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5], z6 = zz[6], z7 = zz[7];
+ zz[0] = z0 ^ (z1 << 60);
+ zz[1] = (z1 >>> 4) ^ (z2 << 56);
+ zz[2] = (z2 >>> 8) ^ (z3 << 52);
+ zz[3] = (z3 >>> 12) ^ (z4 << 48);
+ zz[4] = (z4 >>> 16) ^ (z5 << 44);
+ zz[5] = (z5 >>> 20) ^ (z6 << 40);
+ zz[6] = (z6 >>> 24) ^ (z7 << 36);
+ zz[7] = (z7 >>> 28);
+ }
+
+ protected static void implExpand(long[] x, long[] z)
+ {
+ long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+ z[0] = x0 & M60;
+ z[1] = ((x0 >>> 60) ^ (x1 << 4)) & M60;
+ z[2] = ((x1 >>> 56) ^ (x2 << 8)) & M60;
+ z[3] = ((x2 >>> 52) ^ (x3 << 12));
+ }
+
+ protected static void implMultiply(long[] x, long[] y, long[] zz)
+ {
+ /*
+ * "Two-level seven-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein.
+ */
+
+ long[] f = new long[4], g = new long[4];
+ implExpand(x, f);
+ implExpand(y, g);
+
+ implMulwAcc(f[0], g[0], zz, 0);
+ implMulwAcc(f[1], g[1], zz, 1);
+ implMulwAcc(f[2], g[2], zz, 2);
+ implMulwAcc(f[3], g[3], zz, 3);
+
+ // U *= (1 - t^n)
+ for (int i = 5; i > 0; --i)
+ {
+ zz[i] ^= zz[i - 1];
+ }
+
+ implMulwAcc(f[0] ^ f[1], g[0] ^ g[1], zz, 1);
+ implMulwAcc(f[2] ^ f[3], g[2] ^ g[3], zz, 3);
+
+ // V *= (1 - t^2n)
+ for (int i = 7; i > 1; --i)
+ {
+ zz[i] ^= zz[i - 2];
+ }
+
+ // Double-length recursion
+ {
+ long c0 = f[0] ^ f[2], c1 = f[1] ^ f[3];
+ long d0 = g[0] ^ g[2], d1 = g[1] ^ g[3];
+ implMulwAcc(c0 ^ c1, d0 ^ d1, zz, 3);
+ long[] t = new long[3];
+ implMulwAcc(c0, d0, t, 0);
+ implMulwAcc(c1, d1, t, 1);
+ long t0 = t[0], t1 = t[1], t2 = t[2];
+ zz[2] ^= t0;
+ zz[3] ^= t0 ^ t1;
+ zz[4] ^= t2 ^ t1;
+ zz[5] ^= t2;
+ }
+
+ implCompactExt(zz);
+ }
+
+ protected static void implMulwAcc(long x, long y, long[] z, int zOff)
+ {
+// assert x >>> 60 == 0;
+// assert y >>> 60 == 0;
+
+ long[] u = new long[8];
+// u[0] = 0;
+ u[1] = y;
+ u[2] = u[1] << 1;
+ u[3] = u[2] ^ y;
+ u[4] = u[2] << 1;
+ u[5] = u[4] ^ y;
+ u[6] = u[3] << 1;
+ u[7] = u[6] ^ y;
+
+ int j = (int)x;
+ long g, h = 0, l = u[j & 7]
+ ^ (u[(j >>> 3) & 7] << 3);
+ int k = 54;
+ do
+ {
+ j = (int)(x >>> k);
+ g = u[j & 7]
+ ^ u[(j >>> 3) & 7] << 3;
+ l ^= (g << k);
+ h ^= (g >>> -k);
+ }
+ while ((k -= 6) > 0);
+
+ h ^= ((x & 0x0820820820820820L) & ((y << 4) >> 63)) >>> 5;
+
+// assert h >>> 55 == 0;
+
+ z[zOff ] ^= l & M60;
+ z[zOff + 1] ^= (l >>> 60) ^ (h << 4);
+ }
+
+ protected static void implSquare(long[] x, long[] zz)
+ {
+ Interleave.expand64To128(x[0], zz, 0);
+ Interleave.expand64To128(x[1], zz, 2);
+ Interleave.expand64To128(x[2], zz, 4);
+
+ long x3 = x[3];
+ zz[6] = Interleave.expand32to64((int)x3);
+ zz[7] = Interleave.expand16to32((int)(x3 >>> 32)) & 0xFFFFFFFFL;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239FieldElement.java
new file mode 100644
index 00000000..f6553ba2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239FieldElement.java
@@ -0,0 +1,221 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Nat256;
+import org.bouncycastle.util.Arrays;
+
+public class SecT239FieldElement extends ECFieldElement
+{
+ protected long[] x;
+
+ public SecT239FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.bitLength() > 239)
+ {
+ throw new IllegalArgumentException("x value invalid for SecT239FieldElement");
+ }
+
+ this.x = SecT239Field.fromBigInteger(x);
+ }
+
+ public SecT239FieldElement()
+ {
+ this.x = Nat256.create64();
+ }
+
+ protected SecT239FieldElement(long[] x)
+ {
+ this.x = x;
+ }
+
+// public int bitLength()
+// {
+// return x.degree();
+// }
+
+ public boolean isOne()
+ {
+ return Nat256.isOne64(x);
+ }
+
+ public boolean isZero()
+ {
+ return Nat256.isZero64(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return (x[0] & 1L) != 0L;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat256.toBigInteger64(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecT239Field";
+ }
+
+ public int getFieldSize()
+ {
+ return 239;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ long[] z = Nat256.create64();
+ SecT239Field.add(x, ((SecT239FieldElement)b).x, z);
+ return new SecT239FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ long[] z = Nat256.create64();
+ SecT239Field.addOne(x, z);
+ return new SecT239FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ long[] z = Nat256.create64();
+ SecT239Field.multiply(x, ((SecT239FieldElement)b).x, z);
+ return new SecT239FieldElement(z);
+ }
+
+ public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ return multiplyPlusProduct(b, x, y);
+ }
+
+ public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x, bx = ((SecT239FieldElement)b).x;
+ long[] xx = ((SecT239FieldElement)x).x, yx = ((SecT239FieldElement)y).x;
+
+ long[] tt = Nat256.createExt64();
+ SecT239Field.multiplyAddToExt(ax, bx, tt);
+ SecT239Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat256.create64();
+ SecT239Field.reduce(tt, z);
+ return new SecT239FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+ return multiply(b.invert());
+ }
+
+ public ECFieldElement negate()
+ {
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ long[] z = Nat256.create64();
+ SecT239Field.square(x, z);
+ return new SecT239FieldElement(z);
+ }
+
+ public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ return squarePlusProduct(x, y);
+ }
+
+ public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x;
+ long[] xx = ((SecT239FieldElement)x).x, yx = ((SecT239FieldElement)y).x;
+
+ long[] tt = Nat256.createExt64();
+ SecT239Field.squareAddToExt(ax, tt);
+ SecT239Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat256.create64();
+ SecT239Field.reduce(tt, z);
+ return new SecT239FieldElement(z);
+ }
+
+ public ECFieldElement squarePow(int pow)
+ {
+ if (pow < 1)
+ {
+ return this;
+ }
+
+ long[] z = Nat256.create64();
+ SecT239Field.squareN(x, pow, z);
+ return new SecT239FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+ long[] z = Nat256.create64();
+ SecT239Field.invert(x, z);
+ return new SecT239FieldElement(z);
+ }
+
+ public ECFieldElement sqrt()
+ {
+ long[] z = Nat256.create64();
+ SecT239Field.sqrt(x, z);
+ return new SecT239FieldElement(z);
+ }
+
+ public int getRepresentation()
+ {
+ return ECFieldElement.F2m.TPB;
+ }
+
+ public int getM()
+ {
+ return 239;
+ }
+
+ public int getK1()
+ {
+ return 158;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecT239FieldElement))
+ {
+ return false;
+ }
+
+ SecT239FieldElement o = (SecT239FieldElement)other;
+ return Nat256.eq64(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return 23900158 ^ Arrays.hashCode(x, 0, 4);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Curve.java
new file mode 100644
index 00000000..074ff712
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Curve.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECMultiplier;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.WTauNafMultiplier;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT239K1Curve extends AbstractF2m
+{
+ private static final int SecT239K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT239K1Point infinity;
+
+ public SecT239K1Curve()
+ {
+ super(239, 158, 0, 0);
+
+ this.infinity = new SecT239K1Point(this, null, null);
+
+ this.a = fromBigInteger(BigInteger.valueOf(0));
+ this.b = fromBigInteger(BigInteger.valueOf(1));
+ this.order = new BigInteger(1, Hex.decode("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5"));
+ this.cofactor = BigInteger.valueOf(4);
+
+ this.coord = SecT239K1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT239K1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected ECMultiplier createDefaultMultiplier()
+ {
+ return new WTauNafMultiplier();
+ }
+
+ public int getFieldSize()
+ {
+ return 239;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT239FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT239K1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT239K1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return true;
+ }
+
+ public int getM()
+ {
+ return 239;
+ }
+
+ public boolean isTrinomial()
+ {
+ return true;
+ }
+
+ public int getK1()
+ {
+ return 158;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Point.java
new file mode 100644
index 00000000..39384dc2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Point.java
@@ -0,0 +1,324 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT239K1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT239K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT239K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT239K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT239K1Point(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+// X3 = L.square().add(L).add(X1).add(curve.getA());
+ X3 = L.square().add(L).add(X1);
+ if (X3.isZero())
+ {
+// return new SecT239K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT239K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+// return new SecT239K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT239K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT239K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement T;
+ if (Z1IsOne)
+ {
+ T = L1.square().add(L1);
+ }
+ else
+ {
+ T = L1.add(Z1).multiply(L1);
+ }
+
+ if (T.isZero())
+ {
+// return new SecT239K1Point(curve, T, curve.getB().sqrt(), withCompression);
+ return new SecT239K1Point(curve, T, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement t1 = L1.add(X1).square();
+ ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.square();
+ ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3).add(Z3);
+
+ return new SecT239K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ // NOTE: twicePlus() only optimized for lambda-affine argument
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement T = L1Sq.add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement A = L2plus1.multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+// return new SecT239K1Point(curve, A, curve.getB().sqrt(), withCompression);
+ return new SecT239K1Point(curve, A, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT239K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT239K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283Field.java
new file mode 100644
index 00000000..7b6679bd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283Field.java
@@ -0,0 +1,404 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Interleave;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat320;
+
+public class SecT283Field
+{
+ private static final long M27 = -1L >>> 37;
+ private static final long M57 = -1L >>> 7;
+
+ private static final long[] ROOT_Z = new long[]{ 0x0C30C30C30C30808L, 0x30C30C30C30C30C3L, 0x820820820820830CL, 0x0820820820820820L, 0x2082082L };
+
+ public static void add(long[] x, long[] y, long[] z)
+ {
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
+ z[2] = x[2] ^ y[2];
+ z[3] = x[3] ^ y[3];
+ z[4] = x[4] ^ y[4];
+ }
+
+ public static void addExt(long[] xx, long[] yy, long[] zz)
+ {
+ zz[0] = xx[0] ^ yy[0];
+ zz[1] = xx[1] ^ yy[1];
+ zz[2] = xx[2] ^ yy[2];
+ zz[3] = xx[3] ^ yy[3];
+ zz[4] = xx[4] ^ yy[4];
+ zz[5] = xx[5] ^ yy[5];
+ zz[6] = xx[6] ^ yy[6];
+ zz[7] = xx[7] ^ yy[7];
+ zz[8] = xx[8] ^ yy[8];
+ }
+
+ public static void addOne(long[] x, long[] z)
+ {
+ z[0] = x[0] ^ 1L;
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ z[4] = x[4];
+ }
+
+ public static long[] fromBigInteger(BigInteger x)
+ {
+ long[] z = Nat320.fromBigInteger64(x);
+ reduce37(z, 0);
+ return z;
+ }
+
+ public static void invert(long[] x, long[] z)
+ {
+ if (Nat320.isZero64(x))
+ {
+ throw new IllegalStateException();
+ }
+
+ // Itoh-Tsujii inversion
+
+ long[] t0 = Nat320.create64();
+ long[] t1 = Nat320.create64();
+
+ square(x, t0);
+ multiply(t0, x, t0);
+ squareN(t0, 2, t1);
+ multiply(t1, t0, t1);
+ squareN(t1, 4, t0);
+ multiply(t0, t1, t0);
+ squareN(t0, 8, t1);
+ multiply(t1, t0, t1);
+ square(t1, t1);
+ multiply(t1, x, t1);
+ squareN(t1, 17, t0);
+ multiply(t0, t1, t0);
+ square(t0, t0);
+ multiply(t0, x, t0);
+ squareN(t0, 35, t1);
+ multiply(t1, t0, t1);
+ squareN(t1, 70, t0);
+ multiply(t0, t1, t0);
+ square(t0, t0);
+ multiply(t0, x, t0);
+ squareN(t0, 141, t1);
+ multiply(t1, t0, t1);
+ square(t1, z);
+ }
+
+ public static void multiply(long[] x, long[] y, long[] z)
+ {
+ long[] tt = Nat320.createExt64();
+ implMultiply(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(long[] x, long[] y, long[] zz)
+ {
+ long[] tt = Nat320.createExt64();
+ implMultiply(x, y, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void reduce(long[] xx, long[] z)
+ {
+ long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4];
+ long x5 = xx[5], x6 = xx[6], x7 = xx[7], x8 = xx[8];
+
+ x3 ^= (x8 << 37) ^ (x8 << 42) ^ (x8 << 44) ^ (x8 << 49);
+ x4 ^= (x8 >>> 27) ^ (x8 >>> 22) ^ (x8 >>> 20) ^ (x8 >>> 15);
+
+ x2 ^= (x7 << 37) ^ (x7 << 42) ^ (x7 << 44) ^ (x7 << 49);
+ x3 ^= (x7 >>> 27) ^ (x7 >>> 22) ^ (x7 >>> 20) ^ (x7 >>> 15);
+
+ x1 ^= (x6 << 37) ^ (x6 << 42) ^ (x6 << 44) ^ (x6 << 49);
+ x2 ^= (x6 >>> 27) ^ (x6 >>> 22) ^ (x6 >>> 20) ^ (x6 >>> 15);
+
+ x0 ^= (x5 << 37) ^ (x5 << 42) ^ (x5 << 44) ^ (x5 << 49);
+ x1 ^= (x5 >>> 27) ^ (x5 >>> 22) ^ (x5 >>> 20) ^ (x5 >>> 15);
+
+ long t = x4 >>> 27;
+ z[0] = x0 ^ t ^ (t << 5) ^ (t << 7) ^ (t << 12);
+ z[1] = x1;
+ z[2] = x2;
+ z[3] = x3;
+ z[4] = x4 & M27;
+ }
+
+ public static void reduce37(long[] z, int zOff)
+ {
+ long z4 = z[zOff + 4], t = z4 >>> 27;
+ z[zOff ] ^= t ^ (t << 5) ^ (t << 7) ^ (t << 12);
+ z[zOff + 4] = z4 & M27;
+ }
+
+ public static void sqrt(long[] x, long[] z)
+ {
+ long[] odd = Nat320.create64();
+
+ long u0, u1;
+ u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]);
+ long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ odd[0] = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ u0 = Interleave.unshuffle(x[2]); u1 = Interleave.unshuffle(x[3]);
+ long e1 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ odd[1] = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ u0 = Interleave.unshuffle(x[4]);
+ long e2 = (u0 & 0x00000000FFFFFFFFL);
+ odd[2] = (u0 >>> 32);
+
+ multiply(odd, ROOT_Z, z);
+
+ z[0] ^= e0;
+ z[1] ^= e1;
+ z[2] ^= e2;
+ }
+
+ public static void square(long[] x, long[] z)
+ {
+ long[] tt = Nat.create64(9);
+ implSquare(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareAddToExt(long[] x, long[] zz)
+ {
+ long[] tt = Nat.create64(9);
+ implSquare(x, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void squareN(long[] x, int n, long[] z)
+ {
+// assert n > 0;
+
+ long[] tt = Nat.create64(9);
+ implSquare(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ implSquare(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static int trace(long[] x)
+ {
+ // Non-zero-trace bits: 0, 271
+ return (int)(x[0] ^ (x[4] >>> 15)) & 1;
+ }
+
+ protected static void implCompactExt(long[] zz)
+ {
+ long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4];
+ long z5 = zz[5], z6 = zz[6], z7 = zz[7], z8 = zz[8], z9 = zz[9];
+ zz[0] = z0 ^ (z1 << 57);
+ zz[1] = (z1 >>> 7) ^ (z2 << 50);
+ zz[2] = (z2 >>> 14) ^ (z3 << 43);
+ zz[3] = (z3 >>> 21) ^ (z4 << 36);
+ zz[4] = (z4 >>> 28) ^ (z5 << 29);
+ zz[5] = (z5 >>> 35) ^ (z6 << 22);
+ zz[6] = (z6 >>> 42) ^ (z7 << 15);
+ zz[7] = (z7 >>> 49) ^ (z8 << 8);
+ zz[8] = (z8 >>> 56) ^ (z9 << 1);
+ zz[9] = (z9 >>> 63); // Zero!
+ }
+
+ protected static void implExpand(long[] x, long[] z)
+ {
+ long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4];
+ z[0] = x0 & M57;
+ z[1] = ((x0 >>> 57) ^ (x1 << 7)) & M57;
+ z[2] = ((x1 >>> 50) ^ (x2 << 14)) & M57;
+ z[3] = ((x2 >>> 43) ^ (x3 << 21)) & M57;
+ z[4] = ((x3 >>> 36) ^ (x4 << 28));
+ }
+
+// protected static void addMs(long[] zz, int zOff, long[] p, int... ms)
+// {
+// long t0 = 0, t1 = 0;
+// for (int m : ms)
+// {
+// int i = (m - 1) << 1;
+// t0 ^= p[i ];
+// t1 ^= p[i + 1];
+// }
+// zz[zOff ] ^= t0;
+// zz[zOff + 1] ^= t1;
+// }
+
+ protected static void implMultiply(long[] x, long[] y, long[] zz)
+ {
+ /*
+ * Formula (17) from "Some New Results on Binary Polynomial Multiplication",
+ * Murat Cenk and M. Anwar Hasan.
+ *
+ * The formula as given contained an error in the term t25, as noted below
+ */
+ long[] a = new long[5], b = new long[5];
+ implExpand(x, a);
+ implExpand(y, b);
+
+ long[] p = new long[26];
+
+ implMulw(a[0], b[0], p, 0); // m1
+ implMulw(a[1], b[1], p, 2); // m2
+ implMulw(a[2], b[2], p, 4); // m3
+ implMulw(a[3], b[3], p, 6); // m4
+ implMulw(a[4], b[4], p, 8); // m5
+
+ long u0 = a[0] ^ a[1], v0 = b[0] ^ b[1];
+ long u1 = a[0] ^ a[2], v1 = b[0] ^ b[2];
+ long u2 = a[2] ^ a[4], v2 = b[2] ^ b[4];
+ long u3 = a[3] ^ a[4], v3 = b[3] ^ b[4];
+
+ implMulw(u1 ^ a[3], v1 ^ b[3], p, 18); // m10
+ implMulw(u2 ^ a[1], v2 ^ b[1], p, 20); // m11
+
+ long A4 = u0 ^ u3 , B4 = v0 ^ v3;
+ long A5 = A4 ^ a[2], B5 = B4 ^ b[2];
+
+ implMulw(A4, B4, p, 22); // m12
+ implMulw(A5, B5, p, 24); // m13
+
+ implMulw(u0, v0, p, 10); // m6
+ implMulw(u1, v1, p, 12); // m7
+ implMulw(u2, v2, p, 14); // m8
+ implMulw(u3, v3, p, 16); // m9
+
+
+ // Original method, corresponding to formula (16)
+// addMs(zz, 0, p, 1);
+// addMs(zz, 1, p, 1, 2, 6);
+// addMs(zz, 2, p, 1, 2, 3, 7);
+// addMs(zz, 3, p, 1, 3, 4, 5, 8, 10, 12, 13);
+// addMs(zz, 4, p, 1, 2, 4, 5, 6, 9, 10, 11, 13);
+// addMs(zz, 5, p, 1, 2, 3, 5, 7, 11, 12, 13);
+// addMs(zz, 6, p, 3, 4, 5, 8);
+// addMs(zz, 7, p, 4, 5, 9);
+// addMs(zz, 8, p, 5);
+
+ // Improved method factors out common single-word terms
+ // NOTE: p1,...,p26 in the paper maps to p[0],...,p[25] here
+
+ zz[0] = p[ 0];
+ zz[9] = p[ 9];
+
+ long t1 = p[ 0] ^ p[ 1];
+ long t2 = t1 ^ p[ 2];
+ long t3 = t2 ^ p[10];
+
+ zz[1] = t3;
+
+ long t4 = p[ 3] ^ p[ 4];
+ long t5 = p[11] ^ p[12];
+ long t6 = t4 ^ t5;
+ long t7 = t2 ^ t6;
+
+ zz[2] = t7;
+
+ long t8 = t1 ^ t4;
+ long t9 = p[ 5] ^ p[ 6];
+ long t10 = t8 ^ t9;
+ long t11 = t10 ^ p[ 8];
+ long t12 = p[13] ^ p[14];
+ long t13 = t11 ^ t12;
+ long t14 = p[18] ^ p[22];
+ long t15 = t14 ^ p[24];
+ long t16 = t13 ^ t15;
+
+ zz[3] = t16;
+
+ long t17 = p[ 7] ^ p[ 8];
+ long t18 = t17 ^ p[ 9];
+ long t19 = t18 ^ p[17];
+
+ zz[8] = t19;
+
+ long t20 = t18 ^ t9;
+ long t21 = p[15] ^ p[16];
+ long t22 = t20 ^ t21;
+
+ zz[7] = t22;
+
+ long t23 = t22 ^ t3;
+ long t24 = p[19] ^ p[20];
+// long t25 = p[23] ^ p[24];
+ long t25 = p[25] ^ p[24]; // Fixes an error in the paper: p[23] -> p{25]
+ long t26 = p[18] ^ p[23];
+ long t27 = t24 ^ t25;
+ long t28 = t27 ^ t26;
+ long t29 = t28 ^ t23;
+
+ zz[4] = t29;
+
+ long t30 = t7 ^ t19;
+ long t31 = t27 ^ t30;
+ long t32 = p[21] ^ p[22];
+ long t33 = t31 ^ t32;
+
+ zz[5] = t33;
+
+ long t34 = t11 ^ p[0];
+ long t35 = t34 ^ p[9];
+ long t36 = t35 ^ t12;
+ long t37 = t36 ^ p[21];
+ long t38 = t37 ^ p[23];
+ long t39 = t38 ^ p[25];
+
+ zz[6] = t39;
+
+ implCompactExt(zz);
+ }
+
+ protected static void implMulw(long x, long y, long[] z, int zOff)
+ {
+// assert x >>> 57 == 0;
+// assert y >>> 57 == 0;
+
+ long[] u = new long[8];
+// u[0] = 0;
+ u[1] = y;
+ u[2] = u[1] << 1;
+ u[3] = u[2] ^ y;
+ u[4] = u[2] << 1;
+ u[5] = u[4] ^ y;
+ u[6] = u[3] << 1;
+ u[7] = u[6] ^ y;
+
+ int j = (int)x;
+ long g, h = 0, l = u[j & 7];
+ int k = 48;
+ do
+ {
+ j = (int)(x >>> k);
+ g = u[j & 7]
+ ^ u[(j >>> 3) & 7] << 3
+ ^ u[(j >>> 6) & 7] << 6;
+ l ^= (g << k);
+ h ^= (g >>> -k);
+ }
+ while ((k -= 9) > 0);
+
+ h ^= ((x & 0x0100804020100800L) & ((y << 7) >> 63)) >>> 8;
+
+// assert h >>> 49 == 0;
+
+ z[zOff ] = l & M57;
+ z[zOff + 1] = (l >>> 57) ^ (h << 7);
+ }
+
+ protected static void implSquare(long[] x, long[] zz)
+ {
+ for (int i = 0; i < 4; ++i)
+ {
+ Interleave.expand64To128(x[i], zz, i << 1);
+ }
+ zz[8] = Interleave.expand32to64((int)x[4]);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283FieldElement.java
new file mode 100644
index 00000000..ee0024bc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283FieldElement.java
@@ -0,0 +1,222 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat320;
+import org.bouncycastle.util.Arrays;
+
+public class SecT283FieldElement extends ECFieldElement
+{
+ protected long[] x;
+
+ public SecT283FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.bitLength() > 283)
+ {
+ throw new IllegalArgumentException("x value invalid for SecT283FieldElement");
+ }
+
+ this.x = SecT283Field.fromBigInteger(x);
+ }
+
+ public SecT283FieldElement()
+ {
+ this.x = Nat320.create64();
+ }
+
+ protected SecT283FieldElement(long[] x)
+ {
+ this.x = x;
+ }
+
+// public int bitLength()
+// {
+// return x.degree();
+// }
+
+ public boolean isOne()
+ {
+ return Nat320.isOne64(x);
+ }
+
+ public boolean isZero()
+ {
+ return Nat320.isZero64(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return (x[0] & 1L) != 0L;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat320.toBigInteger64(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecT283Field";
+ }
+
+ public int getFieldSize()
+ {
+ return 283;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ long[] z = Nat320.create64();
+ SecT283Field.add(x, ((SecT283FieldElement)b).x, z);
+ return new SecT283FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ long[] z = Nat320.create64();
+ SecT283Field.addOne(x, z);
+ return new SecT283FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ long[] z = Nat320.create64();
+ SecT283Field.multiply(x, ((SecT283FieldElement)b).x, z);
+ return new SecT283FieldElement(z);
+ }
+
+ public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ return multiplyPlusProduct(b, x, y);
+ }
+
+ public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x, bx = ((SecT283FieldElement)b).x;
+ long[] xx = ((SecT283FieldElement)x).x, yx = ((SecT283FieldElement)y).x;
+
+ long[] tt = Nat.create64(9);
+ SecT283Field.multiplyAddToExt(ax, bx, tt);
+ SecT283Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat320.create64();
+ SecT283Field.reduce(tt, z);
+ return new SecT283FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+ return multiply(b.invert());
+ }
+
+ public ECFieldElement negate()
+ {
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ long[] z = Nat320.create64();
+ SecT283Field.square(x, z);
+ return new SecT283FieldElement(z);
+ }
+
+ public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ return squarePlusProduct(x, y);
+ }
+
+ public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x;
+ long[] xx = ((SecT283FieldElement)x).x, yx = ((SecT283FieldElement)y).x;
+
+ long[] tt = Nat.create64(9);
+ SecT283Field.squareAddToExt(ax, tt);
+ SecT283Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat320.create64();
+ SecT283Field.reduce(tt, z);
+ return new SecT283FieldElement(z);
+ }
+
+ public ECFieldElement squarePow(int pow)
+ {
+ if (pow < 1)
+ {
+ return this;
+ }
+
+ long[] z = Nat320.create64();
+ SecT283Field.squareN(x, pow, z);
+ return new SecT283FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+ long[] z = Nat320.create64();
+ SecT283Field.invert(x, z);
+ return new SecT283FieldElement(z);
+ }
+
+ public ECFieldElement sqrt()
+ {
+ long[] z = Nat320.create64();
+ SecT283Field.sqrt(x, z);
+ return new SecT283FieldElement(z);
+ }
+
+ public int getRepresentation()
+ {
+ return ECFieldElement.F2m.PPB;
+ }
+
+ public int getM()
+ {
+ return 283;
+ }
+
+ public int getK1()
+ {
+ return 5;
+ }
+
+ public int getK2()
+ {
+ return 7;
+ }
+
+ public int getK3()
+ {
+ return 12;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecT283FieldElement))
+ {
+ return false;
+ }
+
+ SecT283FieldElement o = (SecT283FieldElement)other;
+ return Nat320.eq64(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return 2831275 ^ Arrays.hashCode(x, 0, 5);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Curve.java
new file mode 100644
index 00000000..1c48a6fb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Curve.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECMultiplier;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.WTauNafMultiplier;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT283K1Curve extends AbstractF2m
+{
+ private static final int SecT283K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT283K1Point infinity;
+
+ public SecT283K1Curve()
+ {
+ super(283, 5, 7, 12);
+
+ this.infinity = new SecT283K1Point(this, null, null);
+
+ this.a = fromBigInteger(BigInteger.valueOf(0));
+ this.b = fromBigInteger(BigInteger.valueOf(1));
+ this.order = new BigInteger(1, Hex.decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61"));
+ this.cofactor = BigInteger.valueOf(4);
+
+ this.coord = SecT283K1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT283K1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected ECMultiplier createDefaultMultiplier()
+ {
+ return new WTauNafMultiplier();
+ }
+
+ public int getFieldSize()
+ {
+ return 283;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT283FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT283K1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT283K1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return true;
+ }
+
+ public int getM()
+ {
+ return 283;
+ }
+
+ public boolean isTrinomial()
+ {
+ return false;
+ }
+
+ public int getK1()
+ {
+ return 5;
+ }
+
+ public int getK2()
+ {
+ return 7;
+ }
+
+ public int getK3()
+ {
+ return 12;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Point.java
new file mode 100644
index 00000000..f3b704e1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Point.java
@@ -0,0 +1,324 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT283K1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT283K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT283K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT283K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT283K1Point(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+// X3 = L.square().add(L).add(X1).add(curve.getA());
+ X3 = L.square().add(L).add(X1);
+ if (X3.isZero())
+ {
+// return new SecT283K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT283K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+// return new SecT283K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT283K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT283K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement T;
+ if (Z1IsOne)
+ {
+ T = L1.square().add(L1);
+ }
+ else
+ {
+ T = L1.add(Z1).multiply(L1);
+ }
+
+ if (T.isZero())
+ {
+// return new SecT283K1Point(curve, T, curve.getB().sqrt(), withCompression);
+ return new SecT283K1Point(curve, T, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement t1 = L1.add(X1).square();
+ ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.square();
+ ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3).add(Z3);
+
+ return new SecT283K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ // NOTE: twicePlus() only optimized for lambda-affine argument
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement T = L1Sq.add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement A = L2plus1.multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+// return new SecT283K1Point(curve, A, curve.getB().sqrt(), withCompression);
+ return new SecT283K1Point(curve, A, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT283K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT283K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Curve.java
new file mode 100644
index 00000000..2909fca8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Curve.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT283R1Curve extends AbstractF2m
+{
+ private static final int SecT283R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT283R1Point infinity;
+
+ public SecT283R1Curve()
+ {
+ super(283, 5, 7, 12);
+
+ this.infinity = new SecT283R1Point(this, null, null);
+
+ this.a = fromBigInteger(BigInteger.valueOf(1));
+ this.b = fromBigInteger(new BigInteger(1, Hex.decode("027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5")));
+ this.order = new BigInteger(1, Hex.decode("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT283R1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT283R1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 283;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT283FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT283R1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT283R1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 283;
+ }
+
+ public boolean isTrinomial()
+ {
+ return false;
+ }
+
+ public int getK1()
+ {
+ return 5;
+ }
+
+ public int getK2()
+ {
+ return 7;
+ }
+
+ public int getK3()
+ {
+ return 12;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Point.java
new file mode 100644
index 00000000..7a95a734
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Point.java
@@ -0,0 +1,309 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT283R1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT283R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT283R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT283R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT283R1Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+// X3 = L.square().add(L).add(X1).add(curve.getA());
+ X3 = L.square().add(L).add(X1).addOne();
+ if (X3.isZero())
+ {
+ return new SecT283R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+ return new SecT283R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT283R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq);
+ if (T.isZero())
+ {
+ return new SecT283R1Point(curve, T, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT283R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement A = L2.multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+ return new SecT283R1Point(curve, A, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT283R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT283R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409Field.java
new file mode 100644
index 00000000..9e58a2ba
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409Field.java
@@ -0,0 +1,333 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Interleave;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat448;
+
+public class SecT409Field
+{
+ private static final long M25 = -1L >>> 39;
+ private static final long M59 = -1L >>> 5;
+
+ public static void add(long[] x, long[] y, long[] z)
+ {
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
+ z[2] = x[2] ^ y[2];
+ z[3] = x[3] ^ y[3];
+ z[4] = x[4] ^ y[4];
+ z[5] = x[5] ^ y[5];
+ z[6] = x[6] ^ y[6];
+ }
+
+ public static void addExt(long[] xx, long[] yy, long[] zz)
+ {
+ for (int i = 0; i < 13; ++i)
+ {
+ zz[i] = xx[i] ^ yy[i];
+ }
+ }
+
+ public static void addOne(long[] x, long[] z)
+ {
+ z[0] = x[0] ^ 1L;
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ z[4] = x[4];
+ z[5] = x[5];
+ z[6] = x[6];
+ }
+
+ public static long[] fromBigInteger(BigInteger x)
+ {
+ long[] z = Nat448.fromBigInteger64(x);
+ reduce39(z, 0);
+ return z;
+ }
+
+ public static void invert(long[] x, long[] z)
+ {
+ if (Nat448.isZero64(x))
+ {
+ throw new IllegalStateException();
+ }
+
+ // Itoh-Tsujii inversion with bases { 2, 3 }
+
+ long[] t0 = Nat448.create64();
+ long[] t1 = Nat448.create64();
+ long[] t2 = Nat448.create64();
+
+ square(x, t0);
+
+ // 3 | 408
+ squareN(t0, 1, t1);
+ multiply(t0, t1, t0);
+ squareN(t1, 1, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 136
+ squareN(t0, 3, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 68
+ squareN(t0, 6, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 34
+ squareN(t0, 12, t1);
+ multiply(t0, t1, t2);
+
+ // ! {2,3} | 17
+ squareN(t2, 24, t0);
+ squareN(t0, 24, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 8
+ squareN(t0, 48, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 4
+ squareN(t0, 96, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 2
+ squareN(t0, 192, t1);
+ multiply(t0, t1, t0);
+
+ multiply(t0, t2, z);
+ }
+
+ public static void multiply(long[] x, long[] y, long[] z)
+ {
+ long[] tt = Nat448.createExt64();
+ implMultiply(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(long[] x, long[] y, long[] zz)
+ {
+ long[] tt = Nat448.createExt64();
+ implMultiply(x, y, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void reduce(long[] xx, long[] z)
+ {
+ long x00 = xx[0], x01 = xx[1], x02 = xx[2], x03 = xx[3];
+ long x04 = xx[4], x05 = xx[5], x06 = xx[6], x07 = xx[7];
+
+ long u = xx[12];
+ x05 ^= (u << 39);
+ x06 ^= (u >>> 25) ^ (u << 62);
+ x07 ^= (u >>> 2);
+
+ u = xx[11];
+ x04 ^= (u << 39);
+ x05 ^= (u >>> 25) ^ (u << 62);
+ x06 ^= (u >>> 2);
+
+ u = xx[10];
+ x03 ^= (u << 39);
+ x04 ^= (u >>> 25) ^ (u << 62);
+ x05 ^= (u >>> 2);
+
+ u = xx[9];
+ x02 ^= (u << 39);
+ x03 ^= (u >>> 25) ^ (u << 62);
+ x04 ^= (u >>> 2);
+
+ u = xx[8];
+ x01 ^= (u << 39);
+ x02 ^= (u >>> 25) ^ (u << 62);
+ x03 ^= (u >>> 2);
+
+ u = x07;
+ x00 ^= (u << 39);
+ x01 ^= (u >>> 25) ^ (u << 62);
+ x02 ^= (u >>> 2);
+
+ long t = x06 >>> 25;
+ z[0] = x00 ^ t;
+ z[1] = x01 ^ (t << 23);
+ z[2] = x02;
+ z[3] = x03;
+ z[4] = x04;
+ z[5] = x05;
+ z[6] = x06 & M25;
+ }
+
+ public static void reduce39(long[] z, int zOff)
+ {
+ long z6 = z[zOff + 6], t = z6 >>> 25;
+ z[zOff ] ^= t;
+ z[zOff + 1] ^= (t << 23);
+ z[zOff + 6] = z6 & M25;
+ }
+
+ public static void sqrt(long[] x, long[] z)
+ {
+ long u0, u1;
+ u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]);
+ long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ long c0 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ u0 = Interleave.unshuffle(x[2]); u1 = Interleave.unshuffle(x[3]);
+ long e1 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ long c1 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ u0 = Interleave.unshuffle(x[4]); u1 = Interleave.unshuffle(x[5]);
+ long e2 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ long c2 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+
+ u0 = Interleave.unshuffle(x[6]);
+ long e3 = (u0 & 0x00000000FFFFFFFFL);
+ long c3 = (u0 >>> 32);
+
+ z[0] = e0 ^ (c0 << 44);
+ z[1] = e1 ^ (c1 << 44) ^ (c0 >>> 20);
+ z[2] = e2 ^ (c2 << 44) ^ (c1 >>> 20);
+ z[3] = e3 ^ (c3 << 44) ^ (c2 >>> 20) ^ (c0 << 13);
+ z[4] = (c3 >>> 20) ^ (c1 << 13) ^ (c0 >>> 51);
+ z[5] = (c2 << 13) ^ (c1 >>> 51);
+ z[6] = (c3 << 13) ^ (c2 >>> 51);
+
+// assert (c3 >>> 51) == 0;
+ }
+
+ public static void square(long[] x, long[] z)
+ {
+ long[] tt = Nat.create64(13);
+ implSquare(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareAddToExt(long[] x, long[] zz)
+ {
+ long[] tt = Nat.create64(13);
+ implSquare(x, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void squareN(long[] x, int n, long[] z)
+ {
+// assert n > 0;
+
+ long[] tt = Nat.create64(13);
+ implSquare(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ implSquare(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static int trace(long[] x)
+ {
+ // Non-zero-trace bits: 0
+ return (int)(x[0]) & 1;
+ }
+
+ protected static void implCompactExt(long[] zz)
+ {
+ long z00 = zz[ 0], z01 = zz[ 1], z02 = zz[ 2], z03 = zz[ 3], z04 = zz[ 4], z05 = zz[ 5], z06 = zz[ 6];
+ long z07 = zz[ 7], z08 = zz[ 8], z09 = zz[ 9], z10 = zz[10], z11 = zz[11], z12 = zz[12], z13 = zz[13];
+ zz[ 0] = z00 ^ (z01 << 59);
+ zz[ 1] = (z01 >>> 5) ^ (z02 << 54);
+ zz[ 2] = (z02 >>> 10) ^ (z03 << 49);
+ zz[ 3] = (z03 >>> 15) ^ (z04 << 44);
+ zz[ 4] = (z04 >>> 20) ^ (z05 << 39);
+ zz[ 5] = (z05 >>> 25) ^ (z06 << 34);
+ zz[ 6] = (z06 >>> 30) ^ (z07 << 29);
+ zz[ 7] = (z07 >>> 35) ^ (z08 << 24);
+ zz[ 8] = (z08 >>> 40) ^ (z09 << 19);
+ zz[ 9] = (z09 >>> 45) ^ (z10 << 14);
+ zz[10] = (z10 >>> 50) ^ (z11 << 9);
+ zz[11] = (z11 >>> 55) ^ (z12 << 4)
+ ^ (z13 << 63);
+ zz[12] = (z12 >>> 60)
+ ^ (z13 >>> 1);
+ zz[13] = 0;
+ }
+
+ protected static void implExpand(long[] x, long[] z)
+ {
+ long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6];
+ z[0] = x0 & M59;
+ z[1] = ((x0 >>> 59) ^ (x1 << 5)) & M59;
+ z[2] = ((x1 >>> 54) ^ (x2 << 10)) & M59;
+ z[3] = ((x2 >>> 49) ^ (x3 << 15)) & M59;
+ z[4] = ((x3 >>> 44) ^ (x4 << 20)) & M59;
+ z[5] = ((x4 >>> 39) ^ (x5 << 25)) & M59;
+ z[6] = ((x5 >>> 34) ^ (x6 << 30));
+ }
+
+ protected static void implMultiply(long[] x, long[] y, long[] zz)
+ {
+ long[] a = new long[7], b = new long[7];
+ implExpand(x, a);
+ implExpand(y, b);
+
+ for (int i = 0; i < 7; ++i)
+ {
+ implMulwAcc(a, b[i], zz, i);
+ }
+
+ implCompactExt(zz);
+ }
+
+ protected static void implMulwAcc(long[] xs, long y, long[] z, int zOff)
+ {
+// assert y >>> 59 == 0;
+
+ long[] u = new long[8];
+// u[0] = 0;
+ u[1] = y;
+ u[2] = u[1] << 1;
+ u[3] = u[2] ^ y;
+ u[4] = u[2] << 1;
+ u[5] = u[4] ^ y;
+ u[6] = u[3] << 1;
+ u[7] = u[6] ^ y;
+
+ for (int i = 0; i < 7; ++i)
+ {
+ long x = xs[i];
+
+// assert x >>> 59 == 0;
+
+ int j = (int)x;
+ long g, h = 0, l = u[j & 7]
+ ^ (u[(j >>> 3) & 7] << 3);
+ int k = 54;
+ do
+ {
+ j = (int)(x >>> k);
+ g = u[j & 7]
+ ^ u[(j >>> 3) & 7] << 3;
+ l ^= (g << k);
+ h ^= (g >>> -k);
+ }
+ while ((k -= 6) > 0);
+
+// assert h >>> 53 == 0;
+
+ z[zOff + i ] ^= l & M59;
+ z[zOff + i + 1] ^= (l >>> 59) ^ (h << 5);
+ }
+ }
+
+ protected static void implSquare(long[] x, long[] zz)
+ {
+ for (int i = 0; i < 6; ++i)
+ {
+ Interleave.expand64To128(x[i], zz, i << 1);
+ }
+ zz[12] = Interleave.expand32to64((int)x[6]);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409FieldElement.java
new file mode 100644
index 00000000..eaf6fc90
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409FieldElement.java
@@ -0,0 +1,222 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat448;
+import org.bouncycastle.util.Arrays;
+
+public class SecT409FieldElement extends ECFieldElement
+{
+ protected long[] x;
+
+ public SecT409FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.bitLength() > 409)
+ {
+ throw new IllegalArgumentException("x value invalid for SecT409FieldElement");
+ }
+
+ this.x = SecT409Field.fromBigInteger(x);
+ }
+
+ public SecT409FieldElement()
+ {
+ this.x = Nat448.create64();
+ }
+
+ protected SecT409FieldElement(long[] x)
+ {
+ this.x = x;
+ }
+
+// public int bitLength()
+// {
+// return x.degree();
+// }
+
+ public boolean isOne()
+ {
+ return Nat448.isOne64(x);
+ }
+
+ public boolean isZero()
+ {
+ return Nat448.isZero64(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return (x[0] & 1L) != 0L;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat448.toBigInteger64(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecT409Field";
+ }
+
+ public int getFieldSize()
+ {
+ return 409;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ long[] z = Nat448.create64();
+ SecT409Field.add(x, ((SecT409FieldElement)b).x, z);
+ return new SecT409FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ long[] z = Nat448.create64();
+ SecT409Field.addOne(x, z);
+ return new SecT409FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ long[] z = Nat448.create64();
+ SecT409Field.multiply(x, ((SecT409FieldElement)b).x, z);
+ return new SecT409FieldElement(z);
+ }
+
+ public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ return multiplyPlusProduct(b, x, y);
+ }
+
+ public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x, bx = ((SecT409FieldElement)b).x;
+ long[] xx = ((SecT409FieldElement)x).x, yx = ((SecT409FieldElement)y).x;
+
+ long[] tt = Nat.create64(13);
+ SecT409Field.multiplyAddToExt(ax, bx, tt);
+ SecT409Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat448.create64();
+ SecT409Field.reduce(tt, z);
+ return new SecT409FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+ return multiply(b.invert());
+ }
+
+ public ECFieldElement negate()
+ {
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ long[] z = Nat448.create64();
+ SecT409Field.square(x, z);
+ return new SecT409FieldElement(z);
+ }
+
+ public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ return squarePlusProduct(x, y);
+ }
+
+ public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x;
+ long[] xx = ((SecT409FieldElement)x).x, yx = ((SecT409FieldElement)y).x;
+
+ long[] tt = Nat.create64(13);
+ SecT409Field.squareAddToExt(ax, tt);
+ SecT409Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat448.create64();
+ SecT409Field.reduce(tt, z);
+ return new SecT409FieldElement(z);
+ }
+
+ public ECFieldElement squarePow(int pow)
+ {
+ if (pow < 1)
+ {
+ return this;
+ }
+
+ long[] z = Nat448.create64();
+ SecT409Field.squareN(x, pow, z);
+ return new SecT409FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+ long[] z = Nat448.create64();
+ SecT409Field.invert(x, z);
+ return new SecT409FieldElement(z);
+ }
+
+ public ECFieldElement sqrt()
+ {
+ long[] z = Nat448.create64();
+ SecT409Field.sqrt(x, z);
+ return new SecT409FieldElement(z);
+ }
+
+ public int getRepresentation()
+ {
+ return ECFieldElement.F2m.TPB;
+ }
+
+ public int getM()
+ {
+ return 409;
+ }
+
+ public int getK1()
+ {
+ return 87;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecT409FieldElement))
+ {
+ return false;
+ }
+
+ SecT409FieldElement o = (SecT409FieldElement)other;
+ return Nat448.eq64(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return 4090087 ^ Arrays.hashCode(x, 0, 7);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Curve.java
new file mode 100644
index 00000000..fb66cd8b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Curve.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECMultiplier;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.WTauNafMultiplier;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT409K1Curve extends AbstractF2m
+{
+ private static final int SecT409K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT409K1Point infinity;
+
+ public SecT409K1Curve()
+ {
+ super(409, 87, 0, 0);
+
+ this.infinity = new SecT409K1Point(this, null, null);
+
+ this.a = fromBigInteger(BigInteger.valueOf(0));
+ this.b = fromBigInteger(BigInteger.valueOf(1));
+ this.order = new BigInteger(1, Hex.decode("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF"));
+ this.cofactor = BigInteger.valueOf(4);
+
+ this.coord = SecT409K1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT409K1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected ECMultiplier createDefaultMultiplier()
+ {
+ return new WTauNafMultiplier();
+ }
+
+ public int getFieldSize()
+ {
+ return 409;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT409FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT409K1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT409K1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return true;
+ }
+
+ public int getM()
+ {
+ return 409;
+ }
+
+ public boolean isTrinomial()
+ {
+ return true;
+ }
+
+ public int getK1()
+ {
+ return 87;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Point.java
new file mode 100644
index 00000000..4204923b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Point.java
@@ -0,0 +1,324 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT409K1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT409K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT409K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT409K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT409K1Point(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+// X3 = L.square().add(L).add(X1).add(curve.getA());
+ X3 = L.square().add(L).add(X1);
+ if (X3.isZero())
+ {
+// return new SecT409K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT409K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+// return new SecT409K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT409K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT409K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement T;
+ if (Z1IsOne)
+ {
+ T = L1.square().add(L1);
+ }
+ else
+ {
+ T = L1.add(Z1).multiply(L1);
+ }
+
+ if (T.isZero())
+ {
+// return new SecT409K1Point(curve, T, curve.getB().sqrt(), withCompression);
+ return new SecT409K1Point(curve, T, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement t1 = L1.add(X1).square();
+ ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.square();
+ ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3).add(Z3);
+
+ return new SecT409K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ // NOTE: twicePlus() only optimized for lambda-affine argument
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement T = L1Sq.add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement A = L2plus1.multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+// return new SecT409K1Point(curve, A, curve.getB().sqrt(), withCompression);
+ return new SecT409K1Point(curve, A, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT409K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT409K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Curve.java
new file mode 100644
index 00000000..55a4b7dd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Curve.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT409R1Curve extends AbstractF2m
+{
+ private static final int SecT409R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT409R1Point infinity;
+
+ public SecT409R1Curve()
+ {
+ super(409, 87, 0, 0);
+
+ this.infinity = new SecT409R1Point(this, null, null);
+
+ this.a = fromBigInteger(BigInteger.valueOf(1));
+ this.b = fromBigInteger(new BigInteger(1, Hex.decode("0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F")));
+ this.order = new BigInteger(1, Hex.decode("010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT409R1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT409R1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 409;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT409FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT409R1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT409R1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 409;
+ }
+
+ public boolean isTrinomial()
+ {
+ return true;
+ }
+
+ public int getK1()
+ {
+ return 87;
+ }
+
+ public int getK2()
+ {
+ return 0;
+ }
+
+ public int getK3()
+ {
+ return 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Point.java
new file mode 100644
index 00000000..078935da
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Point.java
@@ -0,0 +1,309 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT409R1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT409R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT409R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT409R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT409R1Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+// X3 = L.square().add(L).add(X1).add(curve.getA());
+ X3 = L.square().add(L).add(X1).addOne();
+ if (X3.isZero())
+ {
+ return new SecT409R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+ return new SecT409R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT409R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq);
+ if (T.isZero())
+ {
+ return new SecT409R1Point(curve, T, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT409R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement A = L2.multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+ return new SecT409R1Point(curve, A, curve.getB().sqrt(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT409R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT409R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571Field.java
new file mode 100644
index 00000000..68368081
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571Field.java
@@ -0,0 +1,335 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Interleave;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat576;
+
+public class SecT571Field
+{
+ private static final long M59 = -1L >>> 5;
+
+ private static final long RM = 0xEF7BDEF7BDEF7BDEL;
+
+ private static final long[] ROOT_Z = new long[]{ 0x2BE1195F08CAFB99L, 0x95F08CAF84657C23L, 0xCAF84657C232BE11L, 0x657C232BE1195F08L,
+ 0xF84657C2308CAF84L, 0x7C232BE1195F08CAL, 0xBE1195F08CAF8465L, 0x5F08CAF84657C232L, 0x784657C232BE119L };
+
+ public static void add(long[] x, long[] y, long[] z)
+ {
+ for (int i = 0; i < 9; ++i)
+ {
+ z[i] = x[i] ^ y[i];
+ }
+ }
+
+ private static void add(long[] x, int xOff, long[] y, int yOff, long[] z, int zOff)
+ {
+ for (int i = 0; i < 9; ++i)
+ {
+ z[zOff + i] = x[xOff + i] ^ y[yOff + i];
+ }
+ }
+
+ private static void addBothTo(long[] x, int xOff, long[] y, int yOff, long[] z, int zOff)
+ {
+ for (int i = 0; i < 9; ++i)
+ {
+ z[zOff + i] ^= x[xOff + i] ^ y[yOff + i];
+ }
+ }
+
+ public static void addExt(long[] xx, long[] yy, long[] zz)
+ {
+ for (int i = 0; i < 18; ++i)
+ {
+ zz[i] = xx[i] ^ yy[i];
+ }
+ }
+
+ public static void addOne(long[] x, long[] z)
+ {
+ z[0] = x[0] ^ 1L;
+ for (int i = 1; i < 9; ++i)
+ {
+ z[i] = x[i];
+ }
+ }
+
+ public static long[] fromBigInteger(BigInteger x)
+ {
+ long[] z = Nat576.fromBigInteger64(x);
+ reduce5(z, 0);
+ return z;
+ }
+
+ public static void invert(long[] x, long[] z)
+ {
+ if (Nat576.isZero64(x))
+ {
+ throw new IllegalStateException();
+ }
+
+ // Itoh-Tsujii inversion with bases { 2, 3, 5 }
+
+ long[] t0 = Nat576.create64();
+ long[] t1 = Nat576.create64();
+ long[] t2 = Nat576.create64();
+
+ square(x, t2);
+
+ // 5 | 570
+ square(t2, t0);
+ square(t0, t1);
+ multiply(t0, t1, t0);
+ squareN(t0, 2, t1);
+ multiply(t0, t1, t0);
+ multiply(t0, t2, t0);
+
+ // 3 | 114
+ squareN(t0, 5, t1);
+ multiply(t0, t1, t0);
+ squareN(t1, 5, t1);
+ multiply(t0, t1, t0);
+
+ // 2 | 38
+ squareN(t0, 15, t1);
+ multiply(t0, t1, t2);
+
+ // ! {2,3,5} | 19
+ squareN(t2, 30, t0);
+ squareN(t0, 30, t1);
+ multiply(t0, t1, t0);
+
+ // 3 | 9
+ squareN(t0, 60, t1);
+ multiply(t0, t1, t0);
+ squareN(t1, 60, t1);
+ multiply(t0, t1, t0);
+
+ // 3 | 3
+ squareN(t0, 180, t1);
+ multiply(t0, t1, t0);
+ squareN(t1, 180, t1);
+ multiply(t0, t1, t0);
+
+ multiply(t0, t2, z);
+ }
+
+ public static void multiply(long[] x, long[] y, long[] z)
+ {
+ long[] tt = Nat576.createExt64();
+ implMultiply(x, y, tt);
+ reduce(tt, z);
+ }
+
+ public static void multiplyAddToExt(long[] x, long[] y, long[] zz)
+ {
+ long[] tt = Nat576.createExt64();
+ implMultiply(x, y, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void reduce(long[] xx, long[] z)
+ {
+ long xx09 = xx[9];
+ long u = xx[17], v = xx09;
+
+ xx09 = v ^ (u >>> 59) ^ (u >>> 57) ^ (u >>> 54) ^ (u >>> 49);
+ v = xx[8] ^ (u << 5) ^ (u << 7) ^ (u << 10) ^ (u << 15);
+
+ for (int i = 16; i >= 10; --i)
+ {
+ u = xx[i];
+ z[i - 8] = v ^ (u >>> 59) ^ (u >>> 57) ^ (u >>> 54) ^ (u >>> 49);
+ v = xx[i - 9] ^ (u << 5) ^ (u << 7) ^ (u << 10) ^ (u << 15);
+ }
+
+ u = xx09;
+ z[1] = v ^ (u >>> 59) ^ (u >>> 57) ^ (u >>> 54) ^ (u >>> 49);
+ v = xx[0] ^ (u << 5) ^ (u << 7) ^ (u << 10) ^ (u << 15);
+
+ long x08 = z[8];
+ long t = x08 >>> 59;
+ z[0] = v ^ t ^ (t << 2) ^ (t << 5) ^ (t << 10);
+ z[8] = x08 & M59;
+ }
+
+ public static void reduce5(long[] z, int zOff)
+ {
+ long z8 = z[zOff + 8], t = z8 >>> 59;
+ z[zOff ] ^= t ^ (t << 2) ^ (t << 5) ^ (t << 10);
+ z[zOff + 8] = z8 & M59;
+ }
+
+ public static void sqrt(long[] x, long[] z)
+ {
+ long[] evn = Nat576.create64(), odd = Nat576.create64();
+
+ int pos = 0;
+ for (int i = 0; i < 4; ++i)
+ {
+ long u0 = Interleave.unshuffle(x[pos++]);
+ long u1 = Interleave.unshuffle(x[pos++]);
+ evn[i] = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32);
+ odd[i] = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L);
+ }
+ {
+ long u0 = Interleave.unshuffle(x[pos]);
+ evn[4] = (u0 & 0x00000000FFFFFFFFL);
+ odd[4] = (u0 >>> 32);
+ }
+
+ multiply(odd, ROOT_Z, z);
+ add(z, evn, z);
+ }
+
+ public static void square(long[] x, long[] z)
+ {
+ long[] tt = Nat576.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+ }
+
+ public static void squareAddToExt(long[] x, long[] zz)
+ {
+ long[] tt = Nat576.createExt64();
+ implSquare(x, tt);
+ addExt(zz, tt, zz);
+ }
+
+ public static void squareN(long[] x, int n, long[] z)
+ {
+// assert n > 0;
+
+ long[] tt = Nat576.createExt64();
+ implSquare(x, tt);
+ reduce(tt, z);
+
+ while (--n > 0)
+ {
+ implSquare(z, tt);
+ reduce(tt, z);
+ }
+ }
+
+ public static int trace(long[] x)
+ {
+ // Non-zero-trace bits: 0, 561, 569
+ return (int)(x[0] ^ (x[8] >>> 49) ^ (x[8] >>> 57)) & 1;
+ }
+
+ protected static void implMultiply(long[] x, long[] y, long[] zz)
+ {
+// for (int i = 0; i < 9; ++i)
+// {
+// implMulwAcc(x, y[i], zz, i);
+// }
+
+ /*
+ * Precompute table of all 4-bit products of y
+ */
+ long[] T0 = new long[9 << 4];
+ System.arraycopy(y, 0, T0, 9, 9);
+// reduce5(T0, 9);
+ int tOff = 0;
+ for (int i = 7; i > 0; --i)
+ {
+ tOff += 18;
+ Nat.shiftUpBit64(9, T0, tOff >>> 1, 0L, T0, tOff);
+ reduce5(T0, tOff);
+ add(T0, 9, T0, tOff, T0, tOff + 9);
+ }
+
+ /*
+ * Second table with all 4-bit products of B shifted 4 bits
+ */
+ long[] T1 = new long[T0.length];
+ Nat.shiftUpBits64(T0.length, T0, 0, 4, 0L, T1, 0);
+
+ int MASK = 0xF;
+
+ /*
+ * Lopez-Dahab algorithm
+ */
+
+ for (int k = 56; k >= 0; k -= 8)
+ {
+ for (int j = 1; j < 9; j += 2)
+ {
+ int aVal = (int)(x[j] >>> k);
+ int u = aVal & MASK;
+ int v = (aVal >>> 4) & MASK;
+ addBothTo(T0, 9 * u, T1, 9 * v, zz, j - 1);
+ }
+ Nat.shiftUpBits64(16, zz, 0, 8, 0L);
+ }
+
+ for (int k = 56; k >= 0; k -= 8)
+ {
+ for (int j = 0; j < 9; j += 2)
+ {
+ int aVal = (int)(x[j] >>> k);
+ int u = aVal & MASK;
+ int v = (aVal >>> 4) & MASK;
+ addBothTo(T0, 9 * u, T1, 9 * v, zz, j);
+ }
+ if (k > 0)
+ {
+ Nat.shiftUpBits64(18, zz, 0, 8, 0L);
+ }
+ }
+ }
+
+ protected static void implMulwAcc(long[] xs, long y, long[] z, int zOff)
+ {
+ long[] u = new long[32];
+// u[0] = 0;
+ u[1] = y;
+ for (int i = 2; i < 32; i += 2)
+ {
+ u[i ] = u[i >>> 1] << 1;
+ u[i + 1] = u[i ] ^ y;
+ }
+
+ long l = 0;
+ for (int i = 0; i < 9; ++i)
+ {
+ long x = xs[i];
+
+ int j = (int)x;
+
+ l ^= u[j & 31];
+
+ long g, h = 0;
+ int k = 60;
+ do
+ {
+ j = (int)(x >>> k);
+ g = u[j & 31];
+ l ^= (g << k);
+ h ^= (g >>> -k);
+ }
+ while ((k -= 5) > 0);
+
+ for (int p = 0; p < 4; ++p)
+ {
+ x = (x & RM) >>> 1;
+ h ^= x & ((y << p) >> 63);
+ }
+
+ z[zOff + i] ^= l;
+
+ l = h;
+ }
+ z[zOff + 9] ^= l;
+ }
+
+ protected static void implSquare(long[] x, long[] zz)
+ {
+ for (int i = 0; i < 9; ++i)
+ {
+ Interleave.expand64To128(x[i], zz, i << 1);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571FieldElement.java
new file mode 100644
index 00000000..31f5f5a2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571FieldElement.java
@@ -0,0 +1,221 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Nat576;
+import org.bouncycastle.util.Arrays;
+
+public class SecT571FieldElement extends ECFieldElement
+{
+ protected long[] x;
+
+ public SecT571FieldElement(BigInteger x)
+ {
+ if (x == null || x.signum() < 0 || x.bitLength() > 571)
+ {
+ throw new IllegalArgumentException("x value invalid for SecT571FieldElement");
+ }
+
+ this.x = SecT571Field.fromBigInteger(x);
+ }
+
+ public SecT571FieldElement()
+ {
+ this.x = Nat576.create64();
+ }
+
+ protected SecT571FieldElement(long[] x)
+ {
+ this.x = x;
+ }
+
+// public int bitLength()
+// {
+// return x.degree();
+// }
+
+ public boolean isOne()
+ {
+ return Nat576.isOne64(x);
+ }
+
+ public boolean isZero()
+ {
+ return Nat576.isZero64(x);
+ }
+
+ public boolean testBitZero()
+ {
+ return (x[0] & 1L) != 0L;
+ }
+
+ public BigInteger toBigInteger()
+ {
+ return Nat576.toBigInteger64(x);
+ }
+
+ public String getFieldName()
+ {
+ return "SecT571Field";
+ }
+
+ public int getFieldSize()
+ {
+ return 571;
+ }
+
+ public ECFieldElement add(ECFieldElement b)
+ {
+ long[] z = Nat576.create64();
+ SecT571Field.add(x, ((SecT571FieldElement)b).x, z);
+ return new SecT571FieldElement(z);
+ }
+
+ public ECFieldElement addOne()
+ {
+ long[] z = Nat576.create64();
+ SecT571Field.addOne(x, z);
+ return new SecT571FieldElement(z);
+ }
+
+ public ECFieldElement subtract(ECFieldElement b)
+ {
+ // Addition and subtraction are the same in F2m
+ return add(b);
+ }
+
+ public ECFieldElement multiply(ECFieldElement b)
+ {
+ long[] z = Nat576.create64();
+ SecT571Field.multiply(x, ((SecT571FieldElement)b).x, z);
+ return new SecT571FieldElement(z);
+ }
+
+ public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ return multiplyPlusProduct(b, x, y);
+ }
+
+ public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x, bx = ((SecT571FieldElement)b).x;
+ long[] xx = ((SecT571FieldElement)x).x, yx = ((SecT571FieldElement)y).x;
+
+ long[] tt = Nat576.createExt64();
+ SecT571Field.multiplyAddToExt(ax, bx, tt);
+ SecT571Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat576.create64();
+ SecT571Field.reduce(tt, z);
+ return new SecT571FieldElement(z);
+ }
+
+ public ECFieldElement divide(ECFieldElement b)
+ {
+ return multiply(b.invert());
+ }
+
+ public ECFieldElement negate()
+ {
+ return this;
+ }
+
+ public ECFieldElement square()
+ {
+ long[] z = Nat576.create64();
+ SecT571Field.square(x, z);
+ return new SecT571FieldElement(z);
+ }
+
+ public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ return squarePlusProduct(x, y);
+ }
+
+ public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
+ {
+ long[] ax = this.x;
+ long[] xx = ((SecT571FieldElement)x).x, yx = ((SecT571FieldElement)y).x;
+
+ long[] tt = Nat576.createExt64();
+ SecT571Field.squareAddToExt(ax, tt);
+ SecT571Field.multiplyAddToExt(xx, yx, tt);
+
+ long[] z = Nat576.create64();
+ SecT571Field.reduce(tt, z);
+ return new SecT571FieldElement(z);
+ }
+
+ public ECFieldElement squarePow(int pow)
+ {
+ if (pow < 1)
+ {
+ return this;
+ }
+
+ long[] z = Nat576.create64();
+ SecT571Field.squareN(x, pow, z);
+ return new SecT571FieldElement(z);
+ }
+
+ public ECFieldElement invert()
+ {
+ long[] z = Nat576.create64();
+ SecT571Field.invert(x, z);
+ return new SecT571FieldElement(z);
+ }
+
+ public ECFieldElement sqrt()
+ {
+ long[] z = Nat576.create64();
+ SecT571Field.sqrt(x, z);
+ return new SecT571FieldElement(z);
+ }
+
+ public int getRepresentation()
+ {
+ return ECFieldElement.F2m.PPB;
+ }
+
+ public int getM()
+ {
+ return 571;
+ }
+
+ public int getK1()
+ {
+ return 2;
+ }
+
+ public int getK2()
+ {
+ return 5;
+ }
+
+ public int getK3()
+ {
+ return 10;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ if (!(other instanceof SecT571FieldElement))
+ {
+ return false;
+ }
+
+ SecT571FieldElement o = (SecT571FieldElement)other;
+ return Nat576.eq64(x, o.x);
+ }
+
+ public int hashCode()
+ {
+ return 5711052 ^ Arrays.hashCode(x, 0, 9);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Curve.java
new file mode 100644
index 00000000..1fd2ce36
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Curve.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECMultiplier;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.WTauNafMultiplier;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT571K1Curve extends AbstractF2m
+{
+ private static final int SecT571K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT571K1Point infinity;
+
+ public SecT571K1Curve()
+ {
+ super(571, 2, 5, 10);
+
+ this.infinity = new SecT571K1Point(this, null, null);
+
+ this.a = fromBigInteger(BigInteger.valueOf(0));
+ this.b = fromBigInteger(BigInteger.valueOf(1));
+ this.order = new BigInteger(1, Hex.decode("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001"));
+ this.cofactor = BigInteger.valueOf(4);
+
+ this.coord = SecT571K1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT571K1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected ECMultiplier createDefaultMultiplier()
+ {
+ return new WTauNafMultiplier();
+ }
+
+ public int getFieldSize()
+ {
+ return 571;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT571FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT571K1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT571K1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return true;
+ }
+
+ public int getM()
+ {
+ return 571;
+ }
+
+ public boolean isTrinomial()
+ {
+ return false;
+ }
+
+ public int getK1()
+ {
+ return 2;
+ }
+
+ public int getK2()
+ {
+ return 5;
+ }
+
+ public int getK3()
+ {
+ return 10;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Point.java
new file mode 100644
index 00000000..c9238cd8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Point.java
@@ -0,0 +1,324 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT571K1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT571K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT571K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT571K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT571K1Point(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+// X3 = L.square().add(L).add(X1).add(curve.getA());
+ X3 = L.square().add(L).add(X1).addOne();
+ if (X3.isZero())
+ {
+// return new SecT571K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT571K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+// return new SecT571K1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT571K1Point(curve, X3, curve.getB(), this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT571K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement T;
+ if (Z1IsOne)
+ {
+ T = L1.square().add(L1);
+ }
+ else
+ {
+ T = L1.add(Z1).multiply(L1);
+ }
+
+ if (T.isZero())
+ {
+// return new SecT571K1Point(curve, T, curve.getB().sqrt(), withCompression);
+ return new SecT571K1Point(curve, T, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement t1 = L1.add(X1).square();
+ ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.square();
+ ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3).add(Z3);
+
+ return new SecT571K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ // NOTE: twicePlus() only optimized for lambda-affine argument
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement T = L1Sq.add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement A = L2plus1.multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+// return new SecT571K1Point(curve, A, curve.getB().sqrt(), withCompression);
+ return new SecT571K1Point(curve, A, curve.getB(), withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT571K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT571K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Curve.java
new file mode 100644
index 00000000..df39e502
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Curve.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SecT571R1Curve extends AbstractF2m
+{
+ private static final int SecT571R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+ protected SecT571R1Point infinity;
+
+ static final SecT571FieldElement SecT571R1_B = new SecT571FieldElement(
+ new BigInteger(1, Hex.decode("02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A")));
+ static final SecT571FieldElement SecT571R1_B_SQRT = (SecT571FieldElement)SecT571R1_B.sqrt();
+
+ public SecT571R1Curve()
+ {
+ super(571, 2, 5, 10);
+
+ this.infinity = new SecT571R1Point(this, null, null);
+
+ this.a = fromBigInteger(BigInteger.valueOf(1));
+ this.b = SecT571R1_B;
+ this.order = new BigInteger(1, Hex.decode("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47"));
+ this.cofactor = BigInteger.valueOf(2);
+
+ this.coord = SecT571R1_DEFAULT_COORDS;
+ }
+
+ protected ECCurve cloneCurve()
+ {
+ return new SecT571R1Curve();
+ }
+
+ public boolean supportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_LAMBDA_PROJECTIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int getFieldSize()
+ {
+ return 571;
+ }
+
+ public ECFieldElement fromBigInteger(BigInteger x)
+ {
+ return new SecT571FieldElement(x);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ return new SecT571R1Point(this, x, y, withCompression);
+ }
+
+ protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ return new SecT571R1Point(this, x, y, zs, withCompression);
+ }
+
+ public ECPoint getInfinity()
+ {
+ return infinity;
+ }
+
+ public boolean isKoblitz()
+ {
+ return false;
+ }
+
+ public int getM()
+ {
+ return 571;
+ }
+
+ public boolean isTrinomial()
+ {
+ return false;
+ }
+
+ public int getK1()
+ {
+ return 2;
+ }
+
+ public int getK2()
+ {
+ return 5;
+ }
+
+ public int getK3()
+ {
+ return 10;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Point.java
new file mode 100644
index 00000000..921828f2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Point.java
@@ -0,0 +1,313 @@
+package org.bouncycastle.math.ec.custom.sec;
+
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECPoint.AbstractF2m;
+
+public class SecT571R1Point extends AbstractF2m
+{
+ /**
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public SecT571R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ {
+ this(curve, x, y, false);
+ }
+
+ /**
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+ */
+ public SecT571R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+ {
+ super(curve, x, y);
+
+ if ((x == null) != (y == null))
+ {
+ throw new IllegalArgumentException("Exactly one of the field elements is null");
+ }
+
+ this.withCompression = withCompression;
+ }
+
+ SecT571R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+ {
+ super(curve, x, y, zs);
+
+ this.withCompression = withCompression;
+ }
+
+ protected ECPoint detach()
+ {
+ return new SecT571R1Point(null, getAffineXCoord(), getAffineYCoord());
+ }
+
+ public ECFieldElement getYCoord()
+ {
+ ECFieldElement X = x, L = y;
+
+ if (this.isInfinity() || X.isZero())
+ {
+ return L;
+ }
+
+ // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+ ECFieldElement Y = L.add(X).multiply(X);
+
+ ECFieldElement Z = zs[0];
+ if (!Z.isOne())
+ {
+ Y = Y.divide(Z);
+ }
+
+ return Y;
+ }
+
+ protected boolean getCompressionYTilde()
+ {
+ ECFieldElement X = this.getRawXCoord();
+ if (X.isZero())
+ {
+ return false;
+ }
+
+ ECFieldElement Y = this.getRawYCoord();
+
+ // Y is actually Lambda (X + Y/X) here
+ return Y.testBitZero() != X.testBitZero();
+ }
+
+ public ECPoint add(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ ECFieldElement X2 = b.getRawXCoord();
+
+ if (X1.isZero())
+ {
+ if (X2.isZero())
+ {
+ return curve.getInfinity();
+ }
+
+ return b.add(this);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0);
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement U2 = X2, S2 = L2;
+ if (!Z1IsOne)
+ {
+ U2 = U2.multiply(Z1);
+ S2 = S2.multiply(Z1);
+ }
+
+ boolean Z2IsOne = Z2.isOne();
+ ECFieldElement U1 = X1, S1 = L1;
+ if (!Z2IsOne)
+ {
+ U1 = U1.multiply(Z2);
+ S1 = S1.multiply(Z2);
+ }
+
+ ECFieldElement A = S1.add(S2);
+ ECFieldElement B = U1.add(U2);
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ ECFieldElement X3, L3, Z3;
+ if (X2.isZero())
+ {
+ // TODO This can probably be optimized quite a bit
+ ECPoint p = this.normalize();
+ X1 = p.getXCoord();
+ ECFieldElement Y1 = p.getYCoord();
+
+ ECFieldElement Y2 = L2;
+ ECFieldElement L = Y1.add(Y2).divide(X1);
+
+// X3 = L.square().add(L).add(X1).add(curve.getA());
+ X3 = L.square().add(L).add(X1).addOne();
+ if (X3.isZero())
+ {
+// return new SecT571R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT571R1Point(curve, X3, SecT571R1Curve.SecT571R1_B_SQRT, this.withCompression);
+ }
+
+ ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+ L3 = Y3.divide(X3).add(X3);
+ Z3 = curve.fromBigInteger(ECConstants.ONE);
+ }
+ else
+ {
+ B = B.square();
+
+ ECFieldElement AU1 = A.multiply(U1);
+ ECFieldElement AU2 = A.multiply(U2);
+
+ X3 = AU1.multiply(AU2);
+ if (X3.isZero())
+ {
+// return new SecT571R1Point(curve, X3, curve.getB().sqrt(), this.withCompression);
+ return new SecT571R1Point(curve, X3, SecT571R1Curve.SecT571R1_B_SQRT, this.withCompression);
+ }
+
+ ECFieldElement ABZ2 = A.multiply(B);
+ if (!Z2IsOne)
+ {
+ ABZ2 = ABZ2.multiply(Z2);
+ }
+
+ L3 = AU2.add(B).squarePlusProduct(ABZ2, L1.add(Z1));
+
+ Z3 = ABZ2;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.multiply(Z1);
+ }
+ }
+
+ return new SecT571R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twice()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return curve.getInfinity();
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+ boolean Z1IsOne = Z1.isOne();
+ ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+ ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+ ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq);
+ if (T.isZero())
+ {
+// return new SecT571R1Point(curve, T, curve.getB().sqrt(), withCompression);
+ return new SecT571R1Point(curve, T, SecT571R1Curve.SecT571R1_B_SQRT, withCompression);
+ }
+
+ ECFieldElement X3 = T.square();
+ ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+ ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+ ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3);
+
+ return new SecT571R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint twicePlus(ECPoint b)
+ {
+ if (this.isInfinity())
+ {
+ return b;
+ }
+ if (b.isInfinity())
+ {
+ return twice();
+ }
+
+ ECCurve curve = this.getCurve();
+
+ ECFieldElement X1 = this.x;
+ if (X1.isZero())
+ {
+ // A point with X == 0 is it's own additive inverse
+ return b;
+ }
+
+ ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0);
+ if (X2.isZero() || !Z2.isOne())
+ {
+ return twice().add(b);
+ }
+
+ ECFieldElement L1 = this.y, Z1 = this.zs[0];
+ ECFieldElement L2 = b.getRawYCoord();
+
+ ECFieldElement X1Sq = X1.square();
+ ECFieldElement L1Sq = L1.square();
+ ECFieldElement Z1Sq = Z1.square();
+ ECFieldElement L1Z1 = L1.multiply(Z1);
+
+// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+ ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1);
+ ECFieldElement L2plus1 = L2.addOne();
+// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement A = L2.multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq);
+ ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+ ECFieldElement B = X2Z1Sq.add(T).square();
+
+ if (B.isZero())
+ {
+ if (A.isZero())
+ {
+ return b.twice();
+ }
+
+ return curve.getInfinity();
+ }
+
+ if (A.isZero())
+ {
+// return new SecT571R1Point(curve, A, curve.getB().sqrt(), withCompression);
+ return new SecT571R1Point(curve, A, SecT571R1Curve.SecT571R1_B_SQRT, withCompression);
+ }
+
+ ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+ ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+ ECFieldElement L3 = A.add(B).square().multiplyPlusProduct(T, L2plus1, Z3);
+
+ return new SecT571R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+ }
+
+ public ECPoint negate()
+ {
+ if (this.isInfinity())
+ {
+ return this;
+ }
+
+ ECFieldElement X = this.x;
+ if (X.isZero())
+ {
+ return this;
+ }
+
+ // L is actually Lambda (X + Y/X) here
+ ECFieldElement L = this.y, Z = this.zs[0];
+ return new SecT571R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/tools/F2mSqrtOptimizer.java b/bcprov/src/main/java/org/bouncycastle/math/ec/tools/F2mSqrtOptimizer.java
new file mode 100644
index 00000000..31bac8a3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/tools/F2mSqrtOptimizer.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.math.ec.tools;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.ec.CustomNamedCurves;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECFieldElement;
+
+public class F2mSqrtOptimizer
+{
+ public static void main(String[] args)
+ {
+ SortedSet names = new TreeSet(enumToList(ECNamedCurveTable.getNames()));
+ names.addAll(enumToList(CustomNamedCurves.getNames()));
+
+ Iterator it = names.iterator();
+ while (it.hasNext())
+ {
+ String name = (String)it.next();
+ X9ECParameters x9 = CustomNamedCurves.getByName(name);
+ if (x9 == null)
+ {
+ x9 = ECNamedCurveTable.getByName(name);
+ }
+ if (x9 != null && ECAlgorithms.isF2mCurve(x9.getCurve()))
+ {
+ System.out.print(name + ":");
+ implPrintRootZ(x9);
+ }
+ }
+ }
+
+ public static void printRootZ(X9ECParameters x9)
+ {
+ if (!ECAlgorithms.isF2mCurve(x9.getCurve()))
+ {
+ throw new IllegalArgumentException("Sqrt optimization only defined over characteristic-2 fields");
+ }
+
+ implPrintRootZ(x9);
+ }
+
+ private static void implPrintRootZ(X9ECParameters x9)
+ {
+ ECFieldElement z = x9.getCurve().fromBigInteger(BigInteger.valueOf(2));
+ ECFieldElement rootZ = z.sqrt();
+
+ System.out.println(rootZ.toBigInteger().toString(16).toUpperCase());
+
+ if (!rootZ.square().equals(z))
+ {
+ throw new IllegalStateException("Optimized-sqrt sanity check failed");
+ }
+ }
+
+ private static ArrayList enumToList(Enumeration en)
+ {
+ ArrayList rv = new ArrayList();
+ while (en.hasMoreElements())
+ {
+ rv.add(en.nextElement());
+ }
+ return rv;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/tools/TraceOptimizer.java b/bcprov/src/main/java/org/bouncycastle/math/ec/tools/TraceOptimizer.java
new file mode 100644
index 00000000..b21c9e0c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/tools/TraceOptimizer.java
@@ -0,0 +1,138 @@
+package org.bouncycastle.math.ec.tools;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.ec.CustomNamedCurves;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.util.Integers;
+
+public class TraceOptimizer
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private static final SecureRandom R = new SecureRandom();
+
+ public static void main(String[] args)
+ {
+ SortedSet names = new TreeSet(enumToList(ECNamedCurveTable.getNames()));
+ names.addAll(enumToList(CustomNamedCurves.getNames()));
+
+ Iterator it = names.iterator();
+ while (it.hasNext())
+ {
+ String name = (String)it.next();
+ X9ECParameters x9 = CustomNamedCurves.getByName(name);
+ if (x9 == null)
+ {
+ x9 = ECNamedCurveTable.getByName(name);
+ }
+ if (x9 != null && ECAlgorithms.isF2mCurve(x9.getCurve()))
+ {
+ System.out.print(name + ":");
+ implPrintNonZeroTraceBits(x9);
+ }
+ }
+ }
+
+ public static void printNonZeroTraceBits(X9ECParameters x9)
+ {
+ if (!ECAlgorithms.isF2mCurve(x9.getCurve()))
+ {
+ throw new IllegalArgumentException("Trace only defined over characteristic-2 fields");
+ }
+
+ implPrintNonZeroTraceBits(x9);
+ }
+
+ public static void implPrintNonZeroTraceBits(X9ECParameters x9)
+ {
+ ECCurve c = x9.getCurve();
+ int m = c.getFieldSize();
+
+ ArrayList nonZeroTraceBits = new ArrayList();
+
+ /*
+ * Determine which of the bits contribute to the trace.
+ *
+ * See section 3.6.2 of "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone)
+ */
+ {
+ for (int i = 0; i < m; ++i)
+ {
+ BigInteger zi = ONE.shiftLeft(i);
+ ECFieldElement fe = c.fromBigInteger(zi);
+ int tr = calculateTrace(fe);
+ if (tr != 0)
+ {
+ nonZeroTraceBits.add(Integers.valueOf(i));
+ System.out.print(" " + i);
+ }
+ }
+ System.out.println();
+ }
+
+ /*
+ * Check calculation based on the non-zero-trace bits against explicit calculation, for random field elements
+ */
+ {
+ for (int i = 0; i < 1000; ++i)
+ {
+ BigInteger x = new BigInteger(m, R);
+ ECFieldElement fe = c.fromBigInteger(x);
+ int check = calculateTrace(fe);
+
+ int tr = 0;
+ for (int j = 0; j < nonZeroTraceBits.size(); ++j)
+ {
+ int bit = ((Integer)nonZeroTraceBits.get(j)).intValue();
+ if (x.testBit(bit))
+ {
+ tr ^= 1;
+ }
+ }
+
+ if (check != tr)
+ {
+ throw new IllegalStateException("Optimized-trace sanity check failed");
+ }
+ }
+ }
+ }
+
+ private static int calculateTrace(ECFieldElement fe)
+ {
+ int m = fe.getFieldSize();
+ ECFieldElement tr = fe;
+ for (int i = 1; i < m; ++i)
+ {
+ fe = fe.square();
+ tr = tr.add(fe);
+ }
+ BigInteger b = tr.toBigInteger();
+ if (b.bitLength() > 1)
+ {
+ throw new IllegalStateException();
+ }
+ return b.intValue();
+ }
+
+ private static ArrayList enumToList(Enumeration en)
+ {
+ ArrayList rv = new ArrayList();
+ while (en.hasMoreElements())
+ {
+ rv.add(en.nextElement());
+ }
+ return rv;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Interleave.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Interleave.java
new file mode 100644
index 00000000..0d25a5e9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Interleave.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.math.raw;
+
+public class Interleave
+{
+ private static final long M32 = 0x55555555L;
+ private static final long M64 = 0x5555555555555555L;
+
+ /*
+ * This expands 8 bit indices into 16 bit contents (high bit 14), by inserting 0s between bits.
+ * In a binary field, this operation is the same as squaring an 8 bit number.
+ *
+ * NOTE: All entries are positive so sign-extension is not an issue.
+ */
+// private static final short[] INTERLEAVE2_TABLE = new short[]
+// {
+// 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
+// };
+
+ public static int expand8to16(int x)
+ {
+ x &= 0xFF;
+ x = (x | (x << 4)) & 0x0F0F;
+ x = (x | (x << 2)) & 0x3333;
+ x = (x | (x << 1)) & 0x5555;
+ return x;
+ }
+
+ public static int expand16to32(int x)
+ {
+ x &= 0xFFFF;
+ x = (x | (x << 8)) & 0x00FF00FF;
+ x = (x | (x << 4)) & 0x0F0F0F0F;
+ x = (x | (x << 2)) & 0x33333333;
+ x = (x | (x << 1)) & 0x55555555;
+ return x;
+ }
+
+ public static long expand32to64(int x)
+ {
+ // "shuffle" low half to even bits and high half to odd bits
+ int t;
+ t = (x ^ (x >>> 8)) & 0x0000FF00; x ^= (t ^ (t << 8));
+ t = (x ^ (x >>> 4)) & 0x00F000F0; x ^= (t ^ (t << 4));
+ t = (x ^ (x >>> 2)) & 0x0C0C0C0C; x ^= (t ^ (t << 2));
+ t = (x ^ (x >>> 1)) & 0x22222222; x ^= (t ^ (t << 1));
+
+ return ((x >>> 1) & M32) << 32 | (x & M32);
+ }
+
+ public static void expand64To128(long x, long[] z, int zOff)
+ {
+ // "shuffle" low half to even bits and high half to odd bits
+ long t;
+ t = (x ^ (x >>> 16)) & 0x00000000FFFF0000L; x ^= (t ^ (t << 16));
+ t = (x ^ (x >>> 8)) & 0x0000FF000000FF00L; x ^= (t ^ (t << 8));
+ t = (x ^ (x >>> 4)) & 0x00F000F000F000F0L; x ^= (t ^ (t << 4));
+ t = (x ^ (x >>> 2)) & 0x0C0C0C0C0C0C0C0CL; x ^= (t ^ (t << 2));
+ t = (x ^ (x >>> 1)) & 0x2222222222222222L; x ^= (t ^ (t << 1));
+
+ z[zOff ] = (x ) & M64;
+ z[zOff + 1] = (x >>> 1) & M64;
+ }
+
+ public static long unshuffle(long x)
+ {
+ // "unshuffle" even bits to low half and odd bits to high half
+ long t;
+ t = (x ^ (x >>> 1)) & 0x2222222222222222L; x ^= (t ^ (t << 1));
+ t = (x ^ (x >>> 2)) & 0x0C0C0C0C0C0C0C0CL; x ^= (t ^ (t << 2));
+ t = (x ^ (x >>> 4)) & 0x00F000F000F000F0L; x ^= (t ^ (t << 4));
+ t = (x ^ (x >>> 8)) & 0x0000FF000000FF00L; x ^= (t ^ (t << 8));
+ t = (x ^ (x >>> 16)) & 0x00000000FFFF0000L; x ^= (t ^ (t << 16));
+ return x;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat128.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat128.java
new file mode 100644
index 00000000..8d621c17
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat128.java
@@ -0,0 +1,866 @@
+package org.bouncycastle.math.raw;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Pack;
+
+public abstract class Nat128
+{
+ private static final long M = 0xFFFFFFFFL;
+
+ public static int add(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (y[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (y[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (y[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (y[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addBothTo(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (y[0] & M) + (z[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (y[1] & M) + (z[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (y[2] & M) + (z[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (y[3] & M) + (z[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addTo(int[] x, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (z[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (z[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (z[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (z[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn)
+ {
+ long c = cIn & M;
+ c += (x[xOff + 0] & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 1] & M) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 2] & M) + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 3] & M) + (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addToEachOther(int[] u, int uOff, int[] v, int vOff)
+ {
+ long c = 0;
+ c += (u[uOff + 0] & M) + (v[vOff + 0] & M);
+ u[uOff + 0] = (int)c;
+ v[vOff + 0] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 1] & M) + (v[vOff + 1] & M);
+ u[uOff + 1] = (int)c;
+ v[vOff + 1] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 2] & M) + (v[vOff + 2] & M);
+ u[uOff + 2] = (int)c;
+ v[vOff + 2] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 3] & M) + (v[vOff + 3] & M);
+ u[uOff + 3] = (int)c;
+ v[vOff + 3] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static void copy(int[] x, int[] z)
+ {
+ z[0] = x[0];
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ }
+
+ public static void copy64(long[] x, long[] z)
+ {
+ z[0] = x[0];
+ z[1] = x[1];
+ }
+
+ public static int[] create()
+ {
+ return new int[4];
+ }
+
+ public static long[] create64()
+ {
+ return new long[2];
+ }
+
+ public static int[] createExt()
+ {
+ return new int[8];
+ }
+
+ public static long[] createExt64()
+ {
+ return new long[4];
+ }
+
+ public static boolean diff(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ boolean pos = gte(x, xOff, y, yOff);
+ if (pos)
+ {
+ sub(x, xOff, y, yOff, z, zOff);
+ }
+ else
+ {
+ sub(y, yOff, x, xOff, z, zOff);
+ }
+ return pos;
+ }
+
+ public static boolean eq(int[] x, int[] y)
+ {
+ for (int i = 3; i >= 0; --i)
+ {
+ if (x[i] != y[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean eq64(long[] x, long[] y)
+ {
+ for (int i = 1; i >= 0; --i)
+ {
+ if (x[i] != y[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static int[] fromBigInteger(BigInteger x)
+ {
+ if (x.signum() < 0 || x.bitLength() > 128)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ int[] z = create();
+ int i = 0;
+ while (x.signum() != 0)
+ {
+ z[i++] = x.intValue();
+ x = x.shiftRight(32);
+ }
+ return z;
+ }
+
+ public static long[] fromBigInteger64(BigInteger x)
+ {
+ if (x.signum() < 0 || x.bitLength() > 128)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long[] z = create64();
+ int i = 0;
+ while (x.signum() != 0)
+ {
+ z[i++] = x.longValue();
+ x = x.shiftRight(64);
+ }
+ return z;
+ }
+
+ public static int getBit(int[] x, int bit)
+ {
+ if (bit == 0)
+ {
+ return x[0] & 1;
+ }
+ int w = bit >> 5;
+ if (w < 0 || w >= 4)
+ {
+ return 0;
+ }
+ int b = bit & 31;
+ return (x[w] >>> b) & 1;
+ }
+
+ public static boolean gte(int[] x, int[] y)
+ {
+ for (int i = 3; i >= 0; --i)
+ {
+ int x_i = x[i] ^ Integer.MIN_VALUE;
+ int y_i = y[i] ^ Integer.MIN_VALUE;
+ if (x_i < y_i)
+ return false;
+ if (x_i > y_i)
+ return true;
+ }
+ return true;
+ }
+
+ public static boolean gte(int[] x, int xOff, int[] y, int yOff)
+ {
+ for (int i = 3; i >= 0; --i)
+ {
+ int x_i = x[xOff + i] ^ Integer.MIN_VALUE;
+ int y_i = y[yOff + i] ^ Integer.MIN_VALUE;
+ if (x_i < y_i)
+ return false;
+ if (x_i > y_i)
+ return true;
+ }
+ return true;
+ }
+
+ public static boolean isOne(int[] x)
+ {
+ if (x[0] != 1)
+ {
+ return false;
+ }
+ for (int i = 1; i < 4; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isOne64(long[] x)
+ {
+ if (x[0] != 1L)
+ {
+ return false;
+ }
+ for (int i = 1; i < 2; ++i)
+ {
+ if (x[i] != 0L)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isZero(int[] x)
+ {
+ for (int i = 0; i < 4; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isZero64(long[] x)
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ if (x[i] != 0L)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void mul(int[] x, int[] y, int[] zz)
+ {
+ long y_0 = y[0] & M;
+ long y_1 = y[1] & M;
+ long y_2 = y[2] & M;
+ long y_3 = y[3] & M;
+
+ {
+ long c = 0, x_0 = x[0] & M;
+ c += x_0 * y_0;
+ zz[0] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_1;
+ zz[1] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_2;
+ zz[2] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_3;
+ zz[3] = (int)c;
+ c >>>= 32;
+ zz[4] = (int)c;
+ }
+
+ for (int i = 1; i < 4; ++i)
+ {
+ long c = 0, x_i = x[i] & M;
+ c += x_i * y_0 + (zz[i + 0] & M);
+ zz[i + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[i + 1] & M);
+ zz[i + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[i + 2] & M);
+ zz[i + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[i + 3] & M);
+ zz[i + 3] = (int)c;
+ c >>>= 32;
+ zz[i + 4] = (int)c;
+ }
+ }
+
+ public static void mul(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ long y_0 = y[yOff + 0] & M;
+ long y_1 = y[yOff + 1] & M;
+ long y_2 = y[yOff + 2] & M;
+ long y_3 = y[yOff + 3] & M;
+
+ {
+ long c = 0, x_0 = x[xOff + 0] & M;
+ c += x_0 * y_0;
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_1;
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_2;
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_3;
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ zz[zzOff + 4] = (int)c;
+ }
+
+ for (int i = 1; i < 4; ++i)
+ {
+ ++zzOff;
+ long c = 0, x_i = x[xOff + i] & M;
+ c += x_i * y_0 + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ zz[zzOff + 4] = (int)c;
+ }
+ }
+
+ public static int mulAddTo(int[] x, int[] y, int[] zz)
+ {
+ long y_0 = y[0] & M;
+ long y_1 = y[1] & M;
+ long y_2 = y[2] & M;
+ long y_3 = y[3] & M;
+
+ long zc = 0;
+ for (int i = 0; i < 4; ++i)
+ {
+ long c = 0, x_i = x[i] & M;
+ c += x_i * y_0 + (zz[i + 0] & M);
+ zz[i + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[i + 1] & M);
+ zz[i + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[i + 2] & M);
+ zz[i + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[i + 3] & M);
+ zz[i + 3] = (int)c;
+ c >>>= 32;
+ c += zc + (zz[i + 4] & M);
+ zz[i + 4] = (int)c;
+ zc = c >>> 32;
+ }
+ return (int)zc;
+ }
+
+ public static int mulAddTo(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ long y_0 = y[yOff + 0] & M;
+ long y_1 = y[yOff + 1] & M;
+ long y_2 = y[yOff + 2] & M;
+ long y_3 = y[yOff + 3] & M;
+
+ long zc = 0;
+ for (int i = 0; i < 4; ++i)
+ {
+ long c = 0, x_i = x[xOff + i] & M;
+ c += x_i * y_0 + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += zc + (zz[zzOff + 4] & M);
+ zz[zzOff + 4] = (int)c;
+ zc = c >>> 32;
+ ++zzOff;
+ }
+ return (int)zc;
+ }
+
+ public static long mul33Add(int w, int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ // assert w >>> 31 == 0;
+
+ long c = 0, wVal = w & M;
+ long x0 = x[xOff + 0] & M;
+ c += wVal * x0 + (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ long x1 = x[xOff + 1] & M;
+ c += wVal * x1 + x0 + (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ long x2 = x[xOff + 2] & M;
+ c += wVal * x2 + x1 + (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ long x3 = x[xOff + 3] & M;
+ c += wVal * x3 + x2 + (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ c += x3;
+ return c;
+ }
+
+ public static int mulWordAddExt(int x, int[] yy, int yyOff, int[] zz, int zzOff)
+ {
+ // assert yyOff <= 4;
+ // assert zzOff <= 4;
+ long c = 0, xVal = x & M;
+ c += xVal * (yy[yyOff + 0] & M) + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += xVal * (yy[yyOff + 1] & M) + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += xVal * (yy[yyOff + 2] & M) + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += xVal * (yy[yyOff + 3] & M) + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int mul33DWordAdd(int x, long y, int[] z, int zOff)
+ {
+ // assert x >>> 31 == 0;
+ // assert zOff <= 0;
+
+ long c = 0, xVal = x & M;
+ long y00 = y & M;
+ c += xVal * y00 + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ long y01 = y >>> 32;
+ c += xVal * y01 + y00 + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += y01 + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int mul33WordAdd(int x, int y, int[] z, int zOff)
+ {
+ // assert x >>> 31 == 0;
+ // assert zOff <= 1;
+
+ long c = 0, xVal = x & M, yVal = y & M;
+ c += yVal * xVal + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += yVal + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(4, z, zOff, 3);
+ }
+
+ public static int mulWordDwordAdd(int x, long y, int[] z, int zOff)
+ {
+ // assert zOff <= 1;
+ long c = 0, xVal = x & M;
+ c += xVal * (y & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += xVal * (y >>> 32) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(4, z, zOff, 3);
+ }
+
+ public static int mulWordsAdd(int x, int y, int[] z, int zOff)
+ {
+ // assert zOff <= 2;
+
+ long c = 0, xVal = x & M, yVal = y & M;
+ c += yVal * xVal + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(4, z, zOff, 2);
+ }
+
+ public static int mulWord(int x, int[] y, int[] z, int zOff)
+ {
+ long c = 0, xVal = x & M;
+ int i = 0;
+ do
+ {
+ c += xVal * (y[i] & M);
+ z[zOff + i] = (int)c;
+ c >>>= 32;
+ }
+ while (++i < 4);
+ return (int)c;
+ }
+
+ public static void square(int[] x, int[] zz)
+ {
+ long x_0 = x[0] & M;
+ long zz_1;
+
+ int c = 0, w;
+ {
+ int i = 3, j = 8;
+ do
+ {
+ long xVal = (x[i--] & M);
+ long p = xVal * xVal;
+ zz[--j] = (c << 31) | (int)(p >>> 33);
+ zz[--j] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (i > 0);
+
+ {
+ long p = x_0 * x_0;
+ zz_1 = ((c << 31) & M) | (p >>> 33);
+ zz[0] = (int)p;
+ c = (int)(p >>> 32) & 1;
+ }
+ }
+
+ long x_1 = x[1] & M;
+ long zz_2 = zz[2] & M;
+
+ {
+ zz_1 += x_1 * x_0;
+ w = (int)zz_1;
+ zz[1] = (w << 1) | c;
+ c = w >>> 31;
+ zz_2 += zz_1 >>> 32;
+ }
+
+ long x_2 = x[2] & M;
+ long zz_3 = zz[3] & M;
+ long zz_4 = zz[4] & M;
+ {
+ zz_2 += x_2 * x_0;
+ w = (int)zz_2;
+ zz[2] = (w << 1) | c;
+ c = w >>> 31;
+ zz_3 += (zz_2 >>> 32) + x_2 * x_1;
+ zz_4 += zz_3 >>> 32;
+ zz_3 &= M;
+ }
+
+ long x_3 = x[3] & M;
+ long zz_5 = zz[5] & M;
+ long zz_6 = zz[6] & M;
+ {
+ zz_3 += x_3 * x_0;
+ w = (int)zz_3;
+ zz[3] = (w << 1) | c;
+ c = w >>> 31;
+ zz_4 += (zz_3 >>> 32) + x_3 * x_1;
+ zz_5 += (zz_4 >>> 32) + x_3 * x_2;
+ zz_6 += zz_5 >>> 32;
+ zz_5 &= M;
+ }
+
+ w = (int)zz_4;
+ zz[4] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_5;
+ zz[5] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_6;
+ zz[6] = (w << 1) | c;
+ c = w >>> 31;
+ w = zz[7] + (int)(zz_6 >> 32);
+ zz[7] = (w << 1) | c;
+ }
+
+ public static void square(int[] x, int xOff, int[] zz, int zzOff)
+ {
+ long x_0 = x[xOff + 0] & M;
+ long zz_1;
+
+ int c = 0, w;
+ {
+ int i = 3, j = 8;
+ do
+ {
+ long xVal = (x[xOff + i--] & M);
+ long p = xVal * xVal;
+ zz[zzOff + --j] = (c << 31) | (int)(p >>> 33);
+ zz[zzOff + --j] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (i > 0);
+
+ {
+ long p = x_0 * x_0;
+ zz_1 = ((c << 31) & M) | (p >>> 33);
+ zz[zzOff + 0] = (int)p;
+ c = (int)(p >>> 32) & 1;
+ }
+ }
+
+ long x_1 = x[xOff + 1] & M;
+ long zz_2 = zz[zzOff + 2] & M;
+
+ {
+ zz_1 += x_1 * x_0;
+ w = (int)zz_1;
+ zz[zzOff + 1] = (w << 1) | c;
+ c = w >>> 31;
+ zz_2 += zz_1 >>> 32;
+ }
+
+ long x_2 = x[xOff + 2] & M;
+ long zz_3 = zz[zzOff + 3] & M;
+ long zz_4 = zz[zzOff + 4] & M;
+ {
+ zz_2 += x_2 * x_0;
+ w = (int)zz_2;
+ zz[zzOff + 2] = (w << 1) | c;
+ c = w >>> 31;
+ zz_3 += (zz_2 >>> 32) + x_2 * x_1;
+ zz_4 += zz_3 >>> 32;
+ zz_3 &= M;
+ }
+
+ long x_3 = x[xOff + 3] & M;
+ long zz_5 = zz[zzOff + 5] & M;
+ long zz_6 = zz[zzOff + 6] & M;
+ {
+ zz_3 += x_3 * x_0;
+ w = (int)zz_3;
+ zz[zzOff + 3] = (w << 1) | c;
+ c = w >>> 31;
+ zz_4 += (zz_3 >>> 32) + x_3 * x_1;
+ zz_5 += (zz_4 >>> 32) + x_3 * x_2;
+ zz_6 += zz_5 >>> 32;
+ }
+
+ w = (int)zz_4;
+ zz[zzOff + 4] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_5;
+ zz[zzOff + 5] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_6;
+ zz[zzOff + 6] = (w << 1) | c;
+ c = w >>> 31;
+ w = zz[zzOff + 7] + (int)(zz_6 >> 32);
+ zz[zzOff + 7] = (w << 1) | c;
+ }
+
+ public static int sub(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) - (y[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (x[1] & M) - (y[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (x[2] & M) - (y[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (x[3] & M) - (y[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int sub(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (x[xOff + 0] & M) - (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 1] & M) - (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 2] & M) - (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 3] & M) - (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int subBothFrom(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (z[0] & M) - (x[0] & M) - (y[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (z[1] & M) - (x[1] & M) - (y[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (z[2] & M) - (x[2] & M) - (y[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (z[3] & M) - (x[3] & M) - (y[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int subFrom(int[] x, int[] z)
+ {
+ long c = 0;
+ c += (z[0] & M) - (x[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (z[1] & M) - (x[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (z[2] & M) - (x[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (z[3] & M) - (x[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int subFrom(int[] x, int xOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (z[zOff + 0] & M) - (x[xOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 1] & M) - (x[xOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 2] & M) - (x[xOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 3] & M) - (x[xOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static BigInteger toBigInteger(int[] x)
+ {
+ byte[] bs = new byte[16];
+ for (int i = 0; i < 4; ++i)
+ {
+ int x_i = x[i];
+ if (x_i != 0)
+ {
+ Pack.intToBigEndian(x_i, bs, (3 - i) << 2);
+ }
+ }
+ return new BigInteger(1, bs);
+ }
+
+ public static BigInteger toBigInteger64(long[] x)
+ {
+ byte[] bs = new byte[16];
+ for (int i = 0; i < 2; ++i)
+ {
+ long x_i = x[i];
+ if (x_i != 0L)
+ {
+ Pack.longToBigEndian(x_i, bs, (1 - i) << 3);
+ }
+ }
+ return new BigInteger(1, bs);
+ }
+
+ public static void zero(int[] z)
+ {
+ z[0] = 0;
+ z[1] = 0;
+ z[2] = 0;
+ z[3] = 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat160.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat160.java
new file mode 100644
index 00000000..55010dc3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat160.java
@@ -0,0 +1,878 @@
+package org.bouncycastle.math.raw;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Pack;
+
+public abstract class Nat160
+{
+ private static final long M = 0xFFFFFFFFL;
+
+ public static int add(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (y[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (y[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (y[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (y[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += (x[4] & M) + (y[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addBothTo(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (y[0] & M) + (z[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (y[1] & M) + (z[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (y[2] & M) + (z[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (y[3] & M) + (z[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += (x[4] & M) + (y[4] & M) + (z[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addTo(int[] x, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) + (z[0] & M);
+ z[0] = (int)c;
+ c >>>= 32;
+ c += (x[1] & M) + (z[1] & M);
+ z[1] = (int)c;
+ c >>>= 32;
+ c += (x[2] & M) + (z[2] & M);
+ z[2] = (int)c;
+ c >>>= 32;
+ c += (x[3] & M) + (z[3] & M);
+ z[3] = (int)c;
+ c >>>= 32;
+ c += (x[4] & M) + (z[4] & M);
+ z[4] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn)
+ {
+ long c = cIn & M;
+ c += (x[xOff + 0] & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 1] & M) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 2] & M) + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 3] & M) + (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ c += (x[xOff + 4] & M) + (z[zOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int addToEachOther(int[] u, int uOff, int[] v, int vOff)
+ {
+ long c = 0;
+ c += (u[uOff + 0] & M) + (v[vOff + 0] & M);
+ u[uOff + 0] = (int)c;
+ v[vOff + 0] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 1] & M) + (v[vOff + 1] & M);
+ u[uOff + 1] = (int)c;
+ v[vOff + 1] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 2] & M) + (v[vOff + 2] & M);
+ u[uOff + 2] = (int)c;
+ v[vOff + 2] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 3] & M) + (v[vOff + 3] & M);
+ u[uOff + 3] = (int)c;
+ v[vOff + 3] = (int)c;
+ c >>>= 32;
+ c += (u[uOff + 4] & M) + (v[vOff + 4] & M);
+ u[uOff + 4] = (int)c;
+ v[vOff + 4] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static void copy(int[] x, int[] z)
+ {
+ z[0] = x[0];
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ z[4] = x[4];
+ }
+
+ public static int[] create()
+ {
+ return new int[5];
+ }
+
+ public static int[] createExt()
+ {
+ return new int[10];
+ }
+
+ public static boolean diff(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ boolean pos = gte(x, xOff, y, yOff);
+ if (pos)
+ {
+ sub(x, xOff, y, yOff, z, zOff);
+ }
+ else
+ {
+ sub(y, yOff, x, xOff, z, zOff);
+ }
+ return pos;
+ }
+
+ public static boolean eq(int[] x, int[] y)
+ {
+ for (int i = 4; i >= 0; --i)
+ {
+ if (x[i] != y[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static int[] fromBigInteger(BigInteger x)
+ {
+ if (x.signum() < 0 || x.bitLength() > 160)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ int[] z = create();
+ int i = 0;
+ while (x.signum() != 0)
+ {
+ z[i++] = x.intValue();
+ x = x.shiftRight(32);
+ }
+ return z;
+ }
+
+ public static int getBit(int[] x, int bit)
+ {
+ if (bit == 0)
+ {
+ return x[0] & 1;
+ }
+ int w = bit >> 5;
+ if (w < 0 || w >= 5)
+ {
+ return 0;
+ }
+ int b = bit & 31;
+ return (x[w] >>> b) & 1;
+ }
+
+ public static boolean gte(int[] x, int[] y)
+ {
+ for (int i = 4; i >= 0; --i)
+ {
+ int x_i = x[i] ^ Integer.MIN_VALUE;
+ int y_i = y[i] ^ Integer.MIN_VALUE;
+ if (x_i < y_i)
+ return false;
+ if (x_i > y_i)
+ return true;
+ }
+ return true;
+ }
+
+ public static boolean gte(int[] x, int xOff, int[] y, int yOff)
+ {
+ for (int i = 4; i >= 0; --i)
+ {
+ int x_i = x[xOff + i] ^ Integer.MIN_VALUE;
+ int y_i = y[yOff + i] ^ Integer.MIN_VALUE;
+ if (x_i < y_i)
+ return false;
+ if (x_i > y_i)
+ return true;
+ }
+ return true;
+ }
+
+ public static boolean isOne(int[] x)
+ {
+ if (x[0] != 1)
+ {
+ return false;
+ }
+ for (int i = 1; i < 5; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isZero(int[] x)
+ {
+ for (int i = 0; i < 5; ++i)
+ {
+ if (x[i] != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void mul(int[] x, int[] y, int[] zz)
+ {
+ long y_0 = y[0] & M;
+ long y_1 = y[1] & M;
+ long y_2 = y[2] & M;
+ long y_3 = y[3] & M;
+ long y_4 = y[4] & M;
+
+ {
+ long c = 0, x_0 = x[0] & M;
+ c += x_0 * y_0;
+ zz[0] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_1;
+ zz[1] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_2;
+ zz[2] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_3;
+ zz[3] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_4;
+ zz[4] = (int)c;
+ c >>>= 32;
+ zz[5] = (int)c;
+ }
+
+ for (int i = 1; i < 5; ++i)
+ {
+ long c = 0, x_i = x[i] & M;
+ c += x_i * y_0 + (zz[i + 0] & M);
+ zz[i + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[i + 1] & M);
+ zz[i + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[i + 2] & M);
+ zz[i + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[i + 3] & M);
+ zz[i + 3] = (int)c;
+ c >>>= 32;
+ c += x_i * y_4 + (zz[i + 4] & M);
+ zz[i + 4] = (int)c;
+ c >>>= 32;
+ zz[i + 5] = (int)c;
+ }
+ }
+
+ public static void mul(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ long y_0 = y[yOff + 0] & M;
+ long y_1 = y[yOff + 1] & M;
+ long y_2 = y[yOff + 2] & M;
+ long y_3 = y[yOff + 3] & M;
+ long y_4 = y[yOff + 4] & M;
+
+ {
+ long c = 0, x_0 = x[xOff + 0] & M;
+ c += x_0 * y_0;
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_1;
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_2;
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_3;
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_0 * y_4;
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ zz[zzOff + 5] = (int)c;
+ }
+
+ for (int i = 1; i < 5; ++i)
+ {
+ ++zzOff;
+ long c = 0, x_i = x[xOff + i] & M;
+ c += x_i * y_0 + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_i * y_4 + (zz[zzOff + 4] & M);
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ zz[zzOff + 5] = (int)c;
+ }
+ }
+
+ public static int mulAddTo(int[] x, int[] y, int[] zz)
+ {
+ long y_0 = y[0] & M;
+ long y_1 = y[1] & M;
+ long y_2 = y[2] & M;
+ long y_3 = y[3] & M;
+ long y_4 = y[4] & M;
+
+ long zc = 0;
+ for (int i = 0; i < 5; ++i)
+ {
+ long c = 0, x_i = x[i] & M;
+ c += x_i * y_0 + (zz[i + 0] & M);
+ zz[i + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[i + 1] & M);
+ zz[i + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[i + 2] & M);
+ zz[i + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[i + 3] & M);
+ zz[i + 3] = (int)c;
+ c >>>= 32;
+ c += x_i * y_4 + (zz[i + 4] & M);
+ zz[i + 4] = (int)c;
+ c >>>= 32;
+ c += zc + (zz[i + 5] & M);
+ zz[i + 5] = (int)c;
+ zc = c >>> 32;
+ }
+ return (int)zc;
+ }
+
+ public static int mulAddTo(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff)
+ {
+ long y_0 = y[yOff + 0] & M;
+ long y_1 = y[yOff + 1] & M;
+ long y_2 = y[yOff + 2] & M;
+ long y_3 = y[yOff + 3] & M;
+ long y_4 = y[yOff + 4] & M;
+
+ long zc = 0;
+ for (int i = 0; i < 5; ++i)
+ {
+ long c = 0, x_i = x[xOff + i] & M;
+ c += x_i * y_0 + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += x_i * y_1 + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += x_i * y_2 + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += x_i * y_3 + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += x_i * y_4 + (zz[zzOff + 4] & M);
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ c += zc + (zz[zzOff + 5] & M);
+ zz[zzOff + 5] = (int)c;
+ zc = c >>> 32;
+ ++zzOff;
+ }
+ return (int)zc;
+ }
+
+ public static long mul33Add(int w, int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ // assert w >>> 31 == 0;
+
+ long c = 0, wVal = w & M;
+ long x0 = x[xOff + 0] & M;
+ c += wVal * x0 + (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ long x1 = x[xOff + 1] & M;
+ c += wVal * x1 + x0 + (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ long x2 = x[xOff + 2] & M;
+ c += wVal * x2 + x1 + (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ long x3 = x[xOff + 3] & M;
+ c += wVal * x3 + x2 + (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ long x4 = x[xOff + 4] & M;
+ c += wVal * x4 + x3 + (y[yOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>>= 32;
+ c += x4;
+ return c;
+ }
+
+ public static int mulWordAddExt(int x, int[] yy, int yyOff, int[] zz, int zzOff)
+ {
+ // assert yyOff <= 5;
+ // assert zzOff <= 5;
+ long c = 0, xVal = x & M;
+ c += xVal * (yy[yyOff + 0] & M) + (zz[zzOff + 0] & M);
+ zz[zzOff + 0] = (int)c;
+ c >>>= 32;
+ c += xVal * (yy[yyOff + 1] & M) + (zz[zzOff + 1] & M);
+ zz[zzOff + 1] = (int)c;
+ c >>>= 32;
+ c += xVal * (yy[yyOff + 2] & M) + (zz[zzOff + 2] & M);
+ zz[zzOff + 2] = (int)c;
+ c >>>= 32;
+ c += xVal * (yy[yyOff + 3] & M) + (zz[zzOff + 3] & M);
+ zz[zzOff + 3] = (int)c;
+ c >>>= 32;
+ c += xVal * (yy[yyOff + 4] & M) + (zz[zzOff + 4] & M);
+ zz[zzOff + 4] = (int)c;
+ c >>>= 32;
+ return (int)c;
+ }
+
+ public static int mul33DWordAdd(int x, long y, int[] z, int zOff)
+ {
+ // assert x >>> 31 == 0;
+ // assert zOff <= 1;
+
+ long c = 0, xVal = x & M;
+ long y00 = y & M;
+ c += xVal * y00 + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ long y01 = y >>> 32;
+ c += xVal * y01 + y00 + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += y01 + (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(5, z, zOff, 4);
+ }
+
+ public static int mul33WordAdd(int x, int y, int[] z, int zOff)
+ {
+ // assert x >>> 31 == 0;
+ // assert zOff <= 2;
+
+ long c = 0, xVal = x & M, yVal = y & M;
+ c += yVal * xVal + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += yVal + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(5, z, zOff, 3);
+ }
+
+ public static int mulWordDwordAdd(int x, long y, int[] z, int zOff)
+ {
+ // assert zOff <= 2;
+ long c = 0, xVal = x & M;
+ c += xVal * (y & M) + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += xVal * (y >>> 32) + (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(5, z, zOff, 3);
+ }
+
+ public static int mulWordsAdd(int x, int y, int[] z, int zOff)
+ {
+ // assert zOff <= 3;
+
+ long c = 0, xVal = x & M, yVal = y & M;
+ c += yVal * xVal + (z[zOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>>= 32;
+ c += (z[zOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>>= 32;
+ return c == 0 ? 0 : Nat.incAt(5, z, zOff, 2);
+ }
+
+ public static int mulWord(int x, int[] y, int[] z, int zOff)
+ {
+ long c = 0, xVal = x & M;
+ int i = 0;
+ do
+ {
+ c += xVal * (y[i] & M);
+ z[zOff + i] = (int)c;
+ c >>>= 32;
+ }
+ while (++i < 5);
+ return (int)c;
+ }
+
+ public static void square(int[] x, int[] zz)
+ {
+ long x_0 = x[0] & M;
+ long zz_1;
+
+ int c = 0, w;
+ {
+ int i = 4, j = 10;
+ do
+ {
+ long xVal = (x[i--] & M);
+ long p = xVal * xVal;
+ zz[--j] = (c << 31) | (int)(p >>> 33);
+ zz[--j] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (i > 0);
+
+ {
+ long p = x_0 * x_0;
+ zz_1 = ((c << 31) & M) | (p >>> 33);
+ zz[0] = (int)p;
+ c = (int)(p >>> 32) & 1;
+ }
+ }
+
+ long x_1 = x[1] & M;
+ long zz_2 = zz[2] & M;
+
+ {
+ zz_1 += x_1 * x_0;
+ w = (int)zz_1;
+ zz[1] = (w << 1) | c;
+ c = w >>> 31;
+ zz_2 += zz_1 >>> 32;
+ }
+
+ long x_2 = x[2] & M;
+ long zz_3 = zz[3] & M;
+ long zz_4 = zz[4] & M;
+ {
+ zz_2 += x_2 * x_0;
+ w = (int)zz_2;
+ zz[2] = (w << 1) | c;
+ c = w >>> 31;
+ zz_3 += (zz_2 >>> 32) + x_2 * x_1;
+ zz_4 += zz_3 >>> 32;
+ zz_3 &= M;
+ }
+
+ long x_3 = x[3] & M;
+ long zz_5 = zz[5] & M;
+ long zz_6 = zz[6] & M;
+ {
+ zz_3 += x_3 * x_0;
+ w = (int)zz_3;
+ zz[3] = (w << 1) | c;
+ c = w >>> 31;
+ zz_4 += (zz_3 >>> 32) + x_3 * x_1;
+ zz_5 += (zz_4 >>> 32) + x_3 * x_2;
+ zz_4 &= M;
+ zz_6 += zz_5 >>> 32;
+ zz_5 &= M;
+ }
+
+ long x_4 = x[4] & M;
+ long zz_7 = zz[7] & M;
+ long zz_8 = zz[8] & M;
+ {
+ zz_4 += x_4 * x_0;
+ w = (int)zz_4;
+ zz[4] = (w << 1) | c;
+ c = w >>> 31;
+ zz_5 += (zz_4 >>> 32) + x_4 * x_1;
+ zz_6 += (zz_5 >>> 32) + x_4 * x_2;
+ zz_7 += (zz_6 >>> 32) + x_4 * x_3;
+ zz_8 += zz_7 >>> 32;
+ }
+
+ w = (int)zz_5;
+ zz[5] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_6;
+ zz[6] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_7;
+ zz[7] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_8;
+ zz[8] = (w << 1) | c;
+ c = w >>> 31;
+ w = zz[9] + (int)(zz_8 >> 32);
+ zz[9] = (w << 1) | c;
+ }
+
+ public static void square(int[] x, int xOff, int[] zz, int zzOff)
+ {
+ long x_0 = x[xOff + 0] & M;
+ long zz_1;
+
+ int c = 0, w;
+ {
+ int i = 4, j = 10;
+ do
+ {
+ long xVal = (x[xOff + i--] & M);
+ long p = xVal * xVal;
+ zz[zzOff + --j] = (c << 31) | (int)(p >>> 33);
+ zz[zzOff + --j] = (int)(p >>> 1);
+ c = (int)p;
+ }
+ while (i > 0);
+
+ {
+ long p = x_0 * x_0;
+ zz_1 = ((c << 31) & M) | (p >>> 33);
+ zz[zzOff + 0] = (int)p;
+ c = (int)(p >>> 32) & 1;
+ }
+ }
+
+ long x_1 = x[xOff + 1] & M;
+ long zz_2 = zz[zzOff + 2] & M;
+
+ {
+ zz_1 += x_1 * x_0;
+ w = (int)zz_1;
+ zz[zzOff + 1] = (w << 1) | c;
+ c = w >>> 31;
+ zz_2 += zz_1 >>> 32;
+ }
+
+ long x_2 = x[xOff + 2] & M;
+ long zz_3 = zz[zzOff + 3] & M;
+ long zz_4 = zz[zzOff + 4] & M;
+ {
+ zz_2 += x_2 * x_0;
+ w = (int)zz_2;
+ zz[zzOff + 2] = (w << 1) | c;
+ c = w >>> 31;
+ zz_3 += (zz_2 >>> 32) + x_2 * x_1;
+ zz_4 += zz_3 >>> 32;
+ zz_3 &= M;
+ }
+
+ long x_3 = x[xOff + 3] & M;
+ long zz_5 = zz[zzOff + 5] & M;
+ long zz_6 = zz[zzOff + 6] & M;
+ {
+ zz_3 += x_3 * x_0;
+ w = (int)zz_3;
+ zz[zzOff + 3] = (w << 1) | c;
+ c = w >>> 31;
+ zz_4 += (zz_3 >>> 32) + x_3 * x_1;
+ zz_5 += (zz_4 >>> 32) + x_3 * x_2;
+ zz_4 &= M;
+ zz_6 += zz_5 >>> 32;
+ zz_5 &= M;
+ }
+
+ long x_4 = x[xOff + 4] & M;
+ long zz_7 = zz[zzOff + 7] & M;
+ long zz_8 = zz[zzOff + 8] & M;
+ {
+ zz_4 += x_4 * x_0;
+ w = (int)zz_4;
+ zz[zzOff + 4] = (w << 1) | c;
+ c = w >>> 31;
+ zz_5 += (zz_4 >>> 32) + x_4 * x_1;
+ zz_6 += (zz_5 >>> 32) + x_4 * x_2;
+ zz_7 += (zz_6 >>> 32) + x_4 * x_3;
+ zz_8 += zz_7 >>> 32;
+ }
+
+ w = (int)zz_5;
+ zz[zzOff + 5] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_6;
+ zz[zzOff + 6] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_7;
+ zz[zzOff + 7] = (w << 1) | c;
+ c = w >>> 31;
+ w = (int)zz_8;
+ zz[zzOff + 8] = (w << 1) | c;
+ c = w >>> 31;
+ w = zz[zzOff + 9] + (int)(zz_8 >> 32);
+ zz[zzOff + 9] = (w << 1) | c;
+ }
+
+ public static int sub(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (x[0] & M) - (y[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (x[1] & M) - (y[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (x[2] & M) - (y[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (x[3] & M) - (y[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ c += (x[4] & M) - (y[4] & M);
+ z[4] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int sub(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (x[xOff + 0] & M) - (y[yOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 1] & M) - (y[yOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 2] & M) - (y[yOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 3] & M) - (y[yOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>= 32;
+ c += (x[xOff + 4] & M) - (y[yOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int subBothFrom(int[] x, int[] y, int[] z)
+ {
+ long c = 0;
+ c += (z[0] & M) - (x[0] & M) - (y[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (z[1] & M) - (x[1] & M) - (y[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (z[2] & M) - (x[2] & M) - (y[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (z[3] & M) - (x[3] & M) - (y[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ c += (z[4] & M) - (x[4] & M) - (y[4] & M);
+ z[4] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int subFrom(int[] x, int[] z)
+ {
+ long c = 0;
+ c += (z[0] & M) - (x[0] & M);
+ z[0] = (int)c;
+ c >>= 32;
+ c += (z[1] & M) - (x[1] & M);
+ z[1] = (int)c;
+ c >>= 32;
+ c += (z[2] & M) - (x[2] & M);
+ z[2] = (int)c;
+ c >>= 32;
+ c += (z[3] & M) - (x[3] & M);
+ z[3] = (int)c;
+ c >>= 32;
+ c += (z[4] & M) - (x[4] & M);
+ z[4] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static int subFrom(int[] x, int xOff, int[] z, int zOff)
+ {
+ long c = 0;
+ c += (z[zOff + 0] & M) - (x[xOff + 0] & M);
+ z[zOff + 0] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 1] & M) - (x[xOff + 1] & M);
+ z[zOff + 1] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 2] & M) - (x[xOff + 2] & M);
+ z[zOff + 2] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 3] & M) - (x[xOff + 3] & M);
+ z[zOff + 3] = (int)c;
+ c >>= 32;
+ c += (z[zOff + 4] & M) - (x[xOff + 4] & M);
+ z[zOff + 4] = (int)c;
+ c >>= 32;
+ return (int)c;
+ }
+
+ public static BigInteger toBigInteger(int[] x)
+ {
+ byte[] bs = new byte[20];
+ for (int i = 0; i < 5; ++i)
+ {
+ int x_i = x[i];
+ if (x_i != 0)
+ {
+ Pack.intToBigEndian(x_i, bs, (4 - i) << 2);
+ }
+ }
+ return new BigInteger(1, bs);
+ }
+
+ public static void zero(int[] z)
+ {
+ z[0] = 0;
+ z[1] = 0;
+ z[2] = 0;
+ z[3] = 0;
+ z[4] = 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat320.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat320.java
new file mode 100644
index 00000000..764f7961
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat320.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.math.raw;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Pack;
+
+public abstract class Nat320
+{
+ public static void copy64(long[] x, long[] z)
+ {
+ z[0] = x[0];
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ z[4] = x[4];
+ }
+
+ public static long[] create64()
+ {
+ return new long[5];
+ }
+
+ public static long[] createExt64()
+ {
+ return new long[10];
+ }
+
+ public static boolean eq64(long[] x, long[] y)
+ {
+ for (int i = 4; i >= 0; --i)
+ {
+ if (x[i] != y[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static long[] fromBigInteger64(BigInteger x)
+ {
+ if (x.signum() < 0 || x.bitLength() > 320)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long[] z = create64();
+ int i = 0;
+ while (x.signum() != 0)
+ {
+ z[i++] = x.longValue();
+ x = x.shiftRight(64);
+ }
+ return z;
+ }
+
+ public static boolean isOne64(long[] x)
+ {
+ if (x[0] != 1L)
+ {
+ return false;
+ }
+ for (int i = 1; i < 5; ++i)
+ {
+ if (x[i] != 0L)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isZero64(long[] x)
+ {
+ for (int i = 0; i < 5; ++i)
+ {
+ if (x[i] != 0L)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static BigInteger toBigInteger64(long[] x)
+ {
+ byte[] bs = new byte[40];
+ for (int i = 0; i < 5; ++i)
+ {
+ long x_i = x[i];
+ if (x_i != 0L)
+ {
+ Pack.longToBigEndian(x_i, bs, (4 - i) << 3);
+ }
+ }
+ return new BigInteger(1, bs);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat448.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat448.java
new file mode 100644
index 00000000..29c18420
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat448.java
@@ -0,0 +1,100 @@
+package org.bouncycastle.math.raw;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Pack;
+
+public abstract class Nat448
+{
+ public static void copy64(long[] x, long[] z)
+ {
+ z[0] = x[0];
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ z[4] = x[4];
+ z[5] = x[5];
+ z[6] = x[6];
+ }
+
+ public static long[] create64()
+ {
+ return new long[7];
+ }
+
+ public static long[] createExt64()
+ {
+ return new long[14];
+ }
+
+ public static boolean eq64(long[] x, long[] y)
+ {
+ for (int i = 6; i >= 0; --i)
+ {
+ if (x[i] != y[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static long[] fromBigInteger64(BigInteger x)
+ {
+ if (x.signum() < 0 || x.bitLength() > 448)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long[] z = create64();
+ int i = 0;
+ while (x.signum() != 0)
+ {
+ z[i++] = x.longValue();
+ x = x.shiftRight(64);
+ }
+ return z;
+ }
+
+ public static boolean isOne64(long[] x)
+ {
+ if (x[0] != 1L)
+ {
+ return false;
+ }
+ for (int i = 1; i < 7; ++i)
+ {
+ if (x[i] != 0L)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isZero64(long[] x)
+ {
+ for (int i = 0; i < 7; ++i)
+ {
+ if (x[i] != 0L)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static BigInteger toBigInteger64(long[] x)
+ {
+ byte[] bs = new byte[56];
+ for (int i = 0; i < 7; ++i)
+ {
+ long x_i = x[i];
+ if (x_i != 0L)
+ {
+ Pack.longToBigEndian(x_i, bs, (6 - i) << 3);
+ }
+ }
+ return new BigInteger(1, bs);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat576.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat576.java
new file mode 100644
index 00000000..d9e06a6b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat576.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.math.raw;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Pack;
+
+public abstract class Nat576
+{
+ public static void copy64(long[] x, long[] z)
+ {
+ z[0] = x[0];
+ z[1] = x[1];
+ z[2] = x[2];
+ z[3] = x[3];
+ z[4] = x[4];
+ z[5] = x[5];
+ z[6] = x[6];
+ z[7] = x[7];
+ z[8] = x[8];
+ }
+
+ public static long[] create64()
+ {
+ return new long[9];
+ }
+
+ public static long[] createExt64()
+ {
+ return new long[18];
+ }
+
+ public static boolean eq64(long[] x, long[] y)
+ {
+ for (int i = 8; i >= 0; --i)
+ {
+ if (x[i] != y[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static long[] fromBigInteger64(BigInteger x)
+ {
+ if (x.signum() < 0 || x.bitLength() > 576)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long[] z = create64();
+ int i = 0;
+ while (x.signum() != 0)
+ {
+ z[i++] = x.longValue();
+ x = x.shiftRight(64);
+ }
+ return z;
+ }
+
+ public static boolean isOne64(long[] x)
+ {
+ if (x[0] != 1L)
+ {
+ return false;
+ }
+ for (int i = 1; i < 9; ++i)
+ {
+ if (x[i] != 0L)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isZero64(long[] x)
+ {
+ for (int i = 0; i < 9; ++i)
+ {
+ if (x[i] != 0L)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static BigInteger toBigInteger64(long[] x)
+ {
+ byte[] bs = new byte[72];
+ for (int i = 0; i < 9; ++i)
+ {
+ long x_i = x[i];
+ if (x_i != 0L)
+ {
+ Pack.longToBigEndian(x_i, bs, (8 - i) << 3);
+ }
+ }
+ return new BigInteger(1, bs);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Properties.java b/bcprov/src/main/java/org/bouncycastle/util/Properties.java
new file mode 100644
index 00000000..96cef357
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/Properties.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.util;
+
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Utility method for accessing system properties.
+ */
+public class Properties
+{
+ public static boolean isOverrideSet(final String propertyName)
+ {
+ try
+ {
+ return "true".equals(AccessController.doPrivileged(new PrivilegedAction()
+ {
+ // JDK 1.4 compatibility
+ public Object run()
+ {
+ String value = System.getProperty(propertyName);
+ if (value == null)
+ {
+ return null;
+ }
+
+ return Strings.toLowerCase(value);
+ }
+ }));
+ }
+ catch (AccessControlException e)
+ {
+ return false;
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/util/test/TestRandomEntropySourceProvider.java b/bcprov/src/main/java/org/bouncycastle/util/test/TestRandomEntropySourceProvider.java
new file mode 100644
index 00000000..e18dafe4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/test/TestRandomEntropySourceProvider.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.util.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.prng.EntropySource;
+import org.bouncycastle.crypto.prng.EntropySourceProvider;
+
+/**
+ * A class for returning "quick entropy" for testing purposes.
+ */
+public class TestRandomEntropySourceProvider
+ implements EntropySourceProvider
+{
+ private final SecureRandom _sr;
+ private final boolean _predictionResistant;
+
+ /**
+ * Create a test entropy source provider.
+ *
+ * @param isPredictionResistant boolean indicating if the SecureRandom is based on prediction resistant entropy or not (true if it is).
+ */
+ public TestRandomEntropySourceProvider(boolean isPredictionResistant)
+ {
+ _sr = new SecureRandom();
+ _predictionResistant = isPredictionResistant;
+ }
+
+ /**
+ * Return an entropy source that will create bitsRequired bits of entropy on
+ * each invocation of getEntropy().
+ *
+ * @param bitsRequired size (in bits) of entropy to be created by the provided source.
+ * @return an EntropySource that generates bitsRequired bits of entropy on each call to its getEntropy() method.
+ */
+ public EntropySource get(final int bitsRequired)
+ {
+ return new EntropySource()
+ {
+ public boolean isPredictionResistant()
+ {
+ return _predictionResistant;
+ }
+
+ public byte[] getEntropy()
+ {
+ byte[] rv = new byte[(bitsRequired + 7) / 8];
+ _sr.nextBytes(rv);
+ return rv;
+ }
+
+ public int entropySize()
+ {
+ return bitsRequired;
+ }
+ };
+ }
+}