update code
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e66e791
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.war
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+# Ignore gradle build
+build/
+.gradle
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..f51ea63
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: java
+
+jdk:
+ - oraclejdk8
+
+script:
+ - ./gradlew clean build test --stacktrace --info
+
+after_success:
+ - if [ "$TRAVIS_JDK_VERSION" = "oraclejdk8" ]; then ./gradlew jacocoTestReport coveralls; fi;
diff --git a/AMCL.pdf b/AMCL.pdf
new file mode 100644
index 0000000..e4fa685
--- /dev/null
+++ b/AMCL.pdf
Binary files differ
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f9a77cd
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2018 MIRACL UK Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..803fab8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,74 @@
+# MCJL - *Milagro Crypto Java Library*
+
+[![Master Branch](https://img.shields.io/badge/-master:-gray.svg)](https://github.com/milagro-crypto/milagro-crypto-java/tree/master)
+[![Master Build Status](https://secure.travis-ci.org/milagro-crypto/milagro-crypto-java.png?branch=master)](https://travis-ci.org/milagro-crypto/milagro-crypto-java?branch=master)
+[![Master Coverage Status](https://coveralls.io/repos/github/milagro-crypto/milagro-crypto-java/badge.svg?branch=master)](https://coveralls.io/github/milagro-crypto/milagro-crypto-java?branch=master)
+
+* **category**: Library
+* **copyright**: 2018 The Apache Software Foundation
+* **license**: ASL 2.0 ([Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0))
+* **link**: https://github.com/milagro-crypto/milagro-crypto-java
+* **introduction**: [AMCL.pdf](AMCL.pdf)
+
+
+## Description
+
+*MCJL - Milagro Crypto Java Library*
+
+* MCJL is a standards compliant JavaScript cryptographic library with no external dependencies except for the random seed source.
+
+* MCJL is a refactor of the *Java* code of [AMCL](https://github.com/milagro-crypto/amcl). For a detailed explanation about this library please read: [AMCL.pdf](AMCL.pdf).
+
+* MCJL supports the standards for RSA, ECDH, ECIES, ECDSA and M-PIN, AES-GCM encryption/decryption, SHA256, SHA384, SHA512 and SHA3 hash functions and a cryptographically secure random number generator. Furthermore we recently added New Hope, a post-quantum key exchange.
+
+This library is created from the Java code in this directory
+[ACML](https://github.com/milagro-crypto/amcl/tree/master/version3/java)
+project. The config64.py script has been run in this AMCL directory and all
+the curves and RSA security level were selected for a 64-bit build; the output
+Java files from this process are used in this project. If you require a
+smaller JAR file please follow the instructions in the AMCL project.
+
+## Software Dependencies
+
+In order to build this library, the following packages are required:
+
+* [gradle](https://gradle.org/)
+
+## Setup
+This library is avaiable on Maven Central.
+
+Replace `VERSION` below with required version.
+
+To use `MCJL` with Maven project, use:
+```
+<dependency>
+ <groupId>org.miracl.milagro.amcl</groupId>
+ <artifactId>milagro-crypto-java</artifactId>
+ <version>VERSION</version>
+</dependency>
+```
+
+For Gradle project:
+```
+dependencies {
+ compile 'org.miracl.milagro.amcl:milagro-crypto-java:VERSION'
+}
+```
+
+Fill the `gradle.properties` file if you want to upload on Maven Central.
+
+`MCJL` needs Java 8.
+
+## Local Installation
+
+Use this command to compile library and install it as artifact to local Maven
+repository.
+
+ ./gradlew clean build publishToMavenLocal --stacktrace --info
+
+## Contributions
+
+Contributions are very welcome. Please make pull requests to the develop
+branch. You can run this command to build and test the code.
+
+ ./gradlew build
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..1d0ba9e
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.4.0
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..31bf75b
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,104 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.bmuschko:gradle-nexus-plugin:2.3.1'
+ }
+}
+
+plugins {
+ id 'java-library'
+ id 'jacoco'
+ id 'com.github.kt3k.coveralls' version '2.6.3'
+ id 'io.codearte.nexus-staging' version '0.11.0'
+}
+
+apply plugin: 'java'
+apply plugin: 'maven-publish'
+apply plugin: 'com.bmuschko.nexus'
+
+nexusStaging {
+ packageGroup = "org.miracl"
+}
+
+publishing {
+ publications {
+ mavenJava(MavenPublication) {
+ artifactId 'milagro-crypto-java'
+ groupId 'org.miracl.milagro.amcl'
+ version '0.4.0'
+ from components.java
+ }
+ }
+
+ repositories {
+ maven {
+ // change to point to your repo, e.g. http://my.org/repo
+ url "$buildDir/repo"
+ }
+ }
+}
+
+
+dependencies {
+ // This dependency is exported to consumers, that is to say found on their compile classpath.
+ api 'org.apache.commons:commons-math3:3.6.1'
+
+ // This dependency is used internally, and not exposed to consumers on their own compile classpath.
+ implementation 'com.google.guava:guava:23.0'
+
+ // Use JUnit test framework
+ testImplementation 'junit:junit:4.12'
+}
+
+// In this section you declare where to find the dependencies of your project
+repositories {
+ jcenter()
+}
+
+jacocoTestReport {
+ reports {
+ xml.enabled = true
+ html.enabled = true
+ }
+}
+
+archivesBaseName = 'milagro-crypto-java'
+group = "org.miracl.milagro.amcl"
+version = "0.4.0"
+modifyPom {
+ project {
+ name 'milagro-crypto-java'
+ description 'MCJL - Milagro Crypto Java Library'
+ url 'https://github.com/milagro-crypto/milagro-crypto-java'
+ inceptionYear '2018'
+ scm {
+ url 'https://bitbucket.org/objdict/objjson'
+ connection 'scm:https://github.com/milagro-crypto/milagro-crypto-java.git'
+ developerConnection 'scm:git://github.com/milagro-crypto/milagro-crypto-java.git'
+ }
+ licenses {
+ license {
+ name 'The Apache Software License, Version 2.0'
+ url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+ distribution 'repo'
+ }
+ }
+ developers {
+ developer {
+ email 'support@miracl.com'
+ }
+ }
+ }
+}
+extraArchive {
+ sources = true
+ tests = true
+ javadoc = true
+}
+nexus {
+ sign = true
+ repositoryUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
+ snapshotRepositoryUrl = 'https://oss.sonatype.org/content/repositories/snapshots/'
+}
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..a666450
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,11 @@
+# Examples
+
+These are two examples programs that require the library to be built before
+they can be run. These are adapted from the tests for the BN254CX curve.
+Replace `VERSION` below with required version.
+
+ javac -classpath .:../build/libs/milagro-crypto-java-VERSION.jar TestMPIN.java
+ java -classpath .:../build/libs/milagro-crypto-java-VERSION.jar TestMPIN
+
+ javac -classpath .:../build/libs/milagro-crypto-java-VERSION.jar TestECC.java
+ java -classpath .:../build/libs/milagro-crypto-java-VERSION.jar TestECC
diff --git a/examples/TestECC.java b/examples/TestECC.java
new file mode 100644
index 0000000..100c086
--- /dev/null
+++ b/examples/TestECC.java
@@ -0,0 +1,174 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* ECDH/ECIES/ECDSA example for BN254CX curve */
+
+import org.apache.milagro.amcl.BN254CX.*;
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECC {
+ private static void printBinary(byte[] array) {
+ int i;
+ for (i = 0; i < array.length; i++) {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void main(String[] args) {
+ byte[] RAW = new byte[100];
+ RAND rng = new RAND();
+ int i, j = 0, res;
+ int result;
+ String pp = new String("M0ng00se");
+
+ rng.clean();
+ for (i = 0; i < 100; i++) RAW[i] = (byte)(i);
+ rng.seed(100, RAW);
+
+ int EGS = ECDH.EGS;
+ int EFS = ECDH.EFS;
+ int EAS = AES.KS;
+ int sha = ECDH.HASH_TYPE;
+
+ byte[] S1 = new byte[EGS];
+ byte[] W0 = new byte[2 * EFS + 1];
+ byte[] W1 = new byte[2 * EFS + 1];
+ byte[] Z0 = new byte[EFS];
+ byte[] Z1 = new byte[EFS];
+
+ byte[] SALT = new byte[8];
+ byte[] P1 = new byte[3];
+ byte[] P2 = new byte[4];
+ byte[] V = new byte[2 * EFS + 1];
+ byte[] M = new byte[17];
+ byte[] T = new byte[12];
+ byte[] CS = new byte[EGS];
+ byte[] DS = new byte[EGS];
+
+ for (i = 0; i < 8; i++) SALT[i] = (byte)(i + 1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= " + pp);
+ byte[] PW = pp.getBytes();
+
+ /* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0 = ECDH.PBKDF2(sha, PW, SALT, 1000, EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+ /* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null, S0, W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res = ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res != 0) {
+ System.out.println("ECP Public Key is invalid!");
+ }
+ /* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng, S1, W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res = ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res != 0) {
+ System.out.println("ECP Public Key is invalid!");
+ }
+
+ /* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0, W1, Z0);
+ ECDH.SVDP_DH(S1, W0, Z1);
+
+ boolean same = true;
+ for (i = 0; i < EFS; i++)
+ if (Z0[i] != Z1[i]) same = false;
+
+ if (!same) {
+ System.out.println("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY = ECDH.KDF2(sha, Z0, null, EAS);
+
+ System.out.print("Alice's DH Key= 0x");
+ printBinary(KEY);
+ System.out.print("Servers DH Key= 0x");
+ printBinary(KEY);
+
+ if (ECP.CURVETYPE != ECP.MONTGOMERY) {
+ System.out.println("Testing ECIES");
+
+ P1[0] = 0x0;
+ P1[1] = 0x1;
+ P1[2] = 0x2;
+ P2[0] = 0x0;
+ P2[1] = 0x1;
+ P2[2] = 0x2;
+ P2[3] = 0x3;
+
+ for (i = 0; i <= 16; i++) M[i] = (byte) i;
+
+ byte[] C = ECDH.ECIES_ENCRYPT(sha, P1, P2, rng, W1, M, V, T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x");
+ printBinary(V);
+ System.out.print("C= 0x");
+ printBinary(C);
+ System.out.print("T= 0x");
+ printBinary(T);
+
+
+ M = ECDH.ECIES_DECRYPT(sha, P1, P2, V, C, T, S1);
+ if (M.length == 0) {
+ System.out.println("*** ECIES Decryption Failed");
+ } else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x");
+ printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha, rng, S0, M, CS, DS) != 0) {
+ System.out.println("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x");
+ printBinary(CS);
+ System.out.print("D= 0x");
+ printBinary(DS);
+
+ if (ECDH.VP_DSA(sha, W0, M, CS, DS) != 0) {
+ System.out.println("***ECDSA Verification Failed");
+ } else System.out.println("ECDSA Signature/Verification succeeded " + j);
+ System.out.println("");
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/TestMPIN.java b/examples/TestMPIN.java
new file mode 100644
index 0000000..e5d7afc
--- /dev/null
+++ b/examples/TestMPIN.java
@@ -0,0 +1,267 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* MPIN example for BN254CX curve */
+import org.apache.milagro.amcl.BN254CX.*;
+import org.apache.milagro.amcl.RAND;
+
+public class TestMPIN {
+
+ static boolean PERMITS = true;
+ static boolean PINERROR = true;
+ static boolean FULL = true;
+ static boolean SINGLE_PASS = false;
+
+ static void printBinary(byte[] array) {
+ int i;
+ for (i = 0; i < array.length; i++) {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+ public static void main(String[] args) {
+ RAND rng = new RAND();
+ int EGS = MPIN.EGS;
+ int EFS = MPIN.EFS;
+ int G1S = 2 * EFS + 1; /* Group 1 Size */
+ int G2S = 4 * EFS; /* Group 2 Size */
+ int EAS = 16;
+
+ int sha = MPIN.HASH_TYPE;
+
+ byte[] S = new byte[EGS];
+ byte[] SST = new byte[G2S];
+ byte[] TOKEN = new byte[G1S];
+ byte[] PERMIT = new byte[G1S];
+ byte[] SEC = new byte[G1S];
+ byte[] xID = new byte[G1S];
+ byte[] xCID = new byte[G1S];
+ byte[] X = new byte[EGS];
+ byte[] Y = new byte[EGS];
+ byte[] E = new byte[12 * EFS];
+ byte[] F = new byte[12 * EFS];
+ byte[] HID = new byte[G1S];
+ byte[] HTID = new byte[G1S];
+
+ byte[] G1 = new byte[12 * EFS];
+ byte[] G2 = new byte[12 * EFS];
+ byte[] R = new byte[EGS];
+ byte[] Z = new byte[G1S];
+ byte[] W = new byte[EGS];
+ byte[] T = new byte[G1S];
+ byte[] CK = new byte[EAS];
+ byte[] SK = new byte[EAS];
+
+ byte[] HSID = null;
+ byte[] RAW = new byte[100];
+
+ rng.clean();
+ for (int i = 0; i < 100; i++) RAW[i] = (byte)(i);
+ rng.seed(100, RAW);
+
+ System.out.println("Testing MPIN code");
+
+ /* Trusted Authority set-up */
+
+ MPIN.RANDOM_GENERATE(rng, S);
+ System.out.print("Master Secret s: 0x");
+ printBinary(S);
+
+ /* Create Client Identity */
+ String IDstr = "testUser@miracl.com";
+ byte[] CLIENT_ID = IDstr.getBytes();
+
+ byte[] HCID = MPIN.HASH_ID(sha, CLIENT_ID, EFS); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+ System.out.print("Client ID Hash= ");
+ printBinary(HCID);
+ System.out.print("Client ID= ");
+ printBinary(CLIENT_ID);
+
+ /* Client and Server are issued secrets by DTA */
+
+ MPIN.GET_CLIENT_SECRET(S, HCID, TOKEN);
+ System.out.print("Client Secret CS: 0x");
+ printBinary(TOKEN);
+
+ MPIN.GET_SERVER_SECRET(S, SST);
+ System.out.print("Server Secret SS: 0x");
+ printBinary(SST);
+
+
+ /* Client extracts PIN from secret to create Token */
+ int pin = 1234;
+ System.out.println("Client extracts PIN= " + pin);
+ int rtn = MPIN.EXTRACT_PIN(sha, CLIENT_ID, pin, TOKEN);
+ if (rtn != 0)
+ System.out.println("FAILURE: EXTRACT_PIN rtn: " + rtn);
+
+ System.out.print("Client Token TK: 0x");
+ printBinary(TOKEN);
+
+ if (FULL) {
+ MPIN.PRECOMPUTE(TOKEN, HCID, G1, G2);
+ }
+ int date;
+ if (PERMITS) {
+ date = MPIN.today();
+ /* Client gets "Time Token" permit from DTA */
+ MPIN.GET_CLIENT_PERMIT(sha, date, S, HCID, PERMIT);
+ System.out.print("Time Permit TP: 0x");
+ printBinary(PERMIT);
+
+ /* This encoding makes Time permit look random - Elligator squared */
+ MPIN.ENCODING(rng, PERMIT);
+ System.out.print("Encoded Time Permit TP: 0x");
+ printBinary(PERMIT);
+ MPIN.DECODING(PERMIT);
+ System.out.print("Decoded Time Permit TP: 0x");
+ printBinary(PERMIT);
+ } else date = 0;
+
+ // System.out.print("\nPIN= ");
+ // Scanner scan=new Scanner(System.in);
+ // pin=scan.nextInt();
+
+ pin = 1234;
+
+ /* Set date=0 and PERMIT=null if time permits not in use
+
+ Client First pass: Inputs CLIENT_ID, optional RNG, pin, TOKEN and PERMIT. Output xID =x .H(CLIENT_ID) and re-combined secret SEC
+ If PERMITS are is use, then date!=0 and PERMIT is added to secret and xCID = x.(H(CLIENT_ID)+H(date|H(CLIENT_ID)))
+ Random value x is supplied externally if RNG=null, otherwise generated and passed out by RNG
+
+ IMPORTANT: To save space and time..
+ If Time Permits OFF set xCID = null, HTID=null and use xID and HID only
+ If Time permits are ON, AND pin error detection is required then all of xID, xCID, HID and HTID are required
+ If Time permits are ON, AND pin error detection is NOT required, set xID=null, HID=null and use xCID and HTID only.
+
+
+ */
+
+ byte[] pxID = xID;
+ byte[] pxCID = xCID;
+ byte[] pHID = HID;
+ byte[] pHTID = HTID;
+ byte[] pE = E;
+ byte[] pF = F;
+ byte[] pPERMIT = PERMIT;
+ byte[] prHID;
+
+ if (date != 0) {
+
+ prHID = pHTID;
+ if (!PINERROR) {
+ pxID = null;
+ // pHID=null; // new
+ }
+ } else {
+ prHID = pHID;
+ pPERMIT = null;
+ pxCID = null;
+ pHTID = null;
+ }
+ if (!PINERROR) {
+ pE = null;
+ pF = null;
+ }
+
+ if (SINGLE_PASS) {
+ System.out.println("MPIN Single Pass");
+ int timeValue = MPIN.GET_TIME();
+ rtn = MPIN.CLIENT(sha, date, CLIENT_ID, rng, X, pin, TOKEN, SEC, pxID, pxCID, pPERMIT, timeValue, Y);
+ if (rtn != 0)
+ System.out.println("FAILURE: CLIENT rtn: " + rtn);
+
+ if (FULL) {
+ HCID = MPIN.HASH_ID(sha, CLIENT_ID, EFS);
+ MPIN.GET_G1_MULTIPLE(rng, 1, R, HCID, Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ rtn = MPIN.SERVER(sha, date, pHID, pHTID, Y, SST, pxID, pxCID, SEC, pE, pF, CLIENT_ID, timeValue);
+ if (rtn != 0)
+ System.out.println("FAILURE: SERVER rtn: " + rtn);
+
+ if (FULL) {
+ HSID = MPIN.HASH_ID(sha, CLIENT_ID, EFS);
+ MPIN.GET_G1_MULTIPLE(rng, 0, W, prHID, T); /* Also send T=w.ID to client, remember random w */
+ }
+ } else {
+ System.out.println("MPIN Multi Pass");
+ /* Send U=x.ID to server, and recreate secret from token and pin */
+ rtn = MPIN.CLIENT_1(sha, date, CLIENT_ID, rng, X, pin, TOKEN, SEC, pxID, pxCID, pPERMIT);
+ if (rtn != 0)
+ System.out.println("FAILURE: CLIENT_1 rtn: " + rtn);
+
+ if (FULL) {
+ HCID = MPIN.HASH_ID(sha, CLIENT_ID, EFS);
+ MPIN.GET_G1_MULTIPLE(rng, 1, R, HCID, Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ /* Server calculates H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+ MPIN.SERVER_1(sha, date, CLIENT_ID, pHID, pHTID);
+
+ /* Server generates Random number Y and sends it to Client */
+ MPIN.RANDOM_GENERATE(rng, Y);
+
+ if (FULL) {
+ HSID = MPIN.HASH_ID(sha, CLIENT_ID, EFS);
+ MPIN.GET_G1_MULTIPLE(rng, 0, W, prHID, T); /* Also send T=w.ID to client, remember random w */
+ }
+
+ /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+ rtn = MPIN.CLIENT_2(X, Y, SEC);
+ if (rtn != 0)
+ System.out.println("FAILURE: CLIENT_2 rtn: " + rtn);
+
+ /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+ /* If PIN error not required, set E and F = null */
+
+ rtn = MPIN.SERVER_2(date, pHID, pHTID, Y, SST, pxID, pxCID, SEC, pE, pF);
+
+ if (rtn != 0)
+ System.out.println("FAILURE: SERVER_2 rtn: " + rtn);
+ }
+
+ if (rtn == MPIN.BAD_PIN) {
+ if (PINERROR) {
+ int err = MPIN.KANGAROO(E, F);
+ if (err != 0) System.out.println("Client PIN is out by " + err);
+ else System.out.println("Server says - Bad Pin. I don't know you");
+ } else System.out.println("Server says - Bad Pin. I don't know you");
+
+ } else System.out.println("Server says - PIN is good! You really are " + IDstr);
+
+
+ if (FULL) {
+ byte[] H = MPIN.HASH_ALL(sha, HCID, pxID, pxCID, SEC, Y, Z, T, EFS);
+ MPIN.CLIENT_KEY(sha, G1, G2, pin, R, X, H, T, CK);
+ System.out.print("Client Key = 0x");
+ printBinary(CK);
+
+ H = MPIN.HASH_ALL(sha, HSID, pxID, pxCID, SEC, Y, Z, T, EFS);
+ MPIN.SERVER_KEY(sha, Z, SST, W, H, pHID, pxID, pxCID, SK);
+ System.out.print("Server Key = 0x");
+ printBinary(SK);
+ }
+ System.out.println("");
+ }
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..60aa7da
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,6 @@
+#remove '#' and fill following fields:
+#nexusUsername=YOUR_SONATYPE_USER_NAME
+#nexusPassword=YOUR_SONATYPE_USER_PASSWORD
+#signing.keyId=KEY_ID
+#signing.password=KEY_PASSWORD
+#signing.secretKeyRingFile=/PATH/TO/SECRET/RING/FILE
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..a5fe1cb
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..be280be
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-bin.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..f84f2cf
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,18 @@
+/*
+ * This settings file was generated by the Gradle 'init' task.
+ *
+ * The settings file is used to specify which projects to include in your build.
+ * In a single project build this file can be empty or even removed.
+ *
+ * Detailed information about configuring a multi-project build in Gradle can be found
+ * in the user guide at https://docs.gradle.org/3.3/userguide/multi_project_builds.html
+ */
+
+/*
+// To declare projects as part of a multi-project build use the 'include' method
+include 'shared'
+include 'api'
+include 'services:webservice'
+*/
+
+rootProject.name = 'milagro-crypto-java'
diff --git a/src/main/java/org/apache/milagro/amcl/AES.java b/src/main/java/org/apache/milagro/amcl/AES.java
new file mode 100644
index 0000000..35d04dc
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/AES.java
@@ -0,0 +1,695 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+
+/* AES Encryption */
+package org.apache.milagro.amcl;
+
+public class AES {
+ int Nk,Nr;
+ int mode;
+ private int[] fkey=new int[60];
+ private int[] rkey=new int[60];
+ public byte[] f=new byte[16];
+
+
+ public static final int ECB=0;
+ public static final int CBC=1;
+ public static final int CFB1=2;
+ public static final int CFB2=3;
+ public static final int CFB4=5;
+ public static final int OFB1=14;
+ public static final int OFB2=15;
+ public static final int OFB4=17;
+ public static final int OFB8=21;
+ public static final int OFB16=29;
+ public static final int CTR1=30;
+ public static final int CTR2=31;
+ public static final int CTR4=33;
+ public static final int CTR8=37;
+ public static final int CTR16=45;
+
+ private static final byte[] InCo={(byte)0xB,(byte)0xD,(byte)0x9,(byte)0xE}; /* Inverse Coefficients */
+
+ public static final int KS=16; /* Key Size in bytes */
+ public static final int BS=16; /* Block Size */
+
+ private static final byte[] ptab=
+ {(byte)1,(byte)3,(byte)5,(byte)15,(byte)17,(byte)51,(byte)85,(byte)255,(byte)26,(byte)46,(byte)114,(byte)150,(byte)161,(byte)248,(byte)19,(byte)53,
+ (byte)95,(byte)225,(byte)56,(byte)72,(byte)216,(byte)115,(byte)149,(byte)164,(byte)247,(byte)2,(byte)6,(byte)10,(byte)30,(byte)34,(byte)102,(byte)170,
+ (byte)229,(byte)52,(byte)92,(byte)228,(byte)55,(byte)89,(byte)235,(byte)38,(byte)106,(byte)190,(byte)217,(byte)112,(byte)144,(byte)171,(byte)230,(byte)49,
+ (byte)83,(byte)245,(byte)4,(byte)12,(byte)20,(byte)60,(byte)68,(byte)204,(byte)79,(byte)209,(byte)104,(byte)184,(byte)211,(byte)110,(byte)178,(byte)205,
+ (byte)76,(byte)212,(byte)103,(byte)169,(byte)224,(byte)59,(byte)77,(byte)215,(byte)98,(byte)166,(byte)241,(byte)8,(byte)24,(byte)40,(byte)120,(byte)136,
+ (byte)131,(byte)158,(byte)185,(byte)208,(byte)107,(byte)189,(byte)220,(byte)127,(byte)129,(byte)152,(byte)179,(byte)206,(byte)73,(byte)219,(byte)118,(byte)154,
+ (byte)181,(byte)196,(byte)87,(byte)249,(byte)16,(byte)48,(byte)80,(byte)240,(byte)11,(byte)29,(byte)39,(byte)105,(byte)187,(byte)214,(byte)97,(byte)163,
+ (byte)254,(byte)25,(byte)43,(byte)125,(byte)135,(byte)146,(byte)173,(byte)236,(byte)47,(byte)113,(byte)147,(byte)174,(byte)233,(byte)32,(byte)96,(byte)160,
+ (byte)251,(byte)22,(byte)58,(byte)78,(byte)210,(byte)109,(byte)183,(byte)194,(byte)93,(byte)231,(byte)50,(byte)86,(byte)250,(byte)21,(byte)63,(byte)65,
+ (byte)195,(byte)94,(byte)226,(byte)61,(byte)71,(byte)201,(byte)64,(byte)192,(byte)91,(byte)237,(byte)44,(byte)116,(byte)156,(byte)191,(byte)218,(byte)117,
+ (byte)159,(byte)186,(byte)213,(byte)100,(byte)172,(byte)239,(byte)42,(byte)126,(byte)130,(byte)157,(byte)188,(byte)223,(byte)122,(byte)142,(byte)137,(byte)128,
+ (byte)155,(byte)182,(byte)193,(byte)88,(byte)232,(byte)35,(byte)101,(byte)175,(byte)234,(byte)37,(byte)111,(byte)177,(byte)200,(byte)67,(byte)197,(byte)84,
+ (byte)252,(byte)31,(byte)33,(byte)99,(byte)165,(byte)244,(byte)7,(byte)9,(byte)27,(byte)45,(byte)119,(byte)153,(byte)176,(byte)203,(byte)70,(byte)202,
+ (byte)69,(byte)207,(byte)74,(byte)222,(byte)121,(byte)139,(byte)134,(byte)145,(byte)168,(byte)227,(byte)62,(byte)66,(byte)198,(byte)81,(byte)243,(byte)14,
+ (byte)18,(byte)54,(byte)90,(byte)238,(byte)41,(byte)123,(byte)141,(byte)140,(byte)143,(byte)138,(byte)133,(byte)148,(byte)167,(byte)242,(byte)13,(byte)23,
+ (byte)57,(byte)75,(byte)221,(byte)124,(byte)132,(byte)151,(byte)162,(byte)253,(byte)28,(byte)36,(byte)108,(byte)180,(byte)199,(byte)82,(byte)246,(byte)1};
+
+ private static final byte[] ltab=
+ {(byte)0,(byte)255,(byte)25,(byte)1,(byte)50,(byte)2,(byte)26,(byte)198,(byte)75,(byte)199,(byte)27,(byte)104,(byte)51,(byte)238,(byte)223,(byte)3,
+ (byte)100,(byte)4,(byte)224,(byte)14,(byte)52,(byte)141,(byte)129,(byte)239,(byte)76,(byte)113,(byte)8,(byte)200,(byte)248,(byte)105,(byte)28,(byte)193,
+ (byte)125,(byte)194,(byte)29,(byte)181,(byte)249,(byte)185,(byte)39,(byte)106,(byte)77,(byte)228,(byte)166,(byte)114,(byte)154,(byte)201,(byte)9,(byte)120,
+ (byte)101,(byte)47,(byte)138,(byte)5,(byte)33,(byte)15,(byte)225,(byte)36,(byte)18,(byte)240,(byte)130,(byte)69,(byte)53,(byte)147,(byte)218,(byte)142,
+ (byte)150,(byte)143,(byte)219,(byte)189,(byte)54,(byte)208,(byte)206,(byte)148,(byte)19,(byte)92,(byte)210,(byte)241,(byte)64,(byte)70,(byte)131,(byte)56,
+ (byte)102,(byte)221,(byte)253,(byte)48,(byte)191,(byte)6,(byte)139,(byte)98,(byte)179,(byte)37,(byte)226,(byte)152,(byte)34,(byte)136,(byte)145,(byte)16,
+ (byte)126,(byte)110,(byte)72,(byte)195,(byte)163,(byte)182,(byte)30,(byte)66,(byte)58,(byte)107,(byte)40,(byte)84,(byte)250,(byte)133,(byte)61,(byte)186,
+ (byte)43,(byte)121,(byte)10,(byte)21,(byte)155,(byte)159,(byte)94,(byte)202,(byte)78,(byte)212,(byte)172,(byte)229,(byte)243,(byte)115,(byte)167,(byte)87,
+ (byte)175,(byte)88,(byte)168,(byte)80,(byte)244,(byte)234,(byte)214,(byte)116,(byte)79,(byte)174,(byte)233,(byte)213,(byte)231,(byte)230,(byte)173,(byte)232,
+ (byte)44,(byte)215,(byte)117,(byte)122,(byte)235,(byte)22,(byte)11,(byte)245,(byte)89,(byte)203,(byte)95,(byte)176,(byte)156,(byte)169,(byte)81,(byte)160,
+ (byte)127,(byte)12,(byte)246,(byte)111,(byte)23,(byte)196,(byte)73,(byte)236,(byte)216,(byte)67,(byte)31,(byte)45,(byte)164,(byte)118,(byte)123,(byte)183,
+ (byte)204,(byte)187,(byte)62,(byte)90,(byte)251,(byte)96,(byte)177,(byte)134,(byte)59,(byte)82,(byte)161,(byte)108,(byte)170,(byte)85,(byte)41,(byte)157,
+ (byte)151,(byte)178,(byte)135,(byte)144,(byte)97,(byte)190,(byte)220,(byte)252,(byte)188,(byte)149,(byte)207,(byte)205,(byte)55,(byte)63,(byte)91,(byte)209,
+ (byte)83,(byte)57,(byte)132,(byte)60,(byte)65,(byte)162,(byte)109,(byte)71,(byte)20,(byte)42,(byte)158,(byte)93,(byte)86,(byte)242,(byte)211,(byte)171,
+ (byte)68,(byte)17,(byte)146,(byte)217,(byte)35,(byte)32,(byte)46,(byte)137,(byte)180,(byte)124,(byte)184,(byte)38,(byte)119,(byte)153,(byte)227,(byte)165,
+ (byte)103,(byte)74,(byte)237,(byte)222,(byte)197,(byte)49,(byte)254,(byte)24,(byte)13,(byte)99,(byte)140,(byte)128,(byte)192,(byte)247,(byte)112,(byte)7};
+
+ private static final byte[] fbsub=
+ {(byte)99,(byte)124,(byte)119,(byte)123,(byte)242,(byte)107,(byte)111,(byte)197,(byte)48,(byte)1,(byte)103,(byte)43,(byte)254,(byte)215,(byte)171,(byte)118,
+ (byte)202,(byte)130,(byte)201,(byte)125,(byte)250,(byte)89,(byte)71,(byte)240,(byte)173,(byte)212,(byte)162,(byte)175,(byte)156,(byte)164,(byte)114,(byte)192,
+ (byte)183,(byte)253,(byte)147,(byte)38,(byte)54,(byte)63,(byte)247,(byte)204,(byte)52,(byte)165,(byte)229,(byte)241,(byte)113,(byte)216,(byte)49,(byte)21,
+ (byte)4,(byte)199,(byte)35,(byte)195,(byte)24,(byte)150,(byte)5,(byte)154,(byte)7,(byte)18,(byte)128,(byte)226,(byte)235,(byte)39,(byte)178,(byte)117,
+ (byte)9,(byte)131,(byte)44,(byte)26,(byte)27,(byte)110,(byte)90,(byte)160,(byte)82,(byte)59,(byte)214,(byte)179,(byte)41,(byte)227,(byte)47,(byte)132,
+ (byte)83,(byte)209,(byte)0,(byte)237,(byte)32,(byte)252,(byte)177,(byte)91,(byte)106,(byte)203,(byte)190,(byte)57,(byte)74,(byte)76,(byte)88,(byte)207,
+ (byte)208,(byte)239,(byte)170,(byte)251,(byte)67,(byte)77,(byte)51,(byte)133,(byte)69,(byte)249,(byte)2,(byte)127,(byte)80,(byte)60,(byte)159,(byte)168,
+ (byte)81,(byte)163,(byte)64,(byte)143,(byte)146,(byte)157,(byte)56,(byte)245,(byte)188,(byte)182,(byte)218,(byte)33,(byte)16,(byte)255,(byte)243,(byte)210,
+ (byte)205,(byte)12,(byte)19,(byte)236,(byte)95,(byte)151,(byte)68,(byte)23,(byte)196,(byte)167,(byte)126,(byte)61,(byte)100,(byte)93,(byte)25,(byte)115,
+ (byte)96,(byte)129,(byte)79,(byte)220,(byte)34,(byte)42,(byte)144,(byte)136,(byte)70,(byte)238,(byte)184,(byte)20,(byte)222,(byte)94,(byte)11,(byte)219,
+ (byte)224,(byte)50,(byte)58,(byte)10,(byte)73,(byte)6,(byte)36,(byte)92,(byte)194,(byte)211,(byte)172,(byte)98,(byte)145,(byte)149,(byte)228,(byte)121,
+ (byte)231,(byte)200,(byte)55,(byte)109,(byte)141,(byte)213,(byte)78,(byte)169,(byte)108,(byte)86,(byte)244,(byte)234,(byte)101,(byte)122,(byte)174,(byte)8,
+ (byte)186,(byte)120,(byte)37,(byte)46,(byte)28,(byte)166,(byte)180,(byte)198,(byte)232,(byte)221,(byte)116,(byte)31,(byte)75,(byte)189,(byte)139,(byte)138,
+ (byte)112,(byte)62,(byte)181,(byte)102,(byte)72,(byte)3,(byte)246,(byte)14,(byte)97,(byte)53,(byte)87,(byte)185,(byte)134,(byte)193,(byte)29,(byte)158,
+ (byte)225,(byte)248,(byte)152,(byte)17,(byte)105,(byte)217,(byte)142,(byte)148,(byte)155,(byte)30,(byte)135,(byte)233,(byte)206,(byte)85,(byte)40,(byte)223,
+ (byte)140,(byte)161,(byte)137,(byte)13,(byte)191,(byte)230,(byte)66,(byte)104,(byte)65,(byte)153,(byte)45,(byte)15,(byte)176,(byte)84,(byte)187,(byte)22};
+
+ private static final byte[] rbsub=
+ {(byte)82,(byte)9,(byte)106,(byte)213,(byte)48,(byte)54,(byte)165,(byte)56,(byte)191,(byte)64,(byte)163,(byte)158,(byte)129,(byte)243,(byte)215,(byte)251,
+ (byte)124,(byte)227,(byte)57,(byte)130,(byte)155,(byte)47,(byte)255,(byte)135,(byte)52,(byte)142,(byte)67,(byte)68,(byte)196,(byte)222,(byte)233,(byte)203,
+ (byte)84,(byte)123,(byte)148,(byte)50,(byte)166,(byte)194,(byte)35,(byte)61,(byte)238,(byte)76,(byte)149,(byte)11,(byte)66,(byte)250,(byte)195,(byte)78,
+ (byte)8,(byte)46,(byte)161,(byte)102,(byte)40,(byte)217,(byte)36,(byte)178,(byte)118,(byte)91,(byte)162,(byte)73,(byte)109,(byte)139,(byte)209,(byte)37,
+ (byte)114,(byte)248,(byte)246,(byte)100,(byte)134,(byte)104,(byte)152,(byte)22,(byte)212,(byte)164,(byte)92,(byte)204,(byte)93,(byte)101,(byte)182,(byte)146,
+ (byte)108,(byte)112,(byte)72,(byte)80,(byte)253,(byte)237,(byte)185,(byte)218,(byte)94,(byte)21,(byte)70,(byte)87,(byte)167,(byte)141,(byte)157,(byte)132,
+ (byte)144,(byte)216,(byte)171,(byte)0,(byte)140,(byte)188,(byte)211,(byte)10,(byte)247,(byte)228,(byte)88,(byte)5,(byte)184,(byte)179,(byte)69,(byte)6,
+ (byte)208,(byte)44,(byte)30,(byte)143,(byte)202,(byte)63,(byte)15,(byte)2,(byte)193,(byte)175,(byte)189,(byte)3,(byte)1,(byte)19,(byte)138,(byte)107,
+ (byte)58,(byte)145,(byte)17,(byte)65,(byte)79,(byte)103,(byte)220,(byte)234,(byte)151,(byte)242,(byte)207,(byte)206,(byte)240,(byte)180,(byte)230,(byte)115,
+ (byte)150,(byte)172,(byte)116,(byte)34,(byte)231,(byte)173,(byte)53,(byte)133,(byte)226,(byte)249,(byte)55,(byte)232,(byte)28,(byte)117,(byte)223,(byte)110,
+ (byte)71,(byte)241,(byte)26,(byte)113,(byte)29,(byte)41,(byte)197,(byte)137,(byte)111,(byte)183,(byte)98,(byte)14,(byte)170,(byte)24,(byte)190,(byte)27,
+ (byte)252,(byte)86,(byte)62,(byte)75,(byte)198,(byte)210,(byte)121,(byte)32,(byte)154,(byte)219,(byte)192,(byte)254,(byte)120,(byte)205,(byte)90,(byte)244,
+ (byte)31,(byte)221,(byte)168,(byte)51,(byte)136,(byte)7,(byte)199,(byte)49,(byte)177,(byte)18,(byte)16,(byte)89,(byte)39,(byte)128,(byte)236,(byte)95,
+ (byte)96,(byte)81,(byte)127,(byte)169,(byte)25,(byte)181,(byte)74,(byte)13,(byte)45,(byte)229,(byte)122,(byte)159,(byte)147,(byte)201,(byte)156,(byte)239,
+ (byte)160,(byte)224,(byte)59,(byte)77,(byte)174,(byte)42,(byte)245,(byte)176,(byte)200,(byte)235,(byte)187,(byte)60,(byte)131,(byte)83,(byte)153,(byte)97,
+ (byte)23,(byte)43,(byte)4,(byte)126,(byte)186,(byte)119,(byte)214,(byte)38,(byte)225,(byte)105,(byte)20,(byte)99,(byte)85,(byte)33,(byte)12,(byte)125};
+
+ private static final byte[] rco=
+ {(byte)1,(byte)2,(byte)4,(byte)8,(byte)16,(byte)32,(byte)64,(byte)128,(byte)27,(byte)54,(byte)108,(byte)216,(byte)171,(byte)77,(byte)154,(byte)47};
+
+ private static final int[] ftable=
+ {0xa56363c6,0x847c7cf8,0x997777ee,0x8d7b7bf6,0xdf2f2ff,0xbd6b6bd6,
+ 0xb16f6fde,0x54c5c591,0x50303060,0x3010102,0xa96767ce,0x7d2b2b56,
+ 0x19fefee7,0x62d7d7b5,0xe6abab4d,0x9a7676ec,0x45caca8f,0x9d82821f,
+ 0x40c9c989,0x877d7dfa,0x15fafaef,0xeb5959b2,0xc947478e,0xbf0f0fb,
+ 0xecadad41,0x67d4d4b3,0xfda2a25f,0xeaafaf45,0xbf9c9c23,0xf7a4a453,
+ 0x967272e4,0x5bc0c09b,0xc2b7b775,0x1cfdfde1,0xae93933d,0x6a26264c,
+ 0x5a36366c,0x413f3f7e,0x2f7f7f5,0x4fcccc83,0x5c343468,0xf4a5a551,
+ 0x34e5e5d1,0x8f1f1f9,0x937171e2,0x73d8d8ab,0x53313162,0x3f15152a,
+ 0xc040408,0x52c7c795,0x65232346,0x5ec3c39d,0x28181830,0xa1969637,
+ 0xf05050a,0xb59a9a2f,0x907070e,0x36121224,0x9b80801b,0x3de2e2df,
+ 0x26ebebcd,0x6927274e,0xcdb2b27f,0x9f7575ea,0x1b090912,0x9e83831d,
+ 0x742c2c58,0x2e1a1a34,0x2d1b1b36,0xb26e6edc,0xee5a5ab4,0xfba0a05b,
+ 0xf65252a4,0x4d3b3b76,0x61d6d6b7,0xceb3b37d,0x7b292952,0x3ee3e3dd,
+ 0x712f2f5e,0x97848413,0xf55353a6,0x68d1d1b9,0x0,0x2cededc1,
+ 0x60202040,0x1ffcfce3,0xc8b1b179,0xed5b5bb6,0xbe6a6ad4,0x46cbcb8d,
+ 0xd9bebe67,0x4b393972,0xde4a4a94,0xd44c4c98,0xe85858b0,0x4acfcf85,
+ 0x6bd0d0bb,0x2aefefc5,0xe5aaaa4f,0x16fbfbed,0xc5434386,0xd74d4d9a,
+ 0x55333366,0x94858511,0xcf45458a,0x10f9f9e9,0x6020204,0x817f7ffe,
+ 0xf05050a0,0x443c3c78,0xba9f9f25,0xe3a8a84b,0xf35151a2,0xfea3a35d,
+ 0xc0404080,0x8a8f8f05,0xad92923f,0xbc9d9d21,0x48383870,0x4f5f5f1,
+ 0xdfbcbc63,0xc1b6b677,0x75dadaaf,0x63212142,0x30101020,0x1affffe5,
+ 0xef3f3fd,0x6dd2d2bf,0x4ccdcd81,0x140c0c18,0x35131326,0x2fececc3,
+ 0xe15f5fbe,0xa2979735,0xcc444488,0x3917172e,0x57c4c493,0xf2a7a755,
+ 0x827e7efc,0x473d3d7a,0xac6464c8,0xe75d5dba,0x2b191932,0x957373e6,
+ 0xa06060c0,0x98818119,0xd14f4f9e,0x7fdcdca3,0x66222244,0x7e2a2a54,
+ 0xab90903b,0x8388880b,0xca46468c,0x29eeeec7,0xd3b8b86b,0x3c141428,
+ 0x79dedea7,0xe25e5ebc,0x1d0b0b16,0x76dbdbad,0x3be0e0db,0x56323264,
+ 0x4e3a3a74,0x1e0a0a14,0xdb494992,0xa06060c,0x6c242448,0xe45c5cb8,
+ 0x5dc2c29f,0x6ed3d3bd,0xefacac43,0xa66262c4,0xa8919139,0xa4959531,
+ 0x37e4e4d3,0x8b7979f2,0x32e7e7d5,0x43c8c88b,0x5937376e,0xb76d6dda,
+ 0x8c8d8d01,0x64d5d5b1,0xd24e4e9c,0xe0a9a949,0xb46c6cd8,0xfa5656ac,
+ 0x7f4f4f3,0x25eaeacf,0xaf6565ca,0x8e7a7af4,0xe9aeae47,0x18080810,
+ 0xd5baba6f,0x887878f0,0x6f25254a,0x722e2e5c,0x241c1c38,0xf1a6a657,
+ 0xc7b4b473,0x51c6c697,0x23e8e8cb,0x7cdddda1,0x9c7474e8,0x211f1f3e,
+ 0xdd4b4b96,0xdcbdbd61,0x868b8b0d,0x858a8a0f,0x907070e0,0x423e3e7c,
+ 0xc4b5b571,0xaa6666cc,0xd8484890,0x5030306,0x1f6f6f7,0x120e0e1c,
+ 0xa36161c2,0x5f35356a,0xf95757ae,0xd0b9b969,0x91868617,0x58c1c199,
+ 0x271d1d3a,0xb99e9e27,0x38e1e1d9,0x13f8f8eb,0xb398982b,0x33111122,
+ 0xbb6969d2,0x70d9d9a9,0x898e8e07,0xa7949433,0xb69b9b2d,0x221e1e3c,
+ 0x92878715,0x20e9e9c9,0x49cece87,0xff5555aa,0x78282850,0x7adfdfa5,
+ 0x8f8c8c03,0xf8a1a159,0x80898909,0x170d0d1a,0xdabfbf65,0x31e6e6d7,
+ 0xc6424284,0xb86868d0,0xc3414182,0xb0999929,0x772d2d5a,0x110f0f1e,
+ 0xcbb0b07b,0xfc5454a8,0xd6bbbb6d,0x3a16162c};
+
+ private static final int[] rtable=
+ {0x50a7f451,0x5365417e,0xc3a4171a,0x965e273a,0xcb6bab3b,0xf1459d1f,
+ 0xab58faac,0x9303e34b,0x55fa3020,0xf66d76ad,0x9176cc88,0x254c02f5,
+ 0xfcd7e54f,0xd7cb2ac5,0x80443526,0x8fa362b5,0x495ab1de,0x671bba25,
+ 0x980eea45,0xe1c0fe5d,0x2752fc3,0x12f04c81,0xa397468d,0xc6f9d36b,
+ 0xe75f8f03,0x959c9215,0xeb7a6dbf,0xda595295,0x2d83bed4,0xd3217458,
+ 0x2969e049,0x44c8c98e,0x6a89c275,0x78798ef4,0x6b3e5899,0xdd71b927,
+ 0xb64fe1be,0x17ad88f0,0x66ac20c9,0xb43ace7d,0x184adf63,0x82311ae5,
+ 0x60335197,0x457f5362,0xe07764b1,0x84ae6bbb,0x1ca081fe,0x942b08f9,
+ 0x58684870,0x19fd458f,0x876cde94,0xb7f87b52,0x23d373ab,0xe2024b72,
+ 0x578f1fe3,0x2aab5566,0x728ebb2,0x3c2b52f,0x9a7bc586,0xa50837d3,
+ 0xf2872830,0xb2a5bf23,0xba6a0302,0x5c8216ed,0x2b1ccf8a,0x92b479a7,
+ 0xf0f207f3,0xa1e2694e,0xcdf4da65,0xd5be0506,0x1f6234d1,0x8afea6c4,
+ 0x9d532e34,0xa055f3a2,0x32e18a05,0x75ebf6a4,0x39ec830b,0xaaef6040,
+ 0x69f715e,0x51106ebd,0xf98a213e,0x3d06dd96,0xae053edd,0x46bde64d,
+ 0xb58d5491,0x55dc471,0x6fd40604,0xff155060,0x24fb9819,0x97e9bdd6,
+ 0xcc434089,0x779ed967,0xbd42e8b0,0x888b8907,0x385b19e7,0xdbeec879,
+ 0x470a7ca1,0xe90f427c,0xc91e84f8,0x0,0x83868009,0x48ed2b32,
+ 0xac70111e,0x4e725a6c,0xfbff0efd,0x5638850f,0x1ed5ae3d,0x27392d36,
+ 0x64d90f0a,0x21a65c68,0xd1545b9b,0x3a2e3624,0xb1670a0c,0xfe75793,
+ 0xd296eeb4,0x9e919b1b,0x4fc5c080,0xa220dc61,0x694b775a,0x161a121c,
+ 0xaba93e2,0xe52aa0c0,0x43e0223c,0x1d171b12,0xb0d090e,0xadc78bf2,
+ 0xb9a8b62d,0xc8a91e14,0x8519f157,0x4c0775af,0xbbdd99ee,0xfd607fa3,
+ 0x9f2601f7,0xbcf5725c,0xc53b6644,0x347efb5b,0x7629438b,0xdcc623cb,
+ 0x68fcedb6,0x63f1e4b8,0xcadc31d7,0x10856342,0x40229713,0x2011c684,
+ 0x7d244a85,0xf83dbbd2,0x1132f9ae,0x6da129c7,0x4b2f9e1d,0xf330b2dc,
+ 0xec52860d,0xd0e3c177,0x6c16b32b,0x99b970a9,0xfa489411,0x2264e947,
+ 0xc48cfca8,0x1a3ff0a0,0xd82c7d56,0xef903322,0xc74e4987,0xc1d138d9,
+ 0xfea2ca8c,0x360bd498,0xcf81f5a6,0x28de7aa5,0x268eb7da,0xa4bfad3f,
+ 0xe49d3a2c,0xd927850,0x9bcc5f6a,0x62467e54,0xc2138df6,0xe8b8d890,
+ 0x5ef7392e,0xf5afc382,0xbe805d9f,0x7c93d069,0xa92dd56f,0xb31225cf,
+ 0x3b99acc8,0xa77d1810,0x6e639ce8,0x7bbb3bdb,0x97826cd,0xf418596e,
+ 0x1b79aec,0xa89a4f83,0x656e95e6,0x7ee6ffaa,0x8cfbc21,0xe6e815ef,
+ 0xd99be7ba,0xce366f4a,0xd4099fea,0xd67cb029,0xafb2a431,0x31233f2a,
+ 0x3094a5c6,0xc066a235,0x37bc4e74,0xa6ca82fc,0xb0d090e0,0x15d8a733,
+ 0x4a9804f1,0xf7daec41,0xe50cd7f,0x2ff69117,0x8dd64d76,0x4db0ef43,
+ 0x544daacc,0xdf0496e4,0xe3b5d19e,0x1b886a4c,0xb81f2cc1,0x7f516546,
+ 0x4ea5e9d,0x5d358c01,0x737487fa,0x2e410bfb,0x5a1d67b3,0x52d2db92,
+ 0x335610e9,0x1347d66d,0x8c61d79a,0x7a0ca137,0x8e14f859,0x893c13eb,
+ 0xee27a9ce,0x35c961b7,0xede51ce1,0x3cb1477a,0x59dfd29c,0x3f73f255,
+ 0x79ce1418,0xbf37c773,0xeacdf753,0x5baafd5f,0x146f3ddf,0x86db4478,
+ 0x81f3afca,0x3ec468b9,0x2c342438,0x5f40a3c2,0x72c31d16,0xc25e2bc,
+ 0x8b493c28,0x41950dff,0x7101a839,0xdeb30c08,0x9ce4b4d8,0x90c15664,
+ 0x6184cb7b,0x70b632d5,0x745c6c48,0x4257b8d0};
+
+
+/* Rotates 32-bit word left by 1, 2 or 3 byte */
+
+ private static int ROTL8(int x)
+ {
+ return (((x)<<8)|((x)>>>24));
+ }
+
+ private static int ROTL16(int x)
+ {
+ return (((x)<<16)|((x)>>>16));
+ }
+
+ private static int ROTL24(int x)
+ {
+ return (((x)<<24)|((x)>>>8));
+ }
+
+ private static int pack(byte[] b)
+ { /* pack bytes into a 32-bit Word */
+ return ((((int)b[3])&0xff)<<24)|(((int)b[2]&0xff)<<16)|(((int)b[1]&0xff)<<8)|((int)b[0]&0xff);
+ }
+
+ private static byte[] unpack(int a)
+ { /* unpack bytes from a word */
+ byte [] b=new byte[4];
+ b[0]=(byte)(a);
+ b[1]=(byte)(a>>>8);
+ b[2]=(byte)(a>>>16);
+ b[3]=(byte)(a>>>24);
+ return b;
+ }
+
+ private static byte bmul(byte x,byte y)
+ { /* x.y= AntiLog(Log(x) + Log(y)) */
+
+ int ix=((int)x)&0xff;
+ int iy=((int)y)&0xff;
+ int lx=((int)ltab[ix])&0xff;
+ int ly=((int)ltab[iy])&0xff;
+ if (x!=0 && y!=0) return ptab[(lx+ly)%255];
+ else return (byte)0;
+ }
+
+ // if (x && y)
+
+ private static int SubByte(int a)
+ {
+ byte [] b=unpack(a);
+ b[0]=fbsub[(int)b[0]&0xff];
+ b[1]=fbsub[(int)b[1]&0xff];
+ b[2]=fbsub[(int)b[2]&0xff];
+ b[3]=fbsub[(int)b[3]&0xff];
+ return pack(b);
+ }
+
+ private static byte product(int x,int y)
+ { /* dot product of two 4-byte arrays */
+ byte [] xb;//=new byte[4];
+ byte [] yb;//=new byte[4];
+ xb=unpack(x);
+ yb=unpack(y);
+
+ return (byte)(bmul(xb[0],yb[0])^bmul(xb[1],yb[1])^bmul(xb[2],yb[2])^bmul(xb[3],yb[3]));
+ }
+
+ private static int InvMixCol(int x)
+ { /* matrix Multiplication */
+ int y,m;
+ byte [] b=new byte[4];
+
+ m=pack(InCo);
+ b[3]=product(m,x);
+ m=ROTL24(m);
+ b[2]=product(m,x);
+ m=ROTL24(m);
+ b[1]=product(m,x);
+ m=ROTL24(m);
+ b[0]=product(m,x);
+ y=pack(b);
+ return y;
+ }
+
+ private static void increment(byte [] f)
+ {
+ int i;
+ for (i=0;i<16;i++)
+ {
+ f[i]++;
+ if (f[i]!=0) break;
+ }
+ }
+
+/* reset cipher */
+ public void reset(int m,byte[] iv)
+ { /* reset mode, or reset iv */
+ mode=m;
+ for (int i=0;i<16;i++)
+ f[i]=0;
+ if (mode!=ECB && iv!=null)
+ for (int i=0;i<16;i++)
+ f[i]=iv[i];
+ }
+
+ public byte[] getreg()
+ {
+ byte [] ir=new byte[16];
+ for (int i=0;i<16;i++) ir[i]=f[i];
+ return ir;
+ }
+
+/* Initialise cipher */
+ public boolean init(int m,int nk,byte[] key,byte[] iv)
+ { /* Key=16 bytes */
+ /* Key Scheduler. Create expanded encryption key */
+ int i,j,k,N,nr;
+ int [] CipherKey=new int[8];
+ byte [] b=new byte[4];
+ nk/=4;
+
+ if (nk!=4 && nk!=6 && nk!=8) return false;
+
+ nr=6+nk;
+
+ Nk=nk; Nr=nr;
+
+ reset(m,iv);
+ N=4*(nr+1);
+
+ for (i=j=0;i<nk;i++,j+=4)
+ {
+ for (k=0;k<4;k++) b[k]=key[j+k];
+ CipherKey[i]=pack(b);
+ }
+ for (i=0;i<nk;i++) fkey[i]=CipherKey[i];
+ for (j=nk,k=0;j<N;j+=nk,k++)
+ {
+ fkey[j]=fkey[j-nk]^SubByte(ROTL24(fkey[j-1]))^((int)rco[k])&0xff;
+ for (i=1;i<nk && (i+j)<N;i++)
+ fkey[i+j]=fkey[i+j-nk]^fkey[i+j-1];
+ }
+
+ /* now for the expanded decrypt key in reverse order */
+
+ for (j=0;j<4;j++) rkey[j+N-4]=fkey[j];
+ for (i=4;i<N-4;i+=4)
+ {
+ k=N-4-i;
+ for (j=0;j<4;j++) rkey[k+j]=InvMixCol(fkey[i+j]);
+ }
+ for (j=N-4;j<N;j++) rkey[j-N+4]=fkey[j];
+ return true;
+ }
+
+/* Encrypt a single block */
+ public void ecb_encrypt(byte[] buff)
+ {
+ int i,j,k;
+ int t;
+ byte [] b=new byte[4];
+ int [] p=new int[4];
+ int [] q=new int[4];
+
+ for (i=j=0;i<4;i++,j+=4)
+ {
+ for (k=0;k<4;k++) b[k]=buff[j+k];
+ p[i]=pack(b);
+ p[i]^=fkey[i];
+ }
+
+ k=4;
+
+/* State alternates between p and q */
+ for (i=1;i<Nr;i++)
+ {
+ q[0]=fkey[k]^ftable[p[0]&0xff]^
+ ROTL8(ftable[(p[1]>>>8)&0xff])^
+ ROTL16(ftable[(p[2]>>>16)&0xff])^
+ ROTL24(ftable[(p[3]>>>24)&0xff]);
+ q[1]=fkey[k+1]^ftable[p[1]&0xff]^
+ ROTL8(ftable[(p[2]>>>8)&0xff])^
+ ROTL16(ftable[(p[3]>>>16)&0xff])^
+ ROTL24(ftable[(p[0]>>>24)&0xff]);
+ q[2]=fkey[k+2]^ftable[p[2]&0xff]^
+ ROTL8(ftable[(p[3]>>>8)&0xff])^
+ ROTL16(ftable[(p[0]>>>16)&0xff])^
+ ROTL24(ftable[(p[1]>>>24)&0xff]);
+ q[3]=fkey[k+3]^ftable[p[3]&0xff]^
+ ROTL8(ftable[(p[0]>>>8)&0xff])^
+ ROTL16(ftable[(p[1]>>>16)&0xff])^
+ ROTL24(ftable[(p[2]>>>24)&0xff]);
+
+ k+=4;
+ for (j=0;j<4;j++)
+ {
+ t=p[j]; p[j]=q[j]; q[j]=t;
+ }
+ }
+
+/* Last Round */
+
+ q[0]=fkey[k]^((int)fbsub[p[0]&0xff]&0xff)^
+ ROTL8((int)fbsub[(p[1]>>>8)&0xff]&0xff)^
+ ROTL16((int)fbsub[(p[2]>>>16)&0xff]&0xff)^
+ ROTL24((int)fbsub[(p[3]>>>24)&0xff]&0xff);
+
+ q[1]=fkey[k+1]^((int)fbsub[p[1]&0xff]&0xff)^
+ ROTL8((int)fbsub[(p[2]>>>8)&0xff]&0xff)^
+ ROTL16((int)fbsub[(p[3]>>>16)&0xff]&0xff)^
+ ROTL24((int)fbsub[(p[0]>>>24)&0xff]&0xff);
+
+ q[2]=fkey[k+2]^((int)fbsub[p[2]&0xff]&0xff)^
+ ROTL8((int)fbsub[(p[3]>>>8)&0xff]&0xff)^
+ ROTL16((int)fbsub[(p[0]>>>16)&0xff]&0xff)^
+ ROTL24((int)fbsub[(p[1]>>>24)&0xff]&0xff);
+
+ q[3]=fkey[k+3]^((int)fbsub[(p[3])&0xff]&0xff)^
+ ROTL8((int)fbsub[(p[0]>>>8)&0xff]&0xff)^
+ ROTL16((int)fbsub[(p[1]>>>16)&0xff]&0xff)^
+ ROTL24((int)fbsub[(p[2]>>>24)&0xff]&0xff);
+
+ for (i=j=0;i<4;i++,j+=4)
+ {
+ b=unpack(q[i]);
+ for (k=0;k<4;k++) buff[j+k]=b[k];
+ }
+ }
+
+/* Decrypt a single block */
+ public void ecb_decrypt(byte[] buff)
+ {
+ int i,j,k;
+ int t;
+ byte [] b=new byte[4];
+ int [] p=new int[4];
+ int [] q=new int[4];
+
+ for (i=j=0;i<4;i++,j+=4)
+ {
+ for (k=0;k<4;k++) b[k]=buff[j+k];
+ p[i]=pack(b);
+ p[i]^=rkey[i];
+ }
+
+ k=4;
+
+/* State alternates between p and q */
+ for (i=1;i<Nr;i++)
+ {
+ q[0]=rkey[k]^rtable[p[0]&0xff]^
+ ROTL8(rtable[(p[3]>>>8)&0xff])^
+ ROTL16(rtable[(p[2]>>>16)&0xff])^
+ ROTL24(rtable[(p[1]>>>24)&0xff]);
+ q[1]=rkey[k+1]^rtable[p[1]&0xff]^
+ ROTL8(rtable[(p[0]>>>8)&0xff])^
+ ROTL16(rtable[(p[3]>>>16)&0xff])^
+ ROTL24(rtable[(p[2]>>>24)&0xff]);
+ q[2]=rkey[k+2]^rtable[p[2]&0xff]^
+ ROTL8(rtable[(p[1]>>>8)&0xff])^
+ ROTL16(rtable[(p[0]>>>16)&0xff])^
+ ROTL24(rtable[(p[3]>>>24)&0xff]);
+ q[3]=rkey[k+3]^rtable[p[3]&0xff]^
+ ROTL8(rtable[(p[2]>>>8)&0xff])^
+ ROTL16(rtable[(p[1]>>>16)&0xff])^
+ ROTL24(rtable[(p[0]>>>24)&0xff]);
+
+ k+=4;
+ for (j=0;j<4;j++)
+ {
+ t=p[j]; p[j]=q[j]; q[j]=t;
+ }
+ }
+
+/* Last Round */
+
+ q[0]=rkey[k]^((int)rbsub[p[0]&0xff]&0xff)^
+ ROTL8((int)rbsub[(p[3]>>>8)&0xff]&0xff)^
+ ROTL16((int)rbsub[(p[2]>>>16)&0xff]&0xff)^
+ ROTL24((int)rbsub[(p[1]>>>24)&0xff]&0xff);
+ q[1]=rkey[k+1]^((int)rbsub[p[1]&0xff]&0xff)^
+ ROTL8((int)rbsub[(p[0]>>>8)&0xff]&0xff)^
+ ROTL16((int)rbsub[(p[3]>>>16)&0xff]&0xff)^
+ ROTL24((int)rbsub[(p[2]>>>24)&0xff]&0xff);
+ q[2]=rkey[k+2]^((int)rbsub[p[2]&0xff]&0xff)^
+ ROTL8((int)rbsub[(p[1]>>>8)&0xff]&0xff)^
+ ROTL16((int)rbsub[(p[0]>>>16)&0xff]&0xff)^
+ ROTL24((int)rbsub[(p[3]>>>24)&0xff]&0xff);
+ q[3]=rkey[k+3]^((int)rbsub[p[3]&0xff]&0xff)^
+ ROTL8((int)rbsub[(p[2]>>>8)&0xff]&0xff)^
+ ROTL16((int)rbsub[(p[1]>>>16)&0xff]&0xff)^
+ ROTL24((int)rbsub[(p[0]>>>24)&0xff]&0xff);
+
+ for (i=j=0;i<4;i++,j+=4)
+ {
+ b=unpack(q[i]);
+ for (k=0;k<4;k++) buff[j+k]=b[k];
+ }
+
+ }
+
+/* Encrypt using selected mode of operation */
+ public int encrypt(byte[] buff)
+ {
+ int j,bytes;
+ byte[] st=new byte[16];
+ int fell_off;
+
+// Supported Modes of Operation
+
+ fell_off=0;
+ switch (mode)
+ {
+ case ECB:
+ ecb_encrypt(buff);
+ return 0;
+ case CBC:
+ for (j=0;j<16;j++) buff[j]^=f[j];
+ ecb_encrypt(buff);
+ for (j=0;j<16;j++) f[j]=buff[j];
+ return 0;
+
+ case CFB1:
+ case CFB2:
+ case CFB4:
+ bytes=mode-CFB1+1;
+ for (j=0;j<bytes;j++) fell_off=(fell_off<<8)|f[j];
+ for (j=0;j<16;j++) st[j]=f[j];
+ for (j=bytes;j<16;j++) f[j-bytes]=f[j];
+ ecb_encrypt(st);
+ for (j=0;j<bytes;j++)
+ {
+ buff[j]^=st[j];
+ f[16-bytes+j]=buff[j];
+ }
+ return fell_off;
+
+ case OFB1:
+ case OFB2:
+ case OFB4:
+ case OFB8:
+ case OFB16:
+
+ bytes=mode-OFB1+1;
+ ecb_encrypt(f);
+ for (j=0;j<bytes;j++) buff[j]^=f[j];
+ return 0;
+
+ case CTR1:
+ case CTR2:
+ case CTR4:
+ case CTR8:
+ case CTR16:
+
+ bytes=mode-CTR1+1;
+ for (j=0;j<16;j++) st[j]=f[j];
+ ecb_encrypt(st);
+ for (j=0;j<bytes;j++) buff[j]^=st[j];
+ increment(f);
+
+ default:
+ return 0;
+ }
+ }
+
+/* Decrypt using selected mode of operation */
+ public int decrypt(byte[] buff)
+ {
+ int j,bytes;
+ byte[] st=new byte[16];
+ int fell_off;
+
+ // Supported modes of operation
+ fell_off=0;
+ switch (mode)
+ {
+ case ECB:
+ ecb_decrypt(buff);
+ return 0;
+ case CBC:
+ for (j=0;j<16;j++)
+ {
+ st[j]=f[j];
+ f[j]=buff[j];
+ }
+ ecb_decrypt(buff);
+ for (j=0;j<16;j++)
+ {
+ buff[j]^=st[j];
+ st[j]=0;
+ }
+ return 0;
+ case CFB1:
+ case CFB2:
+ case CFB4:
+ bytes=mode-CFB1+1;
+ for (j=0;j<bytes;j++) fell_off=(fell_off<<8)|f[j];
+ for (j=0;j<16;j++) st[j]=f[j];
+ for (j=bytes;j<16;j++) f[j-bytes]=f[j];
+ ecb_encrypt(st);
+ for (j=0;j<bytes;j++)
+ {
+ f[16-bytes+j]=buff[j];
+ buff[j]^=st[j];
+ }
+ return fell_off;
+ case OFB1:
+ case OFB2:
+ case OFB4:
+ case OFB8:
+ case OFB16:
+ bytes=mode-OFB1+1;
+ ecb_encrypt(f);
+ for (j=0;j<bytes;j++) buff[j]^=f[j];
+ return 0;
+
+ case CTR1:
+ case CTR2:
+ case CTR4:
+ case CTR8:
+ case CTR16:
+
+ bytes=mode-CTR1+1;
+ for (j=0;j<16;j++) st[j]=f[j];
+ ecb_encrypt(st);
+ for (j=0;j<bytes;j++) buff[j]^=st[j];
+ increment(f);
+
+ default:
+ return 0;
+ }
+ }
+
+/* Clean up and delete left-overs */
+ public void end()
+ { // clean up
+ int i;
+ for (i=0;i<4*(Nr+1);i++)
+ fkey[i]=rkey[i]=0;
+ for (i=0;i<16;i++)
+ f[i]=0;
+ }
+
+ public static void main(String[] args) {
+ int i;
+
+ byte[] key=new byte[32];
+ byte[] block=new byte[16];
+ byte[] iv=new byte[16];
+
+ for (i=0;i<32;i++) key[i]=0;
+ key[0]=1;
+ for (i=0;i<16;i++) iv[i]=(byte)i;
+ for (i=0;i<16;i++) block[i]=(byte)i;
+
+ AES a=new AES();
+
+ a.init(CTR16,32,key,iv);
+ System.out.println("Plain= ");
+ for (i=0;i<16;i++) System.out.format("%02X ", block[i]&0xff);
+ System.out.println("");
+
+ a.encrypt(block);
+
+ System.out.println("Encrypt= ");
+ for (i=0;i<16;i++) System.out.format("%02X ", block[i]&0xff);
+ System.out.println("");
+
+ a.reset(CTR16,iv);
+ a.decrypt(block);
+
+ System.out.println("Decrypt= ");
+ for (i=0;i<16;i++) System.out.format("%02X ", block[i]&0xff);
+ System.out.println("");
+
+ a.end();
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/ANSSI/BIG.java b/src/main/java/org/apache/milagro/amcl/ANSSI/BIG.java
new file mode 100644
index 0000000..ef3f9fa
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ANSSI/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.ANSSI;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=32; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/ANSSI/DBIG.java b/src/main/java/org/apache/milagro/amcl/ANSSI/DBIG.java
new file mode 100644
index 0000000..4ca545a
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ANSSI/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.ANSSI;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/ANSSI/ECDH.java b/src/main/java/org/apache/milagro/amcl/ANSSI/ECDH.java
new file mode 100644
index 0000000..ec1311c
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ANSSI/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.ANSSI;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/ANSSI/ECP.java b/src/main/java/org/apache/milagro/amcl/ANSSI/ECP.java
new file mode 100644
index 0000000..694fbad
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ANSSI/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.ANSSI;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/ANSSI/FP.java b/src/main/java/org/apache/milagro/amcl/ANSSI/FP.java
new file mode 100644
index 0000000..a28ab41
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ANSSI/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.ANSSI;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=256; /* Number of bits in Modulus */
+ public static final int MOD8=7; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<24); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/ANSSI/ROM.java b/src/main/java/org/apache/milagro/amcl/ANSSI/ROM.java
new file mode 100644
index 0000000..5f0510f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ANSSI/ROM.java
@@ -0,0 +1,43 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.ANSSI;
+
+public class ROM
+{
+
+// Base Bits= 56
+ public static final long[] Modulus= {0xFCF353D86E9C03L,0xADBCABC8CA6DE8L,0xE8CE42435B3961L,0xB3AD58F10126DL,0xF1FD178CL};
+ public static final long[] R2modp= {0x18D2374288CC9CL,0x4929E67646BD2BL,0x220E6C1D6F7F2DL,0x751B1FDABCE02EL,0xE7401B78L};
+ public static final long MConst= 0x97483A164E1155L;
+
+ public static final int CURVE_Cof_I= 1;
+ public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= -3;
+ public static final int CURVE_B_I= 0;
+ public static final long[] CURVE_B= {0x75ED967B7BB73FL,0xC9AE4B1A18030L,0x754A44C00FDFECL,0x5428A9300D4ABAL,0xEE353FCAL};
+ public static final long[] CURVE_Order= {0xFDD459C6D655E1L,0x67E140D2BF941FL,0xE8CE42435B53DCL,0xB3AD58F10126DL,0xF1FD178CL};
+ public static final long[] CURVE_Gx= {0xC97A2DD98F5CFFL,0xD2DCAF98B70164L,0x4749D423958C27L,0x56C139EB31183DL,0xB6B3D4C3L};
+ public static final long[] CURVE_Gy= {0x115A1554062CFBL,0xC307E8E4C9E183L,0xF0F3ECEF8C2701L,0xC8B204911F9271L,0x6142E0F7L};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/BIG.java b/src/main/java/org/apache/milagro/amcl/BLS24/BIG.java
new file mode 100644
index 0000000..df34ec3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.BLS24;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=60; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/DBIG.java b/src/main/java/org/apache/milagro/amcl/BLS24/DBIG.java
new file mode 100644
index 0000000..3e2c9f0
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.BLS24;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/ECDH.java b/src/main/java/org/apache/milagro/amcl/BLS24/ECDH.java
new file mode 100644
index 0000000..b0b19cd
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.BLS24;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/ECP.java b/src/main/java/org/apache/milagro/amcl/BLS24/ECP.java
new file mode 100644
index 0000000..b5b5839
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.BLS24;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=BLS;
+ public static final int SEXTIC_TWIST=M_TYPE;
+ public static final int SIGN_OF_X=POSITIVEX;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=48;
+ public static final int AESKEY=24;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/ECP4.java b/src/main/java/org/apache/milagro/amcl/BLS24/ECP4.java
new file mode 100644
index 0000000..e0205cb
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/ECP4.java
@@ -0,0 +1,768 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Weierstrass elliptic curve functions over FP4 */
+
+package org.apache.milagro.amcl.BLS24;
+
+public final class ECP4 {
+ private FP4 x;
+ private FP4 y;
+ private FP4 z;
+// private boolean INF;
+
+/* Constructor - set this=O */
+ public ECP4() {
+// INF=true;
+ x=new FP4(0);
+ y=new FP4(1);
+ z=new FP4(0);
+ }
+
+ public ECP4(ECP4 e) {
+ this.x = new FP4(e.x);
+ this.y = new FP4(e.y);
+ this.z = new FP4(e.z);
+ }
+
+/* Test this=O? */
+ public boolean is_infinity() {
+// if (INF) return true; //******
+ return (x.iszilch() && z.iszilch());
+ }
+/* copy this=P */
+ public void copy(ECP4 P)
+ {
+ x.copy(P.x);
+ y.copy(P.y);
+ z.copy(P.z);
+// INF=P.INF;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ y.one();
+ z.zero();
+ }
+
+/* Conditional move of Q to P dependant on d */
+ public void cmove(ECP4 Q,int d)
+ {
+ x.cmove(Q.x,d);
+ y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+
+// boolean bd;
+// if (d==0) bd=false;
+// else bd=true;
+// INF^=(INF^Q.INF)&bd;
+ }
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(ECP4 W[],int b)
+ {
+ ECP4 MP=new ECP4();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test if P == Q */
+ public boolean equals(ECP4 Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+
+ FP4 a=new FP4(x); // *****
+ FP4 b=new FP4(Q.x);
+ a.mul(Q.z);
+ b.mul(z);
+ if (!a.equals(b)) return false;
+
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+
+ return true;
+ }
+/* set this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ y.norm();
+ y.neg(); y.norm();
+ return;
+ }
+/* set to Affine - (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return;
+ FP4 one=new FP4(1);
+ if (z.equals(one))
+ {
+ x.reduce();
+ y.reduce();
+ return;
+ }
+ z.inverse();
+
+ x.mul(z); x.reduce(); // *****
+ y.mul(z); y.reduce();
+ z.copy(one);
+ }
+
+/* extract affine x as FP4 */
+ public FP4 getX()
+ {
+ ECP4 W= new ECP4(this);
+ W.affine();
+ return W.x;
+ }
+/* extract affine y as FP4 */
+ public FP4 getY()
+ {
+ ECP4 W= new ECP4(this);
+ W.affine();
+ return W.y;
+ }
+/* extract projective x */
+ public FP4 getx()
+ {
+ return x;
+ }
+/* extract projective y */
+ public FP4 gety()
+ {
+ return y;
+ }
+/* extract projective z */
+ public FP4 getz()
+ {
+ return z;
+ }
+
+/* convert to byte array */
+ public void toBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP4 W=new ECP4(this);
+ //affine();
+ int MB=BIG.MODBYTES;
+
+ W.x.geta().getA().toBytes(t);
+ for (int i=0;i<MB;i++)
+ b[i]=t[i];
+ W.x.geta().getB().toBytes(t);
+ for (int i=0;i<MB;i++)
+ b[i+MB]=t[i];
+ W.x.getb().getA().toBytes(t);
+ for (int i=0;i<MB;i++)
+ b[i+2*MB]=t[i];
+ W.x.getb().getB().toBytes(t);
+ for (int i=0;i<MB;i++)
+ b[i+3*MB]=t[i];
+
+ W.y.geta().getA().toBytes(t);
+ for (int i=0;i<MB;i++)
+ b[i+4*MB]=t[i];
+ W.y.geta().getB().toBytes(t);
+ for (int i=0;i<MB;i++)
+ b[i+5*MB]=t[i];
+ W.y.getb().getA().toBytes(t);
+ for (int i=0;i<MB;i++)
+ b[i+6*MB]=t[i];
+ W.y.getb().getB().toBytes(t);
+ for (int i=0;i<MB;i++)
+ b[i+7*MB]=t[i];
+
+
+ }
+
+/* convert from byte array to point */
+ public static ECP4 fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG ra;
+ BIG rb;
+ int MB=BIG.MODBYTES;
+
+ for (int i=0;i<MB;i++) {t[i]=b[i];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+MB];}
+ rb=BIG.fromBytes(t);
+
+ FP2 ra4=new FP2(ra,rb);
+
+ for (int i=0;i<MB;i++) {t[i]=b[i+2*MB];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+3*MB];}
+ rb=BIG.fromBytes(t);
+
+ FP2 rb4=new FP2(ra,rb);
+
+ FP4 rx=new FP4(ra4,rb4);
+
+ for (int i=0;i<MB;i++) {t[i]=b[i+4*MB];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+5*MB];}
+ rb=BIG.fromBytes(t);
+
+ ra4=new FP2(ra,rb);
+
+ for (int i=0;i<MB;i++) {t[i]=b[i+6*MB];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+7*MB];}
+ rb=BIG.fromBytes(t);
+
+ rb4=new FP2(ra,rb);
+ FP4 ry=new FP4(ra4,rb4);
+
+
+ return new ECP4(rx,ry);
+ }
+
+/* convert this to hex string */
+ public String toString() {
+ ECP4 W=new ECP4(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ return "("+W.x.toString()+","+W.y.toString()+")";
+ }
+
+/* Calculate RHS of twisted curve equation x^3+B/i */
+ public static FP4 RHS(FP4 x) {
+ x.norm();
+ FP4 r=new FP4(x);
+ r.sqr();
+ FP4 b=new FP4(new FP2(new BIG(ROM.CURVE_B)));
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b.div_i();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b.times_i();
+ }
+
+
+ r.mul(x);
+ r.add(b);
+
+ r.reduce();
+ return r;
+ }
+
+/* construct this from (x,y) - but set to O if not on curve */
+ public ECP4(FP4 ix,FP4 iy) {
+ x=new FP4(ix);
+ y=new FP4(iy);
+ z=new FP4(1);
+ FP4 rhs=RHS(x);
+ FP4 y2=new FP4(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else {x.zero();INF=true;}
+ }
+
+/* construct this from x - but set to O if not on curve */
+ public ECP4(FP4 ix) {
+ x=new FP4(ix);
+ y=new FP4(1);
+ z=new FP4(1);
+ FP4 rhs=RHS(x);
+ if (rhs.sqrt())
+ {
+ y.copy(rhs);
+ //INF=false;
+ }
+ else {inf(); /*x.zero();INF=true;*/}
+ }
+
+/* this+=this */
+ public int dbl() {
+// if (INF) return -1;
+
+ FP4 iy=new FP4(y);
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ iy.times_i(); //iy.norm();
+ }
+ FP4 t0=new FP4(y); //***** Change
+ t0.sqr();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.times_i();
+ }
+ FP4 t1=new FP4(iy);
+ t1.mul(z);
+ FP4 t2=new FP4(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z);
+ z.add(z);
+ z.norm();
+
+ t2.imul(3*ROM.CURVE_B_I);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.times_i();
+ //t2.norm();
+ }
+
+ FP4 x3=new FP4(t2);
+ x3.mul(z);
+
+ FP4 y3=new FP4(t0);
+
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1); t2.norm();
+ t0.sub(t2); t0.norm(); //y^2-9bz^2
+ y3.mul(t0); y3.add(x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+ t1.copy(x); t1.mul(iy); //
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x); //(y^2-9bz^2)xy2
+
+ x.norm();
+ y.copy(y3); y.norm();
+
+ return 1;
+ }
+
+/* this+=Q - return 0 for add, 1 for double, -1 for O */
+ public int add(ECP4 Q) {
+// if (INF)
+// {
+// copy(Q);
+// return -1;
+// }
+// if (Q.INF) return -1;
+
+ int b=3*ROM.CURVE_B_I;
+ FP4 t0=new FP4(x);
+ t0.mul(Q.x); // x.Q.x
+ FP4 t1=new FP4(y);
+ t1.mul(Q.y); // y.Q.y
+
+ FP4 t2=new FP4(z);
+ t2.mul(Q.z);
+ FP4 t3=new FP4(x);
+ t3.add(y); t3.norm(); //t3=X1+Y1
+ FP4 t4=new FP4(Q.x);
+ t4.add(Q.y); t4.norm(); //t4=X2+Y2
+ t3.mul(t4); //t3=(X1+Y1)(X2+Y2)
+ t4.copy(t0); t4.add(t1); //t4=X1.X2+Y1.Y2
+
+ t3.sub(t4); t3.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t3.times_i(); //t3.norm(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+ }
+ t4.copy(y);
+ t4.add(z); t4.norm(); //t4=Y1+Z1
+ FP4 x3=new FP4(Q.y);
+ x3.add(Q.z); x3.norm(); //x3=Y2+Z2
+
+ t4.mul(x3); //t4=(Y1+Z1)(Y2+Z2)
+ x3.copy(t1); //
+ x3.add(t2); //X3=Y1.Y2+Z1.Z2
+
+ t4.sub(x3); t4.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t4.times_i(); //t4.norm(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+ }
+ x3.copy(x); x3.add(z); x3.norm(); // x3=X1+Z1
+ FP4 y3=new FP4(Q.x);
+ y3.add(Q.z); y3.norm(); // y3=X2+Z2
+ x3.mul(y3); // x3=(X1+Z1)(X2+Z2)
+ y3.copy(t0);
+ y3.add(t2); // y3=X1.X2+Z1+Z2
+ y3.rsub(x3); y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.times_i(); //t0.norm(); // x.Q.x
+ t1.times_i(); //t1.norm(); // y.Q.y
+ }
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.times_i();
+ }
+ FP4 z3=new FP4(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ y3.times_i();
+ //y3.norm();
+ }
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+
+ return 0;
+ }
+
+/* set this-=Q */
+ public int sub(ECP4 Q) {
+ ECP4 NQ=new ECP4(Q);
+ NQ.neg();
+ int D=add(NQ);
+
+ //Q.neg();
+ //int D=add(Q);
+ //Q.neg();
+ return D;
+ }
+
+ public static FP2[] frob_constants() {
+ BIG Fra=new BIG(ROM.Fra);
+ BIG Frb=new BIG(ROM.Frb);
+ FP2 X=new FP2(Fra,Frb);
+
+ FP2 F0=new FP2(X); F0.sqr();
+ FP2 F2=new FP2(F0);
+ F2.mul_ip(); F2.norm();
+ FP2 F1=new FP2(F2); F1.sqr();
+ F2.mul(F1);
+ F1.copy(X);
+ if (ECP.SEXTIC_TWIST == ECP.M_TYPE)
+ {
+ F1.mul_ip();
+ F1.inverse();
+ F0.copy(F1); F0.sqr();
+ }
+ F0.mul_ip(); F0.norm();
+ F1.mul(F0);
+ FP2[] F={F0,F1,F2};
+ return F;
+ }
+
+
+/* set this*=q, where q is Modulus, using Frobenius */
+ public void frob(FP2 F[],int n)
+ {
+// if (INF) return;
+ for (int i=0;i<n;i++) {
+ x.frob(F[2]);
+ x.pmul(F[0]);
+
+ y.frob(F[2]);
+ y.pmul(F[1]);
+ y.times_i();
+
+ z.frob(F[2]);
+ }
+ }
+
+/* P*=e */
+ public ECP4 mul(BIG e)
+ {
+/* fixed size windows */
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP4 P=new ECP4();
+ ECP4 Q=new ECP4();
+ ECP4 C=new ECP4();
+ ECP4[] W=new ECP4[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ if (is_infinity()) return new ECP4();
+
+ //affine();
+
+/* precompute table */
+ Q.copy(this);
+ Q.dbl();
+ W[0]=new ECP4();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP4();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+/* make exponent odd - add 2P if even, P if odd */
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+/* convert exponent to signed 4-bit window */
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C);
+ P.affine();
+ return P;
+ }
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3... */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static ECP4 mul8(ECP4[] Q,BIG[] u)
+ {
+ int i,j,k,nb,pb1,pb2;
+ ECP4 W=new ECP4();
+ ECP4 P=new ECP4();
+ ECP4[] T1=new ECP4[8];
+ ECP4[] T2=new ECP4[8];
+
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[8];
+
+ byte[] w1=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s1=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] w2=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s2=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<8;i++)
+ {
+ t[i]=new BIG(u[i]);
+ //Q[i].affine();
+ t[i].norm();
+ }
+
+ T1[0] = new ECP4(); T1[0].copy(Q[0]); // Q[0]
+ T1[1] = new ECP4(); T1[1].copy(T1[0]); T1[1].add(Q[1]); // Q[0]+Q[1]
+ T1[2] = new ECP4(); T1[2].copy(T1[0]); T1[2].add(Q[2]); // Q[0]+Q[2]
+ T1[3] = new ECP4(); T1[3].copy(T1[1]); T1[3].add(Q[2]); // Q[0]+Q[1]+Q[2]
+ T1[4] = new ECP4(); T1[4].copy(T1[0]); T1[4].add(Q[3]); // Q[0]+Q[3]
+ T1[5] = new ECP4(); T1[5].copy(T1[1]); T1[5].add(Q[3]); // Q[0]+Q[1]+Q[3]
+ T1[6] = new ECP4(); T1[6].copy(T1[2]); T1[6].add(Q[3]); // Q[0]+Q[2]+Q[3]
+ T1[7] = new ECP4(); T1[7].copy(T1[3]); T1[7].add(Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+// Use Frobenius
+ FP2[] F=ECP4.frob_constants();
+
+ for (i=0;i<8;i++) {
+ T2[i] = new ECP4(); T2[i].copy(T1[i]);
+ T2[i].frob(F,4);
+ }
+
+ // Make it odd
+ pb1=1-t[0].parity();
+ t[0].inc(pb1);
+ t[0].norm();
+
+ pb2=1-t[4].parity();
+ t[4].inc(pb2);
+ t[4].norm();
+
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<8;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s1[nb-1]=1;
+ s2[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s1[i]=(byte)(2*t[0].parity()-1);
+ t[4].fshr(1);
+ s2[i]=(byte)(2*t[4].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w1[i]=0;
+ k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s1[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w1[i]+=bt*(byte)k;
+ k*=2;
+ }
+
+ w2[i]=0;
+ k=1;
+ for (j=5; j<8; j++) {
+ byte bt=(byte)(s2[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w2[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ P.select(T1,(int)(2*w1[nb-1]+1));
+ W.select(T2,(int)(2*w2[nb-1]+1));
+ P.add(W);
+ for (i=nb-2;i>=0;i--) {
+ P.dbl();
+ W.select(T1,(int)(2*w1[i]+s1[i]));
+ P.add(W);
+ W.select(T2,(int)(2*w2[i]+s2[i]));
+ P.add(W);
+
+ }
+
+ // apply correction
+ W.copy(P);
+ W.sub(Q[0]);
+ P.cmove(W,pb1);
+
+ W.copy(P);
+ W.sub(Q[4]);
+ P.cmove(W,pb2);
+
+ P.affine();
+ return P;
+ }
+
+/* needed for SOK */
+ public static ECP4 mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ BIG one=new BIG(1);
+ FP4 X;
+ FP2 X2;
+ ECP4 Q;
+ x.mod(q);
+ while (true)
+ {
+ X2=new FP2(one,x);
+ X=new FP4(X2);
+ Q=new ECP4(X);
+ if (!Q.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+
+ FP2[] F=ECP4.frob_constants();
+ x=new BIG(ROM.CURVE_Bnx);
+
+/* Efficient hash maps to G2 on BLS curves - Budroni, Pintore */
+
+ ECP4 xQ=Q.mul(x);
+ ECP4 x2Q=xQ.mul(x);
+ ECP4 x3Q=x2Q.mul(x);
+ ECP4 x4Q=x3Q.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ xQ.neg();
+ x3Q.neg();
+ }
+
+ x4Q.sub(x3Q);
+ x4Q.sub(Q);
+
+ x3Q.sub(x2Q);
+ x3Q.frob(F,1);
+
+ x2Q.sub(xQ);
+ x2Q.frob(F,2);
+
+ xQ.sub(Q);
+ xQ.frob(F,3);
+
+ Q.dbl();
+ Q.frob(F,4);
+
+ Q.add(x4Q);
+ Q.add(x3Q);
+ Q.add(x2Q);
+ Q.add(xQ);
+
+ Q.affine();
+ return Q;
+ }
+
+ public static ECP4 generator()
+ {
+
+ return new ECP4(
+ new FP4(
+ new FP2(
+ new BIG(ROM.CURVE_Pxaa),new BIG(ROM.CURVE_Pxab)),
+ new FP2(
+ new BIG(ROM.CURVE_Pxba),new BIG(ROM.CURVE_Pxbb))),
+ new FP4(
+ new FP2(
+ new BIG(ROM.CURVE_Pyaa),new BIG(ROM.CURVE_Pyab)),
+ new FP2(
+ new BIG(ROM.CURVE_Pyba),new BIG(ROM.CURVE_Pybb))));
+ }
+
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/FP.java b/src/main/java/org/apache/milagro/amcl/BLS24/FP.java
new file mode 100644
index 0000000..1a0258f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.BLS24;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=479; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<25); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/FP2.java b/src/main/java/org/apache/milagro/amcl/BLS24/FP2.java
new file mode 100644
index 0000000..6704129
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/FP2.java
@@ -0,0 +1,425 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^2 functions */
+
+/* FP2 elements are of the form a+ib, where i is sqrt(-1) */
+
+package org.apache.milagro.amcl.BLS24;
+
+public final class FP2 {
+ private final FP a;
+ private final FP b;
+
+/* reduce components mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+
+/* normalise components of w */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+
+/* test this=0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP2 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this=1 ? */
+ public boolean isunity() {
+ FP one=new FP(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test this=x */
+ public boolean equals(FP2 x) {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+
+/* Constructors */
+ public FP2(int c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(FP2 x)
+ {
+ a=new FP(x.a);
+ b=new FP(x.b);
+ }
+
+ public FP2(FP c,FP d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(BIG c,BIG d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(FP c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(BIG c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+/*
+ public BIG geta()
+ {
+ return a.tobig();
+ }
+*/
+/* extract a */
+ public BIG getA()
+ {
+ return a.redc();
+ }
+
+/* extract b */
+ public BIG getB()
+ {
+ return b.redc();
+ }
+
+/* copy this=x */
+ public void copy(FP2 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+
+/* negate this mod Modulus */
+ public void neg()
+ {
+ FP m=new FP(a);
+ FP t=new FP(0);
+
+ m.add(b);
+ m.neg();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ }
+
+/* set to a-ib */
+ public void conj()
+ {
+ b.neg();
+ b.norm();
+ }
+
+/* this+=a */
+ public void add(FP2 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+
+/* this-=a */
+ public void sub(FP2 x)
+ {
+ FP2 m=new FP2(x);
+ m.neg();
+ add(m);
+ }
+
+ public void rsub(FP2 x) // *****
+ {
+ neg();
+ add(x);
+ }
+
+/* this*=s, where s is an FP */
+ public void pmul(FP s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this*=i, where i is an int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+ FP w1=new FP(a);
+ FP w3=new FP(a);
+ FP mb=new FP(b);
+
+ w1.add(b);
+ mb.neg();
+
+ w3.add(a);
+ w3.norm();
+ b.mul(w3);
+
+ a.add(mb);
+
+ w1.norm();
+ a.norm();
+
+ a.mul(w1);
+ }
+
+/* this*=y */
+/* Now uses Lazy reduction */
+ public void mul(FP2 y)
+ {
+ if ((long)(a.XES+b.XES)*(y.a.XES+y.b.XES)>(long)FP.FEXCESS)
+ {
+ if (a.XES>1) a.reduce();
+ if (b.XES>1) b.reduce();
+ }
+
+ DBIG pR=new DBIG(0);
+ BIG C=new BIG(a.x);
+ BIG D=new BIG(y.a.x);
+
+ pR.ucopy(new BIG(ROM.Modulus));
+
+ DBIG A=BIG.mul(a.x,y.a.x);
+ DBIG B=BIG.mul(b.x,y.b.x);
+
+ C.add(b.x); C.norm();
+ D.add(y.b.x); D.norm();
+
+ DBIG E=BIG.mul(C,D);
+ DBIG F=new DBIG(A); F.add(B);
+ B.rsub(pR);
+
+ A.add(B); A.norm();
+ E.sub(F); E.norm();
+
+ a.x.copy(FP.mod(A)); a.XES=3;
+ b.x.copy(FP.mod(E)); b.XES=2;
+ }
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP w1=new FP(b);
+ FP w2=new FP(a);
+ w1.sqr(); w2.sqr(); w1.add(w2);
+ if (w1.jacobi()!=1) { zero(); return false; }
+ w1=w1.sqrt();
+ w2.copy(a); w2.add(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1)
+ {
+ w2.copy(a); w2.sub(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1) { zero(); return false; }
+ }
+ w2=w2.sqrt();
+ a.copy(w2);
+ w2.add(w2);
+ w2.inverse();
+ b.mul(w2);
+ return true;
+ }
+
+/* output to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ norm();
+ FP w1=new FP(a);
+ FP w2=new FP(b);
+
+ w1.sqr();
+ w2.sqr();
+ w1.add(w2);
+ w1.inverse();
+ a.mul(w1);
+ w1.neg();
+ w1.norm();
+ b.mul(w1);
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+/* this*=sqrt(-1) */
+ public void times_i()
+ {
+ FP z=new FP(a);
+ a.copy(b); a.neg();
+ b.copy(z);
+ }
+
+/* w*=(1+sqrt(-1)) */
+/* where X*2-(1+sqrt(-1)) is irreducible for FP4, assumes p=3 mod 8 */
+ public void mul_ip()
+ {
+ FP2 t=new FP2(this);
+ FP z=new FP(a);
+ a.copy(b);
+ a.neg();
+ b.copy(z);
+ add(t);
+ }
+
+ public void div_ip2()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ }
+
+/* w/=(1+sqrt(-1)) */
+ public void div_ip()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ div2();
+ }
+/*
+ public FP2 pow(BIG e)
+ {
+ int bt;
+ FP2 r=new FP2(1);
+ e.norm();
+ norm();
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(this);
+ if (e.iszilch()) break;
+ sqr();
+ }
+
+ r.reduce();
+ return r;
+ }
+
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(27);
+ BIG pp1=new BIG(m);
+ BIG pm1=new BIG(m);
+ BIG a=new BIG(1);
+ BIG b=new BIG(1);
+ FP2 w=new FP2(a,b);
+ FP2 z=new FP2(w);
+
+ byte[] RAW=new byte[100];
+
+ RAND rng=new RAND();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ // for (int i=0;i<100;i++)
+ // {
+ a.randomnum(rng);
+ b.randomnum(rng);
+
+ w=new FP2(a,b);
+ System.out.println("w="+w.toString());
+
+ z=new FP2(w);
+ z.inverse();
+ System.out.println("z="+z.toString());
+
+ z.inverse();
+ if (!z.equals(w)) System.out.println("Error");
+ // }
+
+// System.out.println("m="+m.toString());
+// w.sqr();
+// w.mul(z);
+
+ System.out.println("w="+w.toString());
+
+
+ pp1.inc(1); pp1.norm();
+ pm1.dec(1); pm1.norm();
+ System.out.println("p+1="+pp1.toString());
+ System.out.println("p-1="+pm1.toString());
+ w=w.pow(pp1);
+ w=w.pow(pm1);
+ System.out.println("w="+w.toString());
+ }
+*/
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/FP24.java b/src/main/java/org/apache/milagro/amcl/BLS24/FP24.java
new file mode 100644
index 0000000..d197f0d
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/FP24.java
@@ -0,0 +1,851 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Fp^24 functions */
+/* FP24 elements are of the form a+i.b+i^2.c */
+
+package org.apache.milagro.amcl.BLS24;
+
+public final class FP24 {
+ private final FP8 a;
+ private final FP8 b;
+ private final FP8 c;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ c.reduce();
+ }
+
+/* normalise all components of this */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ c.norm();
+ }
+/* test x==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch() && c.iszilch());
+ }
+
+ public void cmove(FP24 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ c.cmove(g.c,d);
+ }
+
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(FP24 g[],int b)
+ {
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(g[0],teq(babs,0)); // conditional move
+ cmove(g[1],teq(babs,1));
+ cmove(g[2],teq(babs,2));
+ cmove(g[3],teq(babs,3));
+ cmove(g[4],teq(babs,4));
+ cmove(g[5],teq(babs,5));
+ cmove(g[6],teq(babs,6));
+ cmove(g[7],teq(babs,7));
+
+ FP24 invf=new FP24(this);
+ invf.conj();
+ cmove(invf,(int)(m&1));
+ }
+
+ /* test x==1 ? */
+ public boolean isunity() {
+ FP8 one=new FP8(1);
+ return (a.equals(one) && b.iszilch() && c.iszilch());
+ }
+/* return 1 if x==y, else 0 */
+ public boolean equals(FP24 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b) && c.equals(x.c));
+ }
+/* extract a from this */
+ public FP8 geta()
+ {
+ return a;
+ }
+/* extract b */
+ public FP8 getb()
+ {
+ return b;
+ }
+/* extract c */
+ public FP8 getc()
+ {
+ return c;
+ }
+/* copy this=x */
+ public void copy(FP24 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ c.copy(x.c);
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ c.zero();
+ }
+/* this=conj(this) */
+ public void conj()
+ {
+ a.conj();
+ b.nconj();
+ c.conj();
+ }
+/* Constructors */
+ public FP24(FP8 d)
+ {
+ a=new FP8(d);
+ b=new FP8(0);
+ c=new FP8(0);
+ }
+
+ public FP24(int d)
+ {
+ a=new FP8(d);
+ b=new FP8(0);
+ c=new FP8(0);
+ }
+
+ public FP24(FP8 d,FP8 e,FP8 f)
+ {
+ a=new FP8(d);
+ b=new FP8(e);
+ c=new FP8(f);
+ }
+
+ public FP24(FP24 x)
+ {
+ a=new FP8(x.a);
+ b=new FP8(x.b);
+ c=new FP8(x.c);
+ }
+
+/* Granger-Scott Unitary Squaring */
+ public void usqr()
+ {
+//System.out.println("Into usqr");
+ FP8 A=new FP8(a);
+ FP8 B=new FP8(c);
+ FP8 C=new FP8(b);
+ FP8 D=new FP8(0);
+
+ a.sqr();
+ D.copy(a); D.add(a);
+ a.add(D);
+
+ a.norm();
+ A.nconj();
+
+ A.add(A);
+ a.add(A);
+ B.sqr();
+ B.times_i();
+
+ D.copy(B); D.add(B);
+ B.add(D);
+ B.norm();
+
+ C.sqr();
+ D.copy(C); D.add(C);
+ C.add(D);
+ C.norm();
+
+ b.conj();
+ b.add(b);
+ c.nconj();
+
+ c.add(c);
+ b.add(B);
+ c.add(C);
+ reduce();
+ }
+
+/* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+ public void sqr()
+ {
+ FP8 A=new FP8(a);
+ FP8 B=new FP8(b);
+ FP8 C=new FP8(c);
+ FP8 D=new FP8(a);
+
+ A.sqr();
+ B.mul(c);
+ B.add(B);
+ B.norm();
+ C.sqr();
+ D.mul(b);
+ D.add(D);
+
+ c.add(a);
+ c.add(b);
+ c.norm();
+ c.sqr();
+
+ a.copy(A);
+
+ A.add(B);
+ A.norm();
+ A.add(C);
+ A.add(D);
+ A.norm();
+
+ A.neg();
+ B.times_i();
+ C.times_i();
+
+ a.add(B);
+
+ b.copy(C); b.add(D);
+ c.add(A);
+
+ norm();
+ }
+
+/* FP12 full multiplication this=this*y */
+ public void mul(FP24 y)
+ {
+//System.out.println("Into mul");
+ FP8 z0=new FP8(a);
+ FP8 z1=new FP8(0);
+ FP8 z2=new FP8(b);
+ FP8 z3=new FP8(0);
+ FP8 t0=new FP8(a);
+ FP8 t1=new FP8(y.a);
+
+ z0.mul(y.a);
+ z2.mul(y.b);
+
+ t0.add(b);
+ t1.add(y.b);
+
+ t0.norm();
+ t1.norm();
+
+ z1.copy(t0); z1.mul(t1);
+ t0.copy(b); t0.add(c);
+
+ t1.copy(y.b); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ z3.copy(t0); z3.mul(t1);
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ z1.add(t0);
+ //z1.norm();
+ b.copy(z1); b.add(t1);
+
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+t0.norm();
+t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c); t0.mul(y.c);
+ t1.copy(t0); t1.neg();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ norm();
+
+ }
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+ public void smul(FP24 y,int type)
+ {
+ if (type==ECP.D_TYPE)
+ {
+ FP8 z0=new FP8(a);
+ FP8 z2=new FP8(b);
+ FP8 z3=new FP8(b);
+ FP8 t0=new FP8(0);
+ FP8 t1=new FP8(y.a);
+ z0.mul(y.a);
+ z2.pmul(y.b.real());
+ b.add(a);
+ t1.real().add(y.b.real());
+
+ t1.norm();
+ b.norm();
+ b.mul(t1);
+ z3.add(c);
+ z3.norm();
+ z3.pmul(y.b.real());
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ b.add(t0);
+
+ b.add(t1);
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t0.norm();
+ z3.norm();
+ t0.mul(y.a);
+ c.copy(z2); c.add(t0);
+
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ if (type==ECP.M_TYPE)
+ {
+ FP8 z0=new FP8(a);
+ FP8 z1=new FP8(0);
+ FP8 z2=new FP8(0);
+ FP8 z3=new FP8(0);
+ FP8 t0=new FP8(a);
+ FP8 t1=new FP8(0);
+
+ z0.mul(y.a);
+ t0.add(b);
+ t0.norm();
+
+ z1.copy(t0); z1.mul(y.a);
+ t0.copy(b); t0.add(c);
+ t0.norm();
+
+ z3.copy(t0); //z3.mul(y.c);
+ z3.pmul(y.c.getb());
+ z3.times_i();
+
+ t0.copy(z0); t0.neg();
+
+ z1.add(t0);
+ b.copy(z1);
+ z2.copy(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c);
+
+ t0.pmul(y.c.getb());
+ t0.times_i();
+
+ t1.copy(t0); t1.neg();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ norm();
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ FP8 f0=new FP8(a);
+ FP8 f1=new FP8(b);
+ FP8 f2=new FP8(a);
+ FP8 f3=new FP8(0);
+
+ norm();
+ f0.sqr();
+ f1.mul(c);
+ f1.times_i();
+ f0.sub(f1);
+ f0.norm();
+
+ f1.copy(c); f1.sqr();
+ f1.times_i();
+ f2.mul(b);
+ f1.sub(f2);
+ f1.norm();
+
+ f2.copy(b); f2.sqr();
+ f3.copy(a); f3.mul(c);
+ f2.sub(f3);
+ f2.norm();
+
+ f3.copy(b); f3.mul(f2);
+ f3.times_i();
+ a.mul(f0);
+ f3.add(a);
+ c.mul(f1);
+ c.times_i();
+
+ f3.add(c);
+ f3.norm();
+ f3.inverse();
+ a.copy(f0); a.mul(f3);
+ b.copy(f1); b.mul(f3);
+ c.copy(f2); c.mul(f3);
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f,int n)
+ {
+ FP2 f2=new FP2(f);
+ FP2 f3=new FP2(f);
+
+ f2.sqr();
+ f3.mul(f2);
+
+ f3.mul_ip(); f3.norm();
+
+ for (int i=0;i<n;i++)
+ {
+ a.frob(f3);
+ b.frob(f3);
+ c.frob(f3);
+
+ b.qmul(f); b.times_i2();
+ c.qmul(f2); c.times_i2(); c.times_i2();
+ }
+ }
+
+/* trace function */
+ public FP8 trace()
+ {
+ FP8 t=new FP8(0);
+ t.copy(a);
+ t.imul(3);
+ t.reduce();
+ return t;
+ }
+
+/* convert from byte array to FP12 */
+ public static FP24 fromBytes(byte[] w)
+ {
+ BIG a,b;
+ FP2 c,d;
+ FP4 ea,eb;
+ FP8 e,f,g;
+ byte[] t=new byte[BIG.MODBYTES];
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+2*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+3*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ ea=new FP4(c,d);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+4*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+5*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+6*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+7*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ eb=new FP4(c,d);
+
+ e=new FP8(ea,eb);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+8*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+9*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+10*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+11*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ ea=new FP4(c,d);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+12*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+13*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+14*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+15*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ eb=new FP4(c,d);
+
+ f=new FP8(ea,eb);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+16*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+17*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+18*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+19*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ ea=new FP4(c,d);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+20*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+21*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+22*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+23*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ eb=new FP4(c,d);
+
+ g=new FP8(ea,eb);
+
+ return new FP24(e,f,g);
+ }
+
+/* convert this to byte array */
+ public void toBytes(byte[] w)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+
+ a.geta().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i]=t[i];
+ a.geta().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+BIG.MODBYTES]=t[i];
+ a.geta().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+2*BIG.MODBYTES]=t[i];
+ a.geta().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+3*BIG.MODBYTES]=t[i];
+
+ a.getb().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+4*BIG.MODBYTES]=t[i];
+ a.getb().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+5*BIG.MODBYTES]=t[i];
+ a.getb().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+6*BIG.MODBYTES]=t[i];
+ a.getb().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+7*BIG.MODBYTES]=t[i];
+
+
+ b.geta().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+8*BIG.MODBYTES]=t[i];
+ b.geta().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+9*BIG.MODBYTES]=t[i];
+ b.geta().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+10*BIG.MODBYTES]=t[i];
+ b.geta().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+11*BIG.MODBYTES]=t[i];
+
+ b.getb().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+12*BIG.MODBYTES]=t[i];
+ b.getb().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+13*BIG.MODBYTES]=t[i];
+ b.getb().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+14*BIG.MODBYTES]=t[i];
+ b.getb().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+15*BIG.MODBYTES]=t[i];
+
+ c.geta().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+16*BIG.MODBYTES]=t[i];
+ c.geta().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+17*BIG.MODBYTES]=t[i];
+ c.geta().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+18*BIG.MODBYTES]=t[i];
+ c.geta().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+19*BIG.MODBYTES]=t[i];
+
+ c.getb().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+20*BIG.MODBYTES]=t[i];
+ c.getb().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+21*BIG.MODBYTES]=t[i];
+ c.getb().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+22*BIG.MODBYTES]=t[i];
+ c.getb().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+23*BIG.MODBYTES]=t[i];
+
+ }
+
+/* convert to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+","+c.toString()+"]");
+ }
+
+/* this=this^e */
+/* Note this is simple square and multiply, so not side-channel safe */
+ public FP24 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ BIG e3=new BIG(e);
+ e3.pmul(3);
+ e3.norm();
+
+ FP24 w=new FP24(this);
+
+ int nb=e3.nbits();
+ for (int i=nb-2;i>=1;i--)
+ {
+ w.usqr();
+ int bt=e3.bit(i)-e.bit(i);
+ if (bt==1)
+ w.mul(this);
+ if (bt==-1)
+ {
+ conj(); w.mul(this); conj();
+ }
+ }
+ w.reduce();
+ return w;
+
+ }
+
+/* constant time powering by small integer of max length bts */
+ public void pinpow(int e,int bts)
+ {
+ int i,b;
+ FP24 [] R=new FP24[2];
+ R[0]=new FP24(1);
+ R[1]=new FP24(this);
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ R[1-b].mul(R[b]);
+ R[b].usqr();
+ }
+ this.copy(R[0]);
+ }
+
+ public FP8 compow(BIG e,BIG r)
+ {
+ FP24 g1=new FP24(0);
+ FP24 g2=new FP24(0);
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.Modulus);
+
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(e);
+ a.mod(m);
+
+ BIG b=new BIG(e);
+ b.div(m);
+
+ g1.copy(this);
+ g2.copy(this);
+
+ FP8 c=g1.trace();
+
+ if (b.iszilch())
+ {
+ c=c.xtr_pow(e);
+ return c;
+ }
+
+ g2.frob(f,1);
+ FP8 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP8 cpm1=g2.trace();
+ g2.mul(g1);
+ FP8 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+
+ return c;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3.... */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static FP24 pow8(FP24[] q,BIG[] u)
+ {
+ int i,j,k,nb,pb1,pb2;
+ FP24 [] g1=new FP24[8];
+ FP24 [] g2=new FP24[8];
+ FP24 r=new FP24(1);
+ FP24 p=new FP24(0);
+ BIG [] t=new BIG[8];
+ BIG mt=new BIG(0);
+ byte[] w1=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s1=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] w2=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s2=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<8;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ }
+
+ g1[0]=new FP24(q[0]); // q[0]
+ g1[1]=new FP24(g1[0]); g1[1].mul(q[1]); // q[0].q[1]
+ g1[2]=new FP24(g1[0]); g1[2].mul(q[2]); // q[0].q[2]
+ g1[3]=new FP24(g1[1]); g1[3].mul(q[2]); // q[0].q[1].q[2]
+ g1[4]=new FP24(q[0]); g1[4].mul(q[3]); // q[0].q[3]
+ g1[5]=new FP24(g1[1]); g1[5].mul(q[3]); // q[0].q[1].q[3]
+ g1[6]=new FP24(g1[2]); g1[6].mul(q[3]); // q[0].q[2].q[3]
+ g1[7]=new FP24(g1[3]); g1[7].mul(q[3]); // q[0].q[1].q[2].q[3]
+
+// Use Frobenius
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ for (i=0;i<8;i++)
+ {
+ g2[i]=new FP24(g1[i]);
+ g2[i].frob(f,4);
+ }
+
+ // Make it odd
+ pb1=1-t[0].parity();
+ t[0].inc(pb1);
+ t[0].norm();
+
+ pb2=1-t[4].parity();
+ t[4].inc(pb2);
+ t[4].norm();
+
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<8;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s1[nb-1]=1;
+ s2[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s1[i]=(byte)(2*t[0].parity()-1);
+ t[4].fshr(1);
+ s2[i]=(byte)(2*t[4].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w1[i]=0;
+ k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s1[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w1[i]+=bt*(byte)k;
+ k*=2;
+ }
+
+ w2[i]=0;
+ k=1;
+ for (j=5; j<8; j++) {
+ byte bt=(byte)(s2[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w2[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ p.select(g1,(int)(2*w1[nb-1]+1));
+ r.select(g2,(int)(2*w2[nb-1]+1));
+ p.mul(r);
+ for (i=nb-2;i>=0;i--) {
+ p.usqr();
+ r.select(g1,(int)(2*w1[i]+s1[i]));
+ p.mul(r);
+ r.select(g2,(int)(2*w2[i]+s2[i]));
+ p.mul(r);
+
+ }
+
+ // apply correction
+ r.copy(q[0]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb1);
+
+ r.copy(q[4]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb2);
+
+ p.reduce();
+ return p;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/FP4.java b/src/main/java/org/apache/milagro/amcl/BLS24/FP4.java
new file mode 100644
index 0000000..4580325
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/FP4.java
@@ -0,0 +1,721 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^4 functions */
+
+/* FP4 elements are of the form a+ib, where i is sqrt(-1+sqrt(-1)) */
+
+package org.apache.milagro.amcl.BLS24;
+
+public final class FP4 {
+ private final FP2 a;
+ private final FP2 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP4 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP2 one=new FP2(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP2 real()
+ {
+ return a;
+ }
+
+ public FP2 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP2 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP4 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP4(int c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+
+ public FP4(FP4 x)
+ {
+ a=new FP2(x.a);
+ b=new FP2(x.b);
+ }
+
+ public FP4(FP2 c,FP2 d)
+ {
+ a=new FP2(c);
+ b=new FP2(d);
+ }
+
+ public FP4(FP2 c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+/* copy this=x */
+ public void copy(FP4 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP2 m=new FP2(a);
+ FP2 t=new FP2(0);
+ m.add(b);
+// m.norm();
+ m.neg();
+ // m.norm();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP4 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP4 x)
+ {
+ FP4 m=new FP4(x);
+ m.neg();
+ add(m);
+ }
+
+/* this*=s where s is FP2 */
+ public void pmul(FP2 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this=x-this */
+ public void rsub(FP4 x)
+ {
+ neg();
+ add(x);
+ }
+
+
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.mul_ip();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.mul_ip();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+/* this*=y */
+ public void mul(FP4 y)
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(0);
+ FP2 t4=new FP2(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ // t4.sub(t1);
+ // t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ // b.copy(t4);
+ // b.sub(t2);
+
+ t2.mul_ip();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.mul_ip();
+ t2.norm();
+ t1.sub(t2);
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP2 s=new FP2(b);
+ FP2 t=new FP2(b);
+ s.times_i();
+ t.add(s);
+ // t.norm();
+ b.copy(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ a.conj();
+ b.conj();
+ b.mul(f);
+ }
+
+/* this=this^e */
+ public FP4 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP4 w=new FP4(this);
+ BIG z=new BIG(e);
+ FP4 r=new FP4(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+/* XTR xtr_a function */
+ public void xtr_A(FP4 w,FP4 y,FP4 z)
+ {
+ FP4 r=new FP4(w);
+ FP4 t=new FP4(w);
+ //y.norm();
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP4 w=new FP4(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP4 xtr_pow(BIG n) {
+ FP4 a=new FP4(3);
+ FP4 b=new FP4(this);
+ FP4 c=new FP4(b);
+ c.xtr_D();
+ FP4 t=new FP4(0);
+ FP4 r=new FP4(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP4 xtr_pow2(FP4 ck,FP4 ckml,FP4 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP4 cu=new FP4(ck); // can probably be passed in w/o copying
+ FP4 cv=new FP4(this);
+ FP4 cumv=new FP4(ckml);
+ FP4 cum2v=new FP4(ckm2l);
+ FP4 r=new FP4(0);
+ FP4 t=new FP4(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+ public void div_i()
+ {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip();
+ a.copy(v);
+ b.copy(u);
+ }
+
+ public void div_2i() {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip2();
+ v.add(v); v.norm();
+ a.copy(v);
+ b.copy(u);
+ }
+
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP2 wa=new FP2(a);
+ FP2 ws=new FP2(b);
+ FP2 wt=new FP2(a);
+
+ if (ws.iszilch())
+ {
+ if (wt.sqrt())
+ {
+ a.copy(wt);
+ b.zero();
+ } else {
+ wt.div_ip();
+ wt.sqrt();
+ b.copy(wt);
+ a.zero();
+ }
+ return true;
+ }
+
+ ws.sqr();
+ wa.sqr();
+ ws.mul_ip();
+ ws.norm();
+ wa.sub(ws);
+
+ ws.copy(wa);
+ if (!ws.sqrt()) {
+ return false;
+ }
+
+ wa.copy(wt); wa.add(ws); wa.norm(); wa.div2();
+
+ if (!wa.sqrt()) {
+ wa.copy(wt); wa.sub(ws); wa.norm(); wa.div2();
+ if (!wa.sqrt()) {
+ return false;
+ }
+ }
+ wt.copy(b);
+ ws.copy(wa); ws.add(wa);
+ ws.inverse();
+
+ wt.mul(ws);
+ a.copy(wa);
+ b.copy(wt);
+
+ return true;
+ }
+
+/* this*=s where s is FP */
+ public void qmul(FP s)
+ {
+ a.pmul(s);
+ b.pmul(s);
+ }
+
+
+
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG e=new BIG(12);
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.inc(27); b.inc(45);
+
+ FP2 w0=new FP2(a,b);
+
+ a.zero(); b.zero();
+ a.inc(33); b.inc(54);
+
+ FP2 w1=new FP2(a,b);
+
+
+ FP4 w=new FP4(w0,w1);
+ FP4 t=new FP4(w);
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ System.out.println("w= "+w.toString());
+
+ w=w.pow(m);
+
+ System.out.println("w^p= "+w.toString());
+
+ t.frob(f);
+
+
+ System.out.println("w^p= "+t.toString());
+
+ w=w.pow(m);
+ w=w.pow(m);
+ w=w.pow(m);
+ System.out.println("w^p4= "+w.toString());
+
+
+ System.out.println("Test Inversion");
+
+ w=new FP4(w0,w1);
+
+ w.inverse();
+
+ System.out.println("1/w mod p^4 = "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/(1/w) mod p^4 = "+w.toString());
+
+ FP4 ww=new FP4(w);
+
+ w=w.xtr_pow(e);
+ System.out.println("w^e= "+w.toString());
+
+
+ a.zero(); b.zero();
+ a.inc(37); b.inc(17);
+ w0=new FP2(a,b);
+ a.zero(); b.zero();
+ a.inc(49); b.inc(31);
+ w1=new FP2(a,b);
+
+ FP4 c1=new FP4(w0,w1);
+ FP4 c2=new FP4(w0,w1);
+ FP4 c3=new FP4(w0,w1);
+
+ BIG e1=new BIG(3331);
+ BIG e2=new BIG(3372);
+
+ FP4 cr=w.xtr_pow2(c1,c2,c3,e1,e2);
+
+ System.out.println("c^e= "+cr.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/FP8.java b/src/main/java/org/apache/milagro/amcl/BLS24/FP8.java
new file mode 100644
index 0000000..d164d19
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/FP8.java
@@ -0,0 +1,656 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^8 functions */
+
+/* FP8 elements are of the form a+ib, where i is sqrt(sqrt(-1+sqrt(-1))) */
+
+package org.apache.milagro.amcl.BLS24;
+
+public final class FP8 {
+ private final FP4 a;
+ private final FP4 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP8 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP4 one=new FP4(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP4 real()
+ {
+ return a;
+ }
+
+ public FP4 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP4 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP8 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP8(int c)
+ {
+ a=new FP4(c);
+ b=new FP4(0);
+ }
+
+ public FP8(FP8 x)
+ {
+ a=new FP4(x.a);
+ b=new FP4(x.b);
+ }
+
+ public FP8(FP4 c,FP4 d)
+ {
+ a=new FP4(c);
+ b=new FP4(d);
+ }
+
+ public FP8(FP4 c)
+ {
+ a=new FP4(c);
+ b=new FP4(0);
+ }
+/* copy this=x */
+ public void copy(FP8 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP4 m=new FP4(a);
+ FP4 t=new FP4(0);
+ m.add(b);
+// m.norm();
+ m.neg();
+ // m.norm();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP8 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP8 x)
+ {
+ FP8 m=new FP8(x);
+ m.neg();
+ add(m);
+ }
+
+/* this=x-this */
+ public void rsub(FP8 x)
+ {
+ neg();
+ add(x);
+ }
+
+
+/* this*=s where s is FP4 */
+ public void pmul(FP4 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+/* this*=s where s is FP2 */
+ public void qmul(FP2 s)
+ {
+ a.pmul(s);
+ b.pmul(s);
+ }
+/* this*=s where s is FP */
+ public void tmul(FP s)
+ {
+ a.qmul(s);
+ b.qmul(s);
+ }
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP4 t1=new FP4(a);
+ FP4 t2=new FP4(b);
+ FP4 t3=new FP4(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.times_i();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.times_i();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+
+/* this*=y */
+ public void mul(FP8 y)
+ {
+// norm();
+
+ FP4 t1=new FP4(a);
+ FP4 t2=new FP4(b);
+ FP4 t3=new FP4(0);
+ FP4 t4=new FP4(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ // t4.sub(t1);
+ // t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ // b.copy(t4);
+ // b.sub(t2);
+
+ t2.times_i();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP4 t1=new FP4(a);
+ FP4 t2=new FP4(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.times_i();
+ t2.norm();
+ t1.sub(t2); t1.norm();
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP4 s=new FP4(b);
+ FP4 t=new FP4(a);
+ s.times_i();
+
+ b.copy(t);
+ a.copy(s);
+ norm();
+ }
+
+ public void times_i2()
+ {
+ a.times_i();
+ b.times_i();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ FP2 ff=new FP2(f); ff.sqr(); ff.mul_ip(); ff.norm();
+
+ a.frob(ff);
+ b.frob(ff);
+ b.pmul(f);
+ b.times_i();
+
+ }
+
+/* this=this^e */
+ public FP8 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP8 w=new FP8(this);
+ BIG z=new BIG(e);
+ FP8 r=new FP8(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+
+/* XTR xtr_a function */
+ public void xtr_A(FP8 w,FP8 y,FP8 z)
+ {
+ FP8 r=new FP8(w);
+ FP8 t=new FP8(w);
+
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP8 w=new FP8(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP8 xtr_pow(BIG n) {
+ FP8 a=new FP8(3);
+ FP8 b=new FP8(this);
+ FP8 c=new FP8(b);
+ c.xtr_D();
+ FP8 t=new FP8(0);
+ FP8 r=new FP8(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP8 xtr_pow2(FP8 ck,FP8 ckml,FP8 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP8 cu=new FP8(ck); // can probably be passed in w/o copying
+ FP8 cv=new FP8(this);
+ FP8 cumv=new FP8(ckml);
+ FP8 cum2v=new FP8(ckm2l);
+ FP8 r=new FP8(0);
+ FP8 t=new FP8(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+ public void div_i()
+ {
+ FP4 u=new FP4(a);
+ FP4 v=new FP4(b);
+ u.div_i();
+ a.copy(v);
+ b.copy(u);
+ }
+
+ public void div_i2() {
+ a.div_i();
+ b.div_i();
+ }
+
+ public void div_2i() {
+ FP4 u=new FP4(a);
+ FP4 v=new FP4(b);
+ u.div_2i();
+ v.add(v); v.norm();
+ a.copy(v);
+ b.copy(u);
+ }
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP4 wa=new FP4(a);
+ FP4 ws=new FP4(b);
+ FP4 wt=new FP4(a);
+
+ if (ws.iszilch())
+ {
+ if (wt.sqrt())
+ {
+ a.copy(wt);
+ b.zero();
+ } else {
+ wt.div_i();
+ wt.sqrt();
+ b.copy(wt);
+ a.zero();
+ }
+ return true;
+ }
+
+ ws.sqr();
+ wa.sqr();
+ ws.times_i();
+ ws.norm();
+ wa.sub(ws);
+
+ ws.copy(wa);
+ if (!ws.sqrt()) {
+ return false;
+ }
+
+ wa.copy(wt); wa.add(ws); wa.norm(); wa.div2();
+
+ if (!wa.sqrt()) {
+ wa.copy(wt); wa.sub(ws); wa.norm(); wa.div2();
+ if (!wa.sqrt()) {
+ return false;
+ }
+ }
+ wt.copy(b);
+ ws.copy(wa); ws.add(wa);
+ ws.inverse();
+
+ wt.mul(ws);
+ a.copy(wa);
+ b.copy(wt);
+
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/MPIN192.java b/src/main/java/org/apache/milagro/amcl/BLS24/MPIN192.java
new file mode 100644
index 0000000..ee3f566
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/MPIN192.java
@@ -0,0 +1,806 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* MPIN API Functions */
+
+package org.apache.milagro.amcl.BLS24;
+
+import java.util.Date;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public class MPIN192
+{
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int PAS=16;
+ public static final int INVALID_POINT=-14;
+ public static final int BAD_PARAMS=-11;
+ public static final int WRONG_ORDER=-18;
+ public static final int BAD_PIN=-19;
+
+/* Configure your PIN here */
+
+ public static final int MAXPIN=10000; /* PIN less than this */
+ public static final int PBLEN=14; /* Number of bits in PIN */
+ public static final int TS=10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+ public static final int TRAP=200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN - approx 2*sqrt(MAXPIN) */
+
+// public static final int HASH_TYPE=SHA256;
+
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,int n,byte[] B,int len)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (n>0) H.process_num(n);
+
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+ byte[] W=new byte[len];
+
+ if (sha>=len)
+ for (int i=0;i<len;i++) W[i]=R[i];
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+len-sha]=R[i];
+ for (int i=0;i<len-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<len;i++) W[i]=0;
+ }
+ return W;
+ }
+
+ /* return time in slots since epoch */
+ public static int today() {
+ Date date=new Date();
+ return (int) (date.getTime()/(1000*60*1440));
+ }
+
+ public static byte[] HASH_ID(int sha,byte[] ID,int len)
+ {
+ return hashit(sha,0,ID,len);
+ }
+
+/* Hash the M-Pin transcript - new */
+
+ public static byte[] HASH_ALL(int sha,byte[] HID,byte[] xID,byte[] xCID,byte[] SEC,byte[] Y,byte[] R,byte[] W,int len)
+ {
+ int i,ilen,tlen=0;
+
+ ilen=HID.length+SEC.length+Y.length+R.length+W.length;
+ if (xCID!=null) ilen+=xCID.length;
+ else ilen+=xID.length;
+
+ byte[] T = new byte[ilen];
+
+ for (i=0;i<HID.length;i++) T[i]=HID[i];
+ tlen+=HID.length;
+ if (xCID!=null)
+ {
+ for (i=0;i<xCID.length;i++) T[i+tlen]=xCID[i];
+ tlen+=xCID.length;
+ }
+ else
+ {
+ for (i=0;i<xID.length;i++) T[i+tlen]=xID[i];
+ tlen+=xID.length;
+ }
+ for (i=0;i<SEC.length;i++) T[i+tlen]=SEC[i];
+ tlen+=SEC.length;
+ for (i=0;i<Y.length;i++) T[i+tlen]=Y[i];
+ tlen+=Y.length;
+ for (i=0;i<R.length;i++) T[i+tlen]=R[i];
+ tlen+=R.length;
+ for (i=0;i<W.length;i++) T[i+tlen]=W[i];
+ tlen+=W.length;
+
+ return hashit(sha,0,T,len);
+ }
+
+/* return time since epoch */
+ public static int GET_TIME() {
+ Date date=new Date();
+ return (int) (date.getTime()/1000);
+ }
+
+ public static byte[] mpin_hash(int sha,FP8 c,ECP U)
+ {
+ byte[] w=new byte[EFS];
+ byte[] t=new byte[10*EFS];
+ byte[] h=null;
+ c.geta().geta().getA().toBytes(w); for (int i=0;i<EFS;i++) t[i]=w[i];
+ c.geta().geta().getB().toBytes(w); for (int i=EFS;i<2*EFS;i++) t[i]=w[i-EFS];
+ c.geta().getb().getA().toBytes(w); for (int i=2*EFS;i<3*EFS;i++) t[i]=w[i-2*EFS];
+ c.geta().getb().getB().toBytes(w); for (int i=3*EFS;i<4*EFS;i++) t[i]=w[i-3*EFS];
+
+ c.getb().geta().getA().toBytes(w); for (int i=4*EFS;i<5*EFS;i++) t[i]=w[i-4*EFS];
+ c.getb().geta().getB().toBytes(w); for (int i=5*EFS;i<6*EFS;i++) t[i]=w[i-5*EFS];
+ c.getb().getb().getA().toBytes(w); for (int i=6*EFS;i<7*EFS;i++) t[i]=w[i-6*EFS];
+ c.getb().getb().getB().toBytes(w); for (int i=7*EFS;i<8*EFS;i++) t[i]=w[i-7*EFS];
+
+
+ U.getX().toBytes(w); for (int i=8*EFS;i<9*EFS;i++) t[i]=w[i-8*EFS];
+ U.getY().toBytes(w); for (int i=9*EFS;i<10*EFS;i++) t[i]=w[i-9*EFS];
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (h==null) return null;
+ byte[] R=new byte[ECP.AESKEY];
+ for (int i=0;i<ECP.AESKEY;i++) R[i]=h[i];
+ return R;
+ }
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+ public static ECP map(BIG u,int cb)
+ {
+ ECP P;
+ BIG x=new BIG(u);
+ BIG p=new BIG(ROM.Modulus);
+ x.mod(p);
+ while (true)
+ {
+ P=new ECP(x,cb);
+ if (!P.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+ return P;
+ }
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+ public static int unmap(BIG u,ECP P)
+ {
+ int s=P.getS();
+ ECP R;
+ int r=0;
+ BIG x=P.getX();
+ u.copy(x);
+ while (true)
+ {
+ u.dec(1); u.norm();
+ r++;
+ R=new ECP(u,s);
+ if (!R.is_infinity()) break;
+ }
+ return r;
+ }
+
+
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+ public static int ENCODING(RAND rng,byte[] E)
+ {
+ int rn,m,su,sv;
+ byte[] T=new byte[EFS];
+
+ for (int i=0;i<EFS;i++) T[i]=E[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=E[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ ECP P=new ECP(u,v);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG p=new BIG(ROM.Modulus);
+ u=BIG.randomnum(p,rng);
+
+ su=rng.getByte(); /*if (su<0) su=-su;*/ su%=2;
+
+ ECP W=map(u,su);
+ P.sub(W); //P.affine();
+ sv=P.getS();
+ rn=unmap(v,P);
+ m=rng.getByte(); /*if (m<0) m=-m;*/ m%=rn;
+ v.inc(m+1);
+ E[0]=(byte)(su+2*sv);
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+ public static int DECODING(byte[] D)
+ {
+ int su,sv;
+ byte[] T=new byte[EFS];
+
+ if ((D[0]&0x04)!=0) return INVALID_POINT;
+
+ for (int i=0;i<EFS;i++) T[i]=D[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=D[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ su=D[0]&1;
+ sv=(D[0]>>1)&1;
+ ECP W=map(u,su);
+ ECP P=map(v,sv);
+ P.add(W); //P.affine();
+ u=P.getX();
+ v=P.getY();
+ D[0]=0x04;
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+/* R=R1+R2 in group G1 */
+ public static int RECOMBINE_G1(byte[] R1,byte[] R2,byte[] R)
+ {
+ ECP P=ECP.fromBytes(R1);
+ ECP Q=ECP.fromBytes(R2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(R,false);
+ return 0;
+ }
+
+/* W=W1+W2 in group G2 */
+ public static int RECOMBINE_G2(byte[] W1,byte[] W2,byte[] W)
+ {
+ ECP4 P=ECP4.fromBytes(W1);
+ ECP4 Q=ECP4.fromBytes(W2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(W);
+ return 0;
+ }
+
+/* create random secret S */
+ public static int RANDOM_GENERATE(RAND rng,byte[] S)
+ {
+ BIG s;
+ BIG r=new BIG(ROM.CURVE_Order);
+ s=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+ return 0;
+ }
+
+/* Extract PIN from TOKEN for identity CID */
+ public static int EXTRACT_PIN(int sha,byte[] CID,int pin,byte[] TOKEN)
+ {
+ ECP P=ECP.fromBytes(TOKEN);
+ if (P.is_infinity()) return INVALID_POINT;
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R=ECP.mapit(h);
+
+
+ pin%=MAXPIN;
+
+ R=R.pinmul(pin,PBLEN);
+ P.sub(R); //P.affine();
+
+ P.toBytes(TOKEN,false);
+
+ return 0;
+ }
+
+/* Implement step 2 on client side of MPin protocol */
+ public static int CLIENT_2(byte[] X,byte[] Y,byte[] SEC)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ ECP P=ECP.fromBytes(SEC);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG px=BIG.fromBytes(X);
+ BIG py=BIG.fromBytes(Y);
+ px.add(py);
+ px.mod(r);
+ // px.rsub(r);
+
+ P=PAIR192.G1mul(P,px);
+ P.neg();
+ P.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Implement step 1 on client side of MPin protocol */
+ public static int CLIENT_1(int sha,int date,byte[] CLIENT_ID,RAND rng,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG x;
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P,T,W;
+ BIG px;
+// byte[] t=new byte[EFS];
+
+ byte[] h=hashit(sha,0,CLIENT_ID,EFS);
+ P=ECP.mapit(h);
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ pin%=MAXPIN;
+ W=P.pinmul(pin,PBLEN);
+ T.add(W);
+ if (date!=0)
+ {
+ W=ECP.fromBytes(PERMIT);
+ if (W.is_infinity()) return INVALID_POINT;
+ T.add(W);
+ h=hashit(sha,date,h,EFS);
+ W=ECP.mapit(h);
+ if (xID!=null)
+ {
+ P=PAIR192.G1mul(P,x);
+ P.toBytes(xID,false);
+ W=PAIR192.G1mul(W,x);
+ P.add(W); //P.affine();
+ }
+ else
+ {
+ P.add(W); //P.affine();
+ P=PAIR192.G1mul(P,x);
+ }
+ if (xCID!=null) P.toBytes(xCID,false);
+ }
+ else
+ {
+ if (xID!=null)
+ {
+ P=PAIR192.G1mul(P,x);
+ P.toBytes(xID,false);
+ }
+ }
+
+ //T.affine();
+ T.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+ public static int GET_SERVER_SECRET(byte[] S,byte[] SST)
+ {
+ ECP4 Q=ECP4.generator();
+ BIG s=BIG.fromBytes(S);
+ Q=PAIR192.G2mul(Q,s);
+ Q.toBytes(SST);
+ return 0;
+ }
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+ public static int GET_G1_MULTIPLE(RAND rng, int type,byte[] X,byte[] G,byte[] W)
+ {
+ BIG x;
+ BIG r=new BIG(ROM.CURVE_Order);
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P;
+ if (type==0)
+ {
+ P=ECP.fromBytes(G);
+ if (P.is_infinity()) return INVALID_POINT;
+ }
+ else
+ P=ECP.mapit(G);
+
+ PAIR192.G1mul(P,x).toBytes(W,false);
+ return 0;
+ }
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+ public static int GET_CLIENT_SECRET(byte[] S,byte[] CID,byte[] CST)
+ {
+ return GET_G1_MULTIPLE(null,1,S,CID,CST);
+ }
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+ public static int GET_CLIENT_PERMIT(int sha,int date,byte[] S,byte[] CID,byte[] CTT)
+ {
+ byte[] h=hashit(sha,date,CID,EFS);
+ ECP P=ECP.mapit(h);
+
+ BIG s=BIG.fromBytes(S);
+ ECP OP=PAIR192.G1mul(P,s);
+
+ OP.toBytes(CTT,false);
+ return 0;
+ }
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+ public static void SERVER_1(int sha,int date,byte[] CID,byte[] HID,byte[] HTID)
+ {
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R,P=ECP.mapit(h);
+
+ P.toBytes(HID,false); // new
+ if (date!=0)
+ {
+ // if (HID!=null) P.toBytes(HID,false);
+ h=hashit(sha,date,h,EFS);
+ R=ECP.mapit(h);
+ P.add(R); //P.affine();
+ P.toBytes(HTID,false);
+ }
+ // else P.toBytes(HID,false);
+ }
+
+/* Implement step 2 of MPin protocol on server side */
+ public static int SERVER_2(int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] mSEC,byte[] E,byte[] F)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ ECP4 Q=ECP4.generator();
+
+ ECP4 sQ=ECP4.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+
+ ECP R;
+ if (date!=0)
+ R=ECP.fromBytes(xCID);
+ else
+ {
+ if (xID==null) return BAD_PARAMS;
+ R=ECP.fromBytes(xID);
+ }
+ if (R.is_infinity()) return INVALID_POINT;
+
+ BIG y=BIG.fromBytes(Y);
+ ECP P;
+ if (date!=0) P=ECP.fromBytes(HTID);
+ else
+ {
+ if (HID==null) return BAD_PARAMS;
+ P=ECP.fromBytes(HID);
+ }
+
+ if (P.is_infinity()) return INVALID_POINT;
+
+ P=PAIR192.G1mul(P,y);
+ P.add(R); //P.affine();
+ R=ECP.fromBytes(mSEC);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ FP24 g;
+
+ g=PAIR192.ate2(Q,R,sQ,P);
+ g=PAIR192.fexp(g);
+
+ if (!g.isunity())
+ {
+ if (HID!=null && xID!=null && E!=null && F!=null)
+ {
+ g.toBytes(E);
+ if (date!=0)
+ {
+ P=ECP.fromBytes(HID);
+ if (P.is_infinity()) return INVALID_POINT;
+ R=ECP.fromBytes(xID);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ P=PAIR192.G1mul(P,y);
+ P.add(R); //P.affine();
+ }
+ g=PAIR192.ate(Q,P);
+ g=PAIR192.fexp(g);
+ g.toBytes(F);
+ }
+ return BAD_PIN;
+ }
+
+ return 0;
+ }
+
+/* Pollards kangaroos used to return PIN error */
+ public static int KANGAROO(byte[] E,byte[] F)
+ {
+ FP24 ge=FP24.fromBytes(E);
+ FP24 gf=FP24.fromBytes(F);
+ int[] distance = new int[TS];
+ FP24 t=new FP24(gf);
+ FP24[] table=new FP24[TS];
+ int i,j,m,s,dn,dm,res,steps;
+
+ s=1;
+ for (m=0;m<TS;m++)
+ {
+ distance[m]=s;
+ table[m]=new FP24(t);
+ s*=2;
+ t.usqr();
+ }
+ t.one();
+ dn=0;
+ for (j=0;j<TRAP;j++)
+ {
+ i=t.geta().geta().geta().getA().lastbits(20)%TS;
+ t.mul(table[i]);
+ dn+=distance[i];
+ }
+ gf.copy(t); gf.conj();
+ steps=0; dm=0;
+ res=0;
+ while (dm-dn<MAXPIN)
+ {
+ steps++;
+ if (steps>4*TRAP) break;
+ i=ge.geta().geta().geta().getA().lastbits(20)%TS;
+ ge.mul(table[i]);
+ dm+=distance[i];
+ if (ge.equals(t))
+ {
+ res=dm-dn;
+ break;
+ }
+ if (ge.equals(gf))
+ {
+ res=dn-dm;
+ break;
+ }
+
+ }
+ if (steps>4*TRAP || dm-dn>=MAXPIN) {res=0; } // Trap Failed - probable invalid token
+ return res;
+ }
+
+/* Functions to support M-Pin Full */
+
+ public static int PRECOMPUTE(byte[] TOKEN,byte[] CID,byte[] G1,byte[] G2)
+ {
+ ECP P,T;
+ FP24 g;
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ P=ECP.mapit(CID);
+
+ ECP4 Q=ECP4.generator();
+
+ g=PAIR192.ate(Q,T);
+ g=PAIR192.fexp(g);
+ g.toBytes(G1);
+
+ g=PAIR192.ate(Q,P);
+ g=PAIR192.fexp(g);
+ g.toBytes(G2);
+
+ return 0;
+ }
+
+
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+ public static int CLIENT_KEY(int sha,byte[] G1,byte[] G2,int pin,byte[] R,byte[] X,byte[] H,byte[] wCID,byte[] CK)
+ {
+ byte[] t;
+
+ FP24 g1=FP24.fromBytes(G1);
+ FP24 g2=FP24.fromBytes(G2);
+ BIG z=BIG.fromBytes(R);
+ BIG x=BIG.fromBytes(X);
+ BIG h=BIG.fromBytes(H);
+
+ ECP W=ECP.fromBytes(wCID);
+ if (W.is_infinity()) return INVALID_POINT;
+
+ W=PAIR192.G1mul(W,x);
+
+// FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG r=new BIG(ROM.CURVE_Order);
+// BIG q=new BIG(ROM.Modulus);
+
+ z.add(h); //new
+ z.mod(r);
+
+ g2.pinpow(pin,PBLEN);
+ g1.mul(g2);
+
+ FP8 c=g1.compow(z,r);
+
+ t=mpin_hash(sha,c,W);
+
+ for (int i=0;i<ECP.AESKEY;i++) CK[i]=t[i];
+
+ return 0;
+ }
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+ public static int SERVER_KEY(int sha,byte[] Z,byte[] SST,byte[] W,byte[] H,byte[] HID,byte[] xID,byte[] xCID,byte[] SK)
+ {
+ byte[] t;
+
+ ECP4 sQ=ECP4.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+ ECP R=ECP.fromBytes(Z);
+ if (R.is_infinity()) return INVALID_POINT;
+ ECP A=ECP.fromBytes(HID);
+ if (A.is_infinity()) return INVALID_POINT;
+
+ ECP U;
+ if (xCID!=null)
+ U=ECP.fromBytes(xCID);
+ else
+ U=ECP.fromBytes(xID);
+ if (U.is_infinity()) return INVALID_POINT;
+
+ BIG w=BIG.fromBytes(W);
+ BIG h=BIG.fromBytes(H);
+ A=PAIR192.G1mul(A,h); // new
+ R.add(A); //R.affine();
+
+ U=PAIR192.G1mul(U,w);
+ FP24 g=PAIR192.ate(sQ,R);
+ g=PAIR192.fexp(g);
+
+ FP8 c=g.trace();
+
+ t=mpin_hash(sha,c,U);
+
+ for (int i=0;i<ECP.AESKEY;i++) SK[i]=t[i];
+
+ return 0;
+ }
+
+/* Generate Y = H(epoch, xCID/xID) */
+ public static void GET_Y(int sha,int TimeValue,byte[] xCID,byte[] Y)
+ {
+ byte[] h = hashit(sha,TimeValue,xCID,EFS);
+ BIG y = BIG.fromBytes(h);
+ BIG q=new BIG(ROM.CURVE_Order);
+ y.mod(q);
+ //if (ROM.AES_S>0)
+ //{
+ // y.mod2m(2*ROM.AES_S);
+ //}
+ y.toBytes(Y);
+ }
+
+/* One pass MPIN Client */
+ public static int CLIENT(int sha,int date,byte[] CLIENT_ID,RAND RNG,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT, int TimeValue, byte[] Y)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ rtn = CLIENT_1(sha,date,CLIENT_ID,RNG,X,pin,TOKEN,SEC,xID,xCID,PERMIT);
+ if (rtn != 0)
+ return rtn;
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+
+/* One pass MPIN Server */
+ public static int SERVER(int sha,int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] SEC,byte[] E,byte[] F,byte[] CID, int TimeValue)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ SERVER_1(sha,date,CID,HID,HTID);
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = SERVER_2(date,HID,HTID,Y,SST,xID,xCID,SEC,E,F);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/PAIR192.java b/src/main/java/org/apache/milagro/amcl/BLS24/PAIR192.java
new file mode 100644
index 0000000..da3d100
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/PAIR192.java
@@ -0,0 +1,550 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BN Curve Pairing functions */
+
+package org.apache.milagro.amcl.BLS24;
+
+public final class PAIR192 {
+
+ public static final boolean USE_GLV =true;
+ public static final boolean USE_GS_G2 =true;
+ public static final boolean USE_GS_GT =true;
+ public static final boolean GT_STRONG=false;
+
+
+/* Line function */
+ public static FP24 line(ECP4 A,ECP4 B,FP Qx,FP Qy)
+ {
+//System.out.println("Into line");
+ FP8 a,b,c; // Edits here
+// c=new FP8(0);
+ if (A==B)
+ { // Doubling
+ FP4 XX=new FP4(A.getx()); //X
+ FP4 YY=new FP4(A.gety()); //Y
+ FP4 ZZ=new FP4(A.getz()); //Z
+ FP4 YZ=new FP4(YY); //Y
+ YZ.mul(ZZ); //YZ
+ XX.sqr(); //X^2
+ YY.sqr(); //Y^2
+ ZZ.sqr(); //Z^2
+
+ YZ.imul(4);
+ YZ.neg(); YZ.norm(); //-2YZ
+ YZ.qmul(Qy); //-2YZ.Ys
+
+ XX.imul(6); //3X^2
+ XX.qmul(Qx); //3X^2.Xs
+
+ int sb=3*ROM.CURVE_B_I;
+ ZZ.imul(sb);
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ ZZ.div_2i();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ ZZ.times_i();
+ ZZ.add(ZZ);
+ YZ.times_i();
+ YZ.norm();
+ }
+
+ ZZ.norm(); // 3b.Z^2
+
+ YY.add(YY);
+ ZZ.sub(YY); ZZ.norm(); // 3b.Z^2-Y^2
+
+ a=new FP8(YZ,ZZ); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP8(XX); // L(0,1) | L(0,0) | L(1,0)
+ c=new FP8(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP8(0);
+ c=new FP8(XX); c.times_i();
+ }
+ A.dbl();
+ }
+ else
+ { // Addition - assume B is affine
+
+ FP4 X1=new FP4(A.getx()); // X1
+ FP4 Y1=new FP4(A.gety()); // Y1
+ FP4 T1=new FP4(A.getz()); // Z1
+ FP4 T2=new FP4(A.getz()); // Z1
+
+ T1.mul(B.gety()); // T1=Z1.Y2
+ T2.mul(B.getx()); // T2=Z1.X2
+
+ X1.sub(T2); X1.norm(); // X1=X1-Z1.X2
+ Y1.sub(T1); Y1.norm(); // Y1=Y1-Z1.Y2
+
+ T1.copy(X1); // T1=X1-Z1.X2
+ X1.qmul(Qy); // X1=(X1-Z1.X2).Ys
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X1.times_i();
+ X1.norm();
+ }
+
+ T1.mul(B.gety()); // T1=(X1-Z1.X2).Y2
+
+ T2.copy(Y1); // T2=Y1-Z1.Y2
+ T2.mul(B.getx()); // T2=(Y1-Z1.Y2).X2
+ T2.sub(T1); T2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+ Y1.qmul(Qx); Y1.neg(); Y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+ a=new FP8(X1,T2); // (X1-Z1.X2).Ys | (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2 | - (Y1-Z1.Y2).Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP8(Y1);
+ c=new FP8(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP8(0);
+ c=new FP8(Y1); c.times_i();
+ }
+ A.add(B);
+ }
+//System.out.println("Out of line");
+ return new FP24(a,b,c);
+ }
+
+/* Optimal R-ate pairing */
+ public static FP24 ate(ECP4 P1,ECP Q1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ FP24 lv;
+ int bt;
+
+ ECP4 P=new ECP4(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+
+ ECP4 A=new ECP4();
+ FP24 r=new FP24(1);
+ A.copy(P);
+
+ ECP4 MP=new ECP4();
+ MP.copy(P); MP.neg();
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+ return r;
+ }
+
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+ public static FP24 ate2(ECP4 P1,ECP Q1,ECP4 R1,ECP S1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ FP24 lv;
+ int bt;
+
+ ECP4 P=new ECP4(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ ECP4 R=new ECP4(R1);
+ ECP S=new ECP(S1);
+
+ R.affine();
+ S.affine();
+
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+ FP Sx=new FP(S.getx());
+ FP Sy=new FP(S.gety());
+
+ ECP4 A=new ECP4();
+ ECP4 B=new ECP4();
+ FP24 r=new FP24(1);
+
+ A.copy(P);
+ B.copy(R);
+
+ ECP4 MP=new ECP4();
+ MP.copy(P); MP.neg();
+ ECP4 MR=new ECP4();
+ MR.copy(R); MR.neg();
+
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ lv=line(B,B,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ lv=line(B,R,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ //R.neg();
+ lv=line(B,MR,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //R.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+ return r;
+ }
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+ public static FP24 fexp(FP24 m)
+ {
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ FP24 r=new FP24(m);
+
+/* Easy part of final exp */
+ FP24 lv=new FP24(r);
+ lv.inverse();
+ r.conj();
+
+ r.mul(lv);
+ lv.copy(r);
+ r.frob(f,4);
+ r.mul(lv);
+
+ FP24 t0,t1,t2,t3,t4,t5,t6,t7;
+/* Hard part of final exp */
+// Ghamman & Fouotsa Method
+
+ t7=new FP24(r); t7.usqr();
+ t1=t7.pow(x);
+
+ x.fshr(1);
+ t2=t1.pow(x);
+ x.fshl(1);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+ t3=new FP24(t1); t3.conj();
+ t2.mul(t3);
+ t2.mul(r);
+
+ t3=t2.pow(x);
+ t4=t3.pow(x);
+ t5=t4.pow(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t3.conj(); t5.conj();
+ }
+
+ t3.frob(f,6); t4.frob(f,5);
+ t3.mul(t4);
+
+ t6=t5.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t6.conj();
+ }
+
+ t5.frob(f,4);
+ t3.mul(t5);
+
+ t0=new FP24(t2); t0.conj();
+ t6.mul(t0);
+
+ t5.copy(t6);
+ t5.frob(f,3);
+
+ t3.mul(t5);
+ t5=t6.pow(x);
+ t6=t5.pow(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t5.conj();
+ }
+
+ t0.copy(t5);
+ t0.frob(f,2);
+ t3.mul(t0);
+ t0.copy(t6);
+ t0.frob(f,1);
+
+ t3.mul(t0);
+ t5=t6.pow(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t5.conj();
+ }
+ t2.frob(f,7);
+
+ t5.mul(t7);
+ t3.mul(t2);
+ t3.mul(t5);
+
+ r.mul(t3);
+
+ r.reduce();
+ return r;
+ }
+
+/* GLV method */
+ public static BIG[] glv(BIG e)
+ {
+ BIG[] u=new BIG[2];
+// -(x^4).P = (Beta.x,y)
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG x2=BIG.smul(x,x);
+ x=BIG.smul(x2,x2);
+ u[0]=new BIG(e);
+ u[0].mod(x);
+ u[1]=new BIG(e);
+ u[1].div(x);
+ u[1].rsub(q);
+
+ return u;
+ }
+
+/* Galbraith & Scott Method */
+ public static BIG[] gs(BIG e)
+ {
+ BIG[] u=new BIG[8];
+
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG w=new BIG(e);
+ for (int i=0;i<7;i++)
+ {
+ u[i]=new BIG(w);
+ u[i].mod(x);
+ w.div(x);
+ }
+ u[7]=new BIG(w);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ u[1].copy(BIG.modneg(u[1],q));
+ u[3].copy(BIG.modneg(u[3],q));
+ u[5].copy(BIG.modneg(u[5],q));
+ u[7].copy(BIG.modneg(u[7],q));
+ }
+
+ return u;
+ }
+
+/* Multiply P by e in group G1 */
+ public static ECP G1mul(ECP P,BIG e)
+ {
+ ECP R;
+ if (USE_GLV)
+ {
+ //P.affine();
+ R=new ECP();
+ R.copy(P);
+ int i,np,nn;
+ ECP Q=new ECP();
+ Q.copy(P); Q.affine();
+ BIG q=new BIG(ROM.CURVE_Order);
+ FP cru=new FP(new BIG(ROM.CURVE_Cru));
+ BIG t=new BIG(0);
+ BIG[] u=glv(e);
+ Q.getx().mul(cru);
+
+ np=u[0].nbits();
+ t.copy(BIG.modneg(u[0],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[0].copy(t);
+ R.neg();
+ }
+
+ np=u[1].nbits();
+ t.copy(BIG.modneg(u[1],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[1].copy(t);
+ Q.neg();
+ }
+ u[0].norm();
+ u[1].norm();
+ R=R.mul2(u[0],Q,u[1]);
+
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* Multiply P by e in group G2 */
+ public static ECP4 G2mul(ECP4 P,BIG e)
+ {
+ ECP4 R;
+ if (USE_GS_G2)
+ {
+ ECP4[] Q=new ECP4[8];
+ FP2[] F=ECP4.frob_constants();
+
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] u=gs(e);
+
+ BIG t=new BIG(0);
+ int i,np,nn;
+ //P.affine();
+
+ Q[0]=new ECP4(); Q[0].copy(P);
+ for (i=1;i<8;i++)
+ {
+ Q[i]=new ECP4(); Q[i].copy(Q[i-1]);
+ Q[i].frob(F,1);
+ }
+ for (i=0;i<8;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ Q[i].neg();
+ }
+ u[i].norm();
+ //Q[i].affine();
+ }
+
+ R=ECP4.mul8(Q,u);
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP8.java */
+ public static FP24 GTpow(FP24 d,BIG e)
+ {
+ FP24 r;
+ if (USE_GS_GT)
+ {
+ FP24[] g=new FP24[8];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG t=new BIG(0);
+ int i,np,nn;
+ BIG[] u=gs(e);
+
+ g[0]=new FP24(d);
+ for (i=1;i<8;i++)
+ {
+ g[i]=new FP24(0); g[i].copy(g[i-1]);
+ g[i].frob(f,1);
+ }
+ for (i=0;i<8;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ g[i].conj();
+ }
+ u[i].norm();
+ }
+ r=FP24.pow8(g,u);
+ }
+ else
+ {
+ r=d.pow(e);
+ }
+ return r;
+ }
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS24/ROM.java b/src/main/java/org/apache/milagro/amcl/BLS24/ROM.java
new file mode 100644
index 0000000..14658f9
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS24/ROM.java
@@ -0,0 +1,60 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.BLS24;
+
+public class ROM
+{
+
+// Base Bits= 56
+ public static final long[] Modulus= {0x44C1674A06152BL,0xFFE2E82D30DAF8L,0x6F1C5CBDB6A642L,0x3220DF068A328BL,0xE09E1F24406187L,0xBA825079733568L,0x6E803F2E77E4C1L,0x3CCC5BA839AECL,0x555C0078L};
+ public static final long[] R2modp= {0x6A4A1FE013DF5BL,0xE8E46D4D1BDE65L,0x1F841391F45C67L,0x9148A4516FB28L,0x4398524EDF4C88L,0x41C0E241B6DCE8L,0xE42C208C19411L,0xA7FE6FD73A7B1CL,0xFCCCA76L};
+ public static final long MConst= 0xBD5D7D8095FE7DL;
+ public static final long[] Fra= {0x5CA74ABBF96F1DL,0x1FF8BD0C6FFBADL,0x49E9E26237469CL,0x3CECA48407F8E5L,0x69D68FF59267B7L,0x5D199E33127CBDL,0xB97549184F313AL,0x4E77242DA52D8DL,0x4BBC87B9L};
+ public static final long[] Frb= {0xE81A1C8E0CA60EL,0xDFEA2B20C0DF4AL,0x25327A5B7F5FA6L,0xF5343A828239A6L,0x76C78F2EADF9CFL,0x5D68B24660B8ABL,0xB50AF61628B387L,0xB555A18CDE6D5EL,0x99F78BEL};
+
+ public static final int CURVE_A= 0;
+ public static final int CURVE_B_I= 19;
+ public static final int CURVE_Cof_I= 0;
+ public static final long[] CURVE_B= {0x13L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Order= {0x1A08FFF0000001L,0x1E7033FF551190L,0x6ADE7EE322DDAFL,0x848FC9D0CED13AL,0x50D81729CC224L,0x1F0F05B98BB44AL,0x10010010005A0L,0x0L,0x0L};
+ public static final long[] CURVE_Gx= {0x6760F5EBE3CCD4L,0xEFE2DAED9F4564L,0x783F08EBA1FCC1L,0xC6F8D95AF88134L,0xDCA8D1AE2D8477L,0x9077586CEFE4BFL,0x8B7FEA5D99BC1DL,0x17CAF9486DE9E1L,0x1AB2BE34L};
+ public static final long[] CURVE_Gy= {0xCBA5CAD21E5245L,0x6D6608C55DF6C4L,0xB3ED294F39746BL,0x145824920FF3C8L,0x63AA4FD63E5A64L,0x492A2BF79CE00FL,0x66A7A4529FF79AL,0x6C53E477B861CAL,0x47FCB70CL};
+
+ public static final long[] CURVE_Bnx= {0x100020011FF80L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Cof= {0xC1FFBFF9F415ABL,0x5556AAB7FFL,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Cru= {0xBC27146DD794A9L,0x3A30938AF33A43L,0xB112175223DDC6L,0x125CFBB4236DFBL,0x2358E379CE607L,0xD680C6EB20806EL,0x314C200860FF77L,0x3CBC5A88268E4L,0x555C0078L};
+ public static final long[] CURVE_Pxaa= {0xE2935374E24678L,0xC34342582408BL,0xF765CCDEFC69EL,0xC33AAD2888D7F9L,0x7FD2458967473AL,0x52908ED55CBAB3L,0x786671EB14AB88L,0xA3EC96077958C8L,0x959DE53L};
+ public static final long[] CURVE_Pxab= {0x7F9EBAFFB099B8L,0x3775A012A47038L,0x6B5D1B1FC23856L,0x7F0A26A730F9E3L,0x1C38F85DB2A5CAL,0x76A753E17E6926L,0x2D39D1BE5AD0F9L,0x31733DFC651E4CL,0x3B0DED08L};
+ public static final long[] CURVE_Pxba= {0xA1CDE711AD15D3L,0x853178DF6E16EDL,0x64BF43EA3E09A1L,0x2D8CD6DE566B2FL,0xF21C26C74FDB8BL,0x47BCC89E3F6B1EL,0x3FE2103F329F00L,0x4E507AF2AA28C3L,0x3EC27FADL};
+ public static final long[] CURVE_Pxbb= {0x7AB2875EE0F480L,0x4556E43D6C4B8CL,0xFB22DF80E1CB99L,0xF70FD0122F1FFDL,0xD5DB25698EF5EAL,0x4805CE1AF1BA3AL,0x1DA7CE2E465CB7L,0xCA0799F7E65855L,0xA5B38DBL};
+ public static final long[] CURVE_Pyaa= {0x86499314781AA0L,0x609DA303B70AB1L,0xA52A6145FC44BBL,0x462E04C42A3124L,0xC383AE19AE68BBL,0xA1B34F6BE4FCADL,0x198F901AD0BF4L,0x736C094362CED0L,0x5057F35DL};
+ public static final long[] CURVE_Pyab= {0xBBEC57EEAE08FAL,0x78774BAA5F96ADL,0x64CAF099A42CA0L,0xC89FBBCCF70478L,0x6B720FEF855245L,0x97F916376F7B3EL,0x60F5587B5DF7E1L,0x61EE89637816BDL,0x2CE2B496L};
+ public static final long[] CURVE_Pyba= {0x730276A5F0CC41L,0xF89325530AA1F5L,0xD9CD879AF8A147L,0xEE53E8A9FE2880L,0x420F07D3715390L,0x4C15D519B71F3AL,0x1A39DD3CB5B9B1L,0x3EE631A6BE39F8L,0x18070466L};
+ public static final long[] CURVE_Pybb= {0xF1B2E6515C1CAEL,0xD40D355B0988DCL,0xC243FDC38A7772L,0x5D338136B675CAL,0x164E8A1D72FCDFL,0xBBAE5CD0961ACL,0xD6D04691771EB1L,0xD9BDEC8B792840L,0x499D14EAL};
+ public static final long[][] CURVE_W= {{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}};
+ public static final long[][][] CURVE_SB= {{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}}};
+ public static final long[][] CURVE_WB= {{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}};
+ public static final long[][][] CURVE_BB= {{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}}};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/BIG.java b/src/main/java/org/apache/milagro/amcl/BLS381/BIG.java
new file mode 100644
index 0000000..5a5644f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.BLS381;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=48; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=58;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/DBIG.java b/src/main/java/org/apache/milagro/amcl/BLS381/DBIG.java
new file mode 100644
index 0000000..fad920e
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.BLS381;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/ECDH.java b/src/main/java/org/apache/milagro/amcl/BLS381/ECDH.java
new file mode 100644
index 0000000..672ee85
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.BLS381;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/ECP.java b/src/main/java/org/apache/milagro/amcl/BLS381/ECP.java
new file mode 100644
index 0000000..a08bc11
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.BLS381;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=BLS;
+ public static final int SEXTIC_TWIST=M_TYPE;
+ public static final int SIGN_OF_X=NEGATIVEX;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/ECP2.java b/src/main/java/org/apache/milagro/amcl/BLS381/ECP2.java
new file mode 100644
index 0000000..1870e3a
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/ECP2.java
@@ -0,0 +1,796 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Weierstrass elliptic curve functions over FP2 */
+
+package org.apache.milagro.amcl.BLS381;
+
+public final class ECP2 {
+ private FP2 x;
+ private FP2 y;
+ private FP2 z;
+// private boolean INF;
+
+/* Constructor - set this=O */
+ public ECP2() {
+// INF=true;
+ x=new FP2(0);
+ y=new FP2(1);
+ z=new FP2(0);
+ }
+
+ public ECP2(ECP2 e) {
+ this.x = new FP2(e.x);
+ this.y = new FP2(e.y);
+ this.z = new FP2(e.z);
+ }
+
+/* Test this=O? */
+ public boolean is_infinity() {
+// if (INF) return true; //******
+ return (x.iszilch() && z.iszilch());
+ }
+/* copy this=P */
+ public void copy(ECP2 P)
+ {
+ x.copy(P.x);
+ y.copy(P.y);
+ z.copy(P.z);
+// INF=P.INF;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ y.one();
+ z.zero();
+ }
+
+/* Conditional move of Q to P dependant on d */
+ public void cmove(ECP2 Q,int d)
+ {
+ x.cmove(Q.x,d);
+ y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ }
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(ECP2 W[],int b)
+ {
+ ECP2 MP=new ECP2();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test if P == Q */
+ public boolean equals(ECP2 Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+
+ FP2 a=new FP2(x); // *****
+ FP2 b=new FP2(Q.x);
+ a.mul(Q.z);
+ b.mul(z);
+ if (!a.equals(b)) return false;
+
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+
+ return true;
+ }
+/* set this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ y.norm();
+ y.neg(); y.norm();
+ return;
+ }
+/* set to Affine - (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return;
+ FP2 one=new FP2(1);
+ if (z.equals(one))
+ {
+ x.reduce();
+ y.reduce();
+ return;
+ }
+ z.inverse();
+
+ x.mul(z); x.reduce(); // *****
+ y.mul(z); y.reduce();
+ z.copy(one);
+ }
+/* extract affine x as FP2 */
+ public FP2 getX()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.x;
+ }
+/* extract affine y as FP2 */
+ public FP2 getY()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.y;
+ }
+/* extract projective x */
+ public FP2 getx()
+ {
+ return x;
+ }
+/* extract projective y */
+ public FP2 gety()
+ {
+ return y;
+ }
+/* extract projective z */
+ public FP2 getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP2 W=new ECP2(this);
+ W.affine();
+ W.x.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i]=t[i];
+ W.x.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+BIG.MODBYTES]=t[i];
+
+ W.y.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+2*BIG.MODBYTES]=t[i];
+ W.y.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+3*BIG.MODBYTES]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP2 fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG ra;
+ BIG rb;
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 rx=new FP2(ra,rb);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+2*BIG.MODBYTES];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+3*BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 ry=new FP2(ra,rb);
+
+ return new ECP2(rx,ry);
+ }
+/* convert this to hex string */
+ public String toString() {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ return "("+W.x.toString()+","+W.y.toString()+")";
+ }
+
+/* Calculate RHS of twisted curve equation x^3+B/i */
+ public static FP2 RHS(FP2 x) {
+ x.norm();
+ FP2 r=new FP2(x);
+ r.sqr();
+ FP2 b=new FP2(new BIG(ROM.CURVE_B));
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b.div_ip();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b.norm();
+ b.mul_ip();
+ b.norm();
+ }
+
+
+ r.mul(x);
+ r.add(b);
+
+ r.reduce();
+ return r;
+ }
+
+/* construct this from (x,y) - but set to O if not on curve */
+ public ECP2(FP2 ix,FP2 iy) {
+ x=new FP2(ix);
+ y=new FP2(iy);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ FP2 y2=new FP2(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+// if (y2.equals(rhs)) INF=false;
+// else {x.zero();INF=true;}
+ }
+
+/* construct this from x - but set to O if not on curve */
+ public ECP2(FP2 ix) {
+ x=new FP2(ix);
+ y=new FP2(1);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ if (rhs.sqrt())
+ {
+ y.copy(rhs);
+ //INF=false;
+ }
+ else {/*x.zero();INF=true;*/ inf();}
+ }
+
+/* this+=this */
+ public int dbl() {
+// if (INF) return -1;
+//System.out.println("Into dbl");
+ FP2 iy=new FP2(y);
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ iy.mul_ip(); iy.norm();
+ }
+ FP2 t0=new FP2(y); //***** Change
+ t0.sqr();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip();
+ }
+ FP2 t1=new FP2(iy);
+ t1.mul(z);
+ FP2 t2=new FP2(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z);
+ z.add(z);
+ z.norm();
+
+ t2.imul(3*ROM.CURVE_B_I);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip();
+ t2.norm();
+ }
+
+ FP2 x3=new FP2(t2);
+ x3.mul(z);
+
+ FP2 y3=new FP2(t0);
+
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1); t2.norm();
+ t0.sub(t2); t0.norm(); //y^2-9bz^2
+ y3.mul(t0); y3.add(x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+ t1.copy(x); t1.mul(iy); //
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x); //(y^2-9bz^2)xy2
+
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ return 1;
+ }
+
+/* this+=Q - return 0 for add, 1 for double, -1 for O */
+ public int add(ECP2 Q) {
+// if (INF)
+// {
+// copy(Q);
+// return -1;
+// }
+// if (Q.INF) return -1;
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP2 t0=new FP2(x);
+ t0.mul(Q.x); // x.Q.x
+ FP2 t1=new FP2(y);
+ t1.mul(Q.y); // y.Q.y
+
+ FP2 t2=new FP2(z);
+ t2.mul(Q.z);
+ FP2 t3=new FP2(x);
+ t3.add(y); t3.norm(); //t3=X1+Y1
+ FP2 t4=new FP2(Q.x);
+ t4.add(Q.y); t4.norm(); //t4=X2+Y2
+ t3.mul(t4); //t3=(X1+Y1)(X2+Y2)
+ t4.copy(t0); t4.add(t1); //t4=X1.X2+Y1.Y2
+
+ t3.sub(t4); t3.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t3.mul_ip(); t3.norm(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+ }
+ t4.copy(y);
+ t4.add(z); t4.norm(); //t4=Y1+Z1
+ FP2 x3=new FP2(Q.y);
+ x3.add(Q.z); x3.norm(); //x3=Y2+Z2
+
+ t4.mul(x3); //t4=(Y1+Z1)(Y2+Z2)
+ x3.copy(t1); //
+ x3.add(t2); //X3=Y1.Y2+Z1.Z2
+
+ t4.sub(x3); t4.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t4.mul_ip(); t4.norm(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+ }
+ x3.copy(x); x3.add(z); x3.norm(); // x3=X1+Z1
+ FP2 y3=new FP2(Q.x);
+ y3.add(Q.z); y3.norm(); // y3=X2+Z2
+ x3.mul(y3); // x3=(X1+Z1)(X2+Z2)
+ y3.copy(t0);
+ y3.add(t2); // y3=X1.X2+Z1+Z2
+ y3.rsub(x3); y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip(); t0.norm(); // x.Q.x
+ t1.mul_ip(); t1.norm(); // y.Q.y
+ }
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip(); t2.norm();
+ }
+ FP2 z3=new FP2(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ y3.mul_ip();
+ y3.norm();
+ }
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ return 0;
+ }
+
+/* set this-=Q */
+ public int sub(ECP2 Q) {
+ ECP2 NQ=new ECP2(Q);
+ NQ.neg();
+ int D=add(NQ);
+ //Q.neg();
+ //int D=add(Q);
+ //Q.neg();
+ return D;
+ }
+/* set this*=q, where q is Modulus, using Frobenius */
+ public void frob(FP2 X)
+ {
+// if (INF) return;
+ FP2 X2=new FP2(X);
+
+ X2.sqr();
+ x.conj();
+ y.conj();
+ z.conj();
+ z.reduce();
+ x.mul(X2);
+
+ y.mul(X2);
+ y.mul(X);
+ }
+
+/* P*=e */
+ public ECP2 mul(BIG e)
+ {
+/* fixed size windows */
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP2 P=new ECP2();
+ ECP2 Q=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2[] W=new ECP2[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ if (is_infinity()) return new ECP2();
+
+ //affine();
+
+/* precompute table */
+ Q.copy(this);
+ Q.dbl();
+ W[0]=new ECP2();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP2();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+/* make exponent odd - add 2P if even, P if odd */
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+/* convert exponent to signed 4-bit window */
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C);
+ P.affine();
+ return P;
+ }
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ ECP2 W=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] T=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ //Q[i].affine();
+ }
+
+ T[0] = new ECP2(); T[0].copy(Q[0]); // Q[0]
+ T[1] = new ECP2(); T[1].copy(T[0]); T[1].add(Q[1]); // Q[0]+Q[1]
+ T[2] = new ECP2(); T[2].copy(T[0]); T[2].add(Q[2]); // Q[0]+Q[2]
+ T[3] = new ECP2(); T[3].copy(T[1]); T[3].add(Q[2]); // Q[0]+Q[1]+Q[2]
+ T[4] = new ECP2(); T[4].copy(T[0]); T[4].add(Q[3]); // Q[0]+Q[3]
+ T[5] = new ECP2(); T[5].copy(T[1]); T[5].add(Q[3]); // Q[0]+Q[1]+Q[3]
+ T[6] = new ECP2(); T[6].copy(T[2]); T[6].add(Q[3]); // Q[0]+Q[2]+Q[3]
+ T[7] = new ECP2(); T[7].copy(T[3]); T[7].add(Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ P.select(T,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ P.dbl();
+ W.select(T,(int)(2*w[i]+s[i]));
+ P.add(W);
+ }
+
+ // apply correction
+ W.copy(P);
+ W.sub(Q[0]);
+ P.cmove(W,pb);
+ P.affine();
+ return P;
+ }
+
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+/*
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb;
+ int[] a=new int[4];
+ ECP2 T=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] W=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ Q[i].affine();
+ }
+
+// precompute table
+
+ W[0]=new ECP2(); W[0].copy(Q[0]); W[0].sub(Q[1]);
+
+ W[1]=new ECP2(); W[1].copy(W[0]);
+ W[2]=new ECP2(); W[2].copy(W[0]);
+ W[3]=new ECP2(); W[3].copy(W[0]);
+ W[4]=new ECP2(); W[4].copy(Q[0]); W[4].add(Q[1]);
+ W[5]=new ECP2(); W[5].copy(W[4]);
+ W[6]=new ECP2(); W[6].copy(W[4]);
+ W[7]=new ECP2(); W[7].copy(W[4]);
+ T.copy(Q[2]); T.sub(Q[3]);
+ W[1].sub(T);
+ W[2].add(T);
+ W[5].sub(T);
+ W[6].add(T);
+ T.copy(Q[2]); T.add(Q[3]);
+ W[0].sub(T);
+ W[3].add(T);
+ W[4].sub(T);
+ W[7].add(T);
+
+// if multiplier is even add 1 to multiplier, and add P to correction
+ mt.zero(); C.inf();
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ C.add(Q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(byte)(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ P.dbl();
+ P.add(T);
+ }
+ P.sub(C); // apply correction
+
+ P.affine();
+ return P;
+ }
+*/
+
+/* needed for SOK */
+ public static ECP2 mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ BIG one=new BIG(1);
+ FP2 X;
+ ECP2 Q;
+ x.mod(q);
+ while (true)
+ {
+ X=new FP2(one,x);
+ Q=new ECP2(X);
+ if (!Q.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+
+ BIG Fra=new BIG(ROM.Fra);
+ BIG Frb=new BIG(ROM.Frb);
+ X=new FP2(Fra,Frb);
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X.inverse();
+ X.norm();
+ }
+
+ x=new BIG(ROM.CURVE_Bnx);
+
+/* Fast Hashing to G2 - Fuentes-Castaneda, Knapp and Rodriguez-Henriquez */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ ECP2 T,K;
+
+ T=new ECP2(); T.copy(Q);
+ T=T.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ T.neg();
+ }
+ K=new ECP2(); K.copy(T);
+ K.dbl(); K.add(T); //K.affine();
+
+ K.frob(X);
+ Q.frob(X); Q.frob(X); Q.frob(X);
+ Q.add(T); Q.add(K);
+ T.frob(X); T.frob(X);
+ Q.add(T);
+
+ }
+
+/* Efficient hash maps to G2 on BLS curves - Budroni, Pintore */
+/* Q -> x2Q -xQ -Q +F(xQ -Q) +F(F(2Q)) */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BLS)
+ {
+ // ECP2 xQ,x2Q;
+ // xQ=new ECP2();
+ // x2Q=new ECP2();
+
+ ECP2 xQ=Q.mul(x);
+ ECP2 x2Q=xQ.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ xQ.neg();
+ }
+
+ x2Q.sub(xQ);
+ x2Q.sub(Q);
+
+ xQ.sub(Q);
+ xQ.frob(X);
+
+ Q.dbl();
+ Q.frob(X);
+ Q.frob(X);
+
+ Q.add(x2Q);
+ Q.add(xQ);
+ }
+ Q.affine();
+ return Q;
+ }
+
+ public static ECP2 generator()
+ {
+ return new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+ }
+
+/*
+ public static void main(String[] args) {
+ BIG r=new BIG(ROM.Modulus);
+
+ BIG Pxa=new BIG(ROM.CURVE_Pxa);
+ BIG Pxb=new BIG(ROM.CURVE_Pxb);
+ BIG Pya=new BIG(ROM.CURVE_Pya);
+ BIG Pyb=new BIG(ROM.CURVE_Pyb);
+
+ BIG Fra=new BIG(ROM.CURVE_Fra);
+ BIG Frb=new BIG(ROM.CURVE_Frb);
+
+ FP2 f=new FP2(Fra,Frb);
+
+ FP2 Px=new FP2(Pxa,Pxb);
+ FP2 Py=new FP2(Pya,Pyb);
+
+ ECP2 P=new ECP2(Px,Py);
+
+ System.out.println("P= "+P.toString());
+
+ P=P.mul(r);
+ System.out.println("P= "+P.toString());
+
+ ECP2 Q=new ECP2(Px,Py);
+ Q.frob(f);
+ System.out.println("Q= "+Q.toString());
+ } */
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/FP.java b/src/main/java/org/apache/milagro/amcl/BLS381/FP.java
new file mode 100644
index 0000000..f3ae560
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.BLS381;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=381; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<25); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/FP12.java b/src/main/java/org/apache/milagro/amcl/BLS381/FP12.java
new file mode 100644
index 0000000..f1ffef9
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/FP12.java
@@ -0,0 +1,907 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Fp^12 functions */
+/* FP12 elements are of the form a+i.b+i^2.c */
+
+package org.apache.milagro.amcl.BLS381;
+
+public final class FP12 {
+ private final FP4 a;
+ private final FP4 b;
+ private final FP4 c;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ c.reduce();
+ }
+/* normalise all components of this */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ c.norm();
+ }
+/* test x==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch() && c.iszilch());
+ }
+
+ public void cmove(FP12 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ c.cmove(g.c,d);
+ }
+
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(FP12 g[],int b)
+ {
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(g[0],teq(babs,0)); // conditional move
+ cmove(g[1],teq(babs,1));
+ cmove(g[2],teq(babs,2));
+ cmove(g[3],teq(babs,3));
+ cmove(g[4],teq(babs,4));
+ cmove(g[5],teq(babs,5));
+ cmove(g[6],teq(babs,6));
+ cmove(g[7],teq(babs,7));
+
+ FP12 invf=new FP12(this);
+ invf.conj();
+ cmove(invf,(int)(m&1));
+ }
+
+
+/* test x==1 ? */
+ public boolean isunity() {
+ FP4 one=new FP4(1);
+ return (a.equals(one) && b.iszilch() && c.iszilch());
+ }
+/* return 1 if x==y, else 0 */
+ public boolean equals(FP12 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b) && c.equals(x.c));
+ }
+/* extract a from this */
+ public FP4 geta()
+ {
+ return a;
+ }
+/* extract b */
+ public FP4 getb()
+ {
+ return b;
+ }
+/* extract c */
+ public FP4 getc()
+ {
+ return c;
+ }
+/* copy this=x */
+ public void copy(FP12 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ c.copy(x.c);
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ c.zero();
+ }
+/* this=conj(this) */
+ public void conj()
+ {
+ a.conj();
+ b.nconj();
+ c.conj();
+ }
+/* Constructors */
+ public FP12(FP4 d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(int d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(FP4 d,FP4 e,FP4 f)
+ {
+ a=new FP4(d);
+ b=new FP4(e);
+ c=new FP4(f);
+ }
+
+ public FP12(FP12 x)
+ {
+ a=new FP4(x.a);
+ b=new FP4(x.b);
+ c=new FP4(x.c);
+ }
+
+/* Granger-Scott Unitary Squaring */
+ public void usqr()
+ {
+//System.out.println("Into usqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(c);
+ FP4 C=new FP4(b);
+ FP4 D=new FP4(0);
+
+ a.sqr();
+ D.copy(a); D.add(a);
+ a.add(D);
+
+ a.norm();
+ A.nconj();
+
+ A.add(A);
+ a.add(A);
+ B.sqr();
+ B.times_i();
+
+ D.copy(B); D.add(B);
+ B.add(D);
+ B.norm();
+
+ C.sqr();
+ D.copy(C); D.add(C);
+ C.add(D);
+ C.norm();
+
+ b.conj();
+ b.add(b);
+ c.nconj();
+
+ c.add(c);
+ b.add(B);
+ c.add(C);
+//System.out.println("Out of usqr 1");
+ reduce();
+//System.out.println("Out of usqr 2");
+ }
+
+/* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+ public void sqr()
+ {
+//System.out.println("Into sqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(b);
+ FP4 C=new FP4(c);
+ FP4 D=new FP4(a);
+
+ A.sqr();
+ B.mul(c);
+ B.add(B);
+ B.norm();
+ C.sqr();
+ D.mul(b);
+ D.add(D);
+
+ c.add(a);
+ c.add(b);
+ c.norm();
+ c.sqr();
+
+ a.copy(A);
+
+ A.add(B);
+ A.norm();
+ A.add(C);
+ A.add(D);
+ A.norm();
+
+ A.neg();
+ B.times_i();
+ C.times_i();
+
+ a.add(B);
+
+ b.copy(C); b.add(D);
+ c.add(A);
+//System.out.println("Out of sqr");
+ norm();
+ }
+
+/* FP12 full multiplication this=this*y */
+ public void mul(FP12 y)
+ {
+//System.out.println("Into mul");
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(y.a);
+
+ z0.mul(y.a);
+ z2.mul(y.b);
+
+ t0.add(b);
+ t1.add(y.b);
+
+ t0.norm();
+ t1.norm();
+
+ z1.copy(t0); z1.mul(t1);
+ t0.copy(b); t0.add(c);
+
+ t1.copy(y.b); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ z3.copy(t0); z3.mul(t1);
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ z1.add(t0);
+ //z1.norm();
+ b.copy(z1); b.add(t1);
+
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+t0.norm();
+t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c); t0.mul(y.c);
+ t1.copy(t0); t1.neg();
+
+// z2.norm();
+// z3.norm();
+// b.norm();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ norm();
+//System.out.println("Out of mul");
+ }
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+ public void smul(FP12 y,int type)
+ {
+//System.out.println("Into smul");
+
+ if (type==ECP.D_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(b);
+ FP4 t0=new FP4(0);
+ FP4 t1=new FP4(y.a);
+ z0.mul(y.a);
+ z2.pmul(y.b.real());
+ b.add(a);
+ t1.real().add(y.b.real());
+
+ t1.norm();
+ b.norm();
+ b.mul(t1);
+ z3.add(c);
+ z3.norm();
+ z3.pmul(y.b.real());
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ b.add(t0);
+
+ b.add(t1);
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t0.norm();
+ z3.norm();
+ t0.mul(y.a);
+ c.copy(z2); c.add(t0);
+
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ if (type==ECP.M_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(0);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(0);
+
+ z0.mul(y.a);
+ t0.add(b);
+ t0.norm();
+
+ z1.copy(t0); z1.mul(y.a);
+ t0.copy(b); t0.add(c);
+ t0.norm();
+
+ z3.copy(t0); //z3.mul(y.c);
+ z3.pmul(y.c.getb());
+ z3.times_i();
+
+ t0.copy(z0); t0.neg();
+
+ z1.add(t0);
+ b.copy(z1);
+ z2.copy(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c);
+
+ t0.pmul(y.c.getb());
+ t0.times_i();
+
+ t1.copy(t0); t1.neg();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ norm();
+//System.out.println("Out of smul");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ FP4 f0=new FP4(a);
+ FP4 f1=new FP4(b);
+ FP4 f2=new FP4(a);
+ FP4 f3=new FP4(0);
+
+ norm();
+ f0.sqr();
+ f1.mul(c);
+ f1.times_i();
+ f0.sub(f1);
+ f0.norm();
+
+ f1.copy(c); f1.sqr();
+ f1.times_i();
+ f2.mul(b);
+ f1.sub(f2);
+ f1.norm();
+
+ f2.copy(b); f2.sqr();
+ f3.copy(a); f3.mul(c);
+ f2.sub(f3);
+ f2.norm();
+
+ f3.copy(b); f3.mul(f2);
+ f3.times_i();
+ a.mul(f0);
+ f3.add(a);
+ c.mul(f1);
+ c.times_i();
+
+ f3.add(c);
+ f3.norm();
+ f3.inverse();
+ a.copy(f0); a.mul(f3);
+ b.copy(f1); b.mul(f3);
+ c.copy(f2); c.mul(f3);
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ FP2 f2=new FP2(f);
+ FP2 f3=new FP2(f);
+
+ f2.sqr();
+ f3.mul(f2);
+
+ a.frob(f3);
+ b.frob(f3);
+ c.frob(f3);
+
+ b.pmul(f);
+ c.pmul(f2);
+ }
+
+/* trace function */
+ public FP4 trace()
+ {
+ FP4 t=new FP4(0);
+ t.copy(a);
+ t.imul(3);
+ t.reduce();
+ return t;
+ }
+
+/* convert from byte array to FP12 */
+ public static FP12 fromBytes(byte[] w)
+ {
+ BIG a,b;
+ FP2 c,d;
+ FP4 e,f,g;
+ byte[] t=new byte[BIG.MODBYTES];
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+2*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+3*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ e=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+4*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+5*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+6*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+7*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ f=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+8*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+9*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+10*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+11*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ g=new FP4(c,d);
+
+ return new FP12(e,f,g);
+ }
+
+/* convert this to byte array */
+ public void toBytes(byte[] w)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ a.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i]=t[i];
+ a.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+BIG.MODBYTES]=t[i];
+ a.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+2*BIG.MODBYTES]=t[i];
+ a.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+3*BIG.MODBYTES]=t[i];
+
+ b.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+4*BIG.MODBYTES]=t[i];
+ b.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+5*BIG.MODBYTES]=t[i];
+ b.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+6*BIG.MODBYTES]=t[i];
+ b.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+7*BIG.MODBYTES]=t[i];
+
+ c.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+8*BIG.MODBYTES]=t[i];
+ c.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+9*BIG.MODBYTES]=t[i];
+ c.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+10*BIG.MODBYTES]=t[i];
+ c.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+11*BIG.MODBYTES]=t[i];
+ }
+
+/* convert to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+","+c.toString()+"]");
+ }
+
+/* this=this^e */
+/* Note this is simple square and multiply, so not side-channel safe */
+ public FP12 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ BIG e3=new BIG(e);
+ e3.pmul(3);
+ e3.norm();
+
+ FP12 w=new FP12(this);
+
+ int nb=e3.nbits();
+ for (int i=nb-2;i>=1;i--)
+ {
+ w.usqr();
+ int bt=e3.bit(i)-e.bit(i);
+ if (bt==1)
+ w.mul(this);
+ if (bt==-1)
+ {
+ conj(); w.mul(this); conj();
+ }
+ }
+ w.reduce();
+ return w;
+
+
+/*
+ BIG z=new BIG(e);
+ FP12 r=new FP12(1);
+
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.usqr();
+ }
+ r.reduce();
+ return r; */
+ }
+
+/* constant time powering by small integer of max length bts */
+ public void pinpow(int e,int bts)
+ {
+ int i,b;
+ FP12 [] R=new FP12[2];
+ R[0]=new FP12(1);
+ R[1]=new FP12(this);
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ R[1-b].mul(R[b]);
+ R[b].usqr();
+ }
+ this.copy(R[0]);
+ }
+
+ public FP4 compow(BIG e,BIG r)
+ {
+ FP12 g1=new FP12(0);
+ FP12 g2=new FP12(0);
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.Modulus);
+
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(e);
+ a.mod(m);
+
+ BIG b=new BIG(e);
+ b.div(m);
+
+ g1.copy(this);
+ g2.copy(this);
+
+ FP4 c=g1.trace();
+
+ if (b.iszilch())
+ {
+ c=c.xtr_pow(e);
+ return c;
+ }
+
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+
+ return c;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ FP12 [] g=new FP12[8];
+ FP12 r=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ }
+ g[0]=new FP12(q[0]); // q[0]
+ g[1]=new FP12(g[0]); g[1].mul(q[1]); // q[0].q[1]
+ g[2]=new FP12(g[0]); g[2].mul(q[2]); // q[0].q[2]
+ g[3]=new FP12(g[1]); g[3].mul(q[2]); // q[0].q[1].q[2]
+ g[4]=new FP12(q[0]); g[4].mul(q[3]); // q[0].q[3]
+ g[5]=new FP12(g[1]); g[5].mul(q[3]); // q[0].q[1].q[3]
+ g[6]=new FP12(g[2]); g[6].mul(q[3]); // q[0].q[2].q[3]
+ g[7]=new FP12(g[3]); g[7].mul(q[3]); // q[0].q[1].q[2].q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ p.select(g,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ p.usqr();
+ r.select(g,(int)(2*w[i]+s[i]));
+ p.mul(r);
+ }
+
+ // apply correction
+ r.copy(q[0]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb);
+
+ p.reduce();
+ return p;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+/* Timing attack secure, but not cache attack secure */
+/*
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,m;
+ int[] a=new int[4];
+ FP12 [] g=new FP12[8];
+ FP12 [] s=new FP12[2];
+ FP12 c=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ t[i]=new BIG(u[i]);
+
+ s[0]=new FP12(0);
+ s[1]=new FP12(0);
+
+ g[0]=new FP12(q[0]); s[0].copy(q[1]); s[0].conj(); g[0].mul(s[0]);
+ g[1]=new FP12(g[0]);
+ g[2]=new FP12(g[0]);
+ g[3]=new FP12(g[0]);
+ g[4]=new FP12(q[0]); g[4].mul(q[1]);
+ g[5]=new FP12(g[4]);
+ g[6]=new FP12(g[4]);
+ g[7]=new FP12(g[4]);
+
+ s[1].copy(q[2]); s[0].copy(q[3]); s[0].conj(); s[1].mul(s[0]);
+ s[0].copy(s[1]); s[0].conj(); g[1].mul(s[0]);
+ g[2].mul(s[1]);
+ g[5].mul(s[0]);
+ g[6].mul(s[1]);
+ s[1].copy(q[2]); s[1].mul(q[3]);
+ s[0].copy(s[1]); s[0].conj(); g[0].mul(s[0]);
+ g[3].mul(s[1]);
+ g[4].mul(s[0]);
+ g[7].mul(s[1]);
+
+// if power is even add 1 to power, and add q to correction
+
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ c.mul(q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+ c.conj();
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+ p.copy(g[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ m=w[i]>>7;
+ j=(w[i]^m)-m; // j=abs(w[i])
+ j=(j-1)/2;
+ s[0].copy(g[j]); s[1].copy(g[j]); s[1].conj();
+ p.usqr();
+ p.mul(s[m&1]);
+ }
+ p.mul(c); // apply correction
+ p.reduce();
+ return p;
+ }
+*/
+/*
+ public static void main(String[] args) {
+ BIG p=new BIG(ROM.Modulus);
+ FP2 w0,w1;
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.zero(); b.zero(); a.inc(1); b.inc(2);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(3); b.inc(4);
+ w1=new FP2(a,b);
+ FP4 t0=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(5); b.inc(6);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(7); b.inc(8);
+ w1=new FP2(a,b);
+ FP4 t1=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(9); b.inc(10);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(11); b.inc(12);
+ w1=new FP2(a,b);
+ FP4 t2=new FP4(w0,w1);
+
+ FP12 w=new FP12(t0,t1,t2);
+ FP12 t=new FP12(w);
+
+ System.out.println("w= "+w.toString());
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ w.frob(f);
+ System.out.println("w= "+w.toString());
+
+ w=t.pow(p);
+
+ System.out.println("w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("w= "+w.toString());
+
+ t.copy(w);
+ w.conj();
+ t.inverse();
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)= "+w.toString());
+
+ t.copy(w);
+ w.frob(f);
+ w.frob(f);
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)(p^2+1)= "+w.toString());
+
+ t.copy(w);
+
+ t.inverse();
+ w.conj();
+
+ System.out.println("w= "+w.toString());
+ System.out.println("t= "+t.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/FP2.java b/src/main/java/org/apache/milagro/amcl/BLS381/FP2.java
new file mode 100644
index 0000000..e77fc00
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/FP2.java
@@ -0,0 +1,425 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^2 functions */
+
+/* FP2 elements are of the form a+ib, where i is sqrt(-1) */
+
+package org.apache.milagro.amcl.BLS381;
+
+public final class FP2 {
+ private final FP a;
+ private final FP b;
+
+/* reduce components mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+
+/* normalise components of w */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+
+/* test this=0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP2 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this=1 ? */
+ public boolean isunity() {
+ FP one=new FP(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test this=x */
+ public boolean equals(FP2 x) {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+
+/* Constructors */
+ public FP2(int c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(FP2 x)
+ {
+ a=new FP(x.a);
+ b=new FP(x.b);
+ }
+
+ public FP2(FP c,FP d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(BIG c,BIG d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(FP c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(BIG c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+/*
+ public BIG geta()
+ {
+ return a.tobig();
+ }
+*/
+/* extract a */
+ public BIG getA()
+ {
+ return a.redc();
+ }
+
+/* extract b */
+ public BIG getB()
+ {
+ return b.redc();
+ }
+
+/* copy this=x */
+ public void copy(FP2 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+
+/* negate this mod Modulus */
+ public void neg()
+ {
+ FP m=new FP(a);
+ FP t=new FP(0);
+
+ m.add(b);
+ m.neg();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ }
+
+/* set to a-ib */
+ public void conj()
+ {
+ b.neg();
+ b.norm();
+ }
+
+/* this+=a */
+ public void add(FP2 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+
+/* this-=a */
+ public void sub(FP2 x)
+ {
+ FP2 m=new FP2(x);
+ m.neg();
+ add(m);
+ }
+
+ public void rsub(FP2 x) // *****
+ {
+ neg();
+ add(x);
+ }
+
+/* this*=s, where s is an FP */
+ public void pmul(FP s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this*=i, where i is an int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+ FP w1=new FP(a);
+ FP w3=new FP(a);
+ FP mb=new FP(b);
+
+ w1.add(b);
+ mb.neg();
+
+ w3.add(a);
+ w3.norm();
+ b.mul(w3);
+
+ a.add(mb);
+
+ w1.norm();
+ a.norm();
+
+ a.mul(w1);
+ }
+
+/* this*=y */
+/* Now uses Lazy reduction */
+ public void mul(FP2 y)
+ {
+ if ((long)(a.XES+b.XES)*(y.a.XES+y.b.XES)>(long)FP.FEXCESS)
+ {
+ if (a.XES>1) a.reduce();
+ if (b.XES>1) b.reduce();
+ }
+
+ DBIG pR=new DBIG(0);
+ BIG C=new BIG(a.x);
+ BIG D=new BIG(y.a.x);
+
+ pR.ucopy(new BIG(ROM.Modulus));
+
+ DBIG A=BIG.mul(a.x,y.a.x);
+ DBIG B=BIG.mul(b.x,y.b.x);
+
+ C.add(b.x); C.norm();
+ D.add(y.b.x); D.norm();
+
+ DBIG E=BIG.mul(C,D);
+ DBIG F=new DBIG(A); F.add(B);
+ B.rsub(pR);
+
+ A.add(B); A.norm();
+ E.sub(F); E.norm();
+
+ a.x.copy(FP.mod(A)); a.XES=3;
+ b.x.copy(FP.mod(E)); b.XES=2;
+ }
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP w1=new FP(b);
+ FP w2=new FP(a);
+ w1.sqr(); w2.sqr(); w1.add(w2);
+ if (w1.jacobi()!=1) { zero(); return false; }
+ w1=w1.sqrt();
+ w2.copy(a); w2.add(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1)
+ {
+ w2.copy(a); w2.sub(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1) { zero(); return false; }
+ }
+ w2=w2.sqrt();
+ a.copy(w2);
+ w2.add(w2);
+ w2.inverse();
+ b.mul(w2);
+ return true;
+ }
+
+/* output to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ norm();
+ FP w1=new FP(a);
+ FP w2=new FP(b);
+
+ w1.sqr();
+ w2.sqr();
+ w1.add(w2);
+ w1.inverse();
+ a.mul(w1);
+ w1.neg();
+ w1.norm();
+ b.mul(w1);
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+/* this*=sqrt(-1) */
+ public void times_i()
+ {
+ FP z=new FP(a);
+ a.copy(b); a.neg();
+ b.copy(z);
+ }
+
+/* w*=(1+sqrt(-1)) */
+/* where X*2-(1+sqrt(-1)) is irreducible for FP4, assumes p=3 mod 8 */
+ public void mul_ip()
+ {
+ FP2 t=new FP2(this);
+ FP z=new FP(a);
+ a.copy(b);
+ a.neg();
+ b.copy(z);
+ add(t);
+ }
+
+ public void div_ip2()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ }
+
+/* w/=(1+sqrt(-1)) */
+ public void div_ip()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ div2();
+ }
+/*
+ public FP2 pow(BIG e)
+ {
+ int bt;
+ FP2 r=new FP2(1);
+ e.norm();
+ norm();
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(this);
+ if (e.iszilch()) break;
+ sqr();
+ }
+
+ r.reduce();
+ return r;
+ }
+
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(27);
+ BIG pp1=new BIG(m);
+ BIG pm1=new BIG(m);
+ BIG a=new BIG(1);
+ BIG b=new BIG(1);
+ FP2 w=new FP2(a,b);
+ FP2 z=new FP2(w);
+
+ byte[] RAW=new byte[100];
+
+ RAND rng=new RAND();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ // for (int i=0;i<100;i++)
+ // {
+ a.randomnum(rng);
+ b.randomnum(rng);
+
+ w=new FP2(a,b);
+ System.out.println("w="+w.toString());
+
+ z=new FP2(w);
+ z.inverse();
+ System.out.println("z="+z.toString());
+
+ z.inverse();
+ if (!z.equals(w)) System.out.println("Error");
+ // }
+
+// System.out.println("m="+m.toString());
+// w.sqr();
+// w.mul(z);
+
+ System.out.println("w="+w.toString());
+
+
+ pp1.inc(1); pp1.norm();
+ pm1.dec(1); pm1.norm();
+ System.out.println("p+1="+pp1.toString());
+ System.out.println("p-1="+pm1.toString());
+ w=w.pow(pp1);
+ w=w.pow(pm1);
+ System.out.println("w="+w.toString());
+ }
+*/
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/FP4.java b/src/main/java/org/apache/milagro/amcl/BLS381/FP4.java
new file mode 100644
index 0000000..47c4bf0
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/FP4.java
@@ -0,0 +1,721 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^4 functions */
+
+/* FP4 elements are of the form a+ib, where i is sqrt(-1+sqrt(-1)) */
+
+package org.apache.milagro.amcl.BLS381;
+
+public final class FP4 {
+ private final FP2 a;
+ private final FP2 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP4 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP2 one=new FP2(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP2 real()
+ {
+ return a;
+ }
+
+ public FP2 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP2 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP4 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP4(int c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+
+ public FP4(FP4 x)
+ {
+ a=new FP2(x.a);
+ b=new FP2(x.b);
+ }
+
+ public FP4(FP2 c,FP2 d)
+ {
+ a=new FP2(c);
+ b=new FP2(d);
+ }
+
+ public FP4(FP2 c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+/* copy this=x */
+ public void copy(FP4 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP2 m=new FP2(a);
+ FP2 t=new FP2(0);
+ m.add(b);
+// m.norm();
+ m.neg();
+ // m.norm();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP4 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP4 x)
+ {
+ FP4 m=new FP4(x);
+ m.neg();
+ add(m);
+ }
+
+/* this*=s where s is FP2 */
+ public void pmul(FP2 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this=x-this */
+ public void rsub(FP4 x)
+ {
+ neg();
+ add(x);
+ }
+
+
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.mul_ip();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.mul_ip();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+/* this*=y */
+ public void mul(FP4 y)
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(0);
+ FP2 t4=new FP2(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ // t4.sub(t1);
+ // t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ // b.copy(t4);
+ // b.sub(t2);
+
+ t2.mul_ip();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.mul_ip();
+ t2.norm();
+ t1.sub(t2);
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP2 s=new FP2(b);
+ FP2 t=new FP2(b);
+ s.times_i();
+ t.add(s);
+ // t.norm();
+ b.copy(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ a.conj();
+ b.conj();
+ b.mul(f);
+ }
+
+/* this=this^e */
+ public FP4 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP4 w=new FP4(this);
+ BIG z=new BIG(e);
+ FP4 r=new FP4(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+/* XTR xtr_a function */
+ public void xtr_A(FP4 w,FP4 y,FP4 z)
+ {
+ FP4 r=new FP4(w);
+ FP4 t=new FP4(w);
+ //y.norm();
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP4 w=new FP4(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP4 xtr_pow(BIG n) {
+ FP4 a=new FP4(3);
+ FP4 b=new FP4(this);
+ FP4 c=new FP4(b);
+ c.xtr_D();
+ FP4 t=new FP4(0);
+ FP4 r=new FP4(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP4 xtr_pow2(FP4 ck,FP4 ckml,FP4 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP4 cu=new FP4(ck); // can probably be passed in w/o copying
+ FP4 cv=new FP4(this);
+ FP4 cumv=new FP4(ckml);
+ FP4 cum2v=new FP4(ckm2l);
+ FP4 r=new FP4(0);
+ FP4 t=new FP4(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+ public void div_i()
+ {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip();
+ a.copy(v);
+ b.copy(u);
+ }
+
+ public void div_2i() {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip2();
+ v.add(v); v.norm();
+ a.copy(v);
+ b.copy(u);
+ }
+
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP2 wa=new FP2(a);
+ FP2 ws=new FP2(b);
+ FP2 wt=new FP2(a);
+
+ if (ws.iszilch())
+ {
+ if (wt.sqrt())
+ {
+ a.copy(wt);
+ b.zero();
+ } else {
+ wt.div_ip();
+ wt.sqrt();
+ b.copy(wt);
+ a.zero();
+ }
+ return true;
+ }
+
+ ws.sqr();
+ wa.sqr();
+ ws.mul_ip();
+ ws.norm();
+ wa.sub(ws);
+
+ ws.copy(wa);
+ if (!ws.sqrt()) {
+ return false;
+ }
+
+ wa.copy(wt); wa.add(ws); wa.norm(); wa.div2();
+
+ if (!wa.sqrt()) {
+ wa.copy(wt); wa.sub(ws); wa.norm(); wa.div2();
+ if (!wa.sqrt()) {
+ return false;
+ }
+ }
+ wt.copy(b);
+ ws.copy(wa); ws.add(wa);
+ ws.inverse();
+
+ wt.mul(ws);
+ a.copy(wa);
+ b.copy(wt);
+
+ return true;
+ }
+
+/* this*=s where s is FP */
+ public void qmul(FP s)
+ {
+ a.pmul(s);
+ b.pmul(s);
+ }
+
+
+
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG e=new BIG(12);
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.inc(27); b.inc(45);
+
+ FP2 w0=new FP2(a,b);
+
+ a.zero(); b.zero();
+ a.inc(33); b.inc(54);
+
+ FP2 w1=new FP2(a,b);
+
+
+ FP4 w=new FP4(w0,w1);
+ FP4 t=new FP4(w);
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ System.out.println("w= "+w.toString());
+
+ w=w.pow(m);
+
+ System.out.println("w^p= "+w.toString());
+
+ t.frob(f);
+
+
+ System.out.println("w^p= "+t.toString());
+
+ w=w.pow(m);
+ w=w.pow(m);
+ w=w.pow(m);
+ System.out.println("w^p4= "+w.toString());
+
+
+ System.out.println("Test Inversion");
+
+ w=new FP4(w0,w1);
+
+ w.inverse();
+
+ System.out.println("1/w mod p^4 = "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/(1/w) mod p^4 = "+w.toString());
+
+ FP4 ww=new FP4(w);
+
+ w=w.xtr_pow(e);
+ System.out.println("w^e= "+w.toString());
+
+
+ a.zero(); b.zero();
+ a.inc(37); b.inc(17);
+ w0=new FP2(a,b);
+ a.zero(); b.zero();
+ a.inc(49); b.inc(31);
+ w1=new FP2(a,b);
+
+ FP4 c1=new FP4(w0,w1);
+ FP4 c2=new FP4(w0,w1);
+ FP4 c3=new FP4(w0,w1);
+
+ BIG e1=new BIG(3331);
+ BIG e2=new BIG(3372);
+
+ FP4 cr=w.xtr_pow2(c1,c2,c3,e1,e2);
+
+ System.out.println("c^e= "+cr.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/MPIN.java b/src/main/java/org/apache/milagro/amcl/BLS381/MPIN.java
new file mode 100644
index 0000000..7365f3d
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/MPIN.java
@@ -0,0 +1,823 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* MPIN API Functions */
+
+package org.apache.milagro.amcl.BLS381;
+
+import java.util.Date;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public class MPIN
+{
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int PAS=16;
+ public static final int INVALID_POINT=-14;
+ public static final int BAD_PARAMS=-11;
+ public static final int WRONG_ORDER=-18;
+ public static final int BAD_PIN=-19;
+
+/* Configure your PIN here */
+
+ public static final int MAXPIN=10000; /* PIN less than this */
+ public static final int PBLEN=14; /* Number of bits in PIN */
+ public static final int TS=10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+ public static final int TRAP=200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN - approx 2*sqrt(MAXPIN) */
+
+// public static final int HASH_TYPE=SHA256;
+
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,int n,byte[] B,int len)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (n>0) H.process_num(n);
+
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+ byte[] W=new byte[len];
+
+ if (sha>=len)
+ for (int i=0;i<len;i++) W[i]=R[i];
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+len-sha]=R[i];
+ for (int i=0;i<len-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<len;i++) W[i]=0;
+ }
+ return W;
+ }
+
+ /* return time in slots since epoch */
+ public static int today() {
+ Date date=new Date();
+ return (int) (date.getTime()/(1000*60*1440));
+ }
+
+ public static byte[] HASH_ID(int sha,byte[] ID,int len)
+ {
+ return hashit(sha,0,ID,len);
+ }
+
+/* Hash the M-Pin transcript - new */
+
+ public static byte[] HASH_ALL(int sha,byte[] HID,byte[] xID,byte[] xCID,byte[] SEC,byte[] Y,byte[] R,byte[] W,int len)
+ {
+ int i,ilen,tlen=0;
+
+ ilen=HID.length+SEC.length+Y.length+R.length+W.length;
+ if (xCID!=null) ilen+=xCID.length;
+ else ilen+=xID.length;
+
+ byte[] T = new byte[ilen];
+
+ for (i=0;i<HID.length;i++) T[i]=HID[i];
+ tlen+=HID.length;
+ if (xCID!=null)
+ {
+ for (i=0;i<xCID.length;i++) T[i+tlen]=xCID[i];
+ tlen+=xCID.length;
+ }
+ else
+ {
+ for (i=0;i<xID.length;i++) T[i+tlen]=xID[i];
+ tlen+=xID.length;
+ }
+ for (i=0;i<SEC.length;i++) T[i+tlen]=SEC[i];
+ tlen+=SEC.length;
+ for (i=0;i<Y.length;i++) T[i+tlen]=Y[i];
+ tlen+=Y.length;
+ for (i=0;i<R.length;i++) T[i+tlen]=R[i];
+ tlen+=R.length;
+ for (i=0;i<W.length;i++) T[i+tlen]=W[i];
+ tlen+=W.length;
+
+ return hashit(sha,0,T,len);
+ }
+
+/* return time since epoch */
+ public static int GET_TIME() {
+ Date date=new Date();
+ return (int) (date.getTime()/1000);
+ }
+
+ public static byte[] mpin_hash(int sha,FP4 c,ECP U)
+ {
+ byte[] w=new byte[EFS];
+ byte[] t=new byte[6*EFS];
+ byte[] h=null;
+ c.geta().getA().toBytes(w); for (int i=0;i<EFS;i++) t[i]=w[i];
+ c.geta().getB().toBytes(w); for (int i=EFS;i<2*EFS;i++) t[i]=w[i-EFS];
+ c.getb().getA().toBytes(w); for (int i=2*EFS;i<3*EFS;i++) t[i]=w[i-2*EFS];
+ c.getb().getB().toBytes(w); for (int i=3*EFS;i<4*EFS;i++) t[i]=w[i-3*EFS];
+
+ U.getX().toBytes(w); for (int i=4*EFS;i<5*EFS;i++) t[i]=w[i-4*EFS];
+ U.getY().toBytes(w); for (int i=5*EFS;i<6*EFS;i++) t[i]=w[i-5*EFS];
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (h==null) return null;
+ byte[] R=new byte[ECP.AESKEY];
+ for (int i=0;i<ECP.AESKEY;i++) R[i]=h[i];
+ return R;
+ }
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+ public static ECP map(BIG u,int cb)
+ {
+ ECP P;
+ BIG x=new BIG(u);
+ BIG p=new BIG(ROM.Modulus);
+ x.mod(p);
+ while (true)
+ {
+ P=new ECP(x,cb);
+ if (!P.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+ return P;
+ }
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+ public static int unmap(BIG u,ECP P)
+ {
+ int s=P.getS();
+ ECP R;
+ int r=0;
+ BIG x=P.getX();
+ u.copy(x);
+ while (true)
+ {
+ u.dec(1); u.norm();
+ r++;
+ R=new ECP(u,s);
+ if (!R.is_infinity()) break;
+ }
+ return r;
+ }
+
+
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+ public static int ENCODING(RAND rng,byte[] E)
+ {
+ int rn,m,su,sv;
+ byte[] T=new byte[EFS];
+
+ for (int i=0;i<EFS;i++) T[i]=E[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=E[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ ECP P=new ECP(u,v);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG p=new BIG(ROM.Modulus);
+ u=BIG.randomnum(p,rng);
+
+ su=rng.getByte(); /*if (su<0) su=-su;*/ su%=2;
+
+ ECP W=map(u,su);
+ P.sub(W); //P.affine();
+ sv=P.getS();
+ rn=unmap(v,P);
+ m=rng.getByte(); /*if (m<0) m=-m;*/ m%=rn;
+ v.inc(m+1);
+ E[0]=(byte)(su+2*sv);
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+ public static int DECODING(byte[] D)
+ {
+ int su,sv;
+ byte[] T=new byte[EFS];
+
+ if ((D[0]&0x04)!=0) return INVALID_POINT;
+
+ for (int i=0;i<EFS;i++) T[i]=D[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=D[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ su=D[0]&1;
+ sv=(D[0]>>1)&1;
+ ECP W=map(u,su);
+ ECP P=map(v,sv);
+ P.add(W); //P.affine();
+ u=P.getX();
+ v=P.getY();
+ D[0]=0x04;
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+/* R=R1+R2 in group G1 */
+ public static int RECOMBINE_G1(byte[] R1,byte[] R2,byte[] R)
+ {
+ ECP P=ECP.fromBytes(R1);
+ ECP Q=ECP.fromBytes(R2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(R,false);
+ return 0;
+ }
+
+/* W=W1+W2 in group G2 */
+ public static int RECOMBINE_G2(byte[] W1,byte[] W2,byte[] W)
+ {
+ ECP2 P=ECP2.fromBytes(W1);
+ ECP2 Q=ECP2.fromBytes(W2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(W);
+ return 0;
+ }
+
+/* create random secret S */
+ public static int RANDOM_GENERATE(RAND rng,byte[] S)
+ {
+ BIG s;
+ BIG r=new BIG(ROM.CURVE_Order);
+ s=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+ return 0;
+ }
+
+/* Extract PIN from TOKEN for identity CID */
+ public static int EXTRACT_PIN(int sha,byte[] CID,int pin,byte[] TOKEN)
+ {
+ ECP P=ECP.fromBytes(TOKEN);
+ if (P.is_infinity()) return INVALID_POINT;
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R=ECP.mapit(h);
+
+
+ pin%=MAXPIN;
+
+ R=R.pinmul(pin,PBLEN);
+ P.sub(R); //P.affine();
+
+ P.toBytes(TOKEN,false);
+
+ return 0;
+ }
+
+/* Implement step 2 on client side of MPin protocol */
+ public static int CLIENT_2(byte[] X,byte[] Y,byte[] SEC)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ ECP P=ECP.fromBytes(SEC);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG px=BIG.fromBytes(X);
+ BIG py=BIG.fromBytes(Y);
+ px.add(py);
+ px.mod(r);
+ // px.rsub(r);
+
+ P=PAIR.G1mul(P,px);
+ P.neg();
+ P.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Implement step 1 on client side of MPin protocol */
+ public static int CLIENT_1(int sha,int date,byte[] CLIENT_ID,RAND rng,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG x;
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P,T,W;
+ BIG px;
+// byte[] t=new byte[EFS];
+
+ byte[] h=hashit(sha,0,CLIENT_ID,EFS);
+ P=ECP.mapit(h);
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ pin%=MAXPIN;
+ W=P.pinmul(pin,PBLEN);
+ T.add(W);
+ if (date!=0)
+ {
+ W=ECP.fromBytes(PERMIT);
+ if (W.is_infinity()) return INVALID_POINT;
+ T.add(W);
+ h=hashit(sha,date,h,EFS);
+ W=ECP.mapit(h);
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ W=PAIR.G1mul(W,x);
+ P.add(W);
+ //P.affine();
+ }
+ else
+ {
+ P.add(W); //P.affine();
+ P=PAIR.G1mul(P,x);
+ }
+ if (xCID!=null) P.toBytes(xCID,false);
+ }
+ else
+ {
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ }
+ }
+
+ //T.affine();
+ T.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+ public static int GET_SERVER_SECRET(byte[] S,byte[] SST)
+ {
+ ECP2 Q=ECP2.generator();
+ BIG s=BIG.fromBytes(S);
+ Q=PAIR.G2mul(Q,s);
+ Q.toBytes(SST);
+ return 0;
+ }
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+ public static int GET_G1_MULTIPLE(RAND rng, int type,byte[] X,byte[] G,byte[] W)
+ {
+ BIG x;
+ BIG r=new BIG(ROM.CURVE_Order);
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P;
+ if (type==0)
+ {
+ P=ECP.fromBytes(G);
+ if (P.is_infinity()) return INVALID_POINT;
+ }
+ else
+ P=ECP.mapit(G);
+
+ PAIR.G1mul(P,x).toBytes(W,false);
+ return 0;
+ }
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+ public static int GET_CLIENT_SECRET(byte[] S,byte[] CID,byte[] CST)
+ {
+ return GET_G1_MULTIPLE(null,1,S,CID,CST);
+ }
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+ public static int GET_CLIENT_PERMIT(int sha,int date,byte[] S,byte[] CID,byte[] CTT)
+ {
+ byte[] h=hashit(sha,date,CID,EFS);
+ ECP P=ECP.mapit(h);
+
+ BIG s=BIG.fromBytes(S);
+ ECP OP=PAIR.G1mul(P,s);
+
+ OP.toBytes(CTT,false);
+ return 0;
+ }
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+ public static void SERVER_1(int sha,int date,byte[] CID,byte[] HID,byte[] HTID)
+ {
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R,P=ECP.mapit(h);
+
+ P.toBytes(HID,false); // new
+ if (date!=0)
+ {
+ // if (HID!=null) P.toBytes(HID);
+ h=hashit(sha,date,h,EFS);
+ R=ECP.mapit(h);
+ P.add(R); //P.affine();
+ P.toBytes(HTID,false);
+ }
+ // else P.toBytes(HID,false);
+ }
+
+/* Implement step 2 of MPin protocol on server side */
+ public static int SERVER_2(int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] mSEC,byte[] E,byte[] F)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ ECP2 Q=ECP2.generator();
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+
+ ECP R;
+ if (date!=0)
+ R=ECP.fromBytes(xCID);
+ else
+ {
+ if (xID==null) return BAD_PARAMS;
+ R=ECP.fromBytes(xID);
+ }
+ if (R.is_infinity()) return INVALID_POINT;
+
+ BIG y=BIG.fromBytes(Y);
+ ECP P;
+ if (date!=0) P=ECP.fromBytes(HTID);
+ else
+ {
+ if (HID==null) return BAD_PARAMS;
+ P=ECP.fromBytes(HID);
+ }
+
+ if (P.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ R=ECP.fromBytes(mSEC);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ FP12 g;
+
+ g=PAIR.ate2(Q,R,sQ,P);
+ g=PAIR.fexp(g);
+
+ if (!g.isunity())
+ {
+ if (HID!=null && xID!=null && E!=null && F!=null)
+ {
+ g.toBytes(E);
+ if (date!=0)
+ {
+ P=ECP.fromBytes(HID);
+ if (P.is_infinity()) return INVALID_POINT;
+ R=ECP.fromBytes(xID);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ }
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(F);
+ }
+ return BAD_PIN;
+ }
+
+ return 0;
+ }
+
+/* Pollards kangaroos used to return PIN error */
+ public static int KANGAROO(byte[] E,byte[] F)
+ {
+ FP12 ge=FP12.fromBytes(E);
+ FP12 gf=FP12.fromBytes(F);
+ int[] distance = new int[TS];
+ FP12 t=new FP12(gf);
+ FP12[] table=new FP12[TS];
+ int i,j,m,s,dn,dm,res,steps;
+
+ s=1;
+ for (m=0;m<TS;m++)
+ {
+ distance[m]=s;
+ table[m]=new FP12(t);
+ s*=2;
+ t.usqr();
+ }
+ t.one();
+ dn=0;
+ for (j=0;j<TRAP;j++)
+ {
+ i=t.geta().geta().getA().lastbits(20)%TS;
+ t.mul(table[i]);
+ dn+=distance[i];
+ }
+ gf.copy(t); gf.conj();
+ steps=0; dm=0;
+ res=0;
+ while (dm-dn<MAXPIN)
+ {
+ steps++;
+ if (steps>4*TRAP) break;
+ i=ge.geta().geta().getA().lastbits(20)%TS;
+ ge.mul(table[i]);
+ dm+=distance[i];
+ if (ge.equals(t))
+ {
+ res=dm-dn;
+ break;
+ }
+ if (ge.equals(gf))
+ {
+ res=dn-dm;
+ break;
+ }
+
+ }
+ if (steps>4*TRAP || dm-dn>=MAXPIN) {res=0; } // Trap Failed - probable invalid token
+ return res;
+ }
+
+/* Functions to support M-Pin Full */
+
+ public static int PRECOMPUTE(byte[] TOKEN,byte[] CID,byte[] G1,byte[] G2)
+ {
+ ECP P,T;
+ FP12 g;
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ P=ECP.mapit(CID);
+
+ ECP2 Q=ECP2.generator();
+
+ g=PAIR.ate(Q,T);
+ g=PAIR.fexp(g);
+ g.toBytes(G1);
+
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(G2);
+
+ return 0;
+ }
+
+
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+ public static int CLIENT_KEY(int sha,byte[] G1,byte[] G2,int pin,byte[] R,byte[] X,byte[] H,byte[] wCID,byte[] CK)
+ {
+ byte[] t;
+
+ FP12 g1=FP12.fromBytes(G1);
+ FP12 g2=FP12.fromBytes(G2);
+ BIG z=BIG.fromBytes(R);
+ BIG x=BIG.fromBytes(X);
+ BIG h=BIG.fromBytes(H);
+
+ ECP W=ECP.fromBytes(wCID);
+ if (W.is_infinity()) return INVALID_POINT;
+
+ W=PAIR.G1mul(W,x);
+
+// FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG r=new BIG(ROM.CURVE_Order);
+// BIG q=new BIG(ROM.Modulus);
+
+ z.add(h); //new
+ z.mod(r);
+
+ g2.pinpow(pin,PBLEN);
+ g1.mul(g2);
+
+ FP4 c=g1.compow(z,r);
+/*
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(z);
+ a.mod(m);
+
+ BIG b=new BIG(z);
+ b.div(m);
+
+
+ FP4 c=g1.trace();
+ g2.copy(g1);
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+*/
+ t=mpin_hash(sha,c,W);
+
+ for (int i=0;i<ECP.AESKEY;i++) CK[i]=t[i];
+
+ return 0;
+ }
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+ public static int SERVER_KEY(int sha,byte[] Z,byte[] SST,byte[] W,byte[] H,byte[] HID,byte[] xID,byte[] xCID,byte[] SK)
+ {
+ byte[] t;
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+ ECP R=ECP.fromBytes(Z);
+ if (R.is_infinity()) return INVALID_POINT;
+ ECP A=ECP.fromBytes(HID);
+ if (A.is_infinity()) return INVALID_POINT;
+
+ ECP U;
+ if (xCID!=null)
+ U=ECP.fromBytes(xCID);
+ else
+ U=ECP.fromBytes(xID);
+ if (U.is_infinity()) return INVALID_POINT;
+
+ BIG w=BIG.fromBytes(W);
+ BIG h=BIG.fromBytes(H);
+ A=PAIR.G1mul(A,h); // new
+ R.add(A); //R.affine();
+
+ U=PAIR.G1mul(U,w);
+ FP12 g=PAIR.ate(sQ,R);
+ g=PAIR.fexp(g);
+
+ FP4 c=g.trace();
+
+ t=mpin_hash(sha,c,U);
+
+ for (int i=0;i<ECP.AESKEY;i++) SK[i]=t[i];
+
+ return 0;
+ }
+
+/* Generate Y = H(epoch, xCID/xID) */
+ public static void GET_Y(int sha,int TimeValue,byte[] xCID,byte[] Y)
+ {
+ byte[] h = hashit(sha,TimeValue,xCID,EFS);
+ BIG y = BIG.fromBytes(h);
+ BIG q=new BIG(ROM.CURVE_Order);
+ y.mod(q);
+ //if (ROM.AES_S>0)
+ //{
+ // y.mod2m(2*ROM.AES_S);
+ //}
+ y.toBytes(Y);
+ }
+
+/* One pass MPIN Client */
+ public static int CLIENT(int sha,int date,byte[] CLIENT_ID,RAND RNG,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT, int TimeValue, byte[] Y)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ rtn = CLIENT_1(sha,date,CLIENT_ID,RNG,X,pin,TOKEN,SEC,xID,xCID,PERMIT);
+ if (rtn != 0)
+ return rtn;
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+
+/* One pass MPIN Server */
+ public static int SERVER(int sha,int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] SEC,byte[] E,byte[] F,byte[] CID, int TimeValue)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ SERVER_1(sha,date,CID,HID,HTID);
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = SERVER_2(date,HID,HTID,Y,SST,xID,xCID,SEC,E,F);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/PAIR.java b/src/main/java/org/apache/milagro/amcl/BLS381/PAIR.java
new file mode 100644
index 0000000..55e86b4
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/PAIR.java
@@ -0,0 +1,817 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BN Curve Pairing functions */
+
+package org.apache.milagro.amcl.BLS381;
+
+public final class PAIR {
+
+ public static final boolean USE_GLV =true;
+ public static final boolean USE_GS_G2 =true;
+ public static final boolean USE_GS_GT =true;
+ public static final boolean GT_STRONG=false;
+
+
+/* Line function */
+ public static FP12 line(ECP2 A,ECP2 B,FP Qx,FP Qy)
+ {
+//System.out.println("Into line");
+ FP4 a,b,c; // Edits here
+// c=new FP4(0);
+ if (A==B)
+ { // Doubling
+ FP2 XX=new FP2(A.getx()); //X
+ FP2 YY=new FP2(A.gety()); //Y
+ FP2 ZZ=new FP2(A.getz()); //Z
+ FP2 YZ=new FP2(YY); //Y
+ YZ.mul(ZZ); //YZ
+ XX.sqr(); //X^2
+ YY.sqr(); //Y^2
+ ZZ.sqr(); //Z^2
+
+ YZ.imul(4);
+ YZ.neg(); YZ.norm(); //-2YZ
+ YZ.pmul(Qy); //-2YZ.Ys
+
+ XX.imul(6); //3X^2
+ XX.pmul(Qx); //3X^2.Xs
+
+ int sb=3*ROM.CURVE_B_I;
+ ZZ.imul(sb);
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ ZZ.div_ip2();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ ZZ.mul_ip();
+ ZZ.add(ZZ);
+ YZ.mul_ip();
+ YZ.norm();
+ }
+
+ ZZ.norm(); // 3b.Z^2
+
+ YY.add(YY);
+ ZZ.sub(YY); ZZ.norm(); // 3b.Z^2-Y^2
+
+ a=new FP4(YZ,ZZ); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(XX); // L(0,1) | L(0,0) | L(1,0)
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(XX); c.times_i();
+ }
+ A.dbl();
+ }
+ else
+ { // Addition - assume B is affine
+
+ FP2 X1=new FP2(A.getx()); // X1
+ FP2 Y1=new FP2(A.gety()); // Y1
+ FP2 T1=new FP2(A.getz()); // Z1
+ FP2 T2=new FP2(A.getz()); // Z1
+
+ T1.mul(B.gety()); // T1=Z1.Y2
+ T2.mul(B.getx()); // T2=Z1.X2
+
+ X1.sub(T2); X1.norm(); // X1=X1-Z1.X2
+ Y1.sub(T1); Y1.norm(); // Y1=Y1-Z1.Y2
+
+ T1.copy(X1); // T1=X1-Z1.X2
+ X1.pmul(Qy); // X1=(X1-Z1.X2).Ys
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X1.mul_ip();
+ X1.norm();
+ }
+
+ T1.mul(B.gety()); // T1=(X1-Z1.X2).Y2
+
+ T2.copy(Y1); // T2=Y1-Z1.Y2
+ T2.mul(B.getx()); // T2=(Y1-Z1.Y2).X2
+ T2.sub(T1); T2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+ Y1.pmul(Qx); Y1.neg(); Y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+ a=new FP4(X1,T2); // (X1-Z1.X2).Ys | (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2 | - (Y1-Z1.Y2).Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(Y1);
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(Y1); c.times_i();
+ }
+ A.add(B);
+ }
+//System.out.println("Out of line");
+ return new FP12(a,b,c);
+ }
+
+/* Optimal R-ate pairing */
+ public static FP12 ate(ECP2 P1,ECP Q1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+
+ ECP2 A=new ECP2();
+ FP12 r=new FP12(1);
+ A.copy(P);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ //r.conj();
+ A.neg();
+ }
+ K.copy(P);
+ K.frob(f);
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+ public static FP12 ate2(ECP2 P1,ECP Q1,ECP2 R1,ECP S1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ ECP2 R=new ECP2(R1);
+ ECP S=new ECP(S1);
+
+ R.affine();
+ S.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+ FP Sx=new FP(S.getx());
+ FP Sy=new FP(S.gety());
+
+ ECP2 A=new ECP2();
+ ECP2 B=new ECP2();
+ FP12 r=new FP12(1);
+
+ A.copy(P);
+ B.copy(R);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+ ECP2 MR=new ECP2();
+ MR.copy(R); MR.neg();
+
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ lv=line(B,B,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ lv=line(B,R,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ //R.neg();
+ lv=line(B,MR,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //R.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ // r.conj();
+ A.neg();
+ B.neg();
+ }
+
+ K.copy(P);
+ K.frob(f);
+
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.copy(R);
+ K.frob(f);
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+ public static FP12 fexp(FP12 m)
+ {
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ FP12 r=new FP12(m);
+
+/* Easy part of final exp */
+ FP12 lv=new FP12(r);
+ lv.inverse();
+ r.conj();
+
+ r.mul(lv);
+ lv.copy(r);
+ r.frob(f);
+ r.frob(f);
+ r.mul(lv);
+/* Hard part of final exp */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ FP12 x0,x1,x2,x3,x4,x5;
+ lv.copy(r);
+ lv.frob(f);
+ x0=new FP12(lv);
+ x0.frob(f);
+ lv.mul(r);
+ x0.mul(lv);
+ x0.frob(f);
+ x1=new FP12(r);
+ x1.conj();
+ x4=r.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x4.conj();
+ }
+
+ x3=new FP12(x4);
+ x3.frob(f);
+
+ x2=x4.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x2.conj();
+ }
+ x5=new FP12(x2); x5.conj();
+ lv=x2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ lv.conj();
+ }
+ x2.frob(f);
+ r.copy(x2); r.conj();
+
+ x4.mul(r);
+ x2.frob(f);
+
+ r.copy(lv);
+ r.frob(f);
+ lv.mul(r);
+
+ lv.usqr();
+ lv.mul(x4);
+ lv.mul(x5);
+ r.copy(x3);
+ r.mul(x5);
+ r.mul(lv);
+ lv.mul(x2);
+ r.usqr();
+ r.mul(lv);
+ r.usqr();
+ lv.copy(r);
+ lv.mul(x1);
+ r.mul(x0);
+ lv.usqr();
+ r.mul(lv);
+ r.reduce();
+ }
+ else
+ {
+
+ FP12 y0,y1,y2,y3;
+// Ghamman & Fouotsa Method
+ y0=new FP12(r); y0.usqr();
+ y1=y0.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y1.conj();
+ }
+ x.fshr(1); y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+
+ x.fshl(1);
+ y3=new FP12(r); y3.conj();
+ y1.mul(y3);
+
+ y1.conj();
+ y1.mul(y2);
+
+ y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y3=y2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y3.conj();
+ }
+ y1.conj();
+ y3.mul(y1);
+
+ y1.conj();
+ y1.frob(f); y1.frob(f); y1.frob(f);
+ y2.frob(f); y2.frob(f);
+ y1.mul(y2);
+
+ y2=y3.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y2.mul(y0);
+ y2.mul(r);
+
+ y1.mul(y2);
+ y2.copy(y3); y2.frob(f);
+ y1.mul(y2);
+ r.copy(y1);
+ r.reduce();
+ }
+
+ return r;
+ }
+
+/* GLV method */
+ public static BIG[] glv(BIG e)
+ {
+ BIG[] u=new BIG[2];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+
+ BIG[] v=new BIG[2];
+ for (i=0;i<2;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_W[i])); // why not just t=new BIG(ROM.CURVE_W[i]);
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<2;i++)
+ for (j=0;j<2;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_SB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ { // -(x^2).P = (Beta.x,y)
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG x2=BIG.smul(x,x);
+ u[0]=new BIG(e);
+ u[0].mod(x2);
+ u[1]=new BIG(e);
+ u[1].div(x2);
+ u[1].rsub(q);
+ }
+ return u;
+ }
+
+/* Galbraith & Scott Method */
+ public static BIG[] gs(BIG e)
+ {
+ BIG[] u=new BIG[4];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] v=new BIG[4];
+ for (i=0;i<4;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_WB[i]));
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<4;i++)
+ for (j=0;j<4;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_BB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ {
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG w=new BIG(e);
+ for (int i=0;i<3;i++)
+ {
+ u[i]=new BIG(w);
+ u[i].mod(x);
+ w.div(x);
+ }
+ u[3]=new BIG(w);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ u[1].copy(BIG.modneg(u[1],q));
+ u[3].copy(BIG.modneg(u[3],q));
+ }
+ }
+ return u;
+ }
+
+/* Multiply P by e in group G1 */
+ public static ECP G1mul(ECP P,BIG e)
+ {
+ ECP R;
+ if (USE_GLV)
+ {
+ //P.affine();
+ R=new ECP();
+ R.copy(P);
+ int i,np,nn;
+ ECP Q=new ECP();
+ Q.copy(P); Q.affine();
+ BIG q=new BIG(ROM.CURVE_Order);
+ FP cru=new FP(new BIG(ROM.CURVE_Cru));
+ BIG t=new BIG(0);
+ BIG[] u=glv(e);
+ Q.getx().mul(cru);
+
+ np=u[0].nbits();
+ t.copy(BIG.modneg(u[0],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[0].copy(t);
+ R.neg();
+ }
+
+ np=u[1].nbits();
+ t.copy(BIG.modneg(u[1],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[1].copy(t);
+ Q.neg();
+ }
+ u[0].norm();
+ u[1].norm();
+ R=R.mul2(u[0],Q,u[1]);
+
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* Multiply P by e in group G2 */
+ public static ECP2 G2mul(ECP2 P,BIG e)
+ {
+ ECP2 R;
+ if (USE_GS_G2)
+ {
+ ECP2[] Q=new ECP2[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] u=gs(e);
+
+ BIG t=new BIG(0);
+ int i,np,nn;
+ //P.affine();
+
+ Q[0]=new ECP2(); Q[0].copy(P);
+ for (i=1;i<4;i++)
+ {
+ Q[i]=new ECP2(); Q[i].copy(Q[i-1]);
+ Q[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ Q[i].neg();
+ }
+ u[i].norm();
+ //Q[i].affine();
+ }
+
+ R=ECP2.mul4(Q,u);
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */
+ public static FP12 GTpow(FP12 d,BIG e)
+ {
+ FP12 r;
+ if (USE_GS_GT)
+ {
+ FP12[] g=new FP12[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG t=new BIG(0);
+ int i,np,nn;
+ BIG[] u=gs(e);
+
+ g[0]=new FP12(d);
+ for (i=1;i<4;i++)
+ {
+ g[i]=new FP12(0); g[i].copy(g[i-1]);
+ g[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ g[i].conj();
+ }
+ u[i].norm();
+ }
+ r=FP12.pow4(g,u);
+ }
+ else
+ {
+ r=d.pow(e);
+ }
+ return r;
+ }
+
+/* test group membership - no longer needed */
+/* with GT-Strong curve, now only check that m!=1, conj(m)*m==1, and m.m^{p^4}=m^{p^2} */
+/*
+ public static boolean GTmember(FP12 m)
+ {
+ if (m.isunity()) return false;
+ FP12 r=new FP12(m);
+ r.conj();
+ r.mul(m);
+ if (!r.isunity()) return false;
+
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ r.copy(m); r.frob(f); r.frob(f);
+ FP12 w=new FP12(r); w.frob(f); w.frob(f);
+ w.mul(m);
+ if (!ROM.GT_STRONG)
+ {
+ if (!w.equals(r)) return false;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ r.copy(m); w=r.pow(x); w=w.pow(x);
+ r.copy(w); r.sqr(); r.mul(w); r.sqr();
+ w.copy(m); w.frob(f);
+ }
+ return w.equals(r);
+ }
+*/
+/*
+ public static void main(String[] args) {
+ ECP Q=new ECP(new BIG(ROM.CURVE_Gx),new BIG(ROM.CURVE_Gy));
+ ECP2 P=new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG xa=new BIG(ROM.CURVE_Pxa);
+
+ System.out.println("P= "+P.toString());
+ System.out.println("Q= "+Q.toString());
+
+ BIG m=new BIG(17);
+
+ FP12 e=ate(P,Q);
+ System.out.println("\ne= "+e.toString());
+
+ e=fexp(e);
+
+ for (int i=1;i<1000;i++)
+ {
+ e=ate(P,Q);
+ e=fexp(e);
+ }
+ // e=GTpow(e,m);
+
+ System.out.println("\ne= "+e.toString());
+
+ BIG [] GLV=glv(r);
+
+ System.out.println("GLV[0]= "+GLV[0].toString());
+ System.out.println("GLV[0]= "+GLV[1].toString());
+
+ ECP G=new ECP(); G.copy(Q);
+ ECP2 R=new ECP2(); R.copy(P);
+
+
+ e=ate(R,Q);
+ e=fexp(e);
+
+ e=GTpow(e,xa);
+ System.out.println("\ne= "+e.toString());
+
+
+ R=G2mul(R,xa);
+ e=ate(R,G);
+ e=fexp(e);
+
+ System.out.println("\ne= "+e.toString());
+
+ G=G1mul(G,xa);
+ e=ate(P,G);
+ e=fexp(e);
+ System.out.println("\ne= "+e.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS381/ROM.java b/src/main/java/org/apache/milagro/amcl/BLS381/ROM.java
new file mode 100644
index 0000000..b59e7a1
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS381/ROM.java
@@ -0,0 +1,57 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.BLS381;
+
+public class ROM
+{
+
+// Base Bits= 58
+public static final long[] Modulus= {0x1FEFFFFFFFFAAABL,0x2FFFFAC54FFFFEEL,0x12A0F6B0F6241EAL,0x213CE144AFD9CC3L,0x2434BACD764774BL,0x25FF9A692C6E9EDL,0x1A0111EA3L};
+public static final long[] R2modp= {0x20639A1D5BEF7AEL,0x1244C6462DD93E8L,0x22D09B54E6E2CD2L,0x111C4B63170E5DBL,0x38A6DE8FB366399L,0x4F16CFED1F9CBCL,0x19EA66A2BL};
+public static final long MConst= 0x1F3FFFCFFFCFFFDL;
+
+public static final int CURVE_A= 0;
+public static final int CURVE_B_I= 4;
+public static final int CURVE_Cof_I= 0;
+public static final long[] CURVE_B= {0x4L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Order= {0x3FFFFFF00000001L,0x36900BFFF96FFBFL,0x180809A1D80553BL,0x14CA675F520CCE7L,0x73EDA7L,0x0L,0x0L};
+public static final long[] CURVE_Gx= {0x33AF00ADB22C6BBL,0x17A0FFE5E86BBFEL,0x3A3F171BAC586C5L,0x13E5DD2E4168538L,0x4FA9AC0FC3688CL,0x65F5E509A558E3L,0x17F1D3A73L};
+public static final long[] CURVE_Gy= {0xAA232946C5E7E1L,0x331D128A222B903L,0x18CB2C04B3EDD03L,0x25757402BD8036CL,0x1741D8AE4FCF5E0L,0xEAA83C68278C3BL,0x8B3F481EL};
+
+
+public static final long[] Fra= {0x10775ED92235FB8L,0x3A94F58F9E04F63L,0x3D784BAB9C4F67L,0x3F4F2F57D3DEC91L,0x202C0D1F0FD603L,0xAEC199F08C6FADL,0x1904D3BF0L};
+public static final long[] Frb= {0xF78A126DDC4AF3L,0x356B0535B1FB08BL,0xEC971F63C5F282L,0x21EDB1ECDBFB032L,0x2231F9FB854A147L,0x1B1380CA23A7A40L,0xFC3E2B3L};
+public static final long[] CURVE_Bnx= {0x201000000010000L,0x34L,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Cof= {0xAAAB0000AAABL,0x3230015557855A3L,0x396L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Cru= {0x201FFFFFFFEFFFEL,0x1F604D88280008BL,0x293BE6F89688DE1L,0x1DA83DDFAB76CEL,0x3DF76CE51BA69C6L,0x17C659CBL,0x0L};
+public static final long[] CURVE_Pxa= {0x8056C8C121BDB8L,0x300C9AA016EFBF5L,0xB647AE3D1770BAL,0x353E900EC0AD144L,0x32DC51051C6E47AL,0x23C2A449820149L,0x24AA2B2FL};
+public static final long[] CURVE_Pxb= {0x1AC7D055D042B7EL,0x33C4484E51755F9L,0x21BBDC7F5049334L,0x3426482D86AD769L,0x88274F65596BD0L,0x9C67D81F6B34E8L,0x13E02B605L};
+public static final long[] CURVE_Pya= {0x193548608B82801L,0x2B2730EEB28A278L,0x1A695160D12C923L,0x2AA32F74E9DB50AL,0x2DA2E351AADFD9BL,0x9F5B8463327371L,0xCE5D5277L};
+public static final long[] CURVE_Pyb= {0x2A9075FF05F79BEL,0x1C349D73B07686AL,0x12AB572E99AB3F3L,0x1FA169D8EBC99D2L,0x2BC28B99CB3E28L,0x3A9CD330CAB34ACL,0x606C4A02L};
+public static final long[][] CURVE_W= {{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}};
+public static final long[][][] CURVE_SB= {{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}}};
+public static final long[][] CURVE_WB= {{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}};
+public static final long[][][] CURVE_BB= {{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}}};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/BIG.java b/src/main/java/org/apache/milagro/amcl/BLS383/BIG.java
new file mode 100644
index 0000000..6a9195d
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.BLS383;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=48; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=58;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/DBIG.java b/src/main/java/org/apache/milagro/amcl/BLS383/DBIG.java
new file mode 100644
index 0000000..bfebc30
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.BLS383;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/ECDH.java b/src/main/java/org/apache/milagro/amcl/BLS383/ECDH.java
new file mode 100644
index 0000000..6f678a4
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.BLS383;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/ECP.java b/src/main/java/org/apache/milagro/amcl/BLS383/ECP.java
new file mode 100644
index 0000000..8b11cf9
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.BLS383;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=BLS;
+ public static final int SEXTIC_TWIST=M_TYPE;
+ public static final int SIGN_OF_X=POSITIVEX;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/ECP2.java b/src/main/java/org/apache/milagro/amcl/BLS383/ECP2.java
new file mode 100644
index 0000000..509de9f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/ECP2.java
@@ -0,0 +1,796 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Weierstrass elliptic curve functions over FP2 */
+
+package org.apache.milagro.amcl.BLS383;
+
+public final class ECP2 {
+ private FP2 x;
+ private FP2 y;
+ private FP2 z;
+// private boolean INF;
+
+/* Constructor - set this=O */
+ public ECP2() {
+// INF=true;
+ x=new FP2(0);
+ y=new FP2(1);
+ z=new FP2(0);
+ }
+
+ public ECP2(ECP2 e) {
+ this.x = new FP2(e.x);
+ this.y = new FP2(e.y);
+ this.z = new FP2(e.z);
+ }
+
+/* Test this=O? */
+ public boolean is_infinity() {
+// if (INF) return true; //******
+ return (x.iszilch() && z.iszilch());
+ }
+/* copy this=P */
+ public void copy(ECP2 P)
+ {
+ x.copy(P.x);
+ y.copy(P.y);
+ z.copy(P.z);
+// INF=P.INF;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ y.one();
+ z.zero();
+ }
+
+/* Conditional move of Q to P dependant on d */
+ public void cmove(ECP2 Q,int d)
+ {
+ x.cmove(Q.x,d);
+ y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ }
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(ECP2 W[],int b)
+ {
+ ECP2 MP=new ECP2();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test if P == Q */
+ public boolean equals(ECP2 Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+
+ FP2 a=new FP2(x); // *****
+ FP2 b=new FP2(Q.x);
+ a.mul(Q.z);
+ b.mul(z);
+ if (!a.equals(b)) return false;
+
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+
+ return true;
+ }
+/* set this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ y.norm();
+ y.neg(); y.norm();
+ return;
+ }
+/* set to Affine - (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return;
+ FP2 one=new FP2(1);
+ if (z.equals(one))
+ {
+ x.reduce();
+ y.reduce();
+ return;
+ }
+ z.inverse();
+
+ x.mul(z); x.reduce(); // *****
+ y.mul(z); y.reduce();
+ z.copy(one);
+ }
+/* extract affine x as FP2 */
+ public FP2 getX()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.x;
+ }
+/* extract affine y as FP2 */
+ public FP2 getY()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.y;
+ }
+/* extract projective x */
+ public FP2 getx()
+ {
+ return x;
+ }
+/* extract projective y */
+ public FP2 gety()
+ {
+ return y;
+ }
+/* extract projective z */
+ public FP2 getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP2 W=new ECP2(this);
+ W.affine();
+ W.x.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i]=t[i];
+ W.x.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+BIG.MODBYTES]=t[i];
+
+ W.y.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+2*BIG.MODBYTES]=t[i];
+ W.y.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+3*BIG.MODBYTES]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP2 fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG ra;
+ BIG rb;
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 rx=new FP2(ra,rb);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+2*BIG.MODBYTES];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+3*BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 ry=new FP2(ra,rb);
+
+ return new ECP2(rx,ry);
+ }
+/* convert this to hex string */
+ public String toString() {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ return "("+W.x.toString()+","+W.y.toString()+")";
+ }
+
+/* Calculate RHS of twisted curve equation x^3+B/i */
+ public static FP2 RHS(FP2 x) {
+ x.norm();
+ FP2 r=new FP2(x);
+ r.sqr();
+ FP2 b=new FP2(new BIG(ROM.CURVE_B));
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b.div_ip();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b.norm();
+ b.mul_ip();
+ b.norm();
+ }
+
+
+ r.mul(x);
+ r.add(b);
+
+ r.reduce();
+ return r;
+ }
+
+/* construct this from (x,y) - but set to O if not on curve */
+ public ECP2(FP2 ix,FP2 iy) {
+ x=new FP2(ix);
+ y=new FP2(iy);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ FP2 y2=new FP2(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+// if (y2.equals(rhs)) INF=false;
+// else {x.zero();INF=true;}
+ }
+
+/* construct this from x - but set to O if not on curve */
+ public ECP2(FP2 ix) {
+ x=new FP2(ix);
+ y=new FP2(1);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ if (rhs.sqrt())
+ {
+ y.copy(rhs);
+ //INF=false;
+ }
+ else {/*x.zero();INF=true;*/ inf();}
+ }
+
+/* this+=this */
+ public int dbl() {
+// if (INF) return -1;
+//System.out.println("Into dbl");
+ FP2 iy=new FP2(y);
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ iy.mul_ip(); iy.norm();
+ }
+ FP2 t0=new FP2(y); //***** Change
+ t0.sqr();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip();
+ }
+ FP2 t1=new FP2(iy);
+ t1.mul(z);
+ FP2 t2=new FP2(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z);
+ z.add(z);
+ z.norm();
+
+ t2.imul(3*ROM.CURVE_B_I);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip();
+ t2.norm();
+ }
+
+ FP2 x3=new FP2(t2);
+ x3.mul(z);
+
+ FP2 y3=new FP2(t0);
+
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1); t2.norm();
+ t0.sub(t2); t0.norm(); //y^2-9bz^2
+ y3.mul(t0); y3.add(x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+ t1.copy(x); t1.mul(iy); //
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x); //(y^2-9bz^2)xy2
+
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ return 1;
+ }
+
+/* this+=Q - return 0 for add, 1 for double, -1 for O */
+ public int add(ECP2 Q) {
+// if (INF)
+// {
+// copy(Q);
+// return -1;
+// }
+// if (Q.INF) return -1;
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP2 t0=new FP2(x);
+ t0.mul(Q.x); // x.Q.x
+ FP2 t1=new FP2(y);
+ t1.mul(Q.y); // y.Q.y
+
+ FP2 t2=new FP2(z);
+ t2.mul(Q.z);
+ FP2 t3=new FP2(x);
+ t3.add(y); t3.norm(); //t3=X1+Y1
+ FP2 t4=new FP2(Q.x);
+ t4.add(Q.y); t4.norm(); //t4=X2+Y2
+ t3.mul(t4); //t3=(X1+Y1)(X2+Y2)
+ t4.copy(t0); t4.add(t1); //t4=X1.X2+Y1.Y2
+
+ t3.sub(t4); t3.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t3.mul_ip(); t3.norm(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+ }
+ t4.copy(y);
+ t4.add(z); t4.norm(); //t4=Y1+Z1
+ FP2 x3=new FP2(Q.y);
+ x3.add(Q.z); x3.norm(); //x3=Y2+Z2
+
+ t4.mul(x3); //t4=(Y1+Z1)(Y2+Z2)
+ x3.copy(t1); //
+ x3.add(t2); //X3=Y1.Y2+Z1.Z2
+
+ t4.sub(x3); t4.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t4.mul_ip(); t4.norm(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+ }
+ x3.copy(x); x3.add(z); x3.norm(); // x3=X1+Z1
+ FP2 y3=new FP2(Q.x);
+ y3.add(Q.z); y3.norm(); // y3=X2+Z2
+ x3.mul(y3); // x3=(X1+Z1)(X2+Z2)
+ y3.copy(t0);
+ y3.add(t2); // y3=X1.X2+Z1+Z2
+ y3.rsub(x3); y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip(); t0.norm(); // x.Q.x
+ t1.mul_ip(); t1.norm(); // y.Q.y
+ }
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip(); t2.norm();
+ }
+ FP2 z3=new FP2(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ y3.mul_ip();
+ y3.norm();
+ }
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ return 0;
+ }
+
+/* set this-=Q */
+ public int sub(ECP2 Q) {
+ ECP2 NQ=new ECP2(Q);
+ NQ.neg();
+ int D=add(NQ);
+ //Q.neg();
+ //int D=add(Q);
+ //Q.neg();
+ return D;
+ }
+/* set this*=q, where q is Modulus, using Frobenius */
+ public void frob(FP2 X)
+ {
+// if (INF) return;
+ FP2 X2=new FP2(X);
+
+ X2.sqr();
+ x.conj();
+ y.conj();
+ z.conj();
+ z.reduce();
+ x.mul(X2);
+
+ y.mul(X2);
+ y.mul(X);
+ }
+
+/* P*=e */
+ public ECP2 mul(BIG e)
+ {
+/* fixed size windows */
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP2 P=new ECP2();
+ ECP2 Q=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2[] W=new ECP2[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ if (is_infinity()) return new ECP2();
+
+ //affine();
+
+/* precompute table */
+ Q.copy(this);
+ Q.dbl();
+ W[0]=new ECP2();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP2();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+/* make exponent odd - add 2P if even, P if odd */
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+/* convert exponent to signed 4-bit window */
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C);
+ P.affine();
+ return P;
+ }
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ ECP2 W=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] T=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ //Q[i].affine();
+ }
+
+ T[0] = new ECP2(); T[0].copy(Q[0]); // Q[0]
+ T[1] = new ECP2(); T[1].copy(T[0]); T[1].add(Q[1]); // Q[0]+Q[1]
+ T[2] = new ECP2(); T[2].copy(T[0]); T[2].add(Q[2]); // Q[0]+Q[2]
+ T[3] = new ECP2(); T[3].copy(T[1]); T[3].add(Q[2]); // Q[0]+Q[1]+Q[2]
+ T[4] = new ECP2(); T[4].copy(T[0]); T[4].add(Q[3]); // Q[0]+Q[3]
+ T[5] = new ECP2(); T[5].copy(T[1]); T[5].add(Q[3]); // Q[0]+Q[1]+Q[3]
+ T[6] = new ECP2(); T[6].copy(T[2]); T[6].add(Q[3]); // Q[0]+Q[2]+Q[3]
+ T[7] = new ECP2(); T[7].copy(T[3]); T[7].add(Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ P.select(T,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ P.dbl();
+ W.select(T,(int)(2*w[i]+s[i]));
+ P.add(W);
+ }
+
+ // apply correction
+ W.copy(P);
+ W.sub(Q[0]);
+ P.cmove(W,pb);
+ P.affine();
+ return P;
+ }
+
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+/*
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb;
+ int[] a=new int[4];
+ ECP2 T=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] W=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ Q[i].affine();
+ }
+
+// precompute table
+
+ W[0]=new ECP2(); W[0].copy(Q[0]); W[0].sub(Q[1]);
+
+ W[1]=new ECP2(); W[1].copy(W[0]);
+ W[2]=new ECP2(); W[2].copy(W[0]);
+ W[3]=new ECP2(); W[3].copy(W[0]);
+ W[4]=new ECP2(); W[4].copy(Q[0]); W[4].add(Q[1]);
+ W[5]=new ECP2(); W[5].copy(W[4]);
+ W[6]=new ECP2(); W[6].copy(W[4]);
+ W[7]=new ECP2(); W[7].copy(W[4]);
+ T.copy(Q[2]); T.sub(Q[3]);
+ W[1].sub(T);
+ W[2].add(T);
+ W[5].sub(T);
+ W[6].add(T);
+ T.copy(Q[2]); T.add(Q[3]);
+ W[0].sub(T);
+ W[3].add(T);
+ W[4].sub(T);
+ W[7].add(T);
+
+// if multiplier is even add 1 to multiplier, and add P to correction
+ mt.zero(); C.inf();
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ C.add(Q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(byte)(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ P.dbl();
+ P.add(T);
+ }
+ P.sub(C); // apply correction
+
+ P.affine();
+ return P;
+ }
+*/
+
+/* needed for SOK */
+ public static ECP2 mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ BIG one=new BIG(1);
+ FP2 X;
+ ECP2 Q;
+ x.mod(q);
+ while (true)
+ {
+ X=new FP2(one,x);
+ Q=new ECP2(X);
+ if (!Q.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+
+ BIG Fra=new BIG(ROM.Fra);
+ BIG Frb=new BIG(ROM.Frb);
+ X=new FP2(Fra,Frb);
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X.inverse();
+ X.norm();
+ }
+
+ x=new BIG(ROM.CURVE_Bnx);
+
+/* Fast Hashing to G2 - Fuentes-Castaneda, Knapp and Rodriguez-Henriquez */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ ECP2 T,K;
+
+ T=new ECP2(); T.copy(Q);
+ T=T.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ T.neg();
+ }
+ K=new ECP2(); K.copy(T);
+ K.dbl(); K.add(T); //K.affine();
+
+ K.frob(X);
+ Q.frob(X); Q.frob(X); Q.frob(X);
+ Q.add(T); Q.add(K);
+ T.frob(X); T.frob(X);
+ Q.add(T);
+
+ }
+
+/* Efficient hash maps to G2 on BLS curves - Budroni, Pintore */
+/* Q -> x2Q -xQ -Q +F(xQ -Q) +F(F(2Q)) */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BLS)
+ {
+ // ECP2 xQ,x2Q;
+ // xQ=new ECP2();
+ // x2Q=new ECP2();
+
+ ECP2 xQ=Q.mul(x);
+ ECP2 x2Q=xQ.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ xQ.neg();
+ }
+
+ x2Q.sub(xQ);
+ x2Q.sub(Q);
+
+ xQ.sub(Q);
+ xQ.frob(X);
+
+ Q.dbl();
+ Q.frob(X);
+ Q.frob(X);
+
+ Q.add(x2Q);
+ Q.add(xQ);
+ }
+ Q.affine();
+ return Q;
+ }
+
+ public static ECP2 generator()
+ {
+ return new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+ }
+
+/*
+ public static void main(String[] args) {
+ BIG r=new BIG(ROM.Modulus);
+
+ BIG Pxa=new BIG(ROM.CURVE_Pxa);
+ BIG Pxb=new BIG(ROM.CURVE_Pxb);
+ BIG Pya=new BIG(ROM.CURVE_Pya);
+ BIG Pyb=new BIG(ROM.CURVE_Pyb);
+
+ BIG Fra=new BIG(ROM.CURVE_Fra);
+ BIG Frb=new BIG(ROM.CURVE_Frb);
+
+ FP2 f=new FP2(Fra,Frb);
+
+ FP2 Px=new FP2(Pxa,Pxb);
+ FP2 Py=new FP2(Pya,Pyb);
+
+ ECP2 P=new ECP2(Px,Py);
+
+ System.out.println("P= "+P.toString());
+
+ P=P.mul(r);
+ System.out.println("P= "+P.toString());
+
+ ECP2 Q=new ECP2(Px,Py);
+ Q.frob(f);
+ System.out.println("Q= "+Q.toString());
+ } */
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/FP.java b/src/main/java/org/apache/milagro/amcl/BLS383/FP.java
new file mode 100644
index 0000000..0ec6de4
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.BLS383;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=383; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<23); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/FP12.java b/src/main/java/org/apache/milagro/amcl/BLS383/FP12.java
new file mode 100644
index 0000000..5ac3ac6
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/FP12.java
@@ -0,0 +1,907 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Fp^12 functions */
+/* FP12 elements are of the form a+i.b+i^2.c */
+
+package org.apache.milagro.amcl.BLS383;
+
+public final class FP12 {
+ private final FP4 a;
+ private final FP4 b;
+ private final FP4 c;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ c.reduce();
+ }
+/* normalise all components of this */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ c.norm();
+ }
+/* test x==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch() && c.iszilch());
+ }
+
+ public void cmove(FP12 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ c.cmove(g.c,d);
+ }
+
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(FP12 g[],int b)
+ {
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(g[0],teq(babs,0)); // conditional move
+ cmove(g[1],teq(babs,1));
+ cmove(g[2],teq(babs,2));
+ cmove(g[3],teq(babs,3));
+ cmove(g[4],teq(babs,4));
+ cmove(g[5],teq(babs,5));
+ cmove(g[6],teq(babs,6));
+ cmove(g[7],teq(babs,7));
+
+ FP12 invf=new FP12(this);
+ invf.conj();
+ cmove(invf,(int)(m&1));
+ }
+
+
+/* test x==1 ? */
+ public boolean isunity() {
+ FP4 one=new FP4(1);
+ return (a.equals(one) && b.iszilch() && c.iszilch());
+ }
+/* return 1 if x==y, else 0 */
+ public boolean equals(FP12 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b) && c.equals(x.c));
+ }
+/* extract a from this */
+ public FP4 geta()
+ {
+ return a;
+ }
+/* extract b */
+ public FP4 getb()
+ {
+ return b;
+ }
+/* extract c */
+ public FP4 getc()
+ {
+ return c;
+ }
+/* copy this=x */
+ public void copy(FP12 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ c.copy(x.c);
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ c.zero();
+ }
+/* this=conj(this) */
+ public void conj()
+ {
+ a.conj();
+ b.nconj();
+ c.conj();
+ }
+/* Constructors */
+ public FP12(FP4 d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(int d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(FP4 d,FP4 e,FP4 f)
+ {
+ a=new FP4(d);
+ b=new FP4(e);
+ c=new FP4(f);
+ }
+
+ public FP12(FP12 x)
+ {
+ a=new FP4(x.a);
+ b=new FP4(x.b);
+ c=new FP4(x.c);
+ }
+
+/* Granger-Scott Unitary Squaring */
+ public void usqr()
+ {
+//System.out.println("Into usqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(c);
+ FP4 C=new FP4(b);
+ FP4 D=new FP4(0);
+
+ a.sqr();
+ D.copy(a); D.add(a);
+ a.add(D);
+
+ a.norm();
+ A.nconj();
+
+ A.add(A);
+ a.add(A);
+ B.sqr();
+ B.times_i();
+
+ D.copy(B); D.add(B);
+ B.add(D);
+ B.norm();
+
+ C.sqr();
+ D.copy(C); D.add(C);
+ C.add(D);
+ C.norm();
+
+ b.conj();
+ b.add(b);
+ c.nconj();
+
+ c.add(c);
+ b.add(B);
+ c.add(C);
+//System.out.println("Out of usqr 1");
+ reduce();
+//System.out.println("Out of usqr 2");
+ }
+
+/* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+ public void sqr()
+ {
+//System.out.println("Into sqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(b);
+ FP4 C=new FP4(c);
+ FP4 D=new FP4(a);
+
+ A.sqr();
+ B.mul(c);
+ B.add(B);
+ B.norm();
+ C.sqr();
+ D.mul(b);
+ D.add(D);
+
+ c.add(a);
+ c.add(b);
+ c.norm();
+ c.sqr();
+
+ a.copy(A);
+
+ A.add(B);
+ A.norm();
+ A.add(C);
+ A.add(D);
+ A.norm();
+
+ A.neg();
+ B.times_i();
+ C.times_i();
+
+ a.add(B);
+
+ b.copy(C); b.add(D);
+ c.add(A);
+//System.out.println("Out of sqr");
+ norm();
+ }
+
+/* FP12 full multiplication this=this*y */
+ public void mul(FP12 y)
+ {
+//System.out.println("Into mul");
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(y.a);
+
+ z0.mul(y.a);
+ z2.mul(y.b);
+
+ t0.add(b);
+ t1.add(y.b);
+
+ t0.norm();
+ t1.norm();
+
+ z1.copy(t0); z1.mul(t1);
+ t0.copy(b); t0.add(c);
+
+ t1.copy(y.b); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ z3.copy(t0); z3.mul(t1);
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ z1.add(t0);
+ //z1.norm();
+ b.copy(z1); b.add(t1);
+
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+t0.norm();
+t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c); t0.mul(y.c);
+ t1.copy(t0); t1.neg();
+
+// z2.norm();
+// z3.norm();
+// b.norm();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ norm();
+//System.out.println("Out of mul");
+ }
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+ public void smul(FP12 y,int type)
+ {
+//System.out.println("Into smul");
+
+ if (type==ECP.D_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(b);
+ FP4 t0=new FP4(0);
+ FP4 t1=new FP4(y.a);
+ z0.mul(y.a);
+ z2.pmul(y.b.real());
+ b.add(a);
+ t1.real().add(y.b.real());
+
+ t1.norm();
+ b.norm();
+ b.mul(t1);
+ z3.add(c);
+ z3.norm();
+ z3.pmul(y.b.real());
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ b.add(t0);
+
+ b.add(t1);
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t0.norm();
+ z3.norm();
+ t0.mul(y.a);
+ c.copy(z2); c.add(t0);
+
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ if (type==ECP.M_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(0);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(0);
+
+ z0.mul(y.a);
+ t0.add(b);
+ t0.norm();
+
+ z1.copy(t0); z1.mul(y.a);
+ t0.copy(b); t0.add(c);
+ t0.norm();
+
+ z3.copy(t0); //z3.mul(y.c);
+ z3.pmul(y.c.getb());
+ z3.times_i();
+
+ t0.copy(z0); t0.neg();
+
+ z1.add(t0);
+ b.copy(z1);
+ z2.copy(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c);
+
+ t0.pmul(y.c.getb());
+ t0.times_i();
+
+ t1.copy(t0); t1.neg();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ norm();
+//System.out.println("Out of smul");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ FP4 f0=new FP4(a);
+ FP4 f1=new FP4(b);
+ FP4 f2=new FP4(a);
+ FP4 f3=new FP4(0);
+
+ norm();
+ f0.sqr();
+ f1.mul(c);
+ f1.times_i();
+ f0.sub(f1);
+ f0.norm();
+
+ f1.copy(c); f1.sqr();
+ f1.times_i();
+ f2.mul(b);
+ f1.sub(f2);
+ f1.norm();
+
+ f2.copy(b); f2.sqr();
+ f3.copy(a); f3.mul(c);
+ f2.sub(f3);
+ f2.norm();
+
+ f3.copy(b); f3.mul(f2);
+ f3.times_i();
+ a.mul(f0);
+ f3.add(a);
+ c.mul(f1);
+ c.times_i();
+
+ f3.add(c);
+ f3.norm();
+ f3.inverse();
+ a.copy(f0); a.mul(f3);
+ b.copy(f1); b.mul(f3);
+ c.copy(f2); c.mul(f3);
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ FP2 f2=new FP2(f);
+ FP2 f3=new FP2(f);
+
+ f2.sqr();
+ f3.mul(f2);
+
+ a.frob(f3);
+ b.frob(f3);
+ c.frob(f3);
+
+ b.pmul(f);
+ c.pmul(f2);
+ }
+
+/* trace function */
+ public FP4 trace()
+ {
+ FP4 t=new FP4(0);
+ t.copy(a);
+ t.imul(3);
+ t.reduce();
+ return t;
+ }
+
+/* convert from byte array to FP12 */
+ public static FP12 fromBytes(byte[] w)
+ {
+ BIG a,b;
+ FP2 c,d;
+ FP4 e,f,g;
+ byte[] t=new byte[BIG.MODBYTES];
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+2*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+3*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ e=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+4*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+5*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+6*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+7*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ f=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+8*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+9*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+10*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+11*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ g=new FP4(c,d);
+
+ return new FP12(e,f,g);
+ }
+
+/* convert this to byte array */
+ public void toBytes(byte[] w)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ a.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i]=t[i];
+ a.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+BIG.MODBYTES]=t[i];
+ a.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+2*BIG.MODBYTES]=t[i];
+ a.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+3*BIG.MODBYTES]=t[i];
+
+ b.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+4*BIG.MODBYTES]=t[i];
+ b.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+5*BIG.MODBYTES]=t[i];
+ b.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+6*BIG.MODBYTES]=t[i];
+ b.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+7*BIG.MODBYTES]=t[i];
+
+ c.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+8*BIG.MODBYTES]=t[i];
+ c.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+9*BIG.MODBYTES]=t[i];
+ c.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+10*BIG.MODBYTES]=t[i];
+ c.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+11*BIG.MODBYTES]=t[i];
+ }
+
+/* convert to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+","+c.toString()+"]");
+ }
+
+/* this=this^e */
+/* Note this is simple square and multiply, so not side-channel safe */
+ public FP12 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ BIG e3=new BIG(e);
+ e3.pmul(3);
+ e3.norm();
+
+ FP12 w=new FP12(this);
+
+ int nb=e3.nbits();
+ for (int i=nb-2;i>=1;i--)
+ {
+ w.usqr();
+ int bt=e3.bit(i)-e.bit(i);
+ if (bt==1)
+ w.mul(this);
+ if (bt==-1)
+ {
+ conj(); w.mul(this); conj();
+ }
+ }
+ w.reduce();
+ return w;
+
+
+/*
+ BIG z=new BIG(e);
+ FP12 r=new FP12(1);
+
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.usqr();
+ }
+ r.reduce();
+ return r; */
+ }
+
+/* constant time powering by small integer of max length bts */
+ public void pinpow(int e,int bts)
+ {
+ int i,b;
+ FP12 [] R=new FP12[2];
+ R[0]=new FP12(1);
+ R[1]=new FP12(this);
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ R[1-b].mul(R[b]);
+ R[b].usqr();
+ }
+ this.copy(R[0]);
+ }
+
+ public FP4 compow(BIG e,BIG r)
+ {
+ FP12 g1=new FP12(0);
+ FP12 g2=new FP12(0);
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.Modulus);
+
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(e);
+ a.mod(m);
+
+ BIG b=new BIG(e);
+ b.div(m);
+
+ g1.copy(this);
+ g2.copy(this);
+
+ FP4 c=g1.trace();
+
+ if (b.iszilch())
+ {
+ c=c.xtr_pow(e);
+ return c;
+ }
+
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+
+ return c;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ FP12 [] g=new FP12[8];
+ FP12 r=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ }
+ g[0]=new FP12(q[0]); // q[0]
+ g[1]=new FP12(g[0]); g[1].mul(q[1]); // q[0].q[1]
+ g[2]=new FP12(g[0]); g[2].mul(q[2]); // q[0].q[2]
+ g[3]=new FP12(g[1]); g[3].mul(q[2]); // q[0].q[1].q[2]
+ g[4]=new FP12(q[0]); g[4].mul(q[3]); // q[0].q[3]
+ g[5]=new FP12(g[1]); g[5].mul(q[3]); // q[0].q[1].q[3]
+ g[6]=new FP12(g[2]); g[6].mul(q[3]); // q[0].q[2].q[3]
+ g[7]=new FP12(g[3]); g[7].mul(q[3]); // q[0].q[1].q[2].q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ p.select(g,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ p.usqr();
+ r.select(g,(int)(2*w[i]+s[i]));
+ p.mul(r);
+ }
+
+ // apply correction
+ r.copy(q[0]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb);
+
+ p.reduce();
+ return p;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+/* Timing attack secure, but not cache attack secure */
+/*
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,m;
+ int[] a=new int[4];
+ FP12 [] g=new FP12[8];
+ FP12 [] s=new FP12[2];
+ FP12 c=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ t[i]=new BIG(u[i]);
+
+ s[0]=new FP12(0);
+ s[1]=new FP12(0);
+
+ g[0]=new FP12(q[0]); s[0].copy(q[1]); s[0].conj(); g[0].mul(s[0]);
+ g[1]=new FP12(g[0]);
+ g[2]=new FP12(g[0]);
+ g[3]=new FP12(g[0]);
+ g[4]=new FP12(q[0]); g[4].mul(q[1]);
+ g[5]=new FP12(g[4]);
+ g[6]=new FP12(g[4]);
+ g[7]=new FP12(g[4]);
+
+ s[1].copy(q[2]); s[0].copy(q[3]); s[0].conj(); s[1].mul(s[0]);
+ s[0].copy(s[1]); s[0].conj(); g[1].mul(s[0]);
+ g[2].mul(s[1]);
+ g[5].mul(s[0]);
+ g[6].mul(s[1]);
+ s[1].copy(q[2]); s[1].mul(q[3]);
+ s[0].copy(s[1]); s[0].conj(); g[0].mul(s[0]);
+ g[3].mul(s[1]);
+ g[4].mul(s[0]);
+ g[7].mul(s[1]);
+
+// if power is even add 1 to power, and add q to correction
+
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ c.mul(q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+ c.conj();
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+ p.copy(g[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ m=w[i]>>7;
+ j=(w[i]^m)-m; // j=abs(w[i])
+ j=(j-1)/2;
+ s[0].copy(g[j]); s[1].copy(g[j]); s[1].conj();
+ p.usqr();
+ p.mul(s[m&1]);
+ }
+ p.mul(c); // apply correction
+ p.reduce();
+ return p;
+ }
+*/
+/*
+ public static void main(String[] args) {
+ BIG p=new BIG(ROM.Modulus);
+ FP2 w0,w1;
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.zero(); b.zero(); a.inc(1); b.inc(2);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(3); b.inc(4);
+ w1=new FP2(a,b);
+ FP4 t0=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(5); b.inc(6);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(7); b.inc(8);
+ w1=new FP2(a,b);
+ FP4 t1=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(9); b.inc(10);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(11); b.inc(12);
+ w1=new FP2(a,b);
+ FP4 t2=new FP4(w0,w1);
+
+ FP12 w=new FP12(t0,t1,t2);
+ FP12 t=new FP12(w);
+
+ System.out.println("w= "+w.toString());
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ w.frob(f);
+ System.out.println("w= "+w.toString());
+
+ w=t.pow(p);
+
+ System.out.println("w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("w= "+w.toString());
+
+ t.copy(w);
+ w.conj();
+ t.inverse();
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)= "+w.toString());
+
+ t.copy(w);
+ w.frob(f);
+ w.frob(f);
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)(p^2+1)= "+w.toString());
+
+ t.copy(w);
+
+ t.inverse();
+ w.conj();
+
+ System.out.println("w= "+w.toString());
+ System.out.println("t= "+t.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/FP2.java b/src/main/java/org/apache/milagro/amcl/BLS383/FP2.java
new file mode 100644
index 0000000..2423571
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/FP2.java
@@ -0,0 +1,425 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^2 functions */
+
+/* FP2 elements are of the form a+ib, where i is sqrt(-1) */
+
+package org.apache.milagro.amcl.BLS383;
+
+public final class FP2 {
+ private final FP a;
+ private final FP b;
+
+/* reduce components mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+
+/* normalise components of w */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+
+/* test this=0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP2 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this=1 ? */
+ public boolean isunity() {
+ FP one=new FP(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test this=x */
+ public boolean equals(FP2 x) {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+
+/* Constructors */
+ public FP2(int c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(FP2 x)
+ {
+ a=new FP(x.a);
+ b=new FP(x.b);
+ }
+
+ public FP2(FP c,FP d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(BIG c,BIG d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(FP c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(BIG c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+/*
+ public BIG geta()
+ {
+ return a.tobig();
+ }
+*/
+/* extract a */
+ public BIG getA()
+ {
+ return a.redc();
+ }
+
+/* extract b */
+ public BIG getB()
+ {
+ return b.redc();
+ }
+
+/* copy this=x */
+ public void copy(FP2 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+
+/* negate this mod Modulus */
+ public void neg()
+ {
+ FP m=new FP(a);
+ FP t=new FP(0);
+
+ m.add(b);
+ m.neg();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ }
+
+/* set to a-ib */
+ public void conj()
+ {
+ b.neg();
+ b.norm();
+ }
+
+/* this+=a */
+ public void add(FP2 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+
+/* this-=a */
+ public void sub(FP2 x)
+ {
+ FP2 m=new FP2(x);
+ m.neg();
+ add(m);
+ }
+
+ public void rsub(FP2 x) // *****
+ {
+ neg();
+ add(x);
+ }
+
+/* this*=s, where s is an FP */
+ public void pmul(FP s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this*=i, where i is an int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+ FP w1=new FP(a);
+ FP w3=new FP(a);
+ FP mb=new FP(b);
+
+ w1.add(b);
+ mb.neg();
+
+ w3.add(a);
+ w3.norm();
+ b.mul(w3);
+
+ a.add(mb);
+
+ w1.norm();
+ a.norm();
+
+ a.mul(w1);
+ }
+
+/* this*=y */
+/* Now uses Lazy reduction */
+ public void mul(FP2 y)
+ {
+ if ((long)(a.XES+b.XES)*(y.a.XES+y.b.XES)>(long)FP.FEXCESS)
+ {
+ if (a.XES>1) a.reduce();
+ if (b.XES>1) b.reduce();
+ }
+
+ DBIG pR=new DBIG(0);
+ BIG C=new BIG(a.x);
+ BIG D=new BIG(y.a.x);
+
+ pR.ucopy(new BIG(ROM.Modulus));
+
+ DBIG A=BIG.mul(a.x,y.a.x);
+ DBIG B=BIG.mul(b.x,y.b.x);
+
+ C.add(b.x); C.norm();
+ D.add(y.b.x); D.norm();
+
+ DBIG E=BIG.mul(C,D);
+ DBIG F=new DBIG(A); F.add(B);
+ B.rsub(pR);
+
+ A.add(B); A.norm();
+ E.sub(F); E.norm();
+
+ a.x.copy(FP.mod(A)); a.XES=3;
+ b.x.copy(FP.mod(E)); b.XES=2;
+ }
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP w1=new FP(b);
+ FP w2=new FP(a);
+ w1.sqr(); w2.sqr(); w1.add(w2);
+ if (w1.jacobi()!=1) { zero(); return false; }
+ w1=w1.sqrt();
+ w2.copy(a); w2.add(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1)
+ {
+ w2.copy(a); w2.sub(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1) { zero(); return false; }
+ }
+ w2=w2.sqrt();
+ a.copy(w2);
+ w2.add(w2);
+ w2.inverse();
+ b.mul(w2);
+ return true;
+ }
+
+/* output to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ norm();
+ FP w1=new FP(a);
+ FP w2=new FP(b);
+
+ w1.sqr();
+ w2.sqr();
+ w1.add(w2);
+ w1.inverse();
+ a.mul(w1);
+ w1.neg();
+ w1.norm();
+ b.mul(w1);
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+/* this*=sqrt(-1) */
+ public void times_i()
+ {
+ FP z=new FP(a);
+ a.copy(b); a.neg();
+ b.copy(z);
+ }
+
+/* w*=(1+sqrt(-1)) */
+/* where X*2-(1+sqrt(-1)) is irreducible for FP4, assumes p=3 mod 8 */
+ public void mul_ip()
+ {
+ FP2 t=new FP2(this);
+ FP z=new FP(a);
+ a.copy(b);
+ a.neg();
+ b.copy(z);
+ add(t);
+ }
+
+ public void div_ip2()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ }
+
+/* w/=(1+sqrt(-1)) */
+ public void div_ip()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ div2();
+ }
+/*
+ public FP2 pow(BIG e)
+ {
+ int bt;
+ FP2 r=new FP2(1);
+ e.norm();
+ norm();
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(this);
+ if (e.iszilch()) break;
+ sqr();
+ }
+
+ r.reduce();
+ return r;
+ }
+
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(27);
+ BIG pp1=new BIG(m);
+ BIG pm1=new BIG(m);
+ BIG a=new BIG(1);
+ BIG b=new BIG(1);
+ FP2 w=new FP2(a,b);
+ FP2 z=new FP2(w);
+
+ byte[] RAW=new byte[100];
+
+ RAND rng=new RAND();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ // for (int i=0;i<100;i++)
+ // {
+ a.randomnum(rng);
+ b.randomnum(rng);
+
+ w=new FP2(a,b);
+ System.out.println("w="+w.toString());
+
+ z=new FP2(w);
+ z.inverse();
+ System.out.println("z="+z.toString());
+
+ z.inverse();
+ if (!z.equals(w)) System.out.println("Error");
+ // }
+
+// System.out.println("m="+m.toString());
+// w.sqr();
+// w.mul(z);
+
+ System.out.println("w="+w.toString());
+
+
+ pp1.inc(1); pp1.norm();
+ pm1.dec(1); pm1.norm();
+ System.out.println("p+1="+pp1.toString());
+ System.out.println("p-1="+pm1.toString());
+ w=w.pow(pp1);
+ w=w.pow(pm1);
+ System.out.println("w="+w.toString());
+ }
+*/
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/FP4.java b/src/main/java/org/apache/milagro/amcl/BLS383/FP4.java
new file mode 100644
index 0000000..1723f0a
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/FP4.java
@@ -0,0 +1,721 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^4 functions */
+
+/* FP4 elements are of the form a+ib, where i is sqrt(-1+sqrt(-1)) */
+
+package org.apache.milagro.amcl.BLS383;
+
+public final class FP4 {
+ private final FP2 a;
+ private final FP2 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP4 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP2 one=new FP2(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP2 real()
+ {
+ return a;
+ }
+
+ public FP2 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP2 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP4 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP4(int c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+
+ public FP4(FP4 x)
+ {
+ a=new FP2(x.a);
+ b=new FP2(x.b);
+ }
+
+ public FP4(FP2 c,FP2 d)
+ {
+ a=new FP2(c);
+ b=new FP2(d);
+ }
+
+ public FP4(FP2 c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+/* copy this=x */
+ public void copy(FP4 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP2 m=new FP2(a);
+ FP2 t=new FP2(0);
+ m.add(b);
+// m.norm();
+ m.neg();
+ // m.norm();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP4 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP4 x)
+ {
+ FP4 m=new FP4(x);
+ m.neg();
+ add(m);
+ }
+
+/* this*=s where s is FP2 */
+ public void pmul(FP2 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this=x-this */
+ public void rsub(FP4 x)
+ {
+ neg();
+ add(x);
+ }
+
+
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.mul_ip();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.mul_ip();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+/* this*=y */
+ public void mul(FP4 y)
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(0);
+ FP2 t4=new FP2(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ // t4.sub(t1);
+ // t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ // b.copy(t4);
+ // b.sub(t2);
+
+ t2.mul_ip();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.mul_ip();
+ t2.norm();
+ t1.sub(t2);
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP2 s=new FP2(b);
+ FP2 t=new FP2(b);
+ s.times_i();
+ t.add(s);
+ // t.norm();
+ b.copy(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ a.conj();
+ b.conj();
+ b.mul(f);
+ }
+
+/* this=this^e */
+ public FP4 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP4 w=new FP4(this);
+ BIG z=new BIG(e);
+ FP4 r=new FP4(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+/* XTR xtr_a function */
+ public void xtr_A(FP4 w,FP4 y,FP4 z)
+ {
+ FP4 r=new FP4(w);
+ FP4 t=new FP4(w);
+ //y.norm();
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP4 w=new FP4(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP4 xtr_pow(BIG n) {
+ FP4 a=new FP4(3);
+ FP4 b=new FP4(this);
+ FP4 c=new FP4(b);
+ c.xtr_D();
+ FP4 t=new FP4(0);
+ FP4 r=new FP4(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP4 xtr_pow2(FP4 ck,FP4 ckml,FP4 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP4 cu=new FP4(ck); // can probably be passed in w/o copying
+ FP4 cv=new FP4(this);
+ FP4 cumv=new FP4(ckml);
+ FP4 cum2v=new FP4(ckm2l);
+ FP4 r=new FP4(0);
+ FP4 t=new FP4(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+ public void div_i()
+ {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip();
+ a.copy(v);
+ b.copy(u);
+ }
+
+ public void div_2i() {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip2();
+ v.add(v); v.norm();
+ a.copy(v);
+ b.copy(u);
+ }
+
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP2 wa=new FP2(a);
+ FP2 ws=new FP2(b);
+ FP2 wt=new FP2(a);
+
+ if (ws.iszilch())
+ {
+ if (wt.sqrt())
+ {
+ a.copy(wt);
+ b.zero();
+ } else {
+ wt.div_ip();
+ wt.sqrt();
+ b.copy(wt);
+ a.zero();
+ }
+ return true;
+ }
+
+ ws.sqr();
+ wa.sqr();
+ ws.mul_ip();
+ ws.norm();
+ wa.sub(ws);
+
+ ws.copy(wa);
+ if (!ws.sqrt()) {
+ return false;
+ }
+
+ wa.copy(wt); wa.add(ws); wa.norm(); wa.div2();
+
+ if (!wa.sqrt()) {
+ wa.copy(wt); wa.sub(ws); wa.norm(); wa.div2();
+ if (!wa.sqrt()) {
+ return false;
+ }
+ }
+ wt.copy(b);
+ ws.copy(wa); ws.add(wa);
+ ws.inverse();
+
+ wt.mul(ws);
+ a.copy(wa);
+ b.copy(wt);
+
+ return true;
+ }
+
+/* this*=s where s is FP */
+ public void qmul(FP s)
+ {
+ a.pmul(s);
+ b.pmul(s);
+ }
+
+
+
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG e=new BIG(12);
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.inc(27); b.inc(45);
+
+ FP2 w0=new FP2(a,b);
+
+ a.zero(); b.zero();
+ a.inc(33); b.inc(54);
+
+ FP2 w1=new FP2(a,b);
+
+
+ FP4 w=new FP4(w0,w1);
+ FP4 t=new FP4(w);
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ System.out.println("w= "+w.toString());
+
+ w=w.pow(m);
+
+ System.out.println("w^p= "+w.toString());
+
+ t.frob(f);
+
+
+ System.out.println("w^p= "+t.toString());
+
+ w=w.pow(m);
+ w=w.pow(m);
+ w=w.pow(m);
+ System.out.println("w^p4= "+w.toString());
+
+
+ System.out.println("Test Inversion");
+
+ w=new FP4(w0,w1);
+
+ w.inverse();
+
+ System.out.println("1/w mod p^4 = "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/(1/w) mod p^4 = "+w.toString());
+
+ FP4 ww=new FP4(w);
+
+ w=w.xtr_pow(e);
+ System.out.println("w^e= "+w.toString());
+
+
+ a.zero(); b.zero();
+ a.inc(37); b.inc(17);
+ w0=new FP2(a,b);
+ a.zero(); b.zero();
+ a.inc(49); b.inc(31);
+ w1=new FP2(a,b);
+
+ FP4 c1=new FP4(w0,w1);
+ FP4 c2=new FP4(w0,w1);
+ FP4 c3=new FP4(w0,w1);
+
+ BIG e1=new BIG(3331);
+ BIG e2=new BIG(3372);
+
+ FP4 cr=w.xtr_pow2(c1,c2,c3,e1,e2);
+
+ System.out.println("c^e= "+cr.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/MPIN.java b/src/main/java/org/apache/milagro/amcl/BLS383/MPIN.java
new file mode 100644
index 0000000..4dde8c6
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/MPIN.java
@@ -0,0 +1,823 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* MPIN API Functions */
+
+package org.apache.milagro.amcl.BLS383;
+
+import java.util.Date;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public class MPIN
+{
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int PAS=16;
+ public static final int INVALID_POINT=-14;
+ public static final int BAD_PARAMS=-11;
+ public static final int WRONG_ORDER=-18;
+ public static final int BAD_PIN=-19;
+
+/* Configure your PIN here */
+
+ public static final int MAXPIN=10000; /* PIN less than this */
+ public static final int PBLEN=14; /* Number of bits in PIN */
+ public static final int TS=10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+ public static final int TRAP=200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN - approx 2*sqrt(MAXPIN) */
+
+// public static final int HASH_TYPE=SHA256;
+
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,int n,byte[] B,int len)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (n>0) H.process_num(n);
+
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+ byte[] W=new byte[len];
+
+ if (sha>=len)
+ for (int i=0;i<len;i++) W[i]=R[i];
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+len-sha]=R[i];
+ for (int i=0;i<len-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<len;i++) W[i]=0;
+ }
+ return W;
+ }
+
+ /* return time in slots since epoch */
+ public static int today() {
+ Date date=new Date();
+ return (int) (date.getTime()/(1000*60*1440));
+ }
+
+ public static byte[] HASH_ID(int sha,byte[] ID,int len)
+ {
+ return hashit(sha,0,ID,len);
+ }
+
+/* Hash the M-Pin transcript - new */
+
+ public static byte[] HASH_ALL(int sha,byte[] HID,byte[] xID,byte[] xCID,byte[] SEC,byte[] Y,byte[] R,byte[] W,int len)
+ {
+ int i,ilen,tlen=0;
+
+ ilen=HID.length+SEC.length+Y.length+R.length+W.length;
+ if (xCID!=null) ilen+=xCID.length;
+ else ilen+=xID.length;
+
+ byte[] T = new byte[ilen];
+
+ for (i=0;i<HID.length;i++) T[i]=HID[i];
+ tlen+=HID.length;
+ if (xCID!=null)
+ {
+ for (i=0;i<xCID.length;i++) T[i+tlen]=xCID[i];
+ tlen+=xCID.length;
+ }
+ else
+ {
+ for (i=0;i<xID.length;i++) T[i+tlen]=xID[i];
+ tlen+=xID.length;
+ }
+ for (i=0;i<SEC.length;i++) T[i+tlen]=SEC[i];
+ tlen+=SEC.length;
+ for (i=0;i<Y.length;i++) T[i+tlen]=Y[i];
+ tlen+=Y.length;
+ for (i=0;i<R.length;i++) T[i+tlen]=R[i];
+ tlen+=R.length;
+ for (i=0;i<W.length;i++) T[i+tlen]=W[i];
+ tlen+=W.length;
+
+ return hashit(sha,0,T,len);
+ }
+
+/* return time since epoch */
+ public static int GET_TIME() {
+ Date date=new Date();
+ return (int) (date.getTime()/1000);
+ }
+
+ public static byte[] mpin_hash(int sha,FP4 c,ECP U)
+ {
+ byte[] w=new byte[EFS];
+ byte[] t=new byte[6*EFS];
+ byte[] h=null;
+ c.geta().getA().toBytes(w); for (int i=0;i<EFS;i++) t[i]=w[i];
+ c.geta().getB().toBytes(w); for (int i=EFS;i<2*EFS;i++) t[i]=w[i-EFS];
+ c.getb().getA().toBytes(w); for (int i=2*EFS;i<3*EFS;i++) t[i]=w[i-2*EFS];
+ c.getb().getB().toBytes(w); for (int i=3*EFS;i<4*EFS;i++) t[i]=w[i-3*EFS];
+
+ U.getX().toBytes(w); for (int i=4*EFS;i<5*EFS;i++) t[i]=w[i-4*EFS];
+ U.getY().toBytes(w); for (int i=5*EFS;i<6*EFS;i++) t[i]=w[i-5*EFS];
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (h==null) return null;
+ byte[] R=new byte[ECP.AESKEY];
+ for (int i=0;i<ECP.AESKEY;i++) R[i]=h[i];
+ return R;
+ }
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+ public static ECP map(BIG u,int cb)
+ {
+ ECP P;
+ BIG x=new BIG(u);
+ BIG p=new BIG(ROM.Modulus);
+ x.mod(p);
+ while (true)
+ {
+ P=new ECP(x,cb);
+ if (!P.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+ return P;
+ }
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+ public static int unmap(BIG u,ECP P)
+ {
+ int s=P.getS();
+ ECP R;
+ int r=0;
+ BIG x=P.getX();
+ u.copy(x);
+ while (true)
+ {
+ u.dec(1); u.norm();
+ r++;
+ R=new ECP(u,s);
+ if (!R.is_infinity()) break;
+ }
+ return r;
+ }
+
+
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+ public static int ENCODING(RAND rng,byte[] E)
+ {
+ int rn,m,su,sv;
+ byte[] T=new byte[EFS];
+
+ for (int i=0;i<EFS;i++) T[i]=E[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=E[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ ECP P=new ECP(u,v);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG p=new BIG(ROM.Modulus);
+ u=BIG.randomnum(p,rng);
+
+ su=rng.getByte(); /*if (su<0) su=-su;*/ su%=2;
+
+ ECP W=map(u,su);
+ P.sub(W); //P.affine();
+ sv=P.getS();
+ rn=unmap(v,P);
+ m=rng.getByte(); /*if (m<0) m=-m;*/ m%=rn;
+ v.inc(m+1);
+ E[0]=(byte)(su+2*sv);
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+ public static int DECODING(byte[] D)
+ {
+ int su,sv;
+ byte[] T=new byte[EFS];
+
+ if ((D[0]&0x04)!=0) return INVALID_POINT;
+
+ for (int i=0;i<EFS;i++) T[i]=D[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=D[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ su=D[0]&1;
+ sv=(D[0]>>1)&1;
+ ECP W=map(u,su);
+ ECP P=map(v,sv);
+ P.add(W); //P.affine();
+ u=P.getX();
+ v=P.getY();
+ D[0]=0x04;
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+/* R=R1+R2 in group G1 */
+ public static int RECOMBINE_G1(byte[] R1,byte[] R2,byte[] R)
+ {
+ ECP P=ECP.fromBytes(R1);
+ ECP Q=ECP.fromBytes(R2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(R,false);
+ return 0;
+ }
+
+/* W=W1+W2 in group G2 */
+ public static int RECOMBINE_G2(byte[] W1,byte[] W2,byte[] W)
+ {
+ ECP2 P=ECP2.fromBytes(W1);
+ ECP2 Q=ECP2.fromBytes(W2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(W);
+ return 0;
+ }
+
+/* create random secret S */
+ public static int RANDOM_GENERATE(RAND rng,byte[] S)
+ {
+ BIG s;
+ BIG r=new BIG(ROM.CURVE_Order);
+ s=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+ return 0;
+ }
+
+/* Extract PIN from TOKEN for identity CID */
+ public static int EXTRACT_PIN(int sha,byte[] CID,int pin,byte[] TOKEN)
+ {
+ ECP P=ECP.fromBytes(TOKEN);
+ if (P.is_infinity()) return INVALID_POINT;
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R=ECP.mapit(h);
+
+
+ pin%=MAXPIN;
+
+ R=R.pinmul(pin,PBLEN);
+ P.sub(R); //P.affine();
+
+ P.toBytes(TOKEN,false);
+
+ return 0;
+ }
+
+/* Implement step 2 on client side of MPin protocol */
+ public static int CLIENT_2(byte[] X,byte[] Y,byte[] SEC)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ ECP P=ECP.fromBytes(SEC);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG px=BIG.fromBytes(X);
+ BIG py=BIG.fromBytes(Y);
+ px.add(py);
+ px.mod(r);
+ // px.rsub(r);
+
+ P=PAIR.G1mul(P,px);
+ P.neg();
+ P.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Implement step 1 on client side of MPin protocol */
+ public static int CLIENT_1(int sha,int date,byte[] CLIENT_ID,RAND rng,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG x;
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P,T,W;
+ BIG px;
+// byte[] t=new byte[EFS];
+
+ byte[] h=hashit(sha,0,CLIENT_ID,EFS);
+ P=ECP.mapit(h);
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ pin%=MAXPIN;
+ W=P.pinmul(pin,PBLEN);
+ T.add(W);
+ if (date!=0)
+ {
+ W=ECP.fromBytes(PERMIT);
+ if (W.is_infinity()) return INVALID_POINT;
+ T.add(W);
+ h=hashit(sha,date,h,EFS);
+ W=ECP.mapit(h);
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ W=PAIR.G1mul(W,x);
+ P.add(W);
+ //P.affine();
+ }
+ else
+ {
+ P.add(W); //P.affine();
+ P=PAIR.G1mul(P,x);
+ }
+ if (xCID!=null) P.toBytes(xCID,false);
+ }
+ else
+ {
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ }
+ }
+
+ //T.affine();
+ T.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+ public static int GET_SERVER_SECRET(byte[] S,byte[] SST)
+ {
+ ECP2 Q=ECP2.generator();
+ BIG s=BIG.fromBytes(S);
+ Q=PAIR.G2mul(Q,s);
+ Q.toBytes(SST);
+ return 0;
+ }
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+ public static int GET_G1_MULTIPLE(RAND rng, int type,byte[] X,byte[] G,byte[] W)
+ {
+ BIG x;
+ BIG r=new BIG(ROM.CURVE_Order);
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P;
+ if (type==0)
+ {
+ P=ECP.fromBytes(G);
+ if (P.is_infinity()) return INVALID_POINT;
+ }
+ else
+ P=ECP.mapit(G);
+
+ PAIR.G1mul(P,x).toBytes(W,false);
+ return 0;
+ }
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+ public static int GET_CLIENT_SECRET(byte[] S,byte[] CID,byte[] CST)
+ {
+ return GET_G1_MULTIPLE(null,1,S,CID,CST);
+ }
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+ public static int GET_CLIENT_PERMIT(int sha,int date,byte[] S,byte[] CID,byte[] CTT)
+ {
+ byte[] h=hashit(sha,date,CID,EFS);
+ ECP P=ECP.mapit(h);
+
+ BIG s=BIG.fromBytes(S);
+ ECP OP=PAIR.G1mul(P,s);
+
+ OP.toBytes(CTT,false);
+ return 0;
+ }
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+ public static void SERVER_1(int sha,int date,byte[] CID,byte[] HID,byte[] HTID)
+ {
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R,P=ECP.mapit(h);
+
+ P.toBytes(HID,false); // new
+ if (date!=0)
+ {
+ // if (HID!=null) P.toBytes(HID);
+ h=hashit(sha,date,h,EFS);
+ R=ECP.mapit(h);
+ P.add(R); //P.affine();
+ P.toBytes(HTID,false);
+ }
+ // else P.toBytes(HID,false);
+ }
+
+/* Implement step 2 of MPin protocol on server side */
+ public static int SERVER_2(int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] mSEC,byte[] E,byte[] F)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ ECP2 Q=ECP2.generator();
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+
+ ECP R;
+ if (date!=0)
+ R=ECP.fromBytes(xCID);
+ else
+ {
+ if (xID==null) return BAD_PARAMS;
+ R=ECP.fromBytes(xID);
+ }
+ if (R.is_infinity()) return INVALID_POINT;
+
+ BIG y=BIG.fromBytes(Y);
+ ECP P;
+ if (date!=0) P=ECP.fromBytes(HTID);
+ else
+ {
+ if (HID==null) return BAD_PARAMS;
+ P=ECP.fromBytes(HID);
+ }
+
+ if (P.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ R=ECP.fromBytes(mSEC);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ FP12 g;
+
+ g=PAIR.ate2(Q,R,sQ,P);
+ g=PAIR.fexp(g);
+
+ if (!g.isunity())
+ {
+ if (HID!=null && xID!=null && E!=null && F!=null)
+ {
+ g.toBytes(E);
+ if (date!=0)
+ {
+ P=ECP.fromBytes(HID);
+ if (P.is_infinity()) return INVALID_POINT;
+ R=ECP.fromBytes(xID);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ }
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(F);
+ }
+ return BAD_PIN;
+ }
+
+ return 0;
+ }
+
+/* Pollards kangaroos used to return PIN error */
+ public static int KANGAROO(byte[] E,byte[] F)
+ {
+ FP12 ge=FP12.fromBytes(E);
+ FP12 gf=FP12.fromBytes(F);
+ int[] distance = new int[TS];
+ FP12 t=new FP12(gf);
+ FP12[] table=new FP12[TS];
+ int i,j,m,s,dn,dm,res,steps;
+
+ s=1;
+ for (m=0;m<TS;m++)
+ {
+ distance[m]=s;
+ table[m]=new FP12(t);
+ s*=2;
+ t.usqr();
+ }
+ t.one();
+ dn=0;
+ for (j=0;j<TRAP;j++)
+ {
+ i=t.geta().geta().getA().lastbits(20)%TS;
+ t.mul(table[i]);
+ dn+=distance[i];
+ }
+ gf.copy(t); gf.conj();
+ steps=0; dm=0;
+ res=0;
+ while (dm-dn<MAXPIN)
+ {
+ steps++;
+ if (steps>4*TRAP) break;
+ i=ge.geta().geta().getA().lastbits(20)%TS;
+ ge.mul(table[i]);
+ dm+=distance[i];
+ if (ge.equals(t))
+ {
+ res=dm-dn;
+ break;
+ }
+ if (ge.equals(gf))
+ {
+ res=dn-dm;
+ break;
+ }
+
+ }
+ if (steps>4*TRAP || dm-dn>=MAXPIN) {res=0; } // Trap Failed - probable invalid token
+ return res;
+ }
+
+/* Functions to support M-Pin Full */
+
+ public static int PRECOMPUTE(byte[] TOKEN,byte[] CID,byte[] G1,byte[] G2)
+ {
+ ECP P,T;
+ FP12 g;
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ P=ECP.mapit(CID);
+
+ ECP2 Q=ECP2.generator();
+
+ g=PAIR.ate(Q,T);
+ g=PAIR.fexp(g);
+ g.toBytes(G1);
+
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(G2);
+
+ return 0;
+ }
+
+
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+ public static int CLIENT_KEY(int sha,byte[] G1,byte[] G2,int pin,byte[] R,byte[] X,byte[] H,byte[] wCID,byte[] CK)
+ {
+ byte[] t;
+
+ FP12 g1=FP12.fromBytes(G1);
+ FP12 g2=FP12.fromBytes(G2);
+ BIG z=BIG.fromBytes(R);
+ BIG x=BIG.fromBytes(X);
+ BIG h=BIG.fromBytes(H);
+
+ ECP W=ECP.fromBytes(wCID);
+ if (W.is_infinity()) return INVALID_POINT;
+
+ W=PAIR.G1mul(W,x);
+
+// FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG r=new BIG(ROM.CURVE_Order);
+// BIG q=new BIG(ROM.Modulus);
+
+ z.add(h); //new
+ z.mod(r);
+
+ g2.pinpow(pin,PBLEN);
+ g1.mul(g2);
+
+ FP4 c=g1.compow(z,r);
+/*
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(z);
+ a.mod(m);
+
+ BIG b=new BIG(z);
+ b.div(m);
+
+
+ FP4 c=g1.trace();
+ g2.copy(g1);
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+*/
+ t=mpin_hash(sha,c,W);
+
+ for (int i=0;i<ECP.AESKEY;i++) CK[i]=t[i];
+
+ return 0;
+ }
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+ public static int SERVER_KEY(int sha,byte[] Z,byte[] SST,byte[] W,byte[] H,byte[] HID,byte[] xID,byte[] xCID,byte[] SK)
+ {
+ byte[] t;
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+ ECP R=ECP.fromBytes(Z);
+ if (R.is_infinity()) return INVALID_POINT;
+ ECP A=ECP.fromBytes(HID);
+ if (A.is_infinity()) return INVALID_POINT;
+
+ ECP U;
+ if (xCID!=null)
+ U=ECP.fromBytes(xCID);
+ else
+ U=ECP.fromBytes(xID);
+ if (U.is_infinity()) return INVALID_POINT;
+
+ BIG w=BIG.fromBytes(W);
+ BIG h=BIG.fromBytes(H);
+ A=PAIR.G1mul(A,h); // new
+ R.add(A); //R.affine();
+
+ U=PAIR.G1mul(U,w);
+ FP12 g=PAIR.ate(sQ,R);
+ g=PAIR.fexp(g);
+
+ FP4 c=g.trace();
+
+ t=mpin_hash(sha,c,U);
+
+ for (int i=0;i<ECP.AESKEY;i++) SK[i]=t[i];
+
+ return 0;
+ }
+
+/* Generate Y = H(epoch, xCID/xID) */
+ public static void GET_Y(int sha,int TimeValue,byte[] xCID,byte[] Y)
+ {
+ byte[] h = hashit(sha,TimeValue,xCID,EFS);
+ BIG y = BIG.fromBytes(h);
+ BIG q=new BIG(ROM.CURVE_Order);
+ y.mod(q);
+ //if (ROM.AES_S>0)
+ //{
+ // y.mod2m(2*ROM.AES_S);
+ //}
+ y.toBytes(Y);
+ }
+
+/* One pass MPIN Client */
+ public static int CLIENT(int sha,int date,byte[] CLIENT_ID,RAND RNG,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT, int TimeValue, byte[] Y)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ rtn = CLIENT_1(sha,date,CLIENT_ID,RNG,X,pin,TOKEN,SEC,xID,xCID,PERMIT);
+ if (rtn != 0)
+ return rtn;
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+
+/* One pass MPIN Server */
+ public static int SERVER(int sha,int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] SEC,byte[] E,byte[] F,byte[] CID, int TimeValue)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ SERVER_1(sha,date,CID,HID,HTID);
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = SERVER_2(date,HID,HTID,Y,SST,xID,xCID,SEC,E,F);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/PAIR.java b/src/main/java/org/apache/milagro/amcl/BLS383/PAIR.java
new file mode 100644
index 0000000..24341da
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/PAIR.java
@@ -0,0 +1,817 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BN Curve Pairing functions */
+
+package org.apache.milagro.amcl.BLS383;
+
+public final class PAIR {
+
+ public static final boolean USE_GLV =true;
+ public static final boolean USE_GS_G2 =true;
+ public static final boolean USE_GS_GT =true;
+ public static final boolean GT_STRONG=false;
+
+
+/* Line function */
+ public static FP12 line(ECP2 A,ECP2 B,FP Qx,FP Qy)
+ {
+//System.out.println("Into line");
+ FP4 a,b,c; // Edits here
+// c=new FP4(0);
+ if (A==B)
+ { // Doubling
+ FP2 XX=new FP2(A.getx()); //X
+ FP2 YY=new FP2(A.gety()); //Y
+ FP2 ZZ=new FP2(A.getz()); //Z
+ FP2 YZ=new FP2(YY); //Y
+ YZ.mul(ZZ); //YZ
+ XX.sqr(); //X^2
+ YY.sqr(); //Y^2
+ ZZ.sqr(); //Z^2
+
+ YZ.imul(4);
+ YZ.neg(); YZ.norm(); //-2YZ
+ YZ.pmul(Qy); //-2YZ.Ys
+
+ XX.imul(6); //3X^2
+ XX.pmul(Qx); //3X^2.Xs
+
+ int sb=3*ROM.CURVE_B_I;
+ ZZ.imul(sb);
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ ZZ.div_ip2();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ ZZ.mul_ip();
+ ZZ.add(ZZ);
+ YZ.mul_ip();
+ YZ.norm();
+ }
+
+ ZZ.norm(); // 3b.Z^2
+
+ YY.add(YY);
+ ZZ.sub(YY); ZZ.norm(); // 3b.Z^2-Y^2
+
+ a=new FP4(YZ,ZZ); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(XX); // L(0,1) | L(0,0) | L(1,0)
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(XX); c.times_i();
+ }
+ A.dbl();
+ }
+ else
+ { // Addition - assume B is affine
+
+ FP2 X1=new FP2(A.getx()); // X1
+ FP2 Y1=new FP2(A.gety()); // Y1
+ FP2 T1=new FP2(A.getz()); // Z1
+ FP2 T2=new FP2(A.getz()); // Z1
+
+ T1.mul(B.gety()); // T1=Z1.Y2
+ T2.mul(B.getx()); // T2=Z1.X2
+
+ X1.sub(T2); X1.norm(); // X1=X1-Z1.X2
+ Y1.sub(T1); Y1.norm(); // Y1=Y1-Z1.Y2
+
+ T1.copy(X1); // T1=X1-Z1.X2
+ X1.pmul(Qy); // X1=(X1-Z1.X2).Ys
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X1.mul_ip();
+ X1.norm();
+ }
+
+ T1.mul(B.gety()); // T1=(X1-Z1.X2).Y2
+
+ T2.copy(Y1); // T2=Y1-Z1.Y2
+ T2.mul(B.getx()); // T2=(Y1-Z1.Y2).X2
+ T2.sub(T1); T2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+ Y1.pmul(Qx); Y1.neg(); Y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+ a=new FP4(X1,T2); // (X1-Z1.X2).Ys | (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2 | - (Y1-Z1.Y2).Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(Y1);
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(Y1); c.times_i();
+ }
+ A.add(B);
+ }
+//System.out.println("Out of line");
+ return new FP12(a,b,c);
+ }
+
+/* Optimal R-ate pairing */
+ public static FP12 ate(ECP2 P1,ECP Q1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+
+ ECP2 A=new ECP2();
+ FP12 r=new FP12(1);
+ A.copy(P);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ //r.conj();
+ A.neg();
+ }
+ K.copy(P);
+ K.frob(f);
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+ public static FP12 ate2(ECP2 P1,ECP Q1,ECP2 R1,ECP S1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ ECP2 R=new ECP2(R1);
+ ECP S=new ECP(S1);
+
+ R.affine();
+ S.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+ FP Sx=new FP(S.getx());
+ FP Sy=new FP(S.gety());
+
+ ECP2 A=new ECP2();
+ ECP2 B=new ECP2();
+ FP12 r=new FP12(1);
+
+ A.copy(P);
+ B.copy(R);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+ ECP2 MR=new ECP2();
+ MR.copy(R); MR.neg();
+
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ lv=line(B,B,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ lv=line(B,R,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ //R.neg();
+ lv=line(B,MR,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //R.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ // r.conj();
+ A.neg();
+ B.neg();
+ }
+
+ K.copy(P);
+ K.frob(f);
+
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.copy(R);
+ K.frob(f);
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+ public static FP12 fexp(FP12 m)
+ {
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ FP12 r=new FP12(m);
+
+/* Easy part of final exp */
+ FP12 lv=new FP12(r);
+ lv.inverse();
+ r.conj();
+
+ r.mul(lv);
+ lv.copy(r);
+ r.frob(f);
+ r.frob(f);
+ r.mul(lv);
+/* Hard part of final exp */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ FP12 x0,x1,x2,x3,x4,x5;
+ lv.copy(r);
+ lv.frob(f);
+ x0=new FP12(lv);
+ x0.frob(f);
+ lv.mul(r);
+ x0.mul(lv);
+ x0.frob(f);
+ x1=new FP12(r);
+ x1.conj();
+ x4=r.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x4.conj();
+ }
+
+ x3=new FP12(x4);
+ x3.frob(f);
+
+ x2=x4.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x2.conj();
+ }
+ x5=new FP12(x2); x5.conj();
+ lv=x2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ lv.conj();
+ }
+ x2.frob(f);
+ r.copy(x2); r.conj();
+
+ x4.mul(r);
+ x2.frob(f);
+
+ r.copy(lv);
+ r.frob(f);
+ lv.mul(r);
+
+ lv.usqr();
+ lv.mul(x4);
+ lv.mul(x5);
+ r.copy(x3);
+ r.mul(x5);
+ r.mul(lv);
+ lv.mul(x2);
+ r.usqr();
+ r.mul(lv);
+ r.usqr();
+ lv.copy(r);
+ lv.mul(x1);
+ r.mul(x0);
+ lv.usqr();
+ r.mul(lv);
+ r.reduce();
+ }
+ else
+ {
+
+ FP12 y0,y1,y2,y3;
+// Ghamman & Fouotsa Method
+ y0=new FP12(r); y0.usqr();
+ y1=y0.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y1.conj();
+ }
+ x.fshr(1); y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+
+ x.fshl(1);
+ y3=new FP12(r); y3.conj();
+ y1.mul(y3);
+
+ y1.conj();
+ y1.mul(y2);
+
+ y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y3=y2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y3.conj();
+ }
+ y1.conj();
+ y3.mul(y1);
+
+ y1.conj();
+ y1.frob(f); y1.frob(f); y1.frob(f);
+ y2.frob(f); y2.frob(f);
+ y1.mul(y2);
+
+ y2=y3.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y2.mul(y0);
+ y2.mul(r);
+
+ y1.mul(y2);
+ y2.copy(y3); y2.frob(f);
+ y1.mul(y2);
+ r.copy(y1);
+ r.reduce();
+ }
+
+ return r;
+ }
+
+/* GLV method */
+ public static BIG[] glv(BIG e)
+ {
+ BIG[] u=new BIG[2];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+
+ BIG[] v=new BIG[2];
+ for (i=0;i<2;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_W[i])); // why not just t=new BIG(ROM.CURVE_W[i]);
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<2;i++)
+ for (j=0;j<2;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_SB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ { // -(x^2).P = (Beta.x,y)
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG x2=BIG.smul(x,x);
+ u[0]=new BIG(e);
+ u[0].mod(x2);
+ u[1]=new BIG(e);
+ u[1].div(x2);
+ u[1].rsub(q);
+ }
+ return u;
+ }
+
+/* Galbraith & Scott Method */
+ public static BIG[] gs(BIG e)
+ {
+ BIG[] u=new BIG[4];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] v=new BIG[4];
+ for (i=0;i<4;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_WB[i]));
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<4;i++)
+ for (j=0;j<4;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_BB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ {
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG w=new BIG(e);
+ for (int i=0;i<3;i++)
+ {
+ u[i]=new BIG(w);
+ u[i].mod(x);
+ w.div(x);
+ }
+ u[3]=new BIG(w);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ u[1].copy(BIG.modneg(u[1],q));
+ u[3].copy(BIG.modneg(u[3],q));
+ }
+ }
+ return u;
+ }
+
+/* Multiply P by e in group G1 */
+ public static ECP G1mul(ECP P,BIG e)
+ {
+ ECP R;
+ if (USE_GLV)
+ {
+ //P.affine();
+ R=new ECP();
+ R.copy(P);
+ int i,np,nn;
+ ECP Q=new ECP();
+ Q.copy(P); Q.affine();
+ BIG q=new BIG(ROM.CURVE_Order);
+ FP cru=new FP(new BIG(ROM.CURVE_Cru));
+ BIG t=new BIG(0);
+ BIG[] u=glv(e);
+ Q.getx().mul(cru);
+
+ np=u[0].nbits();
+ t.copy(BIG.modneg(u[0],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[0].copy(t);
+ R.neg();
+ }
+
+ np=u[1].nbits();
+ t.copy(BIG.modneg(u[1],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[1].copy(t);
+ Q.neg();
+ }
+ u[0].norm();
+ u[1].norm();
+ R=R.mul2(u[0],Q,u[1]);
+
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* Multiply P by e in group G2 */
+ public static ECP2 G2mul(ECP2 P,BIG e)
+ {
+ ECP2 R;
+ if (USE_GS_G2)
+ {
+ ECP2[] Q=new ECP2[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] u=gs(e);
+
+ BIG t=new BIG(0);
+ int i,np,nn;
+ //P.affine();
+
+ Q[0]=new ECP2(); Q[0].copy(P);
+ for (i=1;i<4;i++)
+ {
+ Q[i]=new ECP2(); Q[i].copy(Q[i-1]);
+ Q[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ Q[i].neg();
+ }
+ u[i].norm();
+ //Q[i].affine();
+ }
+
+ R=ECP2.mul4(Q,u);
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */
+ public static FP12 GTpow(FP12 d,BIG e)
+ {
+ FP12 r;
+ if (USE_GS_GT)
+ {
+ FP12[] g=new FP12[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG t=new BIG(0);
+ int i,np,nn;
+ BIG[] u=gs(e);
+
+ g[0]=new FP12(d);
+ for (i=1;i<4;i++)
+ {
+ g[i]=new FP12(0); g[i].copy(g[i-1]);
+ g[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ g[i].conj();
+ }
+ u[i].norm();
+ }
+ r=FP12.pow4(g,u);
+ }
+ else
+ {
+ r=d.pow(e);
+ }
+ return r;
+ }
+
+/* test group membership - no longer needed */
+/* with GT-Strong curve, now only check that m!=1, conj(m)*m==1, and m.m^{p^4}=m^{p^2} */
+/*
+ public static boolean GTmember(FP12 m)
+ {
+ if (m.isunity()) return false;
+ FP12 r=new FP12(m);
+ r.conj();
+ r.mul(m);
+ if (!r.isunity()) return false;
+
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ r.copy(m); r.frob(f); r.frob(f);
+ FP12 w=new FP12(r); w.frob(f); w.frob(f);
+ w.mul(m);
+ if (!ROM.GT_STRONG)
+ {
+ if (!w.equals(r)) return false;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ r.copy(m); w=r.pow(x); w=w.pow(x);
+ r.copy(w); r.sqr(); r.mul(w); r.sqr();
+ w.copy(m); w.frob(f);
+ }
+ return w.equals(r);
+ }
+*/
+/*
+ public static void main(String[] args) {
+ ECP Q=new ECP(new BIG(ROM.CURVE_Gx),new BIG(ROM.CURVE_Gy));
+ ECP2 P=new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG xa=new BIG(ROM.CURVE_Pxa);
+
+ System.out.println("P= "+P.toString());
+ System.out.println("Q= "+Q.toString());
+
+ BIG m=new BIG(17);
+
+ FP12 e=ate(P,Q);
+ System.out.println("\ne= "+e.toString());
+
+ e=fexp(e);
+
+ for (int i=1;i<1000;i++)
+ {
+ e=ate(P,Q);
+ e=fexp(e);
+ }
+ // e=GTpow(e,m);
+
+ System.out.println("\ne= "+e.toString());
+
+ BIG [] GLV=glv(r);
+
+ System.out.println("GLV[0]= "+GLV[0].toString());
+ System.out.println("GLV[0]= "+GLV[1].toString());
+
+ ECP G=new ECP(); G.copy(Q);
+ ECP2 R=new ECP2(); R.copy(P);
+
+
+ e=ate(R,Q);
+ e=fexp(e);
+
+ e=GTpow(e,xa);
+ System.out.println("\ne= "+e.toString());
+
+
+ R=G2mul(R,xa);
+ e=ate(R,G);
+ e=fexp(e);
+
+ System.out.println("\ne= "+e.toString());
+
+ G=G1mul(G,xa);
+ e=ate(P,G);
+ e=fexp(e);
+ System.out.println("\ne= "+e.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS383/ROM.java b/src/main/java/org/apache/milagro/amcl/BLS383/ROM.java
new file mode 100644
index 0000000..15b0dc2
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS383/ROM.java
@@ -0,0 +1,55 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.BLS383;
+
+public class ROM
+{
+
+// Base Bits= 58
+public static final long[] Modulus= {0x2371D6485AAB0ABL,0x30FCA6299214AF6L,0x3801696124F47A8L,0xB3CD969446B0C6L,0x1FEA9284A0AD46L,0x12ADBAD681B6B71L,0x556556956L};
+public static final long[] R2modp= {0x80B6E0116907F4L,0xCF53CF9752AC11L,0x35D47189941C581L,0x19D0835CB1E4D22L,0x16963E90A0FC49BL,0x367FB9DB3852312L,0x4DFECE397L};
+public static final long MConst= 0x1BC0571073435FDL;
+public static final long[] Fra= {0x52D72D3311DAC1L,0x24D203F99DCF806L,0x344AE550D8C8A36L,0x348FEE86A1A0959L,0x2C11B52F10E4C6CL,0x9FDA2F0CE2E7F0L,0x22ACD5BF0L};
+public static final long[] Frb= {0x1E446375298D5EAL,0xC2AA22FF4452F0L,0x3B684104C2BD72L,0x16ACEAE2A2CA76DL,0x15ECF3F939260D9L,0x8B017E5B388380L,0x32B880D66L};
+
+public static final int CURVE_A= 0;
+public static final int CURVE_Cof_I= 0;
+public static final long[] CURVE_Cof= {0x150556155169EABL,0x2AAB0002AAEFFEDL,0x555L,0x0L,0x0L,0x0L,0x0L};
+public static final int CURVE_B_I= 15;
+public static final long[] CURVE_B= {0xFL,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Order= {0x32099EBFEBC0001L,0x17C25684834E5CEL,0x1C81698B381DE0L,0x2003002E0270110L,0x1002001L,0x0L,0x0L};
+public static final long[] CURVE_Gx= {0xC4773908734573L,0x176FC20FD1DC11EL,0x3AD84AF1E3445C5L,0x1DAC207D0B0BE1EL,0x52DDB050F31D9FL,0x25E7B3938E0D7D0L,0x41FCBA55BL};
+public static final long[] CURVE_Gy= {0x12D165E8003F224L,0x1F527B21FE63F48L,0xA94ADEB4D2DDE5L,0x319AED912441D4CL,0x1C31C46D99D0DADL,0x133ECC00092BA73L,0x68F16727L};
+
+public static final long[] CURVE_Bnx= {0x8000001001200L,0x40L,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Cru= {0xC367502EAAC2A9L,0x17DA068B7D974B7L,0x2F4A34DEA341BC2L,0xD36F75C5738948L,0x6E94874605445L,0x12ADBAD28116AD1L,0x556556956L};
+public static final long[] CURVE_Pxa= {0x3CB3B62D7F2D86L,0x3F6AD9E57474F85L,0x1C90F562572EE81L,0x3214B55C96F51FCL,0x27CB1E746432501L,0x1FB00FA301E6425L,0x634D2240L};
+public static final long[] CURVE_Pxb= {0x3D9E41EC452DE15L,0x12ACA355FF9837BL,0xBA88E92D5D75B5L,0x3B6741732277F66L,0x3288361DD24F498L,0x592EBCDE9DC5L,0x300D78006L};
+public static final long[] CURVE_Pya= {0x68F0BB9408CB41L,0x27B793C83586597L,0x3ACA913A2E75B4L,0x359CF266CF9A25EL,0x33FE6347B6E990EL,0x34894D1F2527615L,0x33792CF93L};
+public static final long[] CURVE_Pyb= {0x2D846437F479093L,0x10F2C379889218EL,0x32F449F7BC98B01L,0x111ACFBEA3DEBC2L,0x3D15A7AE001CE0DL,0xB3631AC93B9EE9L,0x20E5247DDL};
+public static final long[][] CURVE_W= {{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}};
+public static final long[][][] CURVE_SB= {{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}}};
+public static final long[][] CURVE_WB= {{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}};
+public static final long[][][] CURVE_BB= {{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}}};
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/BIG.java b/src/main/java/org/apache/milagro/amcl/BLS461/BIG.java
new file mode 100644
index 0000000..34077cc
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.BLS461;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=58; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=60;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/DBIG.java b/src/main/java/org/apache/milagro/amcl/BLS461/DBIG.java
new file mode 100644
index 0000000..c4b639a
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.BLS461;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/ECDH.java b/src/main/java/org/apache/milagro/amcl/BLS461/ECDH.java
new file mode 100644
index 0000000..a4bce39
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.BLS461;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/ECP.java b/src/main/java/org/apache/milagro/amcl/BLS461/ECP.java
new file mode 100644
index 0000000..cc790da
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.BLS461;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=BLS;
+ public static final int SEXTIC_TWIST=M_TYPE;
+ public static final int SIGN_OF_X=NEGATIVEX;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/ECP2.java b/src/main/java/org/apache/milagro/amcl/BLS461/ECP2.java
new file mode 100644
index 0000000..0d3303c
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/ECP2.java
@@ -0,0 +1,796 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Weierstrass elliptic curve functions over FP2 */
+
+package org.apache.milagro.amcl.BLS461;
+
+public final class ECP2 {
+ private FP2 x;
+ private FP2 y;
+ private FP2 z;
+// private boolean INF;
+
+/* Constructor - set this=O */
+ public ECP2() {
+// INF=true;
+ x=new FP2(0);
+ y=new FP2(1);
+ z=new FP2(0);
+ }
+
+ public ECP2(ECP2 e) {
+ this.x = new FP2(e.x);
+ this.y = new FP2(e.y);
+ this.z = new FP2(e.z);
+ }
+
+/* Test this=O? */
+ public boolean is_infinity() {
+// if (INF) return true; //******
+ return (x.iszilch() && z.iszilch());
+ }
+/* copy this=P */
+ public void copy(ECP2 P)
+ {
+ x.copy(P.x);
+ y.copy(P.y);
+ z.copy(P.z);
+// INF=P.INF;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ y.one();
+ z.zero();
+ }
+
+/* Conditional move of Q to P dependant on d */
+ public void cmove(ECP2 Q,int d)
+ {
+ x.cmove(Q.x,d);
+ y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ }
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(ECP2 W[],int b)
+ {
+ ECP2 MP=new ECP2();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test if P == Q */
+ public boolean equals(ECP2 Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+
+ FP2 a=new FP2(x); // *****
+ FP2 b=new FP2(Q.x);
+ a.mul(Q.z);
+ b.mul(z);
+ if (!a.equals(b)) return false;
+
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+
+ return true;
+ }
+/* set this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ y.norm();
+ y.neg(); y.norm();
+ return;
+ }
+/* set to Affine - (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return;
+ FP2 one=new FP2(1);
+ if (z.equals(one))
+ {
+ x.reduce();
+ y.reduce();
+ return;
+ }
+ z.inverse();
+
+ x.mul(z); x.reduce(); // *****
+ y.mul(z); y.reduce();
+ z.copy(one);
+ }
+/* extract affine x as FP2 */
+ public FP2 getX()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.x;
+ }
+/* extract affine y as FP2 */
+ public FP2 getY()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.y;
+ }
+/* extract projective x */
+ public FP2 getx()
+ {
+ return x;
+ }
+/* extract projective y */
+ public FP2 gety()
+ {
+ return y;
+ }
+/* extract projective z */
+ public FP2 getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP2 W=new ECP2(this);
+ W.affine();
+ W.x.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i]=t[i];
+ W.x.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+BIG.MODBYTES]=t[i];
+
+ W.y.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+2*BIG.MODBYTES]=t[i];
+ W.y.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+3*BIG.MODBYTES]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP2 fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG ra;
+ BIG rb;
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 rx=new FP2(ra,rb);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+2*BIG.MODBYTES];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+3*BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 ry=new FP2(ra,rb);
+
+ return new ECP2(rx,ry);
+ }
+/* convert this to hex string */
+ public String toString() {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ return "("+W.x.toString()+","+W.y.toString()+")";
+ }
+
+/* Calculate RHS of twisted curve equation x^3+B/i */
+ public static FP2 RHS(FP2 x) {
+ x.norm();
+ FP2 r=new FP2(x);
+ r.sqr();
+ FP2 b=new FP2(new BIG(ROM.CURVE_B));
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b.div_ip();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b.norm();
+ b.mul_ip();
+ b.norm();
+ }
+
+
+ r.mul(x);
+ r.add(b);
+
+ r.reduce();
+ return r;
+ }
+
+/* construct this from (x,y) - but set to O if not on curve */
+ public ECP2(FP2 ix,FP2 iy) {
+ x=new FP2(ix);
+ y=new FP2(iy);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ FP2 y2=new FP2(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+// if (y2.equals(rhs)) INF=false;
+// else {x.zero();INF=true;}
+ }
+
+/* construct this from x - but set to O if not on curve */
+ public ECP2(FP2 ix) {
+ x=new FP2(ix);
+ y=new FP2(1);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ if (rhs.sqrt())
+ {
+ y.copy(rhs);
+ //INF=false;
+ }
+ else {/*x.zero();INF=true;*/ inf();}
+ }
+
+/* this+=this */
+ public int dbl() {
+// if (INF) return -1;
+//System.out.println("Into dbl");
+ FP2 iy=new FP2(y);
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ iy.mul_ip(); iy.norm();
+ }
+ FP2 t0=new FP2(y); //***** Change
+ t0.sqr();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip();
+ }
+ FP2 t1=new FP2(iy);
+ t1.mul(z);
+ FP2 t2=new FP2(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z);
+ z.add(z);
+ z.norm();
+
+ t2.imul(3*ROM.CURVE_B_I);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip();
+ t2.norm();
+ }
+
+ FP2 x3=new FP2(t2);
+ x3.mul(z);
+
+ FP2 y3=new FP2(t0);
+
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1); t2.norm();
+ t0.sub(t2); t0.norm(); //y^2-9bz^2
+ y3.mul(t0); y3.add(x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+ t1.copy(x); t1.mul(iy); //
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x); //(y^2-9bz^2)xy2
+
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ return 1;
+ }
+
+/* this+=Q - return 0 for add, 1 for double, -1 for O */
+ public int add(ECP2 Q) {
+// if (INF)
+// {
+// copy(Q);
+// return -1;
+// }
+// if (Q.INF) return -1;
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP2 t0=new FP2(x);
+ t0.mul(Q.x); // x.Q.x
+ FP2 t1=new FP2(y);
+ t1.mul(Q.y); // y.Q.y
+
+ FP2 t2=new FP2(z);
+ t2.mul(Q.z);
+ FP2 t3=new FP2(x);
+ t3.add(y); t3.norm(); //t3=X1+Y1
+ FP2 t4=new FP2(Q.x);
+ t4.add(Q.y); t4.norm(); //t4=X2+Y2
+ t3.mul(t4); //t3=(X1+Y1)(X2+Y2)
+ t4.copy(t0); t4.add(t1); //t4=X1.X2+Y1.Y2
+
+ t3.sub(t4); t3.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t3.mul_ip(); t3.norm(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+ }
+ t4.copy(y);
+ t4.add(z); t4.norm(); //t4=Y1+Z1
+ FP2 x3=new FP2(Q.y);
+ x3.add(Q.z); x3.norm(); //x3=Y2+Z2
+
+ t4.mul(x3); //t4=(Y1+Z1)(Y2+Z2)
+ x3.copy(t1); //
+ x3.add(t2); //X3=Y1.Y2+Z1.Z2
+
+ t4.sub(x3); t4.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t4.mul_ip(); t4.norm(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+ }
+ x3.copy(x); x3.add(z); x3.norm(); // x3=X1+Z1
+ FP2 y3=new FP2(Q.x);
+ y3.add(Q.z); y3.norm(); // y3=X2+Z2
+ x3.mul(y3); // x3=(X1+Z1)(X2+Z2)
+ y3.copy(t0);
+ y3.add(t2); // y3=X1.X2+Z1+Z2
+ y3.rsub(x3); y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip(); t0.norm(); // x.Q.x
+ t1.mul_ip(); t1.norm(); // y.Q.y
+ }
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip(); t2.norm();
+ }
+ FP2 z3=new FP2(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ y3.mul_ip();
+ y3.norm();
+ }
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ return 0;
+ }
+
+/* set this-=Q */
+ public int sub(ECP2 Q) {
+ ECP2 NQ=new ECP2(Q);
+ NQ.neg();
+ int D=add(NQ);
+ //Q.neg();
+ //int D=add(Q);
+ //Q.neg();
+ return D;
+ }
+/* set this*=q, where q is Modulus, using Frobenius */
+ public void frob(FP2 X)
+ {
+// if (INF) return;
+ FP2 X2=new FP2(X);
+
+ X2.sqr();
+ x.conj();
+ y.conj();
+ z.conj();
+ z.reduce();
+ x.mul(X2);
+
+ y.mul(X2);
+ y.mul(X);
+ }
+
+/* P*=e */
+ public ECP2 mul(BIG e)
+ {
+/* fixed size windows */
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP2 P=new ECP2();
+ ECP2 Q=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2[] W=new ECP2[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ if (is_infinity()) return new ECP2();
+
+ //affine();
+
+/* precompute table */
+ Q.copy(this);
+ Q.dbl();
+ W[0]=new ECP2();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP2();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+/* make exponent odd - add 2P if even, P if odd */
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+/* convert exponent to signed 4-bit window */
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C);
+ P.affine();
+ return P;
+ }
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ ECP2 W=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] T=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ //Q[i].affine();
+ }
+
+ T[0] = new ECP2(); T[0].copy(Q[0]); // Q[0]
+ T[1] = new ECP2(); T[1].copy(T[0]); T[1].add(Q[1]); // Q[0]+Q[1]
+ T[2] = new ECP2(); T[2].copy(T[0]); T[2].add(Q[2]); // Q[0]+Q[2]
+ T[3] = new ECP2(); T[3].copy(T[1]); T[3].add(Q[2]); // Q[0]+Q[1]+Q[2]
+ T[4] = new ECP2(); T[4].copy(T[0]); T[4].add(Q[3]); // Q[0]+Q[3]
+ T[5] = new ECP2(); T[5].copy(T[1]); T[5].add(Q[3]); // Q[0]+Q[1]+Q[3]
+ T[6] = new ECP2(); T[6].copy(T[2]); T[6].add(Q[3]); // Q[0]+Q[2]+Q[3]
+ T[7] = new ECP2(); T[7].copy(T[3]); T[7].add(Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ P.select(T,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ P.dbl();
+ W.select(T,(int)(2*w[i]+s[i]));
+ P.add(W);
+ }
+
+ // apply correction
+ W.copy(P);
+ W.sub(Q[0]);
+ P.cmove(W,pb);
+ P.affine();
+ return P;
+ }
+
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+/*
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb;
+ int[] a=new int[4];
+ ECP2 T=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] W=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ Q[i].affine();
+ }
+
+// precompute table
+
+ W[0]=new ECP2(); W[0].copy(Q[0]); W[0].sub(Q[1]);
+
+ W[1]=new ECP2(); W[1].copy(W[0]);
+ W[2]=new ECP2(); W[2].copy(W[0]);
+ W[3]=new ECP2(); W[3].copy(W[0]);
+ W[4]=new ECP2(); W[4].copy(Q[0]); W[4].add(Q[1]);
+ W[5]=new ECP2(); W[5].copy(W[4]);
+ W[6]=new ECP2(); W[6].copy(W[4]);
+ W[7]=new ECP2(); W[7].copy(W[4]);
+ T.copy(Q[2]); T.sub(Q[3]);
+ W[1].sub(T);
+ W[2].add(T);
+ W[5].sub(T);
+ W[6].add(T);
+ T.copy(Q[2]); T.add(Q[3]);
+ W[0].sub(T);
+ W[3].add(T);
+ W[4].sub(T);
+ W[7].add(T);
+
+// if multiplier is even add 1 to multiplier, and add P to correction
+ mt.zero(); C.inf();
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ C.add(Q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(byte)(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ P.dbl();
+ P.add(T);
+ }
+ P.sub(C); // apply correction
+
+ P.affine();
+ return P;
+ }
+*/
+
+/* needed for SOK */
+ public static ECP2 mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ BIG one=new BIG(1);
+ FP2 X;
+ ECP2 Q;
+ x.mod(q);
+ while (true)
+ {
+ X=new FP2(one,x);
+ Q=new ECP2(X);
+ if (!Q.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+
+ BIG Fra=new BIG(ROM.Fra);
+ BIG Frb=new BIG(ROM.Frb);
+ X=new FP2(Fra,Frb);
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X.inverse();
+ X.norm();
+ }
+
+ x=new BIG(ROM.CURVE_Bnx);
+
+/* Fast Hashing to G2 - Fuentes-Castaneda, Knapp and Rodriguez-Henriquez */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ ECP2 T,K;
+
+ T=new ECP2(); T.copy(Q);
+ T=T.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ T.neg();
+ }
+ K=new ECP2(); K.copy(T);
+ K.dbl(); K.add(T); //K.affine();
+
+ K.frob(X);
+ Q.frob(X); Q.frob(X); Q.frob(X);
+ Q.add(T); Q.add(K);
+ T.frob(X); T.frob(X);
+ Q.add(T);
+
+ }
+
+/* Efficient hash maps to G2 on BLS curves - Budroni, Pintore */
+/* Q -> x2Q -xQ -Q +F(xQ -Q) +F(F(2Q)) */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BLS)
+ {
+ // ECP2 xQ,x2Q;
+ // xQ=new ECP2();
+ // x2Q=new ECP2();
+
+ ECP2 xQ=Q.mul(x);
+ ECP2 x2Q=xQ.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ xQ.neg();
+ }
+
+ x2Q.sub(xQ);
+ x2Q.sub(Q);
+
+ xQ.sub(Q);
+ xQ.frob(X);
+
+ Q.dbl();
+ Q.frob(X);
+ Q.frob(X);
+
+ Q.add(x2Q);
+ Q.add(xQ);
+ }
+ Q.affine();
+ return Q;
+ }
+
+ public static ECP2 generator()
+ {
+ return new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+ }
+
+/*
+ public static void main(String[] args) {
+ BIG r=new BIG(ROM.Modulus);
+
+ BIG Pxa=new BIG(ROM.CURVE_Pxa);
+ BIG Pxb=new BIG(ROM.CURVE_Pxb);
+ BIG Pya=new BIG(ROM.CURVE_Pya);
+ BIG Pyb=new BIG(ROM.CURVE_Pyb);
+
+ BIG Fra=new BIG(ROM.CURVE_Fra);
+ BIG Frb=new BIG(ROM.CURVE_Frb);
+
+ FP2 f=new FP2(Fra,Frb);
+
+ FP2 Px=new FP2(Pxa,Pxb);
+ FP2 Py=new FP2(Pya,Pyb);
+
+ ECP2 P=new ECP2(Px,Py);
+
+ System.out.println("P= "+P.toString());
+
+ P=P.mul(r);
+ System.out.println("P= "+P.toString());
+
+ ECP2 Q=new ECP2(Px,Py);
+ Q.frob(f);
+ System.out.println("Q= "+Q.toString());
+ } */
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/FP.java b/src/main/java/org/apache/milagro/amcl/BLS461/FP.java
new file mode 100644
index 0000000..992734e
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.BLS461;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=461; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<19); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/FP12.java b/src/main/java/org/apache/milagro/amcl/BLS461/FP12.java
new file mode 100644
index 0000000..0514cc3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/FP12.java
@@ -0,0 +1,907 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Fp^12 functions */
+/* FP12 elements are of the form a+i.b+i^2.c */
+
+package org.apache.milagro.amcl.BLS461;
+
+public final class FP12 {
+ private final FP4 a;
+ private final FP4 b;
+ private final FP4 c;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ c.reduce();
+ }
+/* normalise all components of this */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ c.norm();
+ }
+/* test x==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch() && c.iszilch());
+ }
+
+ public void cmove(FP12 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ c.cmove(g.c,d);
+ }
+
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(FP12 g[],int b)
+ {
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(g[0],teq(babs,0)); // conditional move
+ cmove(g[1],teq(babs,1));
+ cmove(g[2],teq(babs,2));
+ cmove(g[3],teq(babs,3));
+ cmove(g[4],teq(babs,4));
+ cmove(g[5],teq(babs,5));
+ cmove(g[6],teq(babs,6));
+ cmove(g[7],teq(babs,7));
+
+ FP12 invf=new FP12(this);
+ invf.conj();
+ cmove(invf,(int)(m&1));
+ }
+
+
+/* test x==1 ? */
+ public boolean isunity() {
+ FP4 one=new FP4(1);
+ return (a.equals(one) && b.iszilch() && c.iszilch());
+ }
+/* return 1 if x==y, else 0 */
+ public boolean equals(FP12 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b) && c.equals(x.c));
+ }
+/* extract a from this */
+ public FP4 geta()
+ {
+ return a;
+ }
+/* extract b */
+ public FP4 getb()
+ {
+ return b;
+ }
+/* extract c */
+ public FP4 getc()
+ {
+ return c;
+ }
+/* copy this=x */
+ public void copy(FP12 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ c.copy(x.c);
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ c.zero();
+ }
+/* this=conj(this) */
+ public void conj()
+ {
+ a.conj();
+ b.nconj();
+ c.conj();
+ }
+/* Constructors */
+ public FP12(FP4 d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(int d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(FP4 d,FP4 e,FP4 f)
+ {
+ a=new FP4(d);
+ b=new FP4(e);
+ c=new FP4(f);
+ }
+
+ public FP12(FP12 x)
+ {
+ a=new FP4(x.a);
+ b=new FP4(x.b);
+ c=new FP4(x.c);
+ }
+
+/* Granger-Scott Unitary Squaring */
+ public void usqr()
+ {
+//System.out.println("Into usqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(c);
+ FP4 C=new FP4(b);
+ FP4 D=new FP4(0);
+
+ a.sqr();
+ D.copy(a); D.add(a);
+ a.add(D);
+
+ a.norm();
+ A.nconj();
+
+ A.add(A);
+ a.add(A);
+ B.sqr();
+ B.times_i();
+
+ D.copy(B); D.add(B);
+ B.add(D);
+ B.norm();
+
+ C.sqr();
+ D.copy(C); D.add(C);
+ C.add(D);
+ C.norm();
+
+ b.conj();
+ b.add(b);
+ c.nconj();
+
+ c.add(c);
+ b.add(B);
+ c.add(C);
+//System.out.println("Out of usqr 1");
+ reduce();
+//System.out.println("Out of usqr 2");
+ }
+
+/* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+ public void sqr()
+ {
+//System.out.println("Into sqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(b);
+ FP4 C=new FP4(c);
+ FP4 D=new FP4(a);
+
+ A.sqr();
+ B.mul(c);
+ B.add(B);
+ B.norm();
+ C.sqr();
+ D.mul(b);
+ D.add(D);
+
+ c.add(a);
+ c.add(b);
+ c.norm();
+ c.sqr();
+
+ a.copy(A);
+
+ A.add(B);
+ A.norm();
+ A.add(C);
+ A.add(D);
+ A.norm();
+
+ A.neg();
+ B.times_i();
+ C.times_i();
+
+ a.add(B);
+
+ b.copy(C); b.add(D);
+ c.add(A);
+//System.out.println("Out of sqr");
+ norm();
+ }
+
+/* FP12 full multiplication this=this*y */
+ public void mul(FP12 y)
+ {
+//System.out.println("Into mul");
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(y.a);
+
+ z0.mul(y.a);
+ z2.mul(y.b);
+
+ t0.add(b);
+ t1.add(y.b);
+
+ t0.norm();
+ t1.norm();
+
+ z1.copy(t0); z1.mul(t1);
+ t0.copy(b); t0.add(c);
+
+ t1.copy(y.b); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ z3.copy(t0); z3.mul(t1);
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ z1.add(t0);
+ //z1.norm();
+ b.copy(z1); b.add(t1);
+
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+t0.norm();
+t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c); t0.mul(y.c);
+ t1.copy(t0); t1.neg();
+
+// z2.norm();
+// z3.norm();
+// b.norm();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ norm();
+//System.out.println("Out of mul");
+ }
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+ public void smul(FP12 y,int type)
+ {
+//System.out.println("Into smul");
+
+ if (type==ECP.D_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(b);
+ FP4 t0=new FP4(0);
+ FP4 t1=new FP4(y.a);
+ z0.mul(y.a);
+ z2.pmul(y.b.real());
+ b.add(a);
+ t1.real().add(y.b.real());
+
+ t1.norm();
+ b.norm();
+ b.mul(t1);
+ z3.add(c);
+ z3.norm();
+ z3.pmul(y.b.real());
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ b.add(t0);
+
+ b.add(t1);
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t0.norm();
+ z3.norm();
+ t0.mul(y.a);
+ c.copy(z2); c.add(t0);
+
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ if (type==ECP.M_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(0);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(0);
+
+ z0.mul(y.a);
+ t0.add(b);
+ t0.norm();
+
+ z1.copy(t0); z1.mul(y.a);
+ t0.copy(b); t0.add(c);
+ t0.norm();
+
+ z3.copy(t0); //z3.mul(y.c);
+ z3.pmul(y.c.getb());
+ z3.times_i();
+
+ t0.copy(z0); t0.neg();
+
+ z1.add(t0);
+ b.copy(z1);
+ z2.copy(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c);
+
+ t0.pmul(y.c.getb());
+ t0.times_i();
+
+ t1.copy(t0); t1.neg();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ norm();
+//System.out.println("Out of smul");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ FP4 f0=new FP4(a);
+ FP4 f1=new FP4(b);
+ FP4 f2=new FP4(a);
+ FP4 f3=new FP4(0);
+
+ norm();
+ f0.sqr();
+ f1.mul(c);
+ f1.times_i();
+ f0.sub(f1);
+ f0.norm();
+
+ f1.copy(c); f1.sqr();
+ f1.times_i();
+ f2.mul(b);
+ f1.sub(f2);
+ f1.norm();
+
+ f2.copy(b); f2.sqr();
+ f3.copy(a); f3.mul(c);
+ f2.sub(f3);
+ f2.norm();
+
+ f3.copy(b); f3.mul(f2);
+ f3.times_i();
+ a.mul(f0);
+ f3.add(a);
+ c.mul(f1);
+ c.times_i();
+
+ f3.add(c);
+ f3.norm();
+ f3.inverse();
+ a.copy(f0); a.mul(f3);
+ b.copy(f1); b.mul(f3);
+ c.copy(f2); c.mul(f3);
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ FP2 f2=new FP2(f);
+ FP2 f3=new FP2(f);
+
+ f2.sqr();
+ f3.mul(f2);
+
+ a.frob(f3);
+ b.frob(f3);
+ c.frob(f3);
+
+ b.pmul(f);
+ c.pmul(f2);
+ }
+
+/* trace function */
+ public FP4 trace()
+ {
+ FP4 t=new FP4(0);
+ t.copy(a);
+ t.imul(3);
+ t.reduce();
+ return t;
+ }
+
+/* convert from byte array to FP12 */
+ public static FP12 fromBytes(byte[] w)
+ {
+ BIG a,b;
+ FP2 c,d;
+ FP4 e,f,g;
+ byte[] t=new byte[BIG.MODBYTES];
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+2*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+3*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ e=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+4*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+5*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+6*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+7*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ f=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+8*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+9*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+10*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+11*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ g=new FP4(c,d);
+
+ return new FP12(e,f,g);
+ }
+
+/* convert this to byte array */
+ public void toBytes(byte[] w)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ a.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i]=t[i];
+ a.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+BIG.MODBYTES]=t[i];
+ a.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+2*BIG.MODBYTES]=t[i];
+ a.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+3*BIG.MODBYTES]=t[i];
+
+ b.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+4*BIG.MODBYTES]=t[i];
+ b.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+5*BIG.MODBYTES]=t[i];
+ b.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+6*BIG.MODBYTES]=t[i];
+ b.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+7*BIG.MODBYTES]=t[i];
+
+ c.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+8*BIG.MODBYTES]=t[i];
+ c.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+9*BIG.MODBYTES]=t[i];
+ c.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+10*BIG.MODBYTES]=t[i];
+ c.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+11*BIG.MODBYTES]=t[i];
+ }
+
+/* convert to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+","+c.toString()+"]");
+ }
+
+/* this=this^e */
+/* Note this is simple square and multiply, so not side-channel safe */
+ public FP12 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ BIG e3=new BIG(e);
+ e3.pmul(3);
+ e3.norm();
+
+ FP12 w=new FP12(this);
+
+ int nb=e3.nbits();
+ for (int i=nb-2;i>=1;i--)
+ {
+ w.usqr();
+ int bt=e3.bit(i)-e.bit(i);
+ if (bt==1)
+ w.mul(this);
+ if (bt==-1)
+ {
+ conj(); w.mul(this); conj();
+ }
+ }
+ w.reduce();
+ return w;
+
+
+/*
+ BIG z=new BIG(e);
+ FP12 r=new FP12(1);
+
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.usqr();
+ }
+ r.reduce();
+ return r; */
+ }
+
+/* constant time powering by small integer of max length bts */
+ public void pinpow(int e,int bts)
+ {
+ int i,b;
+ FP12 [] R=new FP12[2];
+ R[0]=new FP12(1);
+ R[1]=new FP12(this);
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ R[1-b].mul(R[b]);
+ R[b].usqr();
+ }
+ this.copy(R[0]);
+ }
+
+ public FP4 compow(BIG e,BIG r)
+ {
+ FP12 g1=new FP12(0);
+ FP12 g2=new FP12(0);
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.Modulus);
+
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(e);
+ a.mod(m);
+
+ BIG b=new BIG(e);
+ b.div(m);
+
+ g1.copy(this);
+ g2.copy(this);
+
+ FP4 c=g1.trace();
+
+ if (b.iszilch())
+ {
+ c=c.xtr_pow(e);
+ return c;
+ }
+
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+
+ return c;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ FP12 [] g=new FP12[8];
+ FP12 r=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ }
+ g[0]=new FP12(q[0]); // q[0]
+ g[1]=new FP12(g[0]); g[1].mul(q[1]); // q[0].q[1]
+ g[2]=new FP12(g[0]); g[2].mul(q[2]); // q[0].q[2]
+ g[3]=new FP12(g[1]); g[3].mul(q[2]); // q[0].q[1].q[2]
+ g[4]=new FP12(q[0]); g[4].mul(q[3]); // q[0].q[3]
+ g[5]=new FP12(g[1]); g[5].mul(q[3]); // q[0].q[1].q[3]
+ g[6]=new FP12(g[2]); g[6].mul(q[3]); // q[0].q[2].q[3]
+ g[7]=new FP12(g[3]); g[7].mul(q[3]); // q[0].q[1].q[2].q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ p.select(g,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ p.usqr();
+ r.select(g,(int)(2*w[i]+s[i]));
+ p.mul(r);
+ }
+
+ // apply correction
+ r.copy(q[0]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb);
+
+ p.reduce();
+ return p;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+/* Timing attack secure, but not cache attack secure */
+/*
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,m;
+ int[] a=new int[4];
+ FP12 [] g=new FP12[8];
+ FP12 [] s=new FP12[2];
+ FP12 c=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ t[i]=new BIG(u[i]);
+
+ s[0]=new FP12(0);
+ s[1]=new FP12(0);
+
+ g[0]=new FP12(q[0]); s[0].copy(q[1]); s[0].conj(); g[0].mul(s[0]);
+ g[1]=new FP12(g[0]);
+ g[2]=new FP12(g[0]);
+ g[3]=new FP12(g[0]);
+ g[4]=new FP12(q[0]); g[4].mul(q[1]);
+ g[5]=new FP12(g[4]);
+ g[6]=new FP12(g[4]);
+ g[7]=new FP12(g[4]);
+
+ s[1].copy(q[2]); s[0].copy(q[3]); s[0].conj(); s[1].mul(s[0]);
+ s[0].copy(s[1]); s[0].conj(); g[1].mul(s[0]);
+ g[2].mul(s[1]);
+ g[5].mul(s[0]);
+ g[6].mul(s[1]);
+ s[1].copy(q[2]); s[1].mul(q[3]);
+ s[0].copy(s[1]); s[0].conj(); g[0].mul(s[0]);
+ g[3].mul(s[1]);
+ g[4].mul(s[0]);
+ g[7].mul(s[1]);
+
+// if power is even add 1 to power, and add q to correction
+
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ c.mul(q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+ c.conj();
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+ p.copy(g[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ m=w[i]>>7;
+ j=(w[i]^m)-m; // j=abs(w[i])
+ j=(j-1)/2;
+ s[0].copy(g[j]); s[1].copy(g[j]); s[1].conj();
+ p.usqr();
+ p.mul(s[m&1]);
+ }
+ p.mul(c); // apply correction
+ p.reduce();
+ return p;
+ }
+*/
+/*
+ public static void main(String[] args) {
+ BIG p=new BIG(ROM.Modulus);
+ FP2 w0,w1;
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.zero(); b.zero(); a.inc(1); b.inc(2);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(3); b.inc(4);
+ w1=new FP2(a,b);
+ FP4 t0=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(5); b.inc(6);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(7); b.inc(8);
+ w1=new FP2(a,b);
+ FP4 t1=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(9); b.inc(10);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(11); b.inc(12);
+ w1=new FP2(a,b);
+ FP4 t2=new FP4(w0,w1);
+
+ FP12 w=new FP12(t0,t1,t2);
+ FP12 t=new FP12(w);
+
+ System.out.println("w= "+w.toString());
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ w.frob(f);
+ System.out.println("w= "+w.toString());
+
+ w=t.pow(p);
+
+ System.out.println("w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("w= "+w.toString());
+
+ t.copy(w);
+ w.conj();
+ t.inverse();
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)= "+w.toString());
+
+ t.copy(w);
+ w.frob(f);
+ w.frob(f);
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)(p^2+1)= "+w.toString());
+
+ t.copy(w);
+
+ t.inverse();
+ w.conj();
+
+ System.out.println("w= "+w.toString());
+ System.out.println("t= "+t.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/FP2.java b/src/main/java/org/apache/milagro/amcl/BLS461/FP2.java
new file mode 100644
index 0000000..28d70ed
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/FP2.java
@@ -0,0 +1,425 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^2 functions */
+
+/* FP2 elements are of the form a+ib, where i is sqrt(-1) */
+
+package org.apache.milagro.amcl.BLS461;
+
+public final class FP2 {
+ private final FP a;
+ private final FP b;
+
+/* reduce components mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+
+/* normalise components of w */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+
+/* test this=0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP2 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this=1 ? */
+ public boolean isunity() {
+ FP one=new FP(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test this=x */
+ public boolean equals(FP2 x) {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+
+/* Constructors */
+ public FP2(int c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(FP2 x)
+ {
+ a=new FP(x.a);
+ b=new FP(x.b);
+ }
+
+ public FP2(FP c,FP d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(BIG c,BIG d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(FP c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(BIG c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+/*
+ public BIG geta()
+ {
+ return a.tobig();
+ }
+*/
+/* extract a */
+ public BIG getA()
+ {
+ return a.redc();
+ }
+
+/* extract b */
+ public BIG getB()
+ {
+ return b.redc();
+ }
+
+/* copy this=x */
+ public void copy(FP2 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+
+/* negate this mod Modulus */
+ public void neg()
+ {
+ FP m=new FP(a);
+ FP t=new FP(0);
+
+ m.add(b);
+ m.neg();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ }
+
+/* set to a-ib */
+ public void conj()
+ {
+ b.neg();
+ b.norm();
+ }
+
+/* this+=a */
+ public void add(FP2 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+
+/* this-=a */
+ public void sub(FP2 x)
+ {
+ FP2 m=new FP2(x);
+ m.neg();
+ add(m);
+ }
+
+ public void rsub(FP2 x) // *****
+ {
+ neg();
+ add(x);
+ }
+
+/* this*=s, where s is an FP */
+ public void pmul(FP s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this*=i, where i is an int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+ FP w1=new FP(a);
+ FP w3=new FP(a);
+ FP mb=new FP(b);
+
+ w1.add(b);
+ mb.neg();
+
+ w3.add(a);
+ w3.norm();
+ b.mul(w3);
+
+ a.add(mb);
+
+ w1.norm();
+ a.norm();
+
+ a.mul(w1);
+ }
+
+/* this*=y */
+/* Now uses Lazy reduction */
+ public void mul(FP2 y)
+ {
+ if ((long)(a.XES+b.XES)*(y.a.XES+y.b.XES)>(long)FP.FEXCESS)
+ {
+ if (a.XES>1) a.reduce();
+ if (b.XES>1) b.reduce();
+ }
+
+ DBIG pR=new DBIG(0);
+ BIG C=new BIG(a.x);
+ BIG D=new BIG(y.a.x);
+
+ pR.ucopy(new BIG(ROM.Modulus));
+
+ DBIG A=BIG.mul(a.x,y.a.x);
+ DBIG B=BIG.mul(b.x,y.b.x);
+
+ C.add(b.x); C.norm();
+ D.add(y.b.x); D.norm();
+
+ DBIG E=BIG.mul(C,D);
+ DBIG F=new DBIG(A); F.add(B);
+ B.rsub(pR);
+
+ A.add(B); A.norm();
+ E.sub(F); E.norm();
+
+ a.x.copy(FP.mod(A)); a.XES=3;
+ b.x.copy(FP.mod(E)); b.XES=2;
+ }
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP w1=new FP(b);
+ FP w2=new FP(a);
+ w1.sqr(); w2.sqr(); w1.add(w2);
+ if (w1.jacobi()!=1) { zero(); return false; }
+ w1=w1.sqrt();
+ w2.copy(a); w2.add(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1)
+ {
+ w2.copy(a); w2.sub(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1) { zero(); return false; }
+ }
+ w2=w2.sqrt();
+ a.copy(w2);
+ w2.add(w2);
+ w2.inverse();
+ b.mul(w2);
+ return true;
+ }
+
+/* output to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ norm();
+ FP w1=new FP(a);
+ FP w2=new FP(b);
+
+ w1.sqr();
+ w2.sqr();
+ w1.add(w2);
+ w1.inverse();
+ a.mul(w1);
+ w1.neg();
+ w1.norm();
+ b.mul(w1);
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+/* this*=sqrt(-1) */
+ public void times_i()
+ {
+ FP z=new FP(a);
+ a.copy(b); a.neg();
+ b.copy(z);
+ }
+
+/* w*=(1+sqrt(-1)) */
+/* where X*2-(1+sqrt(-1)) is irreducible for FP4, assumes p=3 mod 8 */
+ public void mul_ip()
+ {
+ FP2 t=new FP2(this);
+ FP z=new FP(a);
+ a.copy(b);
+ a.neg();
+ b.copy(z);
+ add(t);
+ }
+
+ public void div_ip2()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ }
+
+/* w/=(1+sqrt(-1)) */
+ public void div_ip()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ div2();
+ }
+/*
+ public FP2 pow(BIG e)
+ {
+ int bt;
+ FP2 r=new FP2(1);
+ e.norm();
+ norm();
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(this);
+ if (e.iszilch()) break;
+ sqr();
+ }
+
+ r.reduce();
+ return r;
+ }
+
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(27);
+ BIG pp1=new BIG(m);
+ BIG pm1=new BIG(m);
+ BIG a=new BIG(1);
+ BIG b=new BIG(1);
+ FP2 w=new FP2(a,b);
+ FP2 z=new FP2(w);
+
+ byte[] RAW=new byte[100];
+
+ RAND rng=new RAND();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ // for (int i=0;i<100;i++)
+ // {
+ a.randomnum(rng);
+ b.randomnum(rng);
+
+ w=new FP2(a,b);
+ System.out.println("w="+w.toString());
+
+ z=new FP2(w);
+ z.inverse();
+ System.out.println("z="+z.toString());
+
+ z.inverse();
+ if (!z.equals(w)) System.out.println("Error");
+ // }
+
+// System.out.println("m="+m.toString());
+// w.sqr();
+// w.mul(z);
+
+ System.out.println("w="+w.toString());
+
+
+ pp1.inc(1); pp1.norm();
+ pm1.dec(1); pm1.norm();
+ System.out.println("p+1="+pp1.toString());
+ System.out.println("p-1="+pm1.toString());
+ w=w.pow(pp1);
+ w=w.pow(pm1);
+ System.out.println("w="+w.toString());
+ }
+*/
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/FP4.java b/src/main/java/org/apache/milagro/amcl/BLS461/FP4.java
new file mode 100644
index 0000000..2f68288
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/FP4.java
@@ -0,0 +1,721 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^4 functions */
+
+/* FP4 elements are of the form a+ib, where i is sqrt(-1+sqrt(-1)) */
+
+package org.apache.milagro.amcl.BLS461;
+
+public final class FP4 {
+ private final FP2 a;
+ private final FP2 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP4 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP2 one=new FP2(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP2 real()
+ {
+ return a;
+ }
+
+ public FP2 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP2 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP4 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP4(int c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+
+ public FP4(FP4 x)
+ {
+ a=new FP2(x.a);
+ b=new FP2(x.b);
+ }
+
+ public FP4(FP2 c,FP2 d)
+ {
+ a=new FP2(c);
+ b=new FP2(d);
+ }
+
+ public FP4(FP2 c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+/* copy this=x */
+ public void copy(FP4 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP2 m=new FP2(a);
+ FP2 t=new FP2(0);
+ m.add(b);
+// m.norm();
+ m.neg();
+ // m.norm();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP4 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP4 x)
+ {
+ FP4 m=new FP4(x);
+ m.neg();
+ add(m);
+ }
+
+/* this*=s where s is FP2 */
+ public void pmul(FP2 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this=x-this */
+ public void rsub(FP4 x)
+ {
+ neg();
+ add(x);
+ }
+
+
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.mul_ip();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.mul_ip();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+/* this*=y */
+ public void mul(FP4 y)
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(0);
+ FP2 t4=new FP2(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ // t4.sub(t1);
+ // t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ // b.copy(t4);
+ // b.sub(t2);
+
+ t2.mul_ip();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.mul_ip();
+ t2.norm();
+ t1.sub(t2);
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP2 s=new FP2(b);
+ FP2 t=new FP2(b);
+ s.times_i();
+ t.add(s);
+ // t.norm();
+ b.copy(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ a.conj();
+ b.conj();
+ b.mul(f);
+ }
+
+/* this=this^e */
+ public FP4 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP4 w=new FP4(this);
+ BIG z=new BIG(e);
+ FP4 r=new FP4(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+/* XTR xtr_a function */
+ public void xtr_A(FP4 w,FP4 y,FP4 z)
+ {
+ FP4 r=new FP4(w);
+ FP4 t=new FP4(w);
+ //y.norm();
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP4 w=new FP4(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP4 xtr_pow(BIG n) {
+ FP4 a=new FP4(3);
+ FP4 b=new FP4(this);
+ FP4 c=new FP4(b);
+ c.xtr_D();
+ FP4 t=new FP4(0);
+ FP4 r=new FP4(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP4 xtr_pow2(FP4 ck,FP4 ckml,FP4 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP4 cu=new FP4(ck); // can probably be passed in w/o copying
+ FP4 cv=new FP4(this);
+ FP4 cumv=new FP4(ckml);
+ FP4 cum2v=new FP4(ckm2l);
+ FP4 r=new FP4(0);
+ FP4 t=new FP4(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+ public void div_i()
+ {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip();
+ a.copy(v);
+ b.copy(u);
+ }
+
+ public void div_2i() {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip2();
+ v.add(v); v.norm();
+ a.copy(v);
+ b.copy(u);
+ }
+
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP2 wa=new FP2(a);
+ FP2 ws=new FP2(b);
+ FP2 wt=new FP2(a);
+
+ if (ws.iszilch())
+ {
+ if (wt.sqrt())
+ {
+ a.copy(wt);
+ b.zero();
+ } else {
+ wt.div_ip();
+ wt.sqrt();
+ b.copy(wt);
+ a.zero();
+ }
+ return true;
+ }
+
+ ws.sqr();
+ wa.sqr();
+ ws.mul_ip();
+ ws.norm();
+ wa.sub(ws);
+
+ ws.copy(wa);
+ if (!ws.sqrt()) {
+ return false;
+ }
+
+ wa.copy(wt); wa.add(ws); wa.norm(); wa.div2();
+
+ if (!wa.sqrt()) {
+ wa.copy(wt); wa.sub(ws); wa.norm(); wa.div2();
+ if (!wa.sqrt()) {
+ return false;
+ }
+ }
+ wt.copy(b);
+ ws.copy(wa); ws.add(wa);
+ ws.inverse();
+
+ wt.mul(ws);
+ a.copy(wa);
+ b.copy(wt);
+
+ return true;
+ }
+
+/* this*=s where s is FP */
+ public void qmul(FP s)
+ {
+ a.pmul(s);
+ b.pmul(s);
+ }
+
+
+
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG e=new BIG(12);
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.inc(27); b.inc(45);
+
+ FP2 w0=new FP2(a,b);
+
+ a.zero(); b.zero();
+ a.inc(33); b.inc(54);
+
+ FP2 w1=new FP2(a,b);
+
+
+ FP4 w=new FP4(w0,w1);
+ FP4 t=new FP4(w);
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ System.out.println("w= "+w.toString());
+
+ w=w.pow(m);
+
+ System.out.println("w^p= "+w.toString());
+
+ t.frob(f);
+
+
+ System.out.println("w^p= "+t.toString());
+
+ w=w.pow(m);
+ w=w.pow(m);
+ w=w.pow(m);
+ System.out.println("w^p4= "+w.toString());
+
+
+ System.out.println("Test Inversion");
+
+ w=new FP4(w0,w1);
+
+ w.inverse();
+
+ System.out.println("1/w mod p^4 = "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/(1/w) mod p^4 = "+w.toString());
+
+ FP4 ww=new FP4(w);
+
+ w=w.xtr_pow(e);
+ System.out.println("w^e= "+w.toString());
+
+
+ a.zero(); b.zero();
+ a.inc(37); b.inc(17);
+ w0=new FP2(a,b);
+ a.zero(); b.zero();
+ a.inc(49); b.inc(31);
+ w1=new FP2(a,b);
+
+ FP4 c1=new FP4(w0,w1);
+ FP4 c2=new FP4(w0,w1);
+ FP4 c3=new FP4(w0,w1);
+
+ BIG e1=new BIG(3331);
+ BIG e2=new BIG(3372);
+
+ FP4 cr=w.xtr_pow2(c1,c2,c3,e1,e2);
+
+ System.out.println("c^e= "+cr.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/MPIN.java b/src/main/java/org/apache/milagro/amcl/BLS461/MPIN.java
new file mode 100644
index 0000000..67890d5
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/MPIN.java
@@ -0,0 +1,823 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* MPIN API Functions */
+
+package org.apache.milagro.amcl.BLS461;
+
+import java.util.Date;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public class MPIN
+{
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int PAS=16;
+ public static final int INVALID_POINT=-14;
+ public static final int BAD_PARAMS=-11;
+ public static final int WRONG_ORDER=-18;
+ public static final int BAD_PIN=-19;
+
+/* Configure your PIN here */
+
+ public static final int MAXPIN=10000; /* PIN less than this */
+ public static final int PBLEN=14; /* Number of bits in PIN */
+ public static final int TS=10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+ public static final int TRAP=200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN - approx 2*sqrt(MAXPIN) */
+
+// public static final int HASH_TYPE=SHA256;
+
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,int n,byte[] B,int len)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (n>0) H.process_num(n);
+
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+ byte[] W=new byte[len];
+
+ if (sha>=len)
+ for (int i=0;i<len;i++) W[i]=R[i];
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+len-sha]=R[i];
+ for (int i=0;i<len-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<len;i++) W[i]=0;
+ }
+ return W;
+ }
+
+ /* return time in slots since epoch */
+ public static int today() {
+ Date date=new Date();
+ return (int) (date.getTime()/(1000*60*1440));
+ }
+
+ public static byte[] HASH_ID(int sha,byte[] ID,int len)
+ {
+ return hashit(sha,0,ID,len);
+ }
+
+/* Hash the M-Pin transcript - new */
+
+ public static byte[] HASH_ALL(int sha,byte[] HID,byte[] xID,byte[] xCID,byte[] SEC,byte[] Y,byte[] R,byte[] W,int len)
+ {
+ int i,ilen,tlen=0;
+
+ ilen=HID.length+SEC.length+Y.length+R.length+W.length;
+ if (xCID!=null) ilen+=xCID.length;
+ else ilen+=xID.length;
+
+ byte[] T = new byte[ilen];
+
+ for (i=0;i<HID.length;i++) T[i]=HID[i];
+ tlen+=HID.length;
+ if (xCID!=null)
+ {
+ for (i=0;i<xCID.length;i++) T[i+tlen]=xCID[i];
+ tlen+=xCID.length;
+ }
+ else
+ {
+ for (i=0;i<xID.length;i++) T[i+tlen]=xID[i];
+ tlen+=xID.length;
+ }
+ for (i=0;i<SEC.length;i++) T[i+tlen]=SEC[i];
+ tlen+=SEC.length;
+ for (i=0;i<Y.length;i++) T[i+tlen]=Y[i];
+ tlen+=Y.length;
+ for (i=0;i<R.length;i++) T[i+tlen]=R[i];
+ tlen+=R.length;
+ for (i=0;i<W.length;i++) T[i+tlen]=W[i];
+ tlen+=W.length;
+
+ return hashit(sha,0,T,len);
+ }
+
+/* return time since epoch */
+ public static int GET_TIME() {
+ Date date=new Date();
+ return (int) (date.getTime()/1000);
+ }
+
+ public static byte[] mpin_hash(int sha,FP4 c,ECP U)
+ {
+ byte[] w=new byte[EFS];
+ byte[] t=new byte[6*EFS];
+ byte[] h=null;
+ c.geta().getA().toBytes(w); for (int i=0;i<EFS;i++) t[i]=w[i];
+ c.geta().getB().toBytes(w); for (int i=EFS;i<2*EFS;i++) t[i]=w[i-EFS];
+ c.getb().getA().toBytes(w); for (int i=2*EFS;i<3*EFS;i++) t[i]=w[i-2*EFS];
+ c.getb().getB().toBytes(w); for (int i=3*EFS;i<4*EFS;i++) t[i]=w[i-3*EFS];
+
+ U.getX().toBytes(w); for (int i=4*EFS;i<5*EFS;i++) t[i]=w[i-4*EFS];
+ U.getY().toBytes(w); for (int i=5*EFS;i<6*EFS;i++) t[i]=w[i-5*EFS];
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (h==null) return null;
+ byte[] R=new byte[ECP.AESKEY];
+ for (int i=0;i<ECP.AESKEY;i++) R[i]=h[i];
+ return R;
+ }
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+ public static ECP map(BIG u,int cb)
+ {
+ ECP P;
+ BIG x=new BIG(u);
+ BIG p=new BIG(ROM.Modulus);
+ x.mod(p);
+ while (true)
+ {
+ P=new ECP(x,cb);
+ if (!P.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+ return P;
+ }
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+ public static int unmap(BIG u,ECP P)
+ {
+ int s=P.getS();
+ ECP R;
+ int r=0;
+ BIG x=P.getX();
+ u.copy(x);
+ while (true)
+ {
+ u.dec(1); u.norm();
+ r++;
+ R=new ECP(u,s);
+ if (!R.is_infinity()) break;
+ }
+ return r;
+ }
+
+
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+ public static int ENCODING(RAND rng,byte[] E)
+ {
+ int rn,m,su,sv;
+ byte[] T=new byte[EFS];
+
+ for (int i=0;i<EFS;i++) T[i]=E[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=E[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ ECP P=new ECP(u,v);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG p=new BIG(ROM.Modulus);
+ u=BIG.randomnum(p,rng);
+
+ su=rng.getByte(); /*if (su<0) su=-su;*/ su%=2;
+
+ ECP W=map(u,su);
+ P.sub(W); //P.affine();
+ sv=P.getS();
+ rn=unmap(v,P);
+ m=rng.getByte(); /*if (m<0) m=-m;*/ m%=rn;
+ v.inc(m+1);
+ E[0]=(byte)(su+2*sv);
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+ public static int DECODING(byte[] D)
+ {
+ int su,sv;
+ byte[] T=new byte[EFS];
+
+ if ((D[0]&0x04)!=0) return INVALID_POINT;
+
+ for (int i=0;i<EFS;i++) T[i]=D[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=D[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ su=D[0]&1;
+ sv=(D[0]>>1)&1;
+ ECP W=map(u,su);
+ ECP P=map(v,sv);
+ P.add(W); //P.affine();
+ u=P.getX();
+ v=P.getY();
+ D[0]=0x04;
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+/* R=R1+R2 in group G1 */
+ public static int RECOMBINE_G1(byte[] R1,byte[] R2,byte[] R)
+ {
+ ECP P=ECP.fromBytes(R1);
+ ECP Q=ECP.fromBytes(R2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(R,false);
+ return 0;
+ }
+
+/* W=W1+W2 in group G2 */
+ public static int RECOMBINE_G2(byte[] W1,byte[] W2,byte[] W)
+ {
+ ECP2 P=ECP2.fromBytes(W1);
+ ECP2 Q=ECP2.fromBytes(W2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(W);
+ return 0;
+ }
+
+/* create random secret S */
+ public static int RANDOM_GENERATE(RAND rng,byte[] S)
+ {
+ BIG s;
+ BIG r=new BIG(ROM.CURVE_Order);
+ s=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+ return 0;
+ }
+
+/* Extract PIN from TOKEN for identity CID */
+ public static int EXTRACT_PIN(int sha,byte[] CID,int pin,byte[] TOKEN)
+ {
+ ECP P=ECP.fromBytes(TOKEN);
+ if (P.is_infinity()) return INVALID_POINT;
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R=ECP.mapit(h);
+
+
+ pin%=MAXPIN;
+
+ R=R.pinmul(pin,PBLEN);
+ P.sub(R); //P.affine();
+
+ P.toBytes(TOKEN,false);
+
+ return 0;
+ }
+
+/* Implement step 2 on client side of MPin protocol */
+ public static int CLIENT_2(byte[] X,byte[] Y,byte[] SEC)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ ECP P=ECP.fromBytes(SEC);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG px=BIG.fromBytes(X);
+ BIG py=BIG.fromBytes(Y);
+ px.add(py);
+ px.mod(r);
+ // px.rsub(r);
+
+ P=PAIR.G1mul(P,px);
+ P.neg();
+ P.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Implement step 1 on client side of MPin protocol */
+ public static int CLIENT_1(int sha,int date,byte[] CLIENT_ID,RAND rng,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG x;
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P,T,W;
+ BIG px;
+// byte[] t=new byte[EFS];
+
+ byte[] h=hashit(sha,0,CLIENT_ID,EFS);
+ P=ECP.mapit(h);
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ pin%=MAXPIN;
+ W=P.pinmul(pin,PBLEN);
+ T.add(W);
+ if (date!=0)
+ {
+ W=ECP.fromBytes(PERMIT);
+ if (W.is_infinity()) return INVALID_POINT;
+ T.add(W);
+ h=hashit(sha,date,h,EFS);
+ W=ECP.mapit(h);
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ W=PAIR.G1mul(W,x);
+ P.add(W);
+ //P.affine();
+ }
+ else
+ {
+ P.add(W); //P.affine();
+ P=PAIR.G1mul(P,x);
+ }
+ if (xCID!=null) P.toBytes(xCID,false);
+ }
+ else
+ {
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ }
+ }
+
+ //T.affine();
+ T.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+ public static int GET_SERVER_SECRET(byte[] S,byte[] SST)
+ {
+ ECP2 Q=ECP2.generator();
+ BIG s=BIG.fromBytes(S);
+ Q=PAIR.G2mul(Q,s);
+ Q.toBytes(SST);
+ return 0;
+ }
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+ public static int GET_G1_MULTIPLE(RAND rng, int type,byte[] X,byte[] G,byte[] W)
+ {
+ BIG x;
+ BIG r=new BIG(ROM.CURVE_Order);
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P;
+ if (type==0)
+ {
+ P=ECP.fromBytes(G);
+ if (P.is_infinity()) return INVALID_POINT;
+ }
+ else
+ P=ECP.mapit(G);
+
+ PAIR.G1mul(P,x).toBytes(W,false);
+ return 0;
+ }
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+ public static int GET_CLIENT_SECRET(byte[] S,byte[] CID,byte[] CST)
+ {
+ return GET_G1_MULTIPLE(null,1,S,CID,CST);
+ }
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+ public static int GET_CLIENT_PERMIT(int sha,int date,byte[] S,byte[] CID,byte[] CTT)
+ {
+ byte[] h=hashit(sha,date,CID,EFS);
+ ECP P=ECP.mapit(h);
+
+ BIG s=BIG.fromBytes(S);
+ ECP OP=PAIR.G1mul(P,s);
+
+ OP.toBytes(CTT,false);
+ return 0;
+ }
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+ public static void SERVER_1(int sha,int date,byte[] CID,byte[] HID,byte[] HTID)
+ {
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R,P=ECP.mapit(h);
+
+ P.toBytes(HID,false); // new
+ if (date!=0)
+ {
+ // if (HID!=null) P.toBytes(HID);
+ h=hashit(sha,date,h,EFS);
+ R=ECP.mapit(h);
+ P.add(R); //P.affine();
+ P.toBytes(HTID,false);
+ }
+ // else P.toBytes(HID,false);
+ }
+
+/* Implement step 2 of MPin protocol on server side */
+ public static int SERVER_2(int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] mSEC,byte[] E,byte[] F)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ ECP2 Q=ECP2.generator();
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+
+ ECP R;
+ if (date!=0)
+ R=ECP.fromBytes(xCID);
+ else
+ {
+ if (xID==null) return BAD_PARAMS;
+ R=ECP.fromBytes(xID);
+ }
+ if (R.is_infinity()) return INVALID_POINT;
+
+ BIG y=BIG.fromBytes(Y);
+ ECP P;
+ if (date!=0) P=ECP.fromBytes(HTID);
+ else
+ {
+ if (HID==null) return BAD_PARAMS;
+ P=ECP.fromBytes(HID);
+ }
+
+ if (P.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ R=ECP.fromBytes(mSEC);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ FP12 g;
+
+ g=PAIR.ate2(Q,R,sQ,P);
+ g=PAIR.fexp(g);
+
+ if (!g.isunity())
+ {
+ if (HID!=null && xID!=null && E!=null && F!=null)
+ {
+ g.toBytes(E);
+ if (date!=0)
+ {
+ P=ECP.fromBytes(HID);
+ if (P.is_infinity()) return INVALID_POINT;
+ R=ECP.fromBytes(xID);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ }
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(F);
+ }
+ return BAD_PIN;
+ }
+
+ return 0;
+ }
+
+/* Pollards kangaroos used to return PIN error */
+ public static int KANGAROO(byte[] E,byte[] F)
+ {
+ FP12 ge=FP12.fromBytes(E);
+ FP12 gf=FP12.fromBytes(F);
+ int[] distance = new int[TS];
+ FP12 t=new FP12(gf);
+ FP12[] table=new FP12[TS];
+ int i,j,m,s,dn,dm,res,steps;
+
+ s=1;
+ for (m=0;m<TS;m++)
+ {
+ distance[m]=s;
+ table[m]=new FP12(t);
+ s*=2;
+ t.usqr();
+ }
+ t.one();
+ dn=0;
+ for (j=0;j<TRAP;j++)
+ {
+ i=t.geta().geta().getA().lastbits(20)%TS;
+ t.mul(table[i]);
+ dn+=distance[i];
+ }
+ gf.copy(t); gf.conj();
+ steps=0; dm=0;
+ res=0;
+ while (dm-dn<MAXPIN)
+ {
+ steps++;
+ if (steps>4*TRAP) break;
+ i=ge.geta().geta().getA().lastbits(20)%TS;
+ ge.mul(table[i]);
+ dm+=distance[i];
+ if (ge.equals(t))
+ {
+ res=dm-dn;
+ break;
+ }
+ if (ge.equals(gf))
+ {
+ res=dn-dm;
+ break;
+ }
+
+ }
+ if (steps>4*TRAP || dm-dn>=MAXPIN) {res=0; } // Trap Failed - probable invalid token
+ return res;
+ }
+
+/* Functions to support M-Pin Full */
+
+ public static int PRECOMPUTE(byte[] TOKEN,byte[] CID,byte[] G1,byte[] G2)
+ {
+ ECP P,T;
+ FP12 g;
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ P=ECP.mapit(CID);
+
+ ECP2 Q=ECP2.generator();
+
+ g=PAIR.ate(Q,T);
+ g=PAIR.fexp(g);
+ g.toBytes(G1);
+
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(G2);
+
+ return 0;
+ }
+
+
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+ public static int CLIENT_KEY(int sha,byte[] G1,byte[] G2,int pin,byte[] R,byte[] X,byte[] H,byte[] wCID,byte[] CK)
+ {
+ byte[] t;
+
+ FP12 g1=FP12.fromBytes(G1);
+ FP12 g2=FP12.fromBytes(G2);
+ BIG z=BIG.fromBytes(R);
+ BIG x=BIG.fromBytes(X);
+ BIG h=BIG.fromBytes(H);
+
+ ECP W=ECP.fromBytes(wCID);
+ if (W.is_infinity()) return INVALID_POINT;
+
+ W=PAIR.G1mul(W,x);
+
+// FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG r=new BIG(ROM.CURVE_Order);
+// BIG q=new BIG(ROM.Modulus);
+
+ z.add(h); //new
+ z.mod(r);
+
+ g2.pinpow(pin,PBLEN);
+ g1.mul(g2);
+
+ FP4 c=g1.compow(z,r);
+/*
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(z);
+ a.mod(m);
+
+ BIG b=new BIG(z);
+ b.div(m);
+
+
+ FP4 c=g1.trace();
+ g2.copy(g1);
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+*/
+ t=mpin_hash(sha,c,W);
+
+ for (int i=0;i<ECP.AESKEY;i++) CK[i]=t[i];
+
+ return 0;
+ }
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+ public static int SERVER_KEY(int sha,byte[] Z,byte[] SST,byte[] W,byte[] H,byte[] HID,byte[] xID,byte[] xCID,byte[] SK)
+ {
+ byte[] t;
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+ ECP R=ECP.fromBytes(Z);
+ if (R.is_infinity()) return INVALID_POINT;
+ ECP A=ECP.fromBytes(HID);
+ if (A.is_infinity()) return INVALID_POINT;
+
+ ECP U;
+ if (xCID!=null)
+ U=ECP.fromBytes(xCID);
+ else
+ U=ECP.fromBytes(xID);
+ if (U.is_infinity()) return INVALID_POINT;
+
+ BIG w=BIG.fromBytes(W);
+ BIG h=BIG.fromBytes(H);
+ A=PAIR.G1mul(A,h); // new
+ R.add(A); //R.affine();
+
+ U=PAIR.G1mul(U,w);
+ FP12 g=PAIR.ate(sQ,R);
+ g=PAIR.fexp(g);
+
+ FP4 c=g.trace();
+
+ t=mpin_hash(sha,c,U);
+
+ for (int i=0;i<ECP.AESKEY;i++) SK[i]=t[i];
+
+ return 0;
+ }
+
+/* Generate Y = H(epoch, xCID/xID) */
+ public static void GET_Y(int sha,int TimeValue,byte[] xCID,byte[] Y)
+ {
+ byte[] h = hashit(sha,TimeValue,xCID,EFS);
+ BIG y = BIG.fromBytes(h);
+ BIG q=new BIG(ROM.CURVE_Order);
+ y.mod(q);
+ //if (ROM.AES_S>0)
+ //{
+ // y.mod2m(2*ROM.AES_S);
+ //}
+ y.toBytes(Y);
+ }
+
+/* One pass MPIN Client */
+ public static int CLIENT(int sha,int date,byte[] CLIENT_ID,RAND RNG,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT, int TimeValue, byte[] Y)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ rtn = CLIENT_1(sha,date,CLIENT_ID,RNG,X,pin,TOKEN,SEC,xID,xCID,PERMIT);
+ if (rtn != 0)
+ return rtn;
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+
+/* One pass MPIN Server */
+ public static int SERVER(int sha,int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] SEC,byte[] E,byte[] F,byte[] CID, int TimeValue)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ SERVER_1(sha,date,CID,HID,HTID);
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = SERVER_2(date,HID,HTID,Y,SST,xID,xCID,SEC,E,F);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/PAIR.java b/src/main/java/org/apache/milagro/amcl/BLS461/PAIR.java
new file mode 100644
index 0000000..7d49bc0
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/PAIR.java
@@ -0,0 +1,817 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BN Curve Pairing functions */
+
+package org.apache.milagro.amcl.BLS461;
+
+public final class PAIR {
+
+ public static final boolean USE_GLV =true;
+ public static final boolean USE_GS_G2 =true;
+ public static final boolean USE_GS_GT =true;
+ public static final boolean GT_STRONG=false;
+
+
+/* Line function */
+ public static FP12 line(ECP2 A,ECP2 B,FP Qx,FP Qy)
+ {
+//System.out.println("Into line");
+ FP4 a,b,c; // Edits here
+// c=new FP4(0);
+ if (A==B)
+ { // Doubling
+ FP2 XX=new FP2(A.getx()); //X
+ FP2 YY=new FP2(A.gety()); //Y
+ FP2 ZZ=new FP2(A.getz()); //Z
+ FP2 YZ=new FP2(YY); //Y
+ YZ.mul(ZZ); //YZ
+ XX.sqr(); //X^2
+ YY.sqr(); //Y^2
+ ZZ.sqr(); //Z^2
+
+ YZ.imul(4);
+ YZ.neg(); YZ.norm(); //-2YZ
+ YZ.pmul(Qy); //-2YZ.Ys
+
+ XX.imul(6); //3X^2
+ XX.pmul(Qx); //3X^2.Xs
+
+ int sb=3*ROM.CURVE_B_I;
+ ZZ.imul(sb);
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ ZZ.div_ip2();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ ZZ.mul_ip();
+ ZZ.add(ZZ);
+ YZ.mul_ip();
+ YZ.norm();
+ }
+
+ ZZ.norm(); // 3b.Z^2
+
+ YY.add(YY);
+ ZZ.sub(YY); ZZ.norm(); // 3b.Z^2-Y^2
+
+ a=new FP4(YZ,ZZ); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(XX); // L(0,1) | L(0,0) | L(1,0)
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(XX); c.times_i();
+ }
+ A.dbl();
+ }
+ else
+ { // Addition - assume B is affine
+
+ FP2 X1=new FP2(A.getx()); // X1
+ FP2 Y1=new FP2(A.gety()); // Y1
+ FP2 T1=new FP2(A.getz()); // Z1
+ FP2 T2=new FP2(A.getz()); // Z1
+
+ T1.mul(B.gety()); // T1=Z1.Y2
+ T2.mul(B.getx()); // T2=Z1.X2
+
+ X1.sub(T2); X1.norm(); // X1=X1-Z1.X2
+ Y1.sub(T1); Y1.norm(); // Y1=Y1-Z1.Y2
+
+ T1.copy(X1); // T1=X1-Z1.X2
+ X1.pmul(Qy); // X1=(X1-Z1.X2).Ys
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X1.mul_ip();
+ X1.norm();
+ }
+
+ T1.mul(B.gety()); // T1=(X1-Z1.X2).Y2
+
+ T2.copy(Y1); // T2=Y1-Z1.Y2
+ T2.mul(B.getx()); // T2=(Y1-Z1.Y2).X2
+ T2.sub(T1); T2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+ Y1.pmul(Qx); Y1.neg(); Y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+ a=new FP4(X1,T2); // (X1-Z1.X2).Ys | (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2 | - (Y1-Z1.Y2).Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(Y1);
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(Y1); c.times_i();
+ }
+ A.add(B);
+ }
+//System.out.println("Out of line");
+ return new FP12(a,b,c);
+ }
+
+/* Optimal R-ate pairing */
+ public static FP12 ate(ECP2 P1,ECP Q1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+
+ ECP2 A=new ECP2();
+ FP12 r=new FP12(1);
+ A.copy(P);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ //r.conj();
+ A.neg();
+ }
+ K.copy(P);
+ K.frob(f);
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+ public static FP12 ate2(ECP2 P1,ECP Q1,ECP2 R1,ECP S1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ ECP2 R=new ECP2(R1);
+ ECP S=new ECP(S1);
+
+ R.affine();
+ S.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+ FP Sx=new FP(S.getx());
+ FP Sy=new FP(S.gety());
+
+ ECP2 A=new ECP2();
+ ECP2 B=new ECP2();
+ FP12 r=new FP12(1);
+
+ A.copy(P);
+ B.copy(R);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+ ECP2 MR=new ECP2();
+ MR.copy(R); MR.neg();
+
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ lv=line(B,B,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ lv=line(B,R,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ //R.neg();
+ lv=line(B,MR,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //R.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ // r.conj();
+ A.neg();
+ B.neg();
+ }
+
+ K.copy(P);
+ K.frob(f);
+
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.copy(R);
+ K.frob(f);
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+ public static FP12 fexp(FP12 m)
+ {
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ FP12 r=new FP12(m);
+
+/* Easy part of final exp */
+ FP12 lv=new FP12(r);
+ lv.inverse();
+ r.conj();
+
+ r.mul(lv);
+ lv.copy(r);
+ r.frob(f);
+ r.frob(f);
+ r.mul(lv);
+/* Hard part of final exp */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ FP12 x0,x1,x2,x3,x4,x5;
+ lv.copy(r);
+ lv.frob(f);
+ x0=new FP12(lv);
+ x0.frob(f);
+ lv.mul(r);
+ x0.mul(lv);
+ x0.frob(f);
+ x1=new FP12(r);
+ x1.conj();
+ x4=r.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x4.conj();
+ }
+
+ x3=new FP12(x4);
+ x3.frob(f);
+
+ x2=x4.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x2.conj();
+ }
+ x5=new FP12(x2); x5.conj();
+ lv=x2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ lv.conj();
+ }
+ x2.frob(f);
+ r.copy(x2); r.conj();
+
+ x4.mul(r);
+ x2.frob(f);
+
+ r.copy(lv);
+ r.frob(f);
+ lv.mul(r);
+
+ lv.usqr();
+ lv.mul(x4);
+ lv.mul(x5);
+ r.copy(x3);
+ r.mul(x5);
+ r.mul(lv);
+ lv.mul(x2);
+ r.usqr();
+ r.mul(lv);
+ r.usqr();
+ lv.copy(r);
+ lv.mul(x1);
+ r.mul(x0);
+ lv.usqr();
+ r.mul(lv);
+ r.reduce();
+ }
+ else
+ {
+
+ FP12 y0,y1,y2,y3;
+// Ghamman & Fouotsa Method
+ y0=new FP12(r); y0.usqr();
+ y1=y0.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y1.conj();
+ }
+ x.fshr(1); y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+
+ x.fshl(1);
+ y3=new FP12(r); y3.conj();
+ y1.mul(y3);
+
+ y1.conj();
+ y1.mul(y2);
+
+ y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y3=y2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y3.conj();
+ }
+ y1.conj();
+ y3.mul(y1);
+
+ y1.conj();
+ y1.frob(f); y1.frob(f); y1.frob(f);
+ y2.frob(f); y2.frob(f);
+ y1.mul(y2);
+
+ y2=y3.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y2.mul(y0);
+ y2.mul(r);
+
+ y1.mul(y2);
+ y2.copy(y3); y2.frob(f);
+ y1.mul(y2);
+ r.copy(y1);
+ r.reduce();
+ }
+
+ return r;
+ }
+
+/* GLV method */
+ public static BIG[] glv(BIG e)
+ {
+ BIG[] u=new BIG[2];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+
+ BIG[] v=new BIG[2];
+ for (i=0;i<2;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_W[i])); // why not just t=new BIG(ROM.CURVE_W[i]);
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<2;i++)
+ for (j=0;j<2;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_SB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ { // -(x^2).P = (Beta.x,y)
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG x2=BIG.smul(x,x);
+ u[0]=new BIG(e);
+ u[0].mod(x2);
+ u[1]=new BIG(e);
+ u[1].div(x2);
+ u[1].rsub(q);
+ }
+ return u;
+ }
+
+/* Galbraith & Scott Method */
+ public static BIG[] gs(BIG e)
+ {
+ BIG[] u=new BIG[4];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] v=new BIG[4];
+ for (i=0;i<4;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_WB[i]));
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<4;i++)
+ for (j=0;j<4;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_BB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ {
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG w=new BIG(e);
+ for (int i=0;i<3;i++)
+ {
+ u[i]=new BIG(w);
+ u[i].mod(x);
+ w.div(x);
+ }
+ u[3]=new BIG(w);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ u[1].copy(BIG.modneg(u[1],q));
+ u[3].copy(BIG.modneg(u[3],q));
+ }
+ }
+ return u;
+ }
+
+/* Multiply P by e in group G1 */
+ public static ECP G1mul(ECP P,BIG e)
+ {
+ ECP R;
+ if (USE_GLV)
+ {
+ //P.affine();
+ R=new ECP();
+ R.copy(P);
+ int i,np,nn;
+ ECP Q=new ECP();
+ Q.copy(P); Q.affine();
+ BIG q=new BIG(ROM.CURVE_Order);
+ FP cru=new FP(new BIG(ROM.CURVE_Cru));
+ BIG t=new BIG(0);
+ BIG[] u=glv(e);
+ Q.getx().mul(cru);
+
+ np=u[0].nbits();
+ t.copy(BIG.modneg(u[0],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[0].copy(t);
+ R.neg();
+ }
+
+ np=u[1].nbits();
+ t.copy(BIG.modneg(u[1],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[1].copy(t);
+ Q.neg();
+ }
+ u[0].norm();
+ u[1].norm();
+ R=R.mul2(u[0],Q,u[1]);
+
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* Multiply P by e in group G2 */
+ public static ECP2 G2mul(ECP2 P,BIG e)
+ {
+ ECP2 R;
+ if (USE_GS_G2)
+ {
+ ECP2[] Q=new ECP2[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] u=gs(e);
+
+ BIG t=new BIG(0);
+ int i,np,nn;
+ //P.affine();
+
+ Q[0]=new ECP2(); Q[0].copy(P);
+ for (i=1;i<4;i++)
+ {
+ Q[i]=new ECP2(); Q[i].copy(Q[i-1]);
+ Q[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ Q[i].neg();
+ }
+ u[i].norm();
+ //Q[i].affine();
+ }
+
+ R=ECP2.mul4(Q,u);
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */
+ public static FP12 GTpow(FP12 d,BIG e)
+ {
+ FP12 r;
+ if (USE_GS_GT)
+ {
+ FP12[] g=new FP12[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG t=new BIG(0);
+ int i,np,nn;
+ BIG[] u=gs(e);
+
+ g[0]=new FP12(d);
+ for (i=1;i<4;i++)
+ {
+ g[i]=new FP12(0); g[i].copy(g[i-1]);
+ g[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ g[i].conj();
+ }
+ u[i].norm();
+ }
+ r=FP12.pow4(g,u);
+ }
+ else
+ {
+ r=d.pow(e);
+ }
+ return r;
+ }
+
+/* test group membership - no longer needed */
+/* with GT-Strong curve, now only check that m!=1, conj(m)*m==1, and m.m^{p^4}=m^{p^2} */
+/*
+ public static boolean GTmember(FP12 m)
+ {
+ if (m.isunity()) return false;
+ FP12 r=new FP12(m);
+ r.conj();
+ r.mul(m);
+ if (!r.isunity()) return false;
+
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ r.copy(m); r.frob(f); r.frob(f);
+ FP12 w=new FP12(r); w.frob(f); w.frob(f);
+ w.mul(m);
+ if (!ROM.GT_STRONG)
+ {
+ if (!w.equals(r)) return false;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ r.copy(m); w=r.pow(x); w=w.pow(x);
+ r.copy(w); r.sqr(); r.mul(w); r.sqr();
+ w.copy(m); w.frob(f);
+ }
+ return w.equals(r);
+ }
+*/
+/*
+ public static void main(String[] args) {
+ ECP Q=new ECP(new BIG(ROM.CURVE_Gx),new BIG(ROM.CURVE_Gy));
+ ECP2 P=new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG xa=new BIG(ROM.CURVE_Pxa);
+
+ System.out.println("P= "+P.toString());
+ System.out.println("Q= "+Q.toString());
+
+ BIG m=new BIG(17);
+
+ FP12 e=ate(P,Q);
+ System.out.println("\ne= "+e.toString());
+
+ e=fexp(e);
+
+ for (int i=1;i<1000;i++)
+ {
+ e=ate(P,Q);
+ e=fexp(e);
+ }
+ // e=GTpow(e,m);
+
+ System.out.println("\ne= "+e.toString());
+
+ BIG [] GLV=glv(r);
+
+ System.out.println("GLV[0]= "+GLV[0].toString());
+ System.out.println("GLV[0]= "+GLV[1].toString());
+
+ ECP G=new ECP(); G.copy(Q);
+ ECP2 R=new ECP2(); R.copy(P);
+
+
+ e=ate(R,Q);
+ e=fexp(e);
+
+ e=GTpow(e,xa);
+ System.out.println("\ne= "+e.toString());
+
+
+ R=G2mul(R,xa);
+ e=ate(R,G);
+ e=fexp(e);
+
+ System.out.println("\ne= "+e.toString());
+
+ G=G1mul(G,xa);
+ e=ate(P,G);
+ e=fexp(e);
+ System.out.println("\ne= "+e.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS461/ROM.java b/src/main/java/org/apache/milagro/amcl/BLS461/ROM.java
new file mode 100644
index 0000000..c5c5ed1
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS461/ROM.java
@@ -0,0 +1,56 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.BLS461;
+
+public class ROM
+{
+
+// Base Bits= 60
+public static final long[] Modulus= {0xAAC0000AAAAAAABL,0x20000555554AAAAL,0x6AA91557F004000L,0xA8DFFA5C1CC00F2L,0xACCA47B14848B42L,0x935FBD6F1E32D8BL,0xD5A555A55D69414L,0x15555545554L};
+public static final long[] R2modp= {0x96D08774614DDA8L,0xCD45F539225D5BDL,0xD712EB760C95AB1L,0xB3B687155F30B55L,0xC4E62A05C3F5B81L,0xBA1151676CA3CD0L,0x7EDD8A958F442BEL,0x12B89DD3F91L};
+public static final long MConst= 0xC0005FFFFFFFDL;
+
+public static final int CURVE_A= 0;
+public static final int CURVE_B_I= 9;
+public static final int CURVE_Cof_I= 0;
+public static final long[] CURVE_B= {0x9L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Order= {0x1L,0x7FEFFFEFFFFC0L,0xC017FFC80001100L,0x7FE05FD000E801FL,0xFFFF7FFFC018001L,0xFFL,0x0L,0x0L};
+public static final long[] CURVE_Gx= {0x14D026A8ADEE93DL,0xF2D9C00EE74B741L,0x229C3981B531AC7L,0x6650D3564DC9218L,0x436166F7C292A09L,0x2CF668BE922B197L,0x463B73A0C813271L,0xAD0E74E99BL};
+public static final long[] CURVE_Gy= {0xF763157AD1D465L,0x5D17884C8C4FF47L,0x9D0A819E66B8D21L,0x910AE5C3245F495L,0x96EECB8BFA40B84L,0x277ACC8BF9F8CBEL,0x5F68C95F1C3F2FL,0x77BCDB14B3L};
+
+public static final long[] Fra= {0xF7117BF9B812A3AL,0xA1C6308A599C400L,0x5A6510E07505BF8L,0xB31ACE4858D45FAL,0xFC61EBC2CB04770L,0x366190D073588E2L,0x69E55E24DFEFA84L,0x12E40504B7FL};
+public static final long[] Frb= {0xB3AE8410F298071L,0x7E39D4CAFBAE6A9L,0x104404777AFE407L,0xF5C52C13C3EBAF8L,0xB0685BEE7D443D1L,0x5CFE2C9EAADA4A8L,0x6BBFF7807D79990L,0x27150409D5L};
+public static final long[] CURVE_Bnx= {0xFFBFFFE00000000L,0x1FFFFL,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Cof= {0xAA7FFFEAAAAAAABL,0xFFD55AAAB01556AL,0x1555554FFL,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Cru= {0x40001FFFFFFFEL,0x6FFFE7FFFFE0000L,0x6047200C47F0FFFL,0x777115796DB7BCCL,0x3F0E89875433CF4L,0xBFFF60500050261L,0x1FFFFFEL,0x0L};
+public static final long[] CURVE_Pxa= {0x65B503186D0A37CL,0xA9C2E492E75DCC4L,0x564E01F919D6878L,0x3F086DB74FF92FL,0xED78D46D581A668L,0x270C892F97C2907L,0x6A50A9AF679453CL,0x10CC54138A0L};
+public static final long[] CURVE_Pxb= {0x9F85CA8C2C1C0ADL,0x96CD66C425CADEL,0x1AC612951A2896L,0xB17D529ABEBEE24L,0xC5AF5BA09D33F65L,0x6A672E4D4371ED4L,0xACEA37CA279D224L,0x95C1FB4FE5L};
+public static final long[] CURVE_Pya= {0x7CCD0C1B02FB006L,0x953D194A4A12A33L,0x68B4960CFCC92C8L,0xBA0F3A9B00F39FCL,0xCDFD8A7DBBC5ED1L,0xE73ED227CC2F7A9L,0xEBA7E676070F4F4L,0x226AC848E7L};
+public static final long[] CURVE_Pyb= {0x8A506ADFDF1457CL,0xB4D6A31DC04C20AL,0x668EA9A8F136E3FL,0x12973C3BE4492F5L,0xA20BE74BEABA67AL,0x5157F04C42E3856L,0xBB402EA2AB1D004L,0xE38101B4FAL};
+public static final long[][] CURVE_W= {{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}};
+public static final long[][][] CURVE_SB= {{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}}};
+public static final long[][] CURVE_WB= {{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}};
+public static final long[][][] CURVE_BB= {{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}}};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/BIG.java b/src/main/java/org/apache/milagro/amcl/BLS48/BIG.java
new file mode 100644
index 0000000..dcc527a
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.BLS48;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=70; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=58;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/DBIG.java b/src/main/java/org/apache/milagro/amcl/BLS48/DBIG.java
new file mode 100644
index 0000000..f81bf5c
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.BLS48;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/ECDH.java b/src/main/java/org/apache/milagro/amcl/BLS48/ECDH.java
new file mode 100644
index 0000000..9146ab5
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.BLS48;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/ECP.java b/src/main/java/org/apache/milagro/amcl/BLS48/ECP.java
new file mode 100644
index 0000000..280eb49
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.BLS48;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=BLS;
+ public static final int SEXTIC_TWIST=M_TYPE;
+ public static final int SIGN_OF_X=POSITIVEX;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=64;
+ public static final int AESKEY=32;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/ECP8.java b/src/main/java/org/apache/milagro/amcl/BLS48/ECP8.java
new file mode 100644
index 0000000..8b5885e
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/ECP8.java
@@ -0,0 +1,930 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Weierstrass elliptic curve functions over FP8 */
+
+package org.apache.milagro.amcl.BLS48;
+
+public final class ECP8 {
+ private FP8 x;
+ private FP8 y;
+ private FP8 z;
+// private boolean INF;
+
+/* Constructor - set this=O */
+ public ECP8() {
+// INF=true;
+ x=new FP8(0);
+ y=new FP8(1);
+ z=new FP8(0);
+ }
+
+ public ECP8(ECP8 e) {
+ this.x = new FP8(e.x);
+ this.y = new FP8(e.y);
+ this.z = new FP8(e.z);
+ }
+
+/* Test this=O? */
+ public boolean is_infinity() {
+// if (INF) return true; //******
+ return (x.iszilch() && z.iszilch());
+ }
+/* copy this=P */
+ public void copy(ECP8 P)
+ {
+ x.copy(P.x);
+ y.copy(P.y);
+ z.copy(P.z);
+// INF=P.INF;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ y.one();
+ z.zero();
+ }
+
+/* Conditional move of Q to P dependant on d */
+ public void cmove(ECP8 Q,int d)
+ {
+ x.cmove(Q.x,d);
+ y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+
+// boolean bd;
+// if (d==0) bd=false;
+// else bd=true;
+// INF^=(INF^Q.INF)&bd;
+ }
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(ECP8 W[],int b)
+ {
+ ECP8 MP=new ECP8();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test if P == Q */
+ public boolean equals(ECP8 Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+
+ FP8 a=new FP8(x); // *****
+ FP8 b=new FP8(Q.x);
+ a.mul(Q.z);
+ b.mul(z);
+ if (!a.equals(b)) return false;
+
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+
+ return true;
+ }
+
+/* set this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ y.norm();
+ y.neg(); y.norm();
+ return;
+ }
+/* set to Affine - (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return;
+ FP8 one=new FP8(1);
+ if (z.equals(one))
+ {
+ x.reduce();
+ y.reduce();
+ return;
+ }
+ z.inverse();
+
+ x.mul(z); x.reduce(); // *****
+ y.mul(z); y.reduce();
+ z.copy(one);
+ }
+
+/* extract affine x as FP8 */
+ public FP8 getX()
+ {
+ ECP8 W= new ECP8(this);
+ W.affine();
+ return W.x;
+ }
+/* extract affine y as FP8 */
+ public FP8 getY()
+ {
+ ECP8 W= new ECP8(this);
+ W.affine();
+ return W.y;
+ }
+/* extract projective x */
+ public FP8 getx()
+ {
+ return x;
+ }
+/* extract projective y */
+ public FP8 gety()
+ {
+ return y;
+ }
+/* extract projective z */
+ public FP8 getz()
+ {
+ return z;
+ }
+
+/* convert to byte array */
+ public void toBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP8 W=new ECP8(this);
+ W.affine();
+ int MB=BIG.MODBYTES;
+
+ W.x.geta().geta().getA().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i]=t[i];}
+ W.x.geta().geta().getB().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+MB]=t[i];}
+ W.x.geta().getb().getA().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+2*MB]=t[i];}
+ W.x.geta().getb().getB().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+3*MB]=t[i];}
+
+ W.x.getb().geta().getA().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+4*MB]=t[i];}
+ W.x.getb().geta().getB().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+5*MB]=t[i];}
+ W.x.getb().getb().getA().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+6*MB]=t[i];}
+ W.x.getb().getb().getB().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+7*MB]=t[i];}
+
+
+ W.y.geta().geta().getA().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+8*MB]=t[i];}
+ W.y.geta().geta().getB().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+9*MB]=t[i];}
+ W.y.geta().getb().getA().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+10*MB]=t[i];}
+ W.y.geta().getb().getB().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+11*MB]=t[i];}
+
+ W.y.getb().geta().getA().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+12*MB]=t[i];}
+ W.y.getb().geta().getB().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+13*MB]=t[i];}
+ W.y.getb().getb().getA().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+14*MB]=t[i];}
+ W.y.getb().getb().getB().toBytes(t);
+ for (int i=0;i<MB;i++) { b[i+15*MB]=t[i];}
+
+ }
+
+/* convert from byte array to point */
+ public static ECP8 fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG ra;
+ BIG rb;
+ int MB=BIG.MODBYTES;
+
+ for (int i=0;i<MB;i++) {t[i]=b[i];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+MB];}
+ rb=BIG.fromBytes(t);
+
+ FP2 ra4=new FP2(ra,rb);
+
+ for (int i=0;i<MB;i++) {t[i]=b[i+2*MB];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+3*MB];}
+ rb=BIG.fromBytes(t);
+
+ FP2 rb4=new FP2(ra,rb);
+
+ FP4 ra8=new FP4(ra4,rb4);
+
+ for (int i=0;i<MB;i++) {t[i]=b[i+4*MB];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+5*MB];}
+ rb=BIG.fromBytes(t);
+
+ ra4=new FP2(ra,rb);
+
+ for (int i=0;i<MB;i++) {t[i]=b[i+6*MB];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+7*MB];}
+ rb=BIG.fromBytes(t);
+
+ rb4=new FP2(ra,rb);
+
+ FP4 rb8=new FP4(ra4,rb4);
+
+ FP8 rx=new FP8(ra8,rb8);
+
+
+
+ for (int i=0;i<MB;i++) {t[i]=b[i+8*MB];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+9*MB];}
+ rb=BIG.fromBytes(t);
+
+ ra4=new FP2(ra,rb);
+
+ for (int i=0;i<MB;i++) {t[i]=b[i+10*MB];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+11*MB];}
+ rb=BIG.fromBytes(t);
+
+ rb4=new FP2(ra,rb);
+
+ ra8=new FP4(ra4,rb4);
+
+ for (int i=0;i<MB;i++) {t[i]=b[i+12*MB];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+13*MB];}
+ rb=BIG.fromBytes(t);
+
+ ra4=new FP2(ra,rb);
+
+ for (int i=0;i<MB;i++) {t[i]=b[i+14*MB];}
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<MB;i++) {t[i]=b[i+15*MB];}
+ rb=BIG.fromBytes(t);
+
+ rb4=new FP2(ra,rb);
+
+ rb8=new FP4(ra4,rb4);
+
+ FP8 ry=new FP8(ra8,rb8);
+
+ return new ECP8(rx,ry);
+ }
+
+/* convert this to hex string */
+ public String toString() {
+ ECP8 W=new ECP8(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ return "("+W.x.toString()+","+W.y.toString()+")";
+ }
+
+/* Calculate RHS of twisted curve equation x^3+B/i */
+ public static FP8 RHS(FP8 x) {
+ x.norm();
+ FP8 r=new FP8(x);
+ r.sqr();
+ FP8 b=new FP8(new FP4(new FP2(new BIG(ROM.CURVE_B))));
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b.div_i();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b.times_i();
+ }
+
+
+ r.mul(x);
+ r.add(b);
+
+ r.reduce();
+ return r;
+ }
+
+/* construct this from (x,y) - but set to O if not on curve */
+ public ECP8(FP8 ix,FP8 iy) {
+ x=new FP8(ix);
+ y=new FP8(iy);
+ z=new FP8(1);
+ FP8 rhs=RHS(x);
+ FP8 y2=new FP8(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else {x.zero();INF=true;}
+ }
+
+/* construct this from x - but set to O if not on curve */
+ public ECP8(FP8 ix) {
+ x=new FP8(ix);
+ y=new FP8(1);
+ z=new FP8(1);
+ FP8 rhs=RHS(x);
+ if (rhs.sqrt())
+ {
+ y.copy(rhs);
+ // INF=false;
+ }
+ else {inf();/*x.zero();INF=true;*/}
+ }
+
+/* this+=this */
+ public int dbl() {
+// if (INF) return -1;
+
+ FP8 iy=new FP8(y);
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ iy.times_i(); //iy.norm();
+ }
+ FP8 t0=new FP8(y); //***** Change
+ t0.sqr();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.times_i();
+ }
+ FP8 t1=new FP8(iy);
+ t1.mul(z);
+ FP8 t2=new FP8(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z);
+ z.add(z);
+ z.norm();
+
+ t2.imul(3*ROM.CURVE_B_I);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.times_i();
+ //t2.norm();
+ }
+
+ FP8 x3=new FP8(t2);
+ x3.mul(z);
+
+ FP8 y3=new FP8(t0);
+
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1); t2.norm();
+ t0.sub(t2); t0.norm(); //y^2-9bz^2
+ y3.mul(t0); y3.add(x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+ t1.copy(x); t1.mul(iy); //
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x); //(y^2-9bz^2)xy2
+
+ x.norm();
+ y.copy(y3); y.norm();
+
+ return 1;
+ }
+
+/* this+=Q - return 0 for add, 1 for double, -1 for O */
+ public int add(ECP8 Q) {
+// if (INF)
+// {
+// copy(Q);
+// return -1;
+// }
+// if (Q.INF) return -1;
+
+ int b=3*ROM.CURVE_B_I;
+ FP8 t0=new FP8(x);
+ t0.mul(Q.x); // x.Q.x
+ FP8 t1=new FP8(y);
+ t1.mul(Q.y); // y.Q.y
+
+ FP8 t2=new FP8(z);
+ t2.mul(Q.z);
+ FP8 t3=new FP8(x);
+ t3.add(y); t3.norm(); //t3=X1+Y1
+ FP8 t4=new FP8(Q.x);
+ t4.add(Q.y); t4.norm(); //t4=X2+Y2
+ t3.mul(t4); //t3=(X1+Y1)(X2+Y2)
+ t4.copy(t0); t4.add(t1); //t4=X1.X2+Y1.Y2
+
+ t3.sub(t4); t3.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t3.times_i(); //t3.norm(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+ }
+ t4.copy(y);
+ t4.add(z); t4.norm(); //t4=Y1+Z1
+ FP8 x3=new FP8(Q.y);
+ x3.add(Q.z); x3.norm(); //x3=Y2+Z2
+
+ t4.mul(x3); //t4=(Y1+Z1)(Y2+Z2)
+ x3.copy(t1); //
+ x3.add(t2); //X3=Y1.Y2+Z1.Z2
+
+ t4.sub(x3); t4.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t4.times_i(); //t4.norm(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+ }
+ x3.copy(x); x3.add(z); x3.norm(); // x3=X1+Z1
+ FP8 y3=new FP8(Q.x);
+ y3.add(Q.z); y3.norm(); // y3=X2+Z2
+ x3.mul(y3); // x3=(X1+Z1)(X2+Z2)
+ y3.copy(t0);
+ y3.add(t2); // y3=X1.X2+Z1+Z2
+ y3.rsub(x3); y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.times_i(); //t0.norm(); // x.Q.x
+ t1.times_i(); //t1.norm(); // y.Q.y
+ }
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.times_i();
+ }
+ FP8 z3=new FP8(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ y3.times_i();
+ //y3.norm();
+ }
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+
+ return 0;
+ }
+
+/* set this-=Q */
+ public int sub(ECP8 Q) {
+ ECP8 NQ=new ECP8(Q);
+ NQ.neg();
+ int D=add(NQ);
+
+// Q.neg();
+// int D=add(Q);
+// Q.neg();
+ return D;
+ }
+
+ public static FP2[] frob_constants() {
+ BIG Fra=new BIG(ROM.Fra);
+ BIG Frb=new BIG(ROM.Frb);
+ FP2 X=new FP2(Fra,Frb);
+
+ FP2 F0=new FP2(X); F0.sqr();
+ FP2 F2=new FP2(F0);
+ F2.mul_ip(); F2.norm();
+ FP2 F1=new FP2(F2); F1.sqr();
+ F2.mul(F1);
+
+ F2.mul_ip(); F2.norm();
+
+ F1.copy(X);
+ if (ECP.SEXTIC_TWIST == ECP.M_TYPE)
+ {
+ F1.mul_ip();
+ F1.inverse();
+ F0.copy(F1); F0.sqr();
+ }
+ F0.mul_ip(); F0.norm();
+ F1.mul(F0);
+ FP2[] F={F0,F1,F2};
+ return F;
+ }
+
+/* set this*=q, where q is Modulus, using Frobenius */
+ public void frob(FP2 F[],int n)
+ {
+// if (INF) return;
+ for (int i=0;i<n;i++) {
+ x.frob(F[2]);
+ x.qmul(F[0]);
+ if (ECP.SEXTIC_TWIST == ECP.M_TYPE) {
+ x.div_i2();
+ }
+ if (ECP.SEXTIC_TWIST == ECP.D_TYPE) {
+ x.times_i2();
+ }
+
+ y.frob(F[2]);
+ y.qmul(F[1]);
+
+ if (ECP.SEXTIC_TWIST == ECP.M_TYPE) {
+ y.div_i();
+ }
+ if (ECP.SEXTIC_TWIST == ECP.D_TYPE) {
+ y.times_i2(); y.times_i2(); y.times_i();
+ }
+ z.frob(F[2]);
+ }
+ }
+
+/* P*=e */
+ public ECP8 mul(BIG e)
+ {
+/* fixed size windows */
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP8 P=new ECP8();
+ ECP8 Q=new ECP8();
+ ECP8 C=new ECP8();
+ ECP8[] W=new ECP8[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ if (is_infinity()) return new ECP8();
+
+ //affine();
+
+/* precompute table */
+ Q.copy(this);
+ Q.dbl();
+ W[0]=new ECP8();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP8();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+/* make exponent odd - add 2P if even, P if odd */
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+/* convert exponent to signed 4-bit window */
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C);
+ P.affine();
+ return P;
+ }
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3... */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static ECP8 mul16(ECP8[] Q,BIG[] u)
+ {
+ int i,j,k,nb,pb1,pb2,pb3,pb4;
+ ECP8 W=new ECP8();
+ ECP8 P=new ECP8();
+ ECP8[] T1=new ECP8[8];
+ ECP8[] T2=new ECP8[8];
+ ECP8[] T3=new ECP8[8];
+ ECP8[] T4=new ECP8[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[16];
+
+ byte[] w1=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s1=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] w2=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s2=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] w3=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s3=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] w4=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s4=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<16;i++)
+ {
+ t[i]=new BIG(u[i]);
+ //Q[i].affine();
+ t[i].norm();
+ }
+
+ T1[0] = new ECP8(); T1[0].copy(Q[0]); // Q[0]
+ T1[1] = new ECP8(); T1[1].copy(T1[0]); T1[1].add(Q[1]); // Q[0]+Q[1]
+ T1[2] = new ECP8(); T1[2].copy(T1[0]); T1[2].add(Q[2]); // Q[0]+Q[2]
+ T1[3] = new ECP8(); T1[3].copy(T1[1]); T1[3].add(Q[2]); // Q[0]+Q[1]+Q[2]
+ T1[4] = new ECP8(); T1[4].copy(T1[0]); T1[4].add(Q[3]); // Q[0]+Q[3]
+ T1[5] = new ECP8(); T1[5].copy(T1[1]); T1[5].add(Q[3]); // Q[0]+Q[1]+Q[3]
+ T1[6] = new ECP8(); T1[6].copy(T1[2]); T1[6].add(Q[3]); // Q[0]+Q[2]+Q[3]
+ T1[7] = new ECP8(); T1[7].copy(T1[3]); T1[7].add(Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+// Use Frobenius
+ FP2[] F=ECP8.frob_constants();
+
+ for (i=0;i<8;i++) {
+ T2[i] = new ECP8(); T2[i].copy(T1[i]);
+ T2[i].frob(F,4);
+ T3[i] = new ECP8(); T3[i].copy(T2[i]);
+ T3[i].frob(F,4);
+ T4[i] = new ECP8(); T4[i].copy(T3[i]);
+ T4[i].frob(F,4);
+
+ }
+
+ // Make it odd
+ pb1=1-t[0].parity();
+ t[0].inc(pb1);
+ t[0].norm();
+
+ pb2=1-t[4].parity();
+ t[4].inc(pb2);
+ t[4].norm();
+
+ pb3=1-t[8].parity();
+ t[8].inc(pb3);
+ t[8].norm();
+
+ pb4=1-t[12].parity();
+ t[12].inc(pb4);
+ t[12].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<16;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s1[nb-1]=1;
+ s2[nb-1]=1;
+ s3[nb-1]=1;
+ s4[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s1[i]=(byte)(2*t[0].parity()-1);
+ t[4].fshr(1);
+ s2[i]=(byte)(2*t[4].parity()-1);
+ t[8].fshr(1);
+ s3[i]=(byte)(2*t[8].parity()-1);
+ t[12].fshr(1);
+ s4[i]=(byte)(2*t[12].parity()-1);
+
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w1[i]=0;
+ k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s1[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w1[i]+=bt*(byte)k;
+ k*=2;
+ }
+
+ w2[i]=0;
+ k=1;
+ for (j=5; j<8; j++) {
+ byte bt=(byte)(s2[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w2[i]+=bt*(byte)k;
+ k*=2;
+ }
+
+ w3[i]=0;
+ k=1;
+ for (j=9; j<12; j++) {
+ byte bt=(byte)(s3[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w3[i]+=bt*(byte)k;
+ k*=2;
+ }
+
+ w4[i]=0;
+ k=1;
+ for (j=13; j<16; j++) {
+ byte bt=(byte)(s4[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w4[i]+=bt*(byte)k;
+ k*=2;
+ }
+
+ }
+
+ // Main loop
+ P.select(T1,(int)(2*w1[nb-1]+1));
+ W.select(T2,(int)(2*w2[nb-1]+1));
+ P.add(W);
+ W.select(T3,(int)(2*w3[nb-1]+1));
+ P.add(W);
+ W.select(T4,(int)(2*w4[nb-1]+1));
+ P.add(W);
+ for (i=nb-2;i>=0;i--) {
+ P.dbl();
+ W.select(T1,(int)(2*w1[i]+s1[i]));
+ P.add(W);
+ W.select(T2,(int)(2*w2[i]+s2[i]));
+ P.add(W);
+ W.select(T3,(int)(2*w3[i]+s3[i]));
+ P.add(W);
+ W.select(T4,(int)(2*w4[i]+s4[i]));
+ P.add(W);
+
+ }
+
+ // apply correction
+ W.copy(P);
+ W.sub(Q[0]);
+ P.cmove(W,pb1);
+
+ W.copy(P);
+ W.sub(Q[4]);
+ P.cmove(W,pb2);
+
+ W.copy(P);
+ W.sub(Q[8]);
+ P.cmove(W,pb3);
+
+ W.copy(P);
+ W.sub(Q[12]);
+ P.cmove(W,pb4);
+
+ P.affine();
+ return P;
+ }
+
+/* needed for SOK */
+ public static ECP8 mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ BIG one=new BIG(1);
+ FP8 X;
+ FP2 X2;
+ FP4 X4;
+ ECP8 Q;
+ x.mod(q);
+ while (true)
+ {
+ X2=new FP2(one,x);
+ X4=new FP4(X2);
+ X=new FP8(X4);
+ Q=new ECP8(X);
+ if (!Q.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+
+ FP2[] F=ECP8.frob_constants();
+ x=new BIG(ROM.CURVE_Bnx);
+
+/* Efficient hash maps to G2 on BLS curves - Budroni, Pintore */
+
+ ECP8 xQ=Q.mul(x);
+ ECP8 x2Q=xQ.mul(x);
+ ECP8 x3Q=x2Q.mul(x);
+ ECP8 x4Q=x3Q.mul(x);
+ ECP8 x5Q=x4Q.mul(x);
+ ECP8 x6Q=x5Q.mul(x);
+ ECP8 x7Q=x6Q.mul(x);
+ ECP8 x8Q=x7Q.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ xQ.neg();
+ x3Q.neg();
+ x5Q.neg();
+ x7Q.neg();
+ }
+
+ x8Q.sub(x7Q);
+ x8Q.sub(Q);
+
+ x7Q.sub(x6Q);
+ x7Q.frob(F,1);
+
+ x6Q.sub(x5Q);
+ x6Q.frob(F,2);
+
+ x5Q.sub(x4Q);
+ x5Q.frob(F,3);
+
+ x4Q.sub(x3Q);
+ x4Q.frob(F,4);
+
+ x3Q.sub(x2Q);
+ x3Q.frob(F,5);
+
+ x2Q.sub(xQ);
+ x2Q.frob(F,6);
+
+ xQ.sub(Q);
+ xQ.frob(F,7);
+
+ Q.dbl();
+ Q.frob(F,8);
+
+ Q.add(x8Q);
+ Q.add(x7Q);
+ Q.add(x6Q);
+ Q.add(x5Q);
+
+ Q.add(x4Q);
+ Q.add(x3Q);
+ Q.add(x2Q);
+ Q.add(xQ);
+
+ Q.affine();
+ return Q;
+ }
+
+ public static ECP8 generator()
+ {
+ return new ECP8(
+ new FP8(
+ new FP4(
+ new FP2(
+ new BIG(ROM.CURVE_Pxaaa),new BIG(ROM.CURVE_Pxaab)),
+ new FP2(
+ new BIG(ROM.CURVE_Pxaba),new BIG(ROM.CURVE_Pxabb))),
+ new FP4(
+ new FP2(
+ new BIG(ROM.CURVE_Pxbaa),new BIG(ROM.CURVE_Pxbab)),
+ new FP2(
+ new BIG(ROM.CURVE_Pxbba),new BIG(ROM.CURVE_Pxbbb)))),
+ new FP8(
+ new FP4(
+ new FP2(
+ new BIG(ROM.CURVE_Pyaaa),new BIG(ROM.CURVE_Pyaab)),
+ new FP2(
+ new BIG(ROM.CURVE_Pyaba),new BIG(ROM.CURVE_Pyabb))),
+ new FP4(
+ new FP2(
+ new BIG(ROM.CURVE_Pybaa),new BIG(ROM.CURVE_Pybab)),
+ new FP2(
+ new BIG(ROM.CURVE_Pybba),new BIG(ROM.CURVE_Pybbb)))));
+
+ }
+
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/FP.java b/src/main/java/org/apache/milagro/amcl/BLS48/FP.java
new file mode 100644
index 0000000..866c8d8
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.BLS48;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=556; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<24); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/FP16.java b/src/main/java/org/apache/milagro/amcl/BLS48/FP16.java
new file mode 100644
index 0000000..728e940
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/FP16.java
@@ -0,0 +1,563 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^16 functions */
+
+/* FP16 elements are of the form a+ib, where i is sqrt(sqrt(sqrt(-1+sqrt(-1)))) */
+
+package org.apache.milagro.amcl.BLS48;
+
+public final class FP16 {
+ private final FP8 a;
+ private final FP8 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP16 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP8 one=new FP8(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP8 real()
+ {
+ return a;
+ }
+
+ public FP8 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP8 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP16 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP16(int c)
+ {
+ a=new FP8(c);
+ b=new FP8(0);
+ }
+
+ public FP16(FP16 x)
+ {
+ a=new FP8(x.a);
+ b=new FP8(x.b);
+ }
+
+ public FP16(FP8 c,FP8 d)
+ {
+ a=new FP8(c);
+ b=new FP8(d);
+ }
+
+ public FP16(FP8 c)
+ {
+ a=new FP8(c);
+ b=new FP8(0);
+ }
+/* copy this=x */
+ public void copy(FP16 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP8 m=new FP8(a);
+ FP8 t=new FP8(0);
+ m.add(b);
+ m.neg();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP16 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP16 x)
+ {
+ FP16 m=new FP16(x);
+ m.neg();
+ add(m);
+ }
+
+/* this=x-this */
+ public void rsub(FP16 x)
+ {
+ neg();
+ add(x);
+ }
+
+/* this*=s where s is FP8 */
+ public void pmul(FP8 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+/* this*=s where s is FP2 */
+ public void qmul(FP2 s)
+ {
+ a.qmul(s);
+ b.qmul(s);
+ }
+
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP8 t1=new FP8(a);
+ FP8 t2=new FP8(b);
+ FP8 t3=new FP8(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.times_i();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.times_i();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+
+/* this*=y */
+ public void mul(FP16 y)
+ {
+// norm();
+
+ FP8 t1=new FP8(a);
+ FP8 t2=new FP8(b);
+ FP8 t3=new FP8(0);
+ FP8 t4=new FP8(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ t2.times_i();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP8 t1=new FP8(a);
+ FP8 t2=new FP8(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.times_i();
+ t2.norm();
+ t1.sub(t2); t1.norm();
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP8 s=new FP8(b);
+ FP8 t=new FP8(a);
+ s.times_i();
+ b.copy(t);
+ a.copy(s);
+ norm();
+ }
+
+ public void times_i2()
+ {
+ a.times_i();
+ b.times_i();
+ }
+
+ public void times_i4()
+ {
+ a.times_i2();
+ b.times_i2();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ FP2 ff=new FP2(f); ff.sqr(); ff.norm();
+
+ a.frob(ff);
+ b.frob(ff);
+ b.qmul(f);
+ b.times_i();
+ }
+
+/* this=this^e */
+ public FP16 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP16 w=new FP16(this);
+ BIG z=new BIG(e);
+ FP16 r=new FP16(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+
+/* XTR xtr_a function */
+ public void xtr_A(FP16 w,FP16 y,FP16 z)
+ {
+ FP16 r=new FP16(w);
+ FP16 t=new FP16(w);
+
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP16 w=new FP16(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP16 xtr_pow(BIG n) {
+ FP16 a=new FP16(3);
+ FP16 b=new FP16(this);
+ FP16 c=new FP16(b);
+ c.xtr_D();
+ FP16 t=new FP16(0);
+ FP16 r=new FP16(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP16 xtr_pow2(FP16 ck,FP16 ckml,FP16 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP16 cu=new FP16(ck); // can probably be passed in w/o copying
+ FP16 cv=new FP16(this);
+ FP16 cumv=new FP16(ckml);
+ FP16 cum2v=new FP16(ckm2l);
+ FP16 r=new FP16(0);
+ FP16 t=new FP16(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/FP2.java b/src/main/java/org/apache/milagro/amcl/BLS48/FP2.java
new file mode 100644
index 0000000..c27129f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/FP2.java
@@ -0,0 +1,425 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^2 functions */
+
+/* FP2 elements are of the form a+ib, where i is sqrt(-1) */
+
+package org.apache.milagro.amcl.BLS48;
+
+public final class FP2 {
+ private final FP a;
+ private final FP b;
+
+/* reduce components mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+
+/* normalise components of w */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+
+/* test this=0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP2 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this=1 ? */
+ public boolean isunity() {
+ FP one=new FP(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test this=x */
+ public boolean equals(FP2 x) {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+
+/* Constructors */
+ public FP2(int c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(FP2 x)
+ {
+ a=new FP(x.a);
+ b=new FP(x.b);
+ }
+
+ public FP2(FP c,FP d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(BIG c,BIG d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(FP c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(BIG c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+/*
+ public BIG geta()
+ {
+ return a.tobig();
+ }
+*/
+/* extract a */
+ public BIG getA()
+ {
+ return a.redc();
+ }
+
+/* extract b */
+ public BIG getB()
+ {
+ return b.redc();
+ }
+
+/* copy this=x */
+ public void copy(FP2 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+
+/* negate this mod Modulus */
+ public void neg()
+ {
+ FP m=new FP(a);
+ FP t=new FP(0);
+
+ m.add(b);
+ m.neg();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ }
+
+/* set to a-ib */
+ public void conj()
+ {
+ b.neg();
+ b.norm();
+ }
+
+/* this+=a */
+ public void add(FP2 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+
+/* this-=a */
+ public void sub(FP2 x)
+ {
+ FP2 m=new FP2(x);
+ m.neg();
+ add(m);
+ }
+
+ public void rsub(FP2 x) // *****
+ {
+ neg();
+ add(x);
+ }
+
+/* this*=s, where s is an FP */
+ public void pmul(FP s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this*=i, where i is an int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+ FP w1=new FP(a);
+ FP w3=new FP(a);
+ FP mb=new FP(b);
+
+ w1.add(b);
+ mb.neg();
+
+ w3.add(a);
+ w3.norm();
+ b.mul(w3);
+
+ a.add(mb);
+
+ w1.norm();
+ a.norm();
+
+ a.mul(w1);
+ }
+
+/* this*=y */
+/* Now uses Lazy reduction */
+ public void mul(FP2 y)
+ {
+ if ((long)(a.XES+b.XES)*(y.a.XES+y.b.XES)>(long)FP.FEXCESS)
+ {
+ if (a.XES>1) a.reduce();
+ if (b.XES>1) b.reduce();
+ }
+
+ DBIG pR=new DBIG(0);
+ BIG C=new BIG(a.x);
+ BIG D=new BIG(y.a.x);
+
+ pR.ucopy(new BIG(ROM.Modulus));
+
+ DBIG A=BIG.mul(a.x,y.a.x);
+ DBIG B=BIG.mul(b.x,y.b.x);
+
+ C.add(b.x); C.norm();
+ D.add(y.b.x); D.norm();
+
+ DBIG E=BIG.mul(C,D);
+ DBIG F=new DBIG(A); F.add(B);
+ B.rsub(pR);
+
+ A.add(B); A.norm();
+ E.sub(F); E.norm();
+
+ a.x.copy(FP.mod(A)); a.XES=3;
+ b.x.copy(FP.mod(E)); b.XES=2;
+ }
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP w1=new FP(b);
+ FP w2=new FP(a);
+ w1.sqr(); w2.sqr(); w1.add(w2);
+ if (w1.jacobi()!=1) { zero(); return false; }
+ w1=w1.sqrt();
+ w2.copy(a); w2.add(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1)
+ {
+ w2.copy(a); w2.sub(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1) { zero(); return false; }
+ }
+ w2=w2.sqrt();
+ a.copy(w2);
+ w2.add(w2);
+ w2.inverse();
+ b.mul(w2);
+ return true;
+ }
+
+/* output to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ norm();
+ FP w1=new FP(a);
+ FP w2=new FP(b);
+
+ w1.sqr();
+ w2.sqr();
+ w1.add(w2);
+ w1.inverse();
+ a.mul(w1);
+ w1.neg();
+ w1.norm();
+ b.mul(w1);
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+/* this*=sqrt(-1) */
+ public void times_i()
+ {
+ FP z=new FP(a);
+ a.copy(b); a.neg();
+ b.copy(z);
+ }
+
+/* w*=(1+sqrt(-1)) */
+/* where X*2-(1+sqrt(-1)) is irreducible for FP4, assumes p=3 mod 8 */
+ public void mul_ip()
+ {
+ FP2 t=new FP2(this);
+ FP z=new FP(a);
+ a.copy(b);
+ a.neg();
+ b.copy(z);
+ add(t);
+ }
+
+ public void div_ip2()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ }
+
+/* w/=(1+sqrt(-1)) */
+ public void div_ip()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ div2();
+ }
+/*
+ public FP2 pow(BIG e)
+ {
+ int bt;
+ FP2 r=new FP2(1);
+ e.norm();
+ norm();
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(this);
+ if (e.iszilch()) break;
+ sqr();
+ }
+
+ r.reduce();
+ return r;
+ }
+
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(27);
+ BIG pp1=new BIG(m);
+ BIG pm1=new BIG(m);
+ BIG a=new BIG(1);
+ BIG b=new BIG(1);
+ FP2 w=new FP2(a,b);
+ FP2 z=new FP2(w);
+
+ byte[] RAW=new byte[100];
+
+ RAND rng=new RAND();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ // for (int i=0;i<100;i++)
+ // {
+ a.randomnum(rng);
+ b.randomnum(rng);
+
+ w=new FP2(a,b);
+ System.out.println("w="+w.toString());
+
+ z=new FP2(w);
+ z.inverse();
+ System.out.println("z="+z.toString());
+
+ z.inverse();
+ if (!z.equals(w)) System.out.println("Error");
+ // }
+
+// System.out.println("m="+m.toString());
+// w.sqr();
+// w.mul(z);
+
+ System.out.println("w="+w.toString());
+
+
+ pp1.inc(1); pp1.norm();
+ pm1.dec(1); pm1.norm();
+ System.out.println("p+1="+pp1.toString());
+ System.out.println("p-1="+pm1.toString());
+ w=w.pow(pp1);
+ w=w.pow(pm1);
+ System.out.println("w="+w.toString());
+ }
+*/
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/FP4.java b/src/main/java/org/apache/milagro/amcl/BLS48/FP4.java
new file mode 100644
index 0000000..6e672a3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/FP4.java
@@ -0,0 +1,721 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^4 functions */
+
+/* FP4 elements are of the form a+ib, where i is sqrt(-1+sqrt(-1)) */
+
+package org.apache.milagro.amcl.BLS48;
+
+public final class FP4 {
+ private final FP2 a;
+ private final FP2 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP4 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP2 one=new FP2(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP2 real()
+ {
+ return a;
+ }
+
+ public FP2 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP2 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP4 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP4(int c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+
+ public FP4(FP4 x)
+ {
+ a=new FP2(x.a);
+ b=new FP2(x.b);
+ }
+
+ public FP4(FP2 c,FP2 d)
+ {
+ a=new FP2(c);
+ b=new FP2(d);
+ }
+
+ public FP4(FP2 c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+/* copy this=x */
+ public void copy(FP4 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP2 m=new FP2(a);
+ FP2 t=new FP2(0);
+ m.add(b);
+// m.norm();
+ m.neg();
+ // m.norm();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP4 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP4 x)
+ {
+ FP4 m=new FP4(x);
+ m.neg();
+ add(m);
+ }
+
+/* this*=s where s is FP2 */
+ public void pmul(FP2 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this=x-this */
+ public void rsub(FP4 x)
+ {
+ neg();
+ add(x);
+ }
+
+
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.mul_ip();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.mul_ip();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+/* this*=y */
+ public void mul(FP4 y)
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(0);
+ FP2 t4=new FP2(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ // t4.sub(t1);
+ // t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ // b.copy(t4);
+ // b.sub(t2);
+
+ t2.mul_ip();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.mul_ip();
+ t2.norm();
+ t1.sub(t2);
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP2 s=new FP2(b);
+ FP2 t=new FP2(b);
+ s.times_i();
+ t.add(s);
+ // t.norm();
+ b.copy(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ a.conj();
+ b.conj();
+ b.mul(f);
+ }
+
+/* this=this^e */
+ public FP4 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP4 w=new FP4(this);
+ BIG z=new BIG(e);
+ FP4 r=new FP4(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+/* XTR xtr_a function */
+ public void xtr_A(FP4 w,FP4 y,FP4 z)
+ {
+ FP4 r=new FP4(w);
+ FP4 t=new FP4(w);
+ //y.norm();
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP4 w=new FP4(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP4 xtr_pow(BIG n) {
+ FP4 a=new FP4(3);
+ FP4 b=new FP4(this);
+ FP4 c=new FP4(b);
+ c.xtr_D();
+ FP4 t=new FP4(0);
+ FP4 r=new FP4(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP4 xtr_pow2(FP4 ck,FP4 ckml,FP4 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP4 cu=new FP4(ck); // can probably be passed in w/o copying
+ FP4 cv=new FP4(this);
+ FP4 cumv=new FP4(ckml);
+ FP4 cum2v=new FP4(ckm2l);
+ FP4 r=new FP4(0);
+ FP4 t=new FP4(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+ public void div_i()
+ {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip();
+ a.copy(v);
+ b.copy(u);
+ }
+
+ public void div_2i() {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip2();
+ v.add(v); v.norm();
+ a.copy(v);
+ b.copy(u);
+ }
+
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP2 wa=new FP2(a);
+ FP2 ws=new FP2(b);
+ FP2 wt=new FP2(a);
+
+ if (ws.iszilch())
+ {
+ if (wt.sqrt())
+ {
+ a.copy(wt);
+ b.zero();
+ } else {
+ wt.div_ip();
+ wt.sqrt();
+ b.copy(wt);
+ a.zero();
+ }
+ return true;
+ }
+
+ ws.sqr();
+ wa.sqr();
+ ws.mul_ip();
+ ws.norm();
+ wa.sub(ws);
+
+ ws.copy(wa);
+ if (!ws.sqrt()) {
+ return false;
+ }
+
+ wa.copy(wt); wa.add(ws); wa.norm(); wa.div2();
+
+ if (!wa.sqrt()) {
+ wa.copy(wt); wa.sub(ws); wa.norm(); wa.div2();
+ if (!wa.sqrt()) {
+ return false;
+ }
+ }
+ wt.copy(b);
+ ws.copy(wa); ws.add(wa);
+ ws.inverse();
+
+ wt.mul(ws);
+ a.copy(wa);
+ b.copy(wt);
+
+ return true;
+ }
+
+/* this*=s where s is FP */
+ public void qmul(FP s)
+ {
+ a.pmul(s);
+ b.pmul(s);
+ }
+
+
+
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG e=new BIG(12);
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.inc(27); b.inc(45);
+
+ FP2 w0=new FP2(a,b);
+
+ a.zero(); b.zero();
+ a.inc(33); b.inc(54);
+
+ FP2 w1=new FP2(a,b);
+
+
+ FP4 w=new FP4(w0,w1);
+ FP4 t=new FP4(w);
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ System.out.println("w= "+w.toString());
+
+ w=w.pow(m);
+
+ System.out.println("w^p= "+w.toString());
+
+ t.frob(f);
+
+
+ System.out.println("w^p= "+t.toString());
+
+ w=w.pow(m);
+ w=w.pow(m);
+ w=w.pow(m);
+ System.out.println("w^p4= "+w.toString());
+
+
+ System.out.println("Test Inversion");
+
+ w=new FP4(w0,w1);
+
+ w.inverse();
+
+ System.out.println("1/w mod p^4 = "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/(1/w) mod p^4 = "+w.toString());
+
+ FP4 ww=new FP4(w);
+
+ w=w.xtr_pow(e);
+ System.out.println("w^e= "+w.toString());
+
+
+ a.zero(); b.zero();
+ a.inc(37); b.inc(17);
+ w0=new FP2(a,b);
+ a.zero(); b.zero();
+ a.inc(49); b.inc(31);
+ w1=new FP2(a,b);
+
+ FP4 c1=new FP4(w0,w1);
+ FP4 c2=new FP4(w0,w1);
+ FP4 c3=new FP4(w0,w1);
+
+ BIG e1=new BIG(3331);
+ BIG e2=new BIG(3372);
+
+ FP4 cr=w.xtr_pow2(c1,c2,c3,e1,e2);
+
+ System.out.println("c^e= "+cr.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/FP48.java b/src/main/java/org/apache/milagro/amcl/BLS48/FP48.java
new file mode 100644
index 0000000..25c5abd
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/FP48.java
@@ -0,0 +1,1057 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Fp^48 functions */
+/* FP48 elements are of the form a+i.b+i^2.c */
+
+package org.apache.milagro.amcl.BLS48;
+
+public final class FP48 {
+ private final FP16 a;
+ private final FP16 b;
+ private final FP16 c;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ c.reduce();
+ }
+
+/* normalise all components of this */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ c.norm();
+ }
+/* test x==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch() && c.iszilch());
+ }
+
+ public void cmove(FP48 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ c.cmove(g.c,d);
+ }
+
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(FP48 g[],int b)
+ {
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(g[0],teq(babs,0)); // conditional move
+ cmove(g[1],teq(babs,1));
+ cmove(g[2],teq(babs,2));
+ cmove(g[3],teq(babs,3));
+ cmove(g[4],teq(babs,4));
+ cmove(g[5],teq(babs,5));
+ cmove(g[6],teq(babs,6));
+ cmove(g[7],teq(babs,7));
+
+ FP48 invf=new FP48(this);
+ invf.conj();
+ cmove(invf,(int)(m&1));
+ }
+
+ /* test x==1 ? */
+ public boolean isunity() {
+ FP16 one=new FP16(1);
+ return (a.equals(one) && b.iszilch() && c.iszilch());
+ }
+/* return 1 if x==y, else 0 */
+ public boolean equals(FP48 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b) && c.equals(x.c));
+ }
+/* extract a from this */
+ public FP16 geta()
+ {
+ return a;
+ }
+/* extract b */
+ public FP16 getb()
+ {
+ return b;
+ }
+/* extract c */
+ public FP16 getc()
+ {
+ return c;
+ }
+/* copy this=x */
+ public void copy(FP48 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ c.copy(x.c);
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ c.zero();
+ }
+/* this=conj(this) */
+ public void conj()
+ {
+ a.conj();
+ b.nconj();
+ c.conj();
+ }
+/* Constructors */
+ public FP48(FP16 d)
+ {
+ a=new FP16(d);
+ b=new FP16(0);
+ c=new FP16(0);
+ }
+
+ public FP48(int d)
+ {
+ a=new FP16(d);
+ b=new FP16(0);
+ c=new FP16(0);
+ }
+
+ public FP48(FP16 d,FP16 e,FP16 f)
+ {
+ a=new FP16(d);
+ b=new FP16(e);
+ c=new FP16(f);
+ }
+
+ public FP48(FP48 x)
+ {
+ a=new FP16(x.a);
+ b=new FP16(x.b);
+ c=new FP16(x.c);
+ }
+
+/* Granger-Scott Unitary Squaring */
+ public void usqr()
+ {
+ FP16 A=new FP16(a);
+ FP16 B=new FP16(c);
+ FP16 C=new FP16(b);
+ FP16 D=new FP16(0);
+
+ a.sqr();
+ D.copy(a); D.add(a);
+ a.add(D);
+
+ a.norm();
+ A.nconj();
+
+ A.add(A);
+ a.add(A);
+ B.sqr();
+ B.times_i();
+
+ D.copy(B); D.add(B);
+ B.add(D);
+ B.norm();
+
+ C.sqr();
+ D.copy(C); D.add(C);
+ C.add(D);
+ C.norm();
+
+ b.conj();
+ b.add(b);
+ c.nconj();
+
+ c.add(c);
+ b.add(B);
+ c.add(C);
+ reduce();
+ }
+
+/* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+ public void sqr()
+ {
+ FP16 A=new FP16(a);
+ FP16 B=new FP16(b);
+ FP16 C=new FP16(c);
+ FP16 D=new FP16(a);
+
+ A.sqr();
+ B.mul(c);
+ B.add(B);
+ B.norm();
+ C.sqr();
+ D.mul(b);
+ D.add(D);
+
+ c.add(a);
+ c.add(b);
+ c.norm();
+ c.sqr();
+
+ a.copy(A);
+
+ A.add(B);
+ A.norm();
+ A.add(C);
+ A.add(D);
+ A.norm();
+
+ A.neg();
+ B.times_i();
+ C.times_i();
+
+ a.add(B);
+
+ b.copy(C); b.add(D);
+ c.add(A);
+
+ norm();
+ }
+
+/* FP12 full multiplication this=this*y */
+ public void mul(FP48 y)
+ {
+ FP16 z0=new FP16(a);
+ FP16 z1=new FP16(0);
+ FP16 z2=new FP16(b);
+ FP16 z3=new FP16(0);
+ FP16 t0=new FP16(a);
+ FP16 t1=new FP16(y.a);
+
+ z0.mul(y.a);
+ z2.mul(y.b);
+
+ t0.add(b);
+ t1.add(y.b);
+
+ t0.norm();
+ t1.norm();
+
+ z1.copy(t0); z1.mul(t1);
+ t0.copy(b); t0.add(c);
+
+ t1.copy(y.b); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ z3.copy(t0); z3.mul(t1);
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ z1.add(t0);
+ //z1.norm();
+ b.copy(z1); b.add(t1);
+
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c); t0.mul(y.c);
+ t1.copy(t0); t1.neg();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ norm();
+
+ }
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+ public void smul(FP48 y,int type)
+ {
+ if (type==ECP.D_TYPE)
+ {
+ FP16 z0=new FP16(a);
+ FP16 z2=new FP16(b);
+ FP16 z3=new FP16(b);
+ FP16 t0=new FP16(0);
+ FP16 t1=new FP16(y.a);
+ z0.mul(y.a);
+ z2.pmul(y.b.real());
+ b.add(a);
+ t1.real().add(y.b.real());
+
+ t1.norm();
+ b.norm();
+ b.mul(t1);
+ z3.add(c);
+ z3.norm();
+ z3.pmul(y.b.real());
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ b.add(t0);
+
+ b.add(t1);
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t0.norm();
+ z3.norm();
+ t0.mul(y.a);
+ c.copy(z2); c.add(t0);
+
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ if (type==ECP.M_TYPE)
+ {
+ FP16 z0=new FP16(a);
+ FP16 z1=new FP16(0);
+ FP16 z2=new FP16(0);
+ FP16 z3=new FP16(0);
+ FP16 t0=new FP16(a);
+ FP16 t1=new FP16(0);
+
+ z0.mul(y.a);
+ t0.add(b);
+ t0.norm();
+
+ z1.copy(t0); z1.mul(y.a);
+ t0.copy(b); t0.add(c);
+ t0.norm();
+
+ z3.copy(t0); //z3.mul(y.c);
+ z3.pmul(y.c.getb());
+ z3.times_i();
+
+ t0.copy(z0); t0.neg();
+
+ z1.add(t0);
+ b.copy(z1);
+ z2.copy(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c);
+
+ t0.pmul(y.c.getb());
+ t0.times_i();
+
+ t1.copy(t0); t1.neg();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ norm();
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ FP16 f0=new FP16(a);
+ FP16 f1=new FP16(b);
+ FP16 f2=new FP16(a);
+ FP16 f3=new FP16(0);
+
+ norm();
+ f0.sqr();
+ f1.mul(c);
+ f1.times_i();
+ f0.sub(f1);
+ f0.norm();
+
+ f1.copy(c); f1.sqr();
+ f1.times_i();
+ f2.mul(b);
+ f1.sub(f2);
+ f1.norm();
+
+ f2.copy(b); f2.sqr();
+ f3.copy(a); f3.mul(c);
+ f2.sub(f3);
+ f2.norm();
+
+ f3.copy(b); f3.mul(f2);
+ f3.times_i();
+ a.mul(f0);
+ f3.add(a);
+ c.mul(f1);
+ c.times_i();
+
+ f3.add(c);
+ f3.norm();
+ f3.inverse();
+ a.copy(f0); a.mul(f3);
+ b.copy(f1); b.mul(f3);
+ c.copy(f2); c.mul(f3);
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f,int n)
+ {
+ FP2 f2=new FP2(f);
+ FP2 f3=new FP2(f);
+
+ f2.sqr();
+ f3.mul(f2);
+
+ f3.mul_ip(); f3.norm();
+ f3.mul_ip(); f3.norm();
+
+ for (int i=0;i<n;i++)
+ {
+ a.frob(f3);
+ b.frob(f3);
+ c.frob(f3);
+
+ b.qmul(f); b.times_i4(); b.times_i2();
+ c.qmul(f2); c.times_i4(); c.times_i4(); c.times_i4();
+ }
+ }
+
+/* trace function */
+ public FP16 trace()
+ {
+ FP16 t=new FP16(0);
+ t.copy(a);
+ t.imul(3);
+ t.reduce();
+ return t;
+ }
+
+/* convert from byte array to FP12 */
+ public static FP48 fromBytes(byte[] w)
+ {
+ BIG a,b;
+ FP2 c,d;
+ FP4 ea,eb;
+ FP8 fa,fb;
+ FP16 e,f,g;
+ byte[] t=new byte[BIG.MODBYTES];
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+2*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+3*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ ea=new FP4(c,d);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+4*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+5*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+6*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+7*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ eb=new FP4(c,d);
+
+ fa=new FP8(ea,eb);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+8*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+9*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+10*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+11*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ ea=new FP4(c,d);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+12*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+13*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+14*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+15*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ eb=new FP4(c,d);
+
+ fb=new FP8(ea,eb);
+
+ e=new FP16(fa,fb);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+16*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+17*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+18*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+19*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ ea=new FP4(c,d);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+20*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+21*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+22*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+23*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ eb=new FP4(c,d);
+
+ fa=new FP8(ea,eb);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+24*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+25*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+26*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+27*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ ea=new FP4(c,d);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+28*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+29*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+30*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+31*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ eb=new FP4(c,d);
+
+ fb=new FP8(ea,eb);
+
+ f=new FP16(fa,fb);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+32*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+33*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+34*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+35*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ ea=new FP4(c,d);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+36*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+37*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+38*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+39*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ eb=new FP4(c,d);
+
+ fa=new FP8(ea,eb);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+40*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+41*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+42*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+43*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ ea=new FP4(c,d);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+44*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+45*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+46*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+47*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ eb=new FP4(c,d);
+
+ fb=new FP8(ea,eb);
+
+ g=new FP16(fa,fb);
+
+ return new FP48(e,f,g);
+ }
+
+/* convert this to byte array */
+ public void toBytes(byte[] w)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+
+ a.geta().geta().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i]=t[i];
+ a.geta().geta().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+BIG.MODBYTES]=t[i];
+ a.geta().geta().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+2*BIG.MODBYTES]=t[i];
+ a.geta().geta().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+3*BIG.MODBYTES]=t[i];
+
+ a.geta().getb().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+4*BIG.MODBYTES]=t[i];
+ a.geta().getb().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+5*BIG.MODBYTES]=t[i];
+ a.geta().getb().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+6*BIG.MODBYTES]=t[i];
+ a.geta().getb().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+7*BIG.MODBYTES]=t[i];
+ a.getb().geta().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+8*BIG.MODBYTES]=t[i];
+ a.getb().geta().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+9*BIG.MODBYTES]=t[i];
+ a.getb().geta().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+10*BIG.MODBYTES]=t[i];
+ a.getb().geta().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+11*BIG.MODBYTES]=t[i];
+ a.getb().getb().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+12*BIG.MODBYTES]=t[i];
+ a.getb().getb().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+13*BIG.MODBYTES]=t[i];
+ a.getb().getb().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+14*BIG.MODBYTES]=t[i];
+ a.getb().getb().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+15*BIG.MODBYTES]=t[i];
+
+ b.geta().geta().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+16*BIG.MODBYTES]=t[i];
+ b.geta().geta().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+17*BIG.MODBYTES]=t[i];
+ b.geta().geta().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+18*BIG.MODBYTES]=t[i];
+ b.geta().geta().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+19*BIG.MODBYTES]=t[i];
+
+ b.geta().getb().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+20*BIG.MODBYTES]=t[i];
+ b.geta().getb().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+21*BIG.MODBYTES]=t[i];
+ b.geta().getb().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+22*BIG.MODBYTES]=t[i];
+ b.geta().getb().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+23*BIG.MODBYTES]=t[i];
+ b.getb().geta().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+24*BIG.MODBYTES]=t[i];
+ b.getb().geta().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+25*BIG.MODBYTES]=t[i];
+ b.getb().geta().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+26*BIG.MODBYTES]=t[i];
+ b.getb().geta().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+27*BIG.MODBYTES]=t[i];
+ b.getb().getb().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+28*BIG.MODBYTES]=t[i];
+ b.getb().getb().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+29*BIG.MODBYTES]=t[i];
+ b.getb().getb().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+30*BIG.MODBYTES]=t[i];
+ b.getb().getb().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+31*BIG.MODBYTES]=t[i];
+
+
+ c.geta().geta().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+32*BIG.MODBYTES]=t[i];
+ c.geta().geta().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+33*BIG.MODBYTES]=t[i];
+ c.geta().geta().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+34*BIG.MODBYTES]=t[i];
+ c.geta().geta().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+35*BIG.MODBYTES]=t[i];
+ c.geta().getb().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+36*BIG.MODBYTES]=t[i];
+ c.geta().getb().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+37*BIG.MODBYTES]=t[i];
+ c.geta().getb().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+38*BIG.MODBYTES]=t[i];
+ c.geta().getb().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+39*BIG.MODBYTES]=t[i];
+ c.getb().geta().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+40*BIG.MODBYTES]=t[i];
+ c.getb().geta().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+41*BIG.MODBYTES]=t[i];
+ c.getb().geta().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+42*BIG.MODBYTES]=t[i];
+ c.getb().geta().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+43*BIG.MODBYTES]=t[i];
+ c.getb().getb().geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+44*BIG.MODBYTES]=t[i];
+ c.getb().getb().geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+45*BIG.MODBYTES]=t[i];
+ c.getb().getb().getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+46*BIG.MODBYTES]=t[i];
+ c.getb().getb().getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+47*BIG.MODBYTES]=t[i];
+ }
+
+/* convert to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+","+c.toString()+"]");
+ }
+
+/* this=this^e */
+/* Note this is simple square and multiply, so not side-channel safe */
+ public FP48 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ BIG e3=new BIG(e);
+ e3.pmul(3);
+ e3.norm();
+
+ FP48 w=new FP48(this);
+
+ int nb=e3.nbits();
+ for (int i=nb-2;i>=1;i--)
+ {
+ w.usqr();
+ int bt=e3.bit(i)-e.bit(i);
+ if (bt==1)
+ w.mul(this);
+ if (bt==-1)
+ {
+ conj(); w.mul(this); conj();
+ }
+ }
+ w.reduce();
+ return w;
+
+ }
+
+/* constant time powering by small integer of max length bts */
+ public void pinpow(int e,int bts)
+ {
+ int i,b;
+ FP48 [] R=new FP48[2];
+ R[0]=new FP48(1);
+ R[1]=new FP48(this);
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ R[1-b].mul(R[b]);
+ R[b].usqr();
+ }
+ this.copy(R[0]);
+ }
+
+ public FP16 compow(BIG e,BIG r)
+ {
+ FP48 g1=new FP48(0);
+ FP48 g2=new FP48(0);
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.Modulus);
+
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(e);
+ a.mod(m);
+
+ BIG b=new BIG(e);
+ b.div(m);
+
+ g1.copy(this);
+ g2.copy(this);
+
+ FP16 c=g1.trace();
+
+ if (b.iszilch())
+ {
+ c=c.xtr_pow(e);
+ return c;
+ }
+
+ g2.frob(f,1);
+ FP16 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP16 cpm1=g2.trace();
+ g2.mul(g1);
+ FP16 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+
+ return c;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3.... */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static FP48 pow16(FP48[] q,BIG[] u)
+ {
+ int i,j,k,nb,pb1,pb2,pb3,pb4;
+ FP48 [] g1=new FP48[8];
+ FP48 [] g2=new FP48[8];
+ FP48 [] g3=new FP48[8];
+ FP48 [] g4=new FP48[8];
+ FP48 r=new FP48(1);
+ FP48 p=new FP48(0);
+ BIG [] t=new BIG[16];
+ BIG mt=new BIG(0);
+ byte[] w1=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s1=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] w2=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s2=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] w3=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s3=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] w4=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s4=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<16;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ }
+
+ g1[0]=new FP48(q[0]); // q[0]
+ g1[1]=new FP48(g1[0]); g1[1].mul(q[1]); // q[0].q[1]
+ g1[2]=new FP48(g1[0]); g1[2].mul(q[2]); // q[0].q[2]
+ g1[3]=new FP48(g1[1]); g1[3].mul(q[2]); // q[0].q[1].q[2]
+ g1[4]=new FP48(q[0]); g1[4].mul(q[3]); // q[0].q[3]
+ g1[5]=new FP48(g1[1]); g1[5].mul(q[3]); // q[0].q[1].q[3]
+ g1[6]=new FP48(g1[2]); g1[6].mul(q[3]); // q[0].q[2].q[3]
+ g1[7]=new FP48(g1[3]); g1[7].mul(q[3]); // q[0].q[1].q[2].q[3]
+
+// Use Frobenius
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ for (i=0;i<8;i++)
+ {
+ g2[i]=new FP48(g1[i]);
+ g2[i].frob(f,4);
+ g3[i]=new FP48(g2[i]);
+ g3[i].frob(f,4);
+ g4[i]=new FP48(g3[i]);
+ g4[i].frob(f,4);
+ }
+
+ // Make it odd
+ pb1=1-t[0].parity();
+ t[0].inc(pb1);
+ t[0].norm();
+
+ pb2=1-t[4].parity();
+ t[4].inc(pb2);
+ t[4].norm();
+
+ pb3=1-t[8].parity();
+ t[8].inc(pb3);
+ t[8].norm();
+
+ pb4=1-t[12].parity();
+ t[12].inc(pb4);
+ t[12].norm();
+
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<16;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s1[nb-1]=1;
+ s2[nb-1]=1;
+ s3[nb-1]=1;
+ s4[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s1[i]=(byte)(2*t[0].parity()-1);
+ t[4].fshr(1);
+ s2[i]=(byte)(2*t[4].parity()-1);
+ t[8].fshr(1);
+ s3[i]=(byte)(2*t[8].parity()-1);
+ t[12].fshr(1);
+ s4[i]=(byte)(2*t[12].parity()-1);
+
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w1[i]=0;
+ k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s1[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w1[i]+=bt*(byte)k;
+ k*=2;
+ }
+
+ w2[i]=0;
+ k=1;
+ for (j=5; j<8; j++) {
+ byte bt=(byte)(s2[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w2[i]+=bt*(byte)k;
+ k*=2;
+ }
+
+ w3[i]=0;
+ k=1;
+ for (j=9; j<12; j++) {
+ byte bt=(byte)(s3[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w3[i]+=bt*(byte)k;
+ k*=2;
+ }
+
+ w4[i]=0;
+ k=1;
+ for (j=13; j<16; j++) {
+ byte bt=(byte)(s4[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w4[i]+=bt*(byte)k;
+ k*=2;
+ }
+
+ }
+
+
+ // Main loop
+ p.select(g1,(int)(2*w1[nb-1]+1));
+ r.select(g2,(int)(2*w2[nb-1]+1));
+ p.mul(r);
+ r.select(g3,(int)(2*w3[nb-1]+1));
+ p.mul(r);
+ r.select(g4,(int)(2*w4[nb-1]+1));
+ p.mul(r);
+
+ for (i=nb-2;i>=0;i--) {
+ p.usqr();
+ r.select(g1,(int)(2*w1[i]+s1[i]));
+ p.mul(r);
+ r.select(g2,(int)(2*w2[i]+s2[i]));
+ p.mul(r);
+ r.select(g3,(int)(2*w3[i]+s3[i]));
+ p.mul(r);
+ r.select(g4,(int)(2*w4[i]+s4[i]));
+ p.mul(r);
+
+ }
+
+ // apply correction
+ r.copy(q[0]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb1);
+
+ r.copy(q[4]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb2);
+
+ r.copy(q[8]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb3);
+
+ r.copy(q[12]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb4);
+
+ p.reduce();
+ return p;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/FP8.java b/src/main/java/org/apache/milagro/amcl/BLS48/FP8.java
new file mode 100644
index 0000000..145ecfa
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/FP8.java
@@ -0,0 +1,656 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^8 functions */
+
+/* FP8 elements are of the form a+ib, where i is sqrt(sqrt(-1+sqrt(-1))) */
+
+package org.apache.milagro.amcl.BLS48;
+
+public final class FP8 {
+ private final FP4 a;
+ private final FP4 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP8 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP4 one=new FP4(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP4 real()
+ {
+ return a;
+ }
+
+ public FP4 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP4 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP8 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP8(int c)
+ {
+ a=new FP4(c);
+ b=new FP4(0);
+ }
+
+ public FP8(FP8 x)
+ {
+ a=new FP4(x.a);
+ b=new FP4(x.b);
+ }
+
+ public FP8(FP4 c,FP4 d)
+ {
+ a=new FP4(c);
+ b=new FP4(d);
+ }
+
+ public FP8(FP4 c)
+ {
+ a=new FP4(c);
+ b=new FP4(0);
+ }
+/* copy this=x */
+ public void copy(FP8 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP4 m=new FP4(a);
+ FP4 t=new FP4(0);
+ m.add(b);
+// m.norm();
+ m.neg();
+ // m.norm();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP8 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP8 x)
+ {
+ FP8 m=new FP8(x);
+ m.neg();
+ add(m);
+ }
+
+/* this=x-this */
+ public void rsub(FP8 x)
+ {
+ neg();
+ add(x);
+ }
+
+
+/* this*=s where s is FP4 */
+ public void pmul(FP4 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+/* this*=s where s is FP2 */
+ public void qmul(FP2 s)
+ {
+ a.pmul(s);
+ b.pmul(s);
+ }
+/* this*=s where s is FP */
+ public void tmul(FP s)
+ {
+ a.qmul(s);
+ b.qmul(s);
+ }
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP4 t1=new FP4(a);
+ FP4 t2=new FP4(b);
+ FP4 t3=new FP4(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.times_i();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.times_i();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+
+/* this*=y */
+ public void mul(FP8 y)
+ {
+// norm();
+
+ FP4 t1=new FP4(a);
+ FP4 t2=new FP4(b);
+ FP4 t3=new FP4(0);
+ FP4 t4=new FP4(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ // t4.sub(t1);
+ // t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ // b.copy(t4);
+ // b.sub(t2);
+
+ t2.times_i();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP4 t1=new FP4(a);
+ FP4 t2=new FP4(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.times_i();
+ t2.norm();
+ t1.sub(t2); t1.norm();
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP4 s=new FP4(b);
+ FP4 t=new FP4(a);
+ s.times_i();
+
+ b.copy(t);
+ a.copy(s);
+ norm();
+ }
+
+ public void times_i2()
+ {
+ a.times_i();
+ b.times_i();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ FP2 ff=new FP2(f); ff.sqr(); ff.mul_ip(); ff.norm();
+
+ a.frob(ff);
+ b.frob(ff);
+ b.pmul(f);
+ b.times_i();
+
+ }
+
+/* this=this^e */
+ public FP8 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP8 w=new FP8(this);
+ BIG z=new BIG(e);
+ FP8 r=new FP8(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+
+/* XTR xtr_a function */
+ public void xtr_A(FP8 w,FP8 y,FP8 z)
+ {
+ FP8 r=new FP8(w);
+ FP8 t=new FP8(w);
+
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP8 w=new FP8(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP8 xtr_pow(BIG n) {
+ FP8 a=new FP8(3);
+ FP8 b=new FP8(this);
+ FP8 c=new FP8(b);
+ c.xtr_D();
+ FP8 t=new FP8(0);
+ FP8 r=new FP8(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP8 xtr_pow2(FP8 ck,FP8 ckml,FP8 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP8 cu=new FP8(ck); // can probably be passed in w/o copying
+ FP8 cv=new FP8(this);
+ FP8 cumv=new FP8(ckml);
+ FP8 cum2v=new FP8(ckm2l);
+ FP8 r=new FP8(0);
+ FP8 t=new FP8(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+ public void div_i()
+ {
+ FP4 u=new FP4(a);
+ FP4 v=new FP4(b);
+ u.div_i();
+ a.copy(v);
+ b.copy(u);
+ }
+
+ public void div_i2() {
+ a.div_i();
+ b.div_i();
+ }
+
+ public void div_2i() {
+ FP4 u=new FP4(a);
+ FP4 v=new FP4(b);
+ u.div_2i();
+ v.add(v); v.norm();
+ a.copy(v);
+ b.copy(u);
+ }
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP4 wa=new FP4(a);
+ FP4 ws=new FP4(b);
+ FP4 wt=new FP4(a);
+
+ if (ws.iszilch())
+ {
+ if (wt.sqrt())
+ {
+ a.copy(wt);
+ b.zero();
+ } else {
+ wt.div_i();
+ wt.sqrt();
+ b.copy(wt);
+ a.zero();
+ }
+ return true;
+ }
+
+ ws.sqr();
+ wa.sqr();
+ ws.times_i();
+ ws.norm();
+ wa.sub(ws);
+
+ ws.copy(wa);
+ if (!ws.sqrt()) {
+ return false;
+ }
+
+ wa.copy(wt); wa.add(ws); wa.norm(); wa.div2();
+
+ if (!wa.sqrt()) {
+ wa.copy(wt); wa.sub(ws); wa.norm(); wa.div2();
+ if (!wa.sqrt()) {
+ return false;
+ }
+ }
+ wt.copy(b);
+ ws.copy(wa); ws.add(wa);
+ ws.inverse();
+
+ wt.mul(ws);
+ a.copy(wa);
+ b.copy(wt);
+
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/MPIN256.java b/src/main/java/org/apache/milagro/amcl/BLS48/MPIN256.java
new file mode 100644
index 0000000..9a42266
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/MPIN256.java
@@ -0,0 +1,815 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* MPIN API Functions */
+
+package org.apache.milagro.amcl.BLS48;
+
+import java.util.Date;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public class MPIN256
+{
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int PAS=16;
+ public static final int INVALID_POINT=-14;
+ public static final int BAD_PARAMS=-11;
+ public static final int WRONG_ORDER=-18;
+ public static final int BAD_PIN=-19;
+
+/* Configure your PIN here */
+
+ public static final int MAXPIN=10000; /* PIN less than this */
+ public static final int PBLEN=14; /* Number of bits in PIN */
+ public static final int TS=10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+ public static final int TRAP=200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN - approx 2*sqrt(MAXPIN) */
+
+// public static final int HASH_TYPE=SHA256;
+
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,int n,byte[] B,int len)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (n>0) H.process_num(n);
+
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+ byte[] W=new byte[len];
+
+ if (sha>=len)
+ for (int i=0;i<len;i++) W[i]=R[i];
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+len-sha]=R[i];
+ for (int i=0;i<len-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<len;i++) W[i]=0;
+ }
+ return W;
+ }
+
+ /* return time in slots since epoch */
+ public static int today() {
+ Date date=new Date();
+ return (int) (date.getTime()/(1000*60*1440));
+ }
+
+ public static byte[] HASH_ID(int sha,byte[] ID,int len)
+ {
+ return hashit(sha,0,ID,len);
+ }
+
+/* Hash the M-Pin transcript - new */
+
+ public static byte[] HASH_ALL(int sha,byte[] HID,byte[] xID,byte[] xCID,byte[] SEC,byte[] Y,byte[] R,byte[] W,int len)
+ {
+ int i,ilen,tlen=0;
+
+ ilen=HID.length+SEC.length+Y.length+R.length+W.length;
+ if (xCID!=null) ilen+=xCID.length;
+ else ilen+=xID.length;
+
+ byte[] T = new byte[ilen];
+
+ for (i=0;i<HID.length;i++) T[i]=HID[i];
+ tlen+=HID.length;
+ if (xCID!=null)
+ {
+ for (i=0;i<xCID.length;i++) T[i+tlen]=xCID[i];
+ tlen+=xCID.length;
+ }
+ else
+ {
+ for (i=0;i<xID.length;i++) T[i+tlen]=xID[i];
+ tlen+=xID.length;
+ }
+ for (i=0;i<SEC.length;i++) T[i+tlen]=SEC[i];
+ tlen+=SEC.length;
+ for (i=0;i<Y.length;i++) T[i+tlen]=Y[i];
+ tlen+=Y.length;
+ for (i=0;i<R.length;i++) T[i+tlen]=R[i];
+ tlen+=R.length;
+ for (i=0;i<W.length;i++) T[i+tlen]=W[i];
+ tlen+=W.length;
+
+ return hashit(sha,0,T,len);
+ }
+
+/* return time since epoch */
+ public static int GET_TIME() {
+ Date date=new Date();
+ return (int) (date.getTime()/1000);
+ }
+
+ public static byte[] mpin_hash(int sha,FP16 c,ECP U)
+ {
+ byte[] w=new byte[EFS];
+ byte[] t=new byte[18*EFS];
+ byte[] h=null;
+ c.geta().geta().geta().getA().toBytes(w); for (int i=0;i<EFS;i++) t[i]=w[i];
+ c.geta().geta().geta().getB().toBytes(w); for (int i=EFS;i<2*EFS;i++) t[i]=w[i-EFS];
+ c.geta().geta().getb().getA().toBytes(w); for (int i=2*EFS;i<3*EFS;i++) t[i]=w[i-2*EFS];
+ c.geta().geta().getb().getB().toBytes(w); for (int i=3*EFS;i<4*EFS;i++) t[i]=w[i-3*EFS];
+ c.geta().getb().geta().getA().toBytes(w); for (int i=4*EFS;i<5*EFS;i++) t[i]=w[i-4*EFS];
+ c.geta().getb().geta().getB().toBytes(w); for (int i=5*EFS;i<6*EFS;i++) t[i]=w[i-5*EFS];
+ c.geta().getb().getb().getA().toBytes(w); for (int i=6*EFS;i<7*EFS;i++) t[i]=w[i-6*EFS];
+ c.geta().getb().getb().getB().toBytes(w); for (int i=7*EFS;i<8*EFS;i++) t[i]=w[i-7*EFS];
+
+ c.getb().geta().geta().getA().toBytes(w); for (int i=8*EFS;i<9*EFS;i++) t[i]=w[i-8*EFS];
+ c.getb().geta().geta().getB().toBytes(w); for (int i=9*EFS;i<10*EFS;i++) t[i]=w[i-9*EFS];
+ c.getb().geta().getb().getA().toBytes(w); for (int i=10*EFS;i<11*EFS;i++) t[i]=w[i-10*EFS];
+ c.getb().geta().getb().getB().toBytes(w); for (int i=11*EFS;i<12*EFS;i++) t[i]=w[i-11*EFS];
+ c.getb().getb().geta().getA().toBytes(w); for (int i=12*EFS;i<13*EFS;i++) t[i]=w[i-12*EFS];
+ c.getb().getb().geta().getB().toBytes(w); for (int i=13*EFS;i<14*EFS;i++) t[i]=w[i-13*EFS];
+ c.getb().getb().getb().getA().toBytes(w); for (int i=14*EFS;i<15*EFS;i++) t[i]=w[i-14*EFS];
+ c.getb().getb().getb().getB().toBytes(w); for (int i=15*EFS;i<16*EFS;i++) t[i]=w[i-15*EFS];
+
+
+
+ U.getX().toBytes(w); for (int i=16*EFS;i<17*EFS;i++) t[i]=w[i-16*EFS];
+ U.getY().toBytes(w); for (int i=17*EFS;i<18*EFS;i++) t[i]=w[i-17*EFS];
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (h==null) return null;
+ byte[] R=new byte[ECP.AESKEY];
+ for (int i=0;i<ECP.AESKEY;i++) R[i]=h[i];
+ return R;
+ }
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+ public static ECP map(BIG u,int cb)
+ {
+ ECP P;
+ BIG x=new BIG(u);
+ BIG p=new BIG(ROM.Modulus);
+ x.mod(p);
+ while (true)
+ {
+ P=new ECP(x,cb);
+ if (!P.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+ return P;
+ }
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+ public static int unmap(BIG u,ECP P)
+ {
+ int s=P.getS();
+ ECP R;
+ int r=0;
+ BIG x=P.getX();
+ u.copy(x);
+ while (true)
+ {
+ u.dec(1); u.norm();
+ r++;
+ R=new ECP(u,s);
+ if (!R.is_infinity()) break;
+ }
+ return r;
+ }
+
+
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+ public static int ENCODING(RAND rng,byte[] E)
+ {
+ int rn,m,su,sv;
+ byte[] T=new byte[EFS];
+
+ for (int i=0;i<EFS;i++) T[i]=E[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=E[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ ECP P=new ECP(u,v);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG p=new BIG(ROM.Modulus);
+ u=BIG.randomnum(p,rng);
+
+ su=rng.getByte(); /*if (su<0) su=-su;*/ su%=2;
+
+ ECP W=map(u,su);
+ P.sub(W); //P.affine();
+ sv=P.getS();
+ rn=unmap(v,P);
+ m=rng.getByte(); /*if (m<0) m=-m;*/ m%=rn;
+ v.inc(m+1);
+ E[0]=(byte)(su+2*sv);
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+ public static int DECODING(byte[] D)
+ {
+ int su,sv;
+ byte[] T=new byte[EFS];
+
+ if ((D[0]&0x04)!=0) return INVALID_POINT;
+
+ for (int i=0;i<EFS;i++) T[i]=D[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=D[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ su=D[0]&1;
+ sv=(D[0]>>1)&1;
+ ECP W=map(u,su);
+ ECP P=map(v,sv);
+ P.add(W); //P.affine();
+ u=P.getX();
+ v=P.getY();
+ D[0]=0x04;
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+/* R=R1+R2 in group G1 */
+ public static int RECOMBINE_G1(byte[] R1,byte[] R2,byte[] R)
+ {
+ ECP P=ECP.fromBytes(R1);
+ ECP Q=ECP.fromBytes(R2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(R,false);
+ return 0;
+ }
+
+/* W=W1+W2 in group G2 */
+ public static int RECOMBINE_G2(byte[] W1,byte[] W2,byte[] W)
+ {
+ ECP8 P=ECP8.fromBytes(W1);
+ ECP8 Q=ECP8.fromBytes(W2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(W);
+ return 0;
+ }
+
+/* create random secret S */
+ public static int RANDOM_GENERATE(RAND rng,byte[] S)
+ {
+ BIG s;
+ BIG r=new BIG(ROM.CURVE_Order);
+ s=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+ return 0;
+ }
+
+/* Extract PIN from TOKEN for identity CID */
+ public static int EXTRACT_PIN(int sha,byte[] CID,int pin,byte[] TOKEN)
+ {
+ ECP P=ECP.fromBytes(TOKEN);
+ if (P.is_infinity()) return INVALID_POINT;
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R=ECP.mapit(h);
+
+
+ pin%=MAXPIN;
+
+ R=R.pinmul(pin,PBLEN);
+ P.sub(R); //P.affine();
+
+ P.toBytes(TOKEN,false);
+
+ return 0;
+ }
+
+/* Implement step 2 on client side of MPin protocol */
+ public static int CLIENT_2(byte[] X,byte[] Y,byte[] SEC)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ ECP P=ECP.fromBytes(SEC);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG px=BIG.fromBytes(X);
+ BIG py=BIG.fromBytes(Y);
+ px.add(py);
+ px.mod(r);
+ // px.rsub(r);
+
+ P=PAIR256.G1mul(P,px);
+ P.neg();
+ P.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Implement step 1 on client side of MPin protocol */
+ public static int CLIENT_1(int sha,int date,byte[] CLIENT_ID,RAND rng,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG x;
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P,T,W;
+ BIG px;
+// byte[] t=new byte[EFS];
+
+ byte[] h=hashit(sha,0,CLIENT_ID,EFS);
+ P=ECP.mapit(h);
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ pin%=MAXPIN;
+ W=P.pinmul(pin,PBLEN);
+ T.add(W);
+ if (date!=0)
+ {
+ W=ECP.fromBytes(PERMIT);
+ if (W.is_infinity()) return INVALID_POINT;
+ T.add(W);
+ h=hashit(sha,date,h,EFS);
+ W=ECP.mapit(h);
+ if (xID!=null)
+ {
+ P=PAIR256.G1mul(P,x);
+ P.toBytes(xID,false);
+ W=PAIR256.G1mul(W,x);
+ P.add(W); //P.affine();
+ }
+ else
+ {
+ P.add(W); //P.affine();
+ P=PAIR256.G1mul(P,x);
+ }
+ if (xCID!=null) P.toBytes(xCID,false);
+ }
+ else
+ {
+ if (xID!=null)
+ {
+ P=PAIR256.G1mul(P,x);
+ P.toBytes(xID,false);
+ }
+ }
+
+ //T.affine();
+ T.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+ public static int GET_SERVER_SECRET(byte[] S,byte[] SST)
+ {
+ ECP8 Q=ECP8.generator();
+ BIG s=BIG.fromBytes(S);
+ Q=PAIR256.G2mul(Q,s);
+ Q.toBytes(SST);
+ return 0;
+ }
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+ public static int GET_G1_MULTIPLE(RAND rng, int type,byte[] X,byte[] G,byte[] W)
+ {
+ BIG x;
+ BIG r=new BIG(ROM.CURVE_Order);
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P;
+ if (type==0)
+ {
+ P=ECP.fromBytes(G);
+ if (P.is_infinity()) return INVALID_POINT;
+ }
+ else
+ P=ECP.mapit(G);
+
+ PAIR256.G1mul(P,x).toBytes(W,false);
+ return 0;
+ }
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+ public static int GET_CLIENT_SECRET(byte[] S,byte[] CID,byte[] CST)
+ {
+ return GET_G1_MULTIPLE(null,1,S,CID,CST);
+ }
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+ public static int GET_CLIENT_PERMIT(int sha,int date,byte[] S,byte[] CID,byte[] CTT)
+ {
+ byte[] h=hashit(sha,date,CID,EFS);
+ ECP P=ECP.mapit(h);
+
+ BIG s=BIG.fromBytes(S);
+ ECP OP=PAIR256.G1mul(P,s);
+
+ OP.toBytes(CTT,false);
+ return 0;
+ }
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+ public static void SERVER_1(int sha,int date,byte[] CID,byte[] HID,byte[] HTID)
+ {
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R,P=ECP.mapit(h);
+
+ P.toBytes(HID,false); // new
+ if (date!=0)
+ {
+ // if (HID!=null) P.toBytes(HID,false);
+ h=hashit(sha,date,h,EFS);
+ R=ECP.mapit(h);
+ P.add(R); //P.affine();
+ P.toBytes(HTID,false);
+ }
+ // else P.toBytes(HID,false);
+ }
+
+/* Implement step 2 of MPin protocol on server side */
+ public static int SERVER_2(int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] mSEC,byte[] E,byte[] F)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ ECP8 Q=ECP8.generator();
+
+ ECP8 sQ=ECP8.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+
+ ECP R;
+ if (date!=0)
+ R=ECP.fromBytes(xCID);
+ else
+ {
+ if (xID==null) return BAD_PARAMS;
+ R=ECP.fromBytes(xID);
+ }
+ if (R.is_infinity()) return INVALID_POINT;
+
+ BIG y=BIG.fromBytes(Y);
+ ECP P;
+ if (date!=0) P=ECP.fromBytes(HTID);
+ else
+ {
+ if (HID==null) return BAD_PARAMS;
+ P=ECP.fromBytes(HID);
+ }
+
+ if (P.is_infinity()) return INVALID_POINT;
+
+ P=PAIR256.G1mul(P,y);
+ P.add(R); //P.affine();
+ R=ECP.fromBytes(mSEC);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ FP48 g;
+
+ g=PAIR256.ate2(Q,R,sQ,P);
+ g=PAIR256.fexp(g);
+
+ if (!g.isunity())
+ {
+ if (HID!=null && xID!=null && E!=null && F!=null)
+ {
+ g.toBytes(E);
+ if (date!=0)
+ {
+ P=ECP.fromBytes(HID);
+ if (P.is_infinity()) return INVALID_POINT;
+ R=ECP.fromBytes(xID);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ P=PAIR256.G1mul(P,y);
+ P.add(R); //P.affine();
+ }
+ g=PAIR256.ate(Q,P);
+ g=PAIR256.fexp(g);
+ g.toBytes(F);
+ }
+ return BAD_PIN;
+ }
+
+ return 0;
+ }
+
+/* Pollards kangaroos used to return PIN error */
+ public static int KANGAROO(byte[] E,byte[] F)
+ {
+ FP48 ge=FP48.fromBytes(E);
+ FP48 gf=FP48.fromBytes(F);
+ int[] distance = new int[TS];
+ FP48 t=new FP48(gf);
+ FP48[] table=new FP48[TS];
+ int i,j,m,s,dn,dm,res,steps;
+
+ s=1;
+ for (m=0;m<TS;m++)
+ {
+ distance[m]=s;
+ table[m]=new FP48(t);
+ s*=2;
+ t.usqr();
+ }
+ t.one();
+ dn=0;
+ for (j=0;j<TRAP;j++)
+ {
+ i=t.geta().geta().geta().geta().getA().lastbits(20)%TS;
+ t.mul(table[i]);
+ dn+=distance[i];
+ }
+ gf.copy(t); gf.conj();
+ steps=0; dm=0;
+ res=0;
+ while (dm-dn<MAXPIN)
+ {
+ steps++;
+ if (steps>4*TRAP) break;
+ i=ge.geta().geta().geta().geta().getA().lastbits(20)%TS;
+ ge.mul(table[i]);
+ dm+=distance[i];
+ if (ge.equals(t))
+ {
+ res=dm-dn;
+ break;
+ }
+ if (ge.equals(gf))
+ {
+ res=dn-dm;
+ break;
+ }
+
+ }
+ if (steps>4*TRAP || dm-dn>=MAXPIN) {res=0; } // Trap Failed - probable invalid token
+ return res;
+ }
+
+/* Functions to support M-Pin Full */
+
+ public static int PRECOMPUTE(byte[] TOKEN,byte[] CID,byte[] G1,byte[] G2)
+ {
+ ECP P,T;
+ FP48 g;
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ P=ECP.mapit(CID);
+
+ ECP8 Q=ECP8.generator();
+
+ g=PAIR256.ate(Q,T);
+ g=PAIR256.fexp(g);
+ g.toBytes(G1);
+
+ g=PAIR256.ate(Q,P);
+ g=PAIR256.fexp(g);
+ g.toBytes(G2);
+
+ return 0;
+ }
+
+
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+ public static int CLIENT_KEY(int sha,byte[] G1,byte[] G2,int pin,byte[] R,byte[] X,byte[] H,byte[] wCID,byte[] CK)
+ {
+ byte[] t;
+
+ FP48 g1=FP48.fromBytes(G1);
+ FP48 g2=FP48.fromBytes(G2);
+ BIG z=BIG.fromBytes(R);
+ BIG x=BIG.fromBytes(X);
+ BIG h=BIG.fromBytes(H);
+
+ ECP W=ECP.fromBytes(wCID);
+ if (W.is_infinity()) return INVALID_POINT;
+
+ W=PAIR256.G1mul(W,x);
+
+// FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG r=new BIG(ROM.CURVE_Order);
+// BIG q=new BIG(ROM.Modulus);
+
+ z.add(h); //new
+ z.mod(r);
+
+ g2.pinpow(pin,PBLEN);
+ g1.mul(g2);
+
+ FP16 c=g1.compow(z,r);
+
+ t=mpin_hash(sha,c,W);
+
+ for (int i=0;i<ECP.AESKEY;i++) CK[i]=t[i];
+
+ return 0;
+ }
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+ public static int SERVER_KEY(int sha,byte[] Z,byte[] SST,byte[] W,byte[] H,byte[] HID,byte[] xID,byte[] xCID,byte[] SK)
+ {
+ byte[] t;
+
+ ECP8 sQ=ECP8.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+ ECP R=ECP.fromBytes(Z);
+ if (R.is_infinity()) return INVALID_POINT;
+ ECP A=ECP.fromBytes(HID);
+ if (A.is_infinity()) return INVALID_POINT;
+
+ ECP U;
+ if (xCID!=null)
+ U=ECP.fromBytes(xCID);
+ else
+ U=ECP.fromBytes(xID);
+ if (U.is_infinity()) return INVALID_POINT;
+
+ BIG w=BIG.fromBytes(W);
+ BIG h=BIG.fromBytes(H);
+ A=PAIR256.G1mul(A,h); // new
+ R.add(A); //R.affine();
+
+ U=PAIR256.G1mul(U,w);
+ FP48 g=PAIR256.ate(sQ,R);
+ g=PAIR256.fexp(g);
+
+ FP16 c=g.trace();
+
+ t=mpin_hash(sha,c,U);
+
+ for (int i=0;i<ECP.AESKEY;i++) SK[i]=t[i];
+
+ return 0;
+ }
+
+/* Generate Y = H(epoch, xCID/xID) */
+ public static void GET_Y(int sha,int TimeValue,byte[] xCID,byte[] Y)
+ {
+ byte[] h = hashit(sha,TimeValue,xCID,EFS);
+ BIG y = BIG.fromBytes(h);
+ BIG q=new BIG(ROM.CURVE_Order);
+ y.mod(q);
+ //if (ROM.AES_S>0)
+ //{
+ // y.mod2m(2*ROM.AES_S);
+ //}
+ y.toBytes(Y);
+ }
+
+/* One pass MPIN Client */
+ public static int CLIENT(int sha,int date,byte[] CLIENT_ID,RAND RNG,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT, int TimeValue, byte[] Y)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ rtn = CLIENT_1(sha,date,CLIENT_ID,RNG,X,pin,TOKEN,SEC,xID,xCID,PERMIT);
+ if (rtn != 0)
+ return rtn;
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+
+/* One pass MPIN Server */
+ public static int SERVER(int sha,int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] SEC,byte[] E,byte[] F,byte[] CID, int TimeValue)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ SERVER_1(sha,date,CID,HID,HTID);
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = SERVER_2(date,HID,HTID,Y,SST,xID,xCID,SEC,E,F);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/PAIR256.java b/src/main/java/org/apache/milagro/amcl/BLS48/PAIR256.java
new file mode 100644
index 0000000..f80c41b
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/PAIR256.java
@@ -0,0 +1,628 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BN Curve Pairing functions */
+
+package org.apache.milagro.amcl.BLS48;
+
+public final class PAIR256 {
+
+ public static final boolean USE_GLV =true;
+ public static final boolean USE_GS_G2 =true;
+ public static final boolean USE_GS_GT =true;
+ public static final boolean GT_STRONG=false;
+
+
+/* Line function */
+ public static FP48 line(ECP8 A,ECP8 B,FP Qx,FP Qy)
+ {
+//System.out.println("Into line");
+ FP16 a,b,c; // Edits here
+// c=new FP16(0);
+ if (A==B)
+ { // Doubling
+ FP8 XX=new FP8(A.getx()); //X
+ FP8 YY=new FP8(A.gety()); //Y
+ FP8 ZZ=new FP8(A.getz()); //Z
+ FP8 YZ=new FP8(YY); //Y
+ YZ.mul(ZZ); //YZ
+ XX.sqr(); //X^2
+ YY.sqr(); //Y^2
+ ZZ.sqr(); //Z^2
+
+ YZ.imul(4);
+ YZ.neg(); YZ.norm(); //-2YZ
+ YZ.tmul(Qy); //-2YZ.Ys
+
+ XX.imul(6); //3X^2
+ XX.tmul(Qx); //3X^2.Xs
+
+ int sb=3*ROM.CURVE_B_I;
+ ZZ.imul(sb);
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ ZZ.div_2i();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ ZZ.times_i();
+ ZZ.add(ZZ);
+ YZ.times_i();
+ YZ.norm();
+ }
+
+ ZZ.norm(); // 3b.Z^2
+
+ YY.add(YY);
+ ZZ.sub(YY); ZZ.norm(); // 3b.Z^2-Y^2
+
+ a=new FP16(YZ,ZZ); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP16(XX); // L(0,1) | L(0,0) | L(1,0)
+ c=new FP16(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP16(0);
+ c=new FP16(XX); c.times_i();
+ }
+ A.dbl();
+ }
+ else
+ { // Addition - assume B is affine
+
+ FP8 X1=new FP8(A.getx()); // X1
+ FP8 Y1=new FP8(A.gety()); // Y1
+ FP8 T1=new FP8(A.getz()); // Z1
+ FP8 T2=new FP8(A.getz()); // Z1
+
+ T1.mul(B.gety()); // T1=Z1.Y2
+ T2.mul(B.getx()); // T2=Z1.X2
+
+ X1.sub(T2); X1.norm(); // X1=X1-Z1.X2
+ Y1.sub(T1); Y1.norm(); // Y1=Y1-Z1.Y2
+
+ T1.copy(X1); // T1=X1-Z1.X2
+ X1.tmul(Qy); // X1=(X1-Z1.X2).Ys
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X1.times_i();
+ X1.norm();
+ }
+
+ T1.mul(B.gety()); // T1=(X1-Z1.X2).Y2
+
+ T2.copy(Y1); // T2=Y1-Z1.Y2
+ T2.mul(B.getx()); // T2=(Y1-Z1.Y2).X2
+ T2.sub(T1); T2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+ Y1.tmul(Qx); Y1.neg(); Y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+ a=new FP16(X1,T2); // (X1-Z1.X2).Ys | (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2 | - (Y1-Z1.Y2).Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP16(Y1);
+ c=new FP16(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP16(0);
+ c=new FP16(Y1); c.times_i();
+ }
+ A.add(B);
+ }
+//System.out.println("Out of line");
+ return new FP48(a,b,c);
+ }
+
+/* Optimal R-ate pairing */
+ public static FP48 ate(ECP8 P1,ECP Q1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ FP48 lv;
+ int bt;
+
+ ECP8 P=new ECP8(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+
+ ECP8 A=new ECP8();
+ FP48 r=new FP48(1);
+ A.copy(P);
+
+ ECP8 MP=new ECP8();
+ MP.copy(P); MP.neg();
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+ return r;
+ }
+
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+ public static FP48 ate2(ECP8 P1,ECP Q1,ECP8 R1,ECP S1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ FP48 lv;
+ int bt;
+
+ ECP8 P=new ECP8(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ ECP8 R=new ECP8(R1);
+ ECP S=new ECP(S1);
+
+ R.affine();
+ S.affine();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+ FP Sx=new FP(S.getx());
+ FP Sy=new FP(S.gety());
+
+ ECP8 A=new ECP8();
+ ECP8 B=new ECP8();
+ FP48 r=new FP48(1);
+
+ A.copy(P);
+ B.copy(R);
+
+ ECP8 MP=new ECP8();
+ MP.copy(P); MP.neg();
+ ECP8 MR=new ECP8();
+ MR.copy(R); MR.neg();
+
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ lv=line(B,B,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ lv=line(B,R,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ //R.neg();
+ lv=line(B,MR,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //R.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+ return r;
+ }
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+ public static FP48 fexp(FP48 m)
+ {
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ FP48 r=new FP48(m);
+
+/* Easy part of final exp */
+ FP48 lv=new FP48(r);
+ lv.inverse();
+ r.conj();
+
+ r.mul(lv);
+ lv.copy(r);
+ r.frob(f,8);
+ r.mul(lv);
+
+ FP48 t0,t1,t2,t3,t4,t5,t6,t7;
+/* Hard part of final exp */
+// Ghamman & Fouotsa Method
+
+ t7=new FP48(r); t7.usqr();
+ t1=t7.pow(x);
+
+ x.fshr(1);
+ t2=t1.pow(x);
+ x.fshl(1);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3=new FP48(t1); t3.conj();
+ t2.mul(t3);
+ t2.mul(r);
+
+ r.mul(t7);
+
+ t1=t2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+ t3.copy(t1);
+ t3.frob(f,14);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,13);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,12);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,11);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,10);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,9);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,8);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t2); t3.conj();
+ t1.mul(t3);
+ t3.copy(t1);
+ t3.frob(f,7);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,6);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,5);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,4);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,3);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,2);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ t3.copy(t1);
+ t3.frob(f,1);
+ r.mul(t3);
+ t1=t1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
+ t1.conj();
+ }
+
+ r.mul(t1);
+ t2.frob(f,15);
+ r.mul(t2);
+
+ r.reduce();
+ return r;
+ }
+
+/* GLV method */
+ public static BIG[] glv(BIG e)
+ {
+ BIG[] u=new BIG[2];
+// -(x^8).P = (Beta.x,y)
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG x2=BIG.smul(x,x);
+ x=BIG.smul(x2,x2);
+ x2=BIG.smul(x,x);
+ u[0]=new BIG(e);
+ u[0].mod(x2);
+ u[1]=new BIG(e);
+ u[1].div(x2);
+ u[1].rsub(q);
+
+ return u;
+ }
+
+/* Galbraith & Scott Method */
+ public static BIG[] gs(BIG e)
+ {
+ BIG[] u=new BIG[16];
+
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG w=new BIG(e);
+ for (int i=0;i<15;i++)
+ {
+ u[i]=new BIG(w);
+ u[i].mod(x);
+ w.div(x);
+ }
+ u[15]=new BIG(w);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ u[1].copy(BIG.modneg(u[1],q));
+ u[3].copy(BIG.modneg(u[3],q));
+ u[5].copy(BIG.modneg(u[5],q));
+ u[7].copy(BIG.modneg(u[7],q));
+ u[9].copy(BIG.modneg(u[9],q));
+ u[11].copy(BIG.modneg(u[11],q));
+ u[13].copy(BIG.modneg(u[13],q));
+ u[15].copy(BIG.modneg(u[15],q));
+ }
+
+ return u;
+ }
+
+/* Multiply P by e in group G1 */
+ public static ECP G1mul(ECP P,BIG e)
+ {
+ ECP R;
+ if (USE_GLV)
+ {
+ //P.affine();
+ R=new ECP();
+ R.copy(P);
+ int i,np,nn;
+ ECP Q=new ECP();
+ Q.copy(P); Q.affine();
+ BIG q=new BIG(ROM.CURVE_Order);
+ FP cru=new FP(new BIG(ROM.CURVE_Cru));
+ BIG t=new BIG(0);
+ BIG[] u=glv(e);
+ Q.getx().mul(cru);
+
+ np=u[0].nbits();
+ t.copy(BIG.modneg(u[0],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[0].copy(t);
+ R.neg();
+ }
+
+ np=u[1].nbits();
+ t.copy(BIG.modneg(u[1],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[1].copy(t);
+ Q.neg();
+ }
+ u[0].norm();
+ u[1].norm();
+ R=R.mul2(u[0],Q,u[1]);
+
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* Multiply P by e in group G2 */
+ public static ECP8 G2mul(ECP8 P,BIG e)
+ {
+ ECP8 R;
+ if (USE_GS_G2)
+ {
+ ECP8[] Q=new ECP8[16];
+ FP2[] F=ECP8.frob_constants();
+
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] u=gs(e);
+
+ BIG t=new BIG(0);
+ int i,np,nn;
+ //P.affine();
+
+ Q[0]=new ECP8(); Q[0].copy(P);
+ for (i=1;i<16;i++)
+ {
+ Q[i]=new ECP8(); Q[i].copy(Q[i-1]);
+ Q[i].frob(F,1);
+ }
+ for (i=0;i<16;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ Q[i].neg();
+ }
+ u[i].norm();
+ //Q[i].affine();
+ }
+
+ R=ECP8.mul16(Q,u);
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP16.java */
+ public static FP48 GTpow(FP48 d,BIG e)
+ {
+ FP48 r;
+ if (USE_GS_GT)
+ {
+ FP48[] g=new FP48[16];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG t=new BIG(0);
+ int i,np,nn;
+ BIG[] u=gs(e);
+
+ g[0]=new FP48(d);
+ for (i=1;i<16;i++)
+ {
+ g[i]=new FP48(0); g[i].copy(g[i-1]);
+ g[i].frob(f,1);
+ }
+ for (i=0;i<16;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ g[i].conj();
+ }
+ u[i].norm();
+ }
+ r=FP48.pow16(g,u);
+ }
+ else
+ {
+ r=d.pow(e);
+ }
+ return r;
+ }
+
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BLS48/ROM.java b/src/main/java/org/apache/milagro/amcl/BLS48/ROM.java
new file mode 100644
index 0000000..cbe6fa7
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BLS48/ROM.java
@@ -0,0 +1,68 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.BLS48;
+
+public class ROM
+{
+
+// Base Bits= 58
+ public static final long[] Modulus= {0x2F6E60FFCF6AC0BL,0x259C02699877E7BL,0x37A9870D4228402L,0x80821A1DACBB04L,0x13016A7C025A415L,0x2BB355ACDE6E250L,0x20536F405DA950L,0x295B219C54AB351L,0x3FCFC5B23729047L,0x3F45F610BL};
+ public static final long[] R2modp= {0x25E03FA0D59D0FAL,0x6B55DC2DE8FD41L,0xA0E01D0B937F48L,0x20336279F50EFCEL,0x2212822A3470A2FL,0xD5A21C4F9FB72DL,0x89E8F0A1CFD9F8L,0x2291DA62B48793L,0x3DC6978EF609E61L,0x1735D29EL};
+ public static final long MConst= 0x21BFCBCA9DA805DL;
+ public static final long[] Fra= {0x2623CFD9325BF89L,0x341FA8DCCD0A56FL,0x1952FBA0E83BCCAL,0xBE3C26F8D1D297L,0x27F84ABE7AB9F2CL,0x13BDE945C9DECEBL,0x3B3213C83C0F60BL,0x3B7F0411FF27FF7L,0x80089C089BB36CL,0xA62E01EEL};
+ public static final long[] Frb= {0x2623CFD9325BF89L,0x341FA8DCCD0A56FL,0x1952FBA0E83BCCAL,0xBE3C26F8D1D297L,0x27F84ABE7AB9F2CL,0x13BDE945C9DECEBL,0x3B3213C83C0F60BL,0x3B7F0411FF27FF7L,0x80089C089BB36CL,0xA62E01EEL};
+
+ public static final int CURVE_A= 0;
+ public static final int CURVE_B_I= 17;
+ public static final int CURVE_Cof_I= 0;
+ public static final long[] CURVE_B= {0x11L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Order= {0x2FFFF0000000001L,0x11550278A769C21L,0x14D1EB162029C21L,0x2309B4A2B6307F4L,0x34790BD26DFED78L,0x2C6FE3F2571037BL,0x1306A973C465FB0L,0x28446ABB18DF17AL,0xC43BF73EL,0x0L};
+ public static final long[] CURVE_Gx= {0x3286D2F65D71D33L,0x3601553F8CB783FL,0xFF01647711EE0BL,0x268BC07F29FD8CCL,0xE0702E69A80F66L,0x285003EAC056511L,0x35E130D242B2C3AL,0x107024C87924166L,0x17595DB8957EDD7L,0x26A27A4A1L};
+ public static final long[] CURVE_Gy= {0x29A5B3FEA6ED83AL,0x3712E552A29C33DL,0x3391EA8E6958677L,0x29F3C7B9DED7E3EL,0x4E2E3818FB7229L,0x1CC30999551E32DL,0xE67A4086260E3CL,0x2A68CCB8579C437L,0x62C5FAE2B3349DL,0x2B634253L};
+
+ public static final long[] CURVE_Bnx= {0x7DE40020L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Cof= {0xA2D10F7F12ABEBL,0x5L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Cru= {0x364E7E6CBBA429L,0x338C28A4D3DD160L,0x28DC3C68308093AL,0x230B290AE0E768L,0x127B0AB9B73BC2DL,0x23192337B1A639CL,0x1E399AEEFF04AE3L,0x20C88F0091318E5L,0x37CEF23203A79F7L,0x3F45F60F3L};
+ public static final long[] CURVE_Pxaaa= {0x28D2E8E8923CE4AL,0x2F5C40B4AE04F4AL,0x2165D8A1313A20CL,0x2BFC7FD18DF074FL,0x12B37F0A7C90B98L,0x286ED92CE57BD37L,0x1C416C4ABF57375L,0x39779D0B2EE2172L,0x1A1C0497A5D9487L,0x3995E3602L};
+ public static final long[] CURVE_Pxaab= {0x271CDC5AC0A1BE1L,0x2A3F8EC01DF5FDCL,0xAD5478433972C5L,0x22B73FAE2491D8CL,0x30E75C6B40A11FAL,0xE960C8FF259C26L,0x8CE48632D18B04L,0x39549A6BC27F419L,0x274A97B58DB48B6L,0xA5080497L};
+ public static final long[] CURVE_Pxaba= {0x23A724C770C5DC4L,0x2A592B776B3BCB6L,0x110934259BEC736L,0x18E2C5A649AB2A8L,0x2B84CB2C58CD55L,0x168195F91EE8B90L,0xF7D917CE233167L,0x2A38FB7729335BDL,0x36D71DDA78B689BL,0x2D4BFBE3DL};
+ public static final long[] CURVE_Pxabb= {0xD629669A64B740L,0x47F52632481578L,0x40D7054323ADD1L,0x3F9F4CCCD789E1BL,0x1E3C73C41F4EEA7L,0x15E7D7A61968610L,0x1B4186B40590D3BL,0x19F3BA577306AAFL,0x12DFE5F23F63B1AL,0x24E102A76L};
+ public static final long[] CURVE_Pxbaa= {0x1BB0C6172F1E01FL,0x30C4734D2C29802L,0x990FA39218788L,0xEEB80ED6AE2501L,0x34728852870C80BL,0x94841D1637D478L,0x21CBCE27C3AD4D4L,0xA29F9E111E6AD5L,0x18CF1447CC49D3L,0x37787BDFDL};
+ public static final long[] CURVE_Pxbab= {0x30A38238637383DL,0x288094FE661F866L,0x135C51ED5D3D212L,0x3C39C57E8051F25L,0x164639D737D882FL,0x312AEC8AA8DB8FCL,0x3FE838885E54DA8L,0x2FC3978BA297414L,0x850556F014F91FL,0x20B6CE9E3L};
+ public static final long[] CURVE_Pxbba= {0x1683DB3D711939CL,0x26F9475A69066BAL,0xBCDB572CF2F6C0L,0x128DC8902CE1323L,0x29B3233EF353D1CL,0x342402A46B7046L,0x3187D1A403D6070L,0x3E3466F9F23BA45L,0xD943BE2435A9CCL,0x2A08A9CE1L};
+ public static final long[] CURVE_Pxbbb= {0x32D410A856F4899L,0x7147AE4A959750L,0x28AEA57990BC9BCL,0x2807F11E9E26DAL,0x282C5EBA71895E3L,0x2832162D9FEC5FFL,0x33479E30007597CL,0x227A376C26A4B00L,0x1C16F1567857A32L,0x37DD51E0FL};
+ public static final long[] CURVE_Pyaaa= {0x2E097CFB4137844L,0x313B1927FD3CCDDL,0x1EB86FABC768851L,0x4156382E29C659L,0x3C10CC7CF8896E0L,0x22FC388FD1D539CL,0x2C3E202F56CDD39L,0x2E9645FB43E8C72L,0x93548FD8706190L,0x1D9BB42E1L};
+ public static final long[] CURVE_Pyaab= {0x25E3248EDC83190L,0x2BAABD11AA26424L,0x161D23BAC418D32L,0x5B3258247CBFF7L,0xED295CADE03C1FL,0x3A84758C5C741A2L,0x3D207E205E02B9EL,0x86743E24EA6513L,0x1DAA8E268EFA1C4L,0x9E72CE4FL};
+ public static final long[] CURVE_Pyaba= {0x1DC5FD041985C0DL,0x2350136864770FAL,0x3179A5F5483ACE9L,0x2C25AB1A171F32CL,0x397C4403E658341L,0xE7E1C2186E971L,0x15921F60B0A5F40L,0x46E9317635E008L,0x17EF1353F3140D6L,0x35166F259L};
+ public static final long[] CURVE_Pyabb= {0x36FE0A8159D42F8L,0x290EBF4445895D7L,0x20273B0FE9E7F2BL,0x23A6E2FF0F3FD7DL,0x1F37678869E5006L,0x2CA2DB53C9ED8DL,0x4E4BFD902F51DEL,0x1FF8649F125B66L,0x382D89BED80731L,0x28383AAA8L};
+ public static final long[] CURVE_Pybaa= {0xD512B39F38039FL,0x23BA255F3C68984L,0x390AA14058093CFL,0x1FF0B2F2FFA1622L,0x310C5CC5F2ABB75L,0x3D9016C9EB6A2C9L,0x1CF7EE268EC2F18L,0x338BBB12C36B65AL,0x1EEE591B8A1D1ABL,0xAC11927CL};
+ public static final long[] CURVE_Pybab= {0x269E06F295F7865L,0x2FC1D5BA1CE9A0EL,0x3631F3F1DACADD7L,0x30CCF3581D3943L,0x3FBE3B902505BB0L,0x23C4A9D31B36A49L,0x2056135CA438576L,0x2B78046739984F4L,0x12C0AC57B6F180EL,0xC48CA65BL};
+ public static final long[] CURVE_Pybba= {0x1D83A9F67CC1979L,0x12ABE71DD89E6F0L,0x247B1C21635FDA9L,0x92880950076209L,0x2ECF179E0D733D7L,0x20D2DFFB53841F9L,0x3441B1645BC9FE8L,0x3089222CE22EC9DL,0x3699AE4108C86C5L,0x320034967L};
+ public static final long[] CURVE_Pybbb= {0xFC89562FC9F25BL,0xB13E01AE9AB5D3L,0x18E8F169C9D264FL,0x3A5828D76B24A13L,0x1E8FD9BCEF84D9AL,0x36D20E3DBFEE16AL,0x17D3B3DF1AB4C1CL,0xF190510390F005L,0x12640E61B9BF549L,0x283D84D97L};
+ public static final long[][] CURVE_W= {{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}};
+ public static final long[][][] CURVE_SB= {{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}}};
+ public static final long[][] CURVE_WB= {{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}};
+ public static final long[][][] CURVE_BB= {{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}}};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/BIG.java b/src/main/java/org/apache/milagro/amcl/BN254/BIG.java
new file mode 100644
index 0000000..89ac1bd
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.BN254;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=32; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/DBIG.java b/src/main/java/org/apache/milagro/amcl/BN254/DBIG.java
new file mode 100644
index 0000000..33e219d
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.BN254;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/ECDH.java b/src/main/java/org/apache/milagro/amcl/BN254/ECDH.java
new file mode 100644
index 0000000..07050ab
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.BN254;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/ECP.java b/src/main/java/org/apache/milagro/amcl/BN254/ECP.java
new file mode 100644
index 0000000..aef855b
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.BN254;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=BN;
+ public static final int SEXTIC_TWIST=D_TYPE;
+ public static final int SIGN_OF_X=NEGATIVEX;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/ECP2.java b/src/main/java/org/apache/milagro/amcl/BN254/ECP2.java
new file mode 100644
index 0000000..673da96
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/ECP2.java
@@ -0,0 +1,796 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Weierstrass elliptic curve functions over FP2 */
+
+package org.apache.milagro.amcl.BN254;
+
+public final class ECP2 {
+ private FP2 x;
+ private FP2 y;
+ private FP2 z;
+// private boolean INF;
+
+/* Constructor - set this=O */
+ public ECP2() {
+// INF=true;
+ x=new FP2(0);
+ y=new FP2(1);
+ z=new FP2(0);
+ }
+
+ public ECP2(ECP2 e) {
+ this.x = new FP2(e.x);
+ this.y = new FP2(e.y);
+ this.z = new FP2(e.z);
+ }
+
+/* Test this=O? */
+ public boolean is_infinity() {
+// if (INF) return true; //******
+ return (x.iszilch() && z.iszilch());
+ }
+/* copy this=P */
+ public void copy(ECP2 P)
+ {
+ x.copy(P.x);
+ y.copy(P.y);
+ z.copy(P.z);
+// INF=P.INF;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ y.one();
+ z.zero();
+ }
+
+/* Conditional move of Q to P dependant on d */
+ public void cmove(ECP2 Q,int d)
+ {
+ x.cmove(Q.x,d);
+ y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ }
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(ECP2 W[],int b)
+ {
+ ECP2 MP=new ECP2();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test if P == Q */
+ public boolean equals(ECP2 Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+
+ FP2 a=new FP2(x); // *****
+ FP2 b=new FP2(Q.x);
+ a.mul(Q.z);
+ b.mul(z);
+ if (!a.equals(b)) return false;
+
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+
+ return true;
+ }
+/* set this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ y.norm();
+ y.neg(); y.norm();
+ return;
+ }
+/* set to Affine - (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return;
+ FP2 one=new FP2(1);
+ if (z.equals(one))
+ {
+ x.reduce();
+ y.reduce();
+ return;
+ }
+ z.inverse();
+
+ x.mul(z); x.reduce(); // *****
+ y.mul(z); y.reduce();
+ z.copy(one);
+ }
+/* extract affine x as FP2 */
+ public FP2 getX()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.x;
+ }
+/* extract affine y as FP2 */
+ public FP2 getY()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.y;
+ }
+/* extract projective x */
+ public FP2 getx()
+ {
+ return x;
+ }
+/* extract projective y */
+ public FP2 gety()
+ {
+ return y;
+ }
+/* extract projective z */
+ public FP2 getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP2 W=new ECP2(this);
+ W.affine();
+ W.x.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i]=t[i];
+ W.x.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+BIG.MODBYTES]=t[i];
+
+ W.y.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+2*BIG.MODBYTES]=t[i];
+ W.y.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+3*BIG.MODBYTES]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP2 fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG ra;
+ BIG rb;
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 rx=new FP2(ra,rb);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+2*BIG.MODBYTES];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+3*BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 ry=new FP2(ra,rb);
+
+ return new ECP2(rx,ry);
+ }
+/* convert this to hex string */
+ public String toString() {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ return "("+W.x.toString()+","+W.y.toString()+")";
+ }
+
+/* Calculate RHS of twisted curve equation x^3+B/i */
+ public static FP2 RHS(FP2 x) {
+ x.norm();
+ FP2 r=new FP2(x);
+ r.sqr();
+ FP2 b=new FP2(new BIG(ROM.CURVE_B));
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b.div_ip();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b.norm();
+ b.mul_ip();
+ b.norm();
+ }
+
+
+ r.mul(x);
+ r.add(b);
+
+ r.reduce();
+ return r;
+ }
+
+/* construct this from (x,y) - but set to O if not on curve */
+ public ECP2(FP2 ix,FP2 iy) {
+ x=new FP2(ix);
+ y=new FP2(iy);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ FP2 y2=new FP2(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+// if (y2.equals(rhs)) INF=false;
+// else {x.zero();INF=true;}
+ }
+
+/* construct this from x - but set to O if not on curve */
+ public ECP2(FP2 ix) {
+ x=new FP2(ix);
+ y=new FP2(1);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ if (rhs.sqrt())
+ {
+ y.copy(rhs);
+ //INF=false;
+ }
+ else {/*x.zero();INF=true;*/ inf();}
+ }
+
+/* this+=this */
+ public int dbl() {
+// if (INF) return -1;
+//System.out.println("Into dbl");
+ FP2 iy=new FP2(y);
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ iy.mul_ip(); iy.norm();
+ }
+ FP2 t0=new FP2(y); //***** Change
+ t0.sqr();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip();
+ }
+ FP2 t1=new FP2(iy);
+ t1.mul(z);
+ FP2 t2=new FP2(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z);
+ z.add(z);
+ z.norm();
+
+ t2.imul(3*ROM.CURVE_B_I);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip();
+ t2.norm();
+ }
+
+ FP2 x3=new FP2(t2);
+ x3.mul(z);
+
+ FP2 y3=new FP2(t0);
+
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1); t2.norm();
+ t0.sub(t2); t0.norm(); //y^2-9bz^2
+ y3.mul(t0); y3.add(x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+ t1.copy(x); t1.mul(iy); //
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x); //(y^2-9bz^2)xy2
+
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ return 1;
+ }
+
+/* this+=Q - return 0 for add, 1 for double, -1 for O */
+ public int add(ECP2 Q) {
+// if (INF)
+// {
+// copy(Q);
+// return -1;
+// }
+// if (Q.INF) return -1;
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP2 t0=new FP2(x);
+ t0.mul(Q.x); // x.Q.x
+ FP2 t1=new FP2(y);
+ t1.mul(Q.y); // y.Q.y
+
+ FP2 t2=new FP2(z);
+ t2.mul(Q.z);
+ FP2 t3=new FP2(x);
+ t3.add(y); t3.norm(); //t3=X1+Y1
+ FP2 t4=new FP2(Q.x);
+ t4.add(Q.y); t4.norm(); //t4=X2+Y2
+ t3.mul(t4); //t3=(X1+Y1)(X2+Y2)
+ t4.copy(t0); t4.add(t1); //t4=X1.X2+Y1.Y2
+
+ t3.sub(t4); t3.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t3.mul_ip(); t3.norm(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+ }
+ t4.copy(y);
+ t4.add(z); t4.norm(); //t4=Y1+Z1
+ FP2 x3=new FP2(Q.y);
+ x3.add(Q.z); x3.norm(); //x3=Y2+Z2
+
+ t4.mul(x3); //t4=(Y1+Z1)(Y2+Z2)
+ x3.copy(t1); //
+ x3.add(t2); //X3=Y1.Y2+Z1.Z2
+
+ t4.sub(x3); t4.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t4.mul_ip(); t4.norm(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+ }
+ x3.copy(x); x3.add(z); x3.norm(); // x3=X1+Z1
+ FP2 y3=new FP2(Q.x);
+ y3.add(Q.z); y3.norm(); // y3=X2+Z2
+ x3.mul(y3); // x3=(X1+Z1)(X2+Z2)
+ y3.copy(t0);
+ y3.add(t2); // y3=X1.X2+Z1+Z2
+ y3.rsub(x3); y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip(); t0.norm(); // x.Q.x
+ t1.mul_ip(); t1.norm(); // y.Q.y
+ }
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip(); t2.norm();
+ }
+ FP2 z3=new FP2(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ y3.mul_ip();
+ y3.norm();
+ }
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ return 0;
+ }
+
+/* set this-=Q */
+ public int sub(ECP2 Q) {
+ ECP2 NQ=new ECP2(Q);
+ NQ.neg();
+ int D=add(NQ);
+ //Q.neg();
+ //int D=add(Q);
+ //Q.neg();
+ return D;
+ }
+/* set this*=q, where q is Modulus, using Frobenius */
+ public void frob(FP2 X)
+ {
+// if (INF) return;
+ FP2 X2=new FP2(X);
+
+ X2.sqr();
+ x.conj();
+ y.conj();
+ z.conj();
+ z.reduce();
+ x.mul(X2);
+
+ y.mul(X2);
+ y.mul(X);
+ }
+
+/* P*=e */
+ public ECP2 mul(BIG e)
+ {
+/* fixed size windows */
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP2 P=new ECP2();
+ ECP2 Q=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2[] W=new ECP2[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ if (is_infinity()) return new ECP2();
+
+ //affine();
+
+/* precompute table */
+ Q.copy(this);
+ Q.dbl();
+ W[0]=new ECP2();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP2();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+/* make exponent odd - add 2P if even, P if odd */
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+/* convert exponent to signed 4-bit window */
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C);
+ P.affine();
+ return P;
+ }
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ ECP2 W=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] T=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ //Q[i].affine();
+ }
+
+ T[0] = new ECP2(); T[0].copy(Q[0]); // Q[0]
+ T[1] = new ECP2(); T[1].copy(T[0]); T[1].add(Q[1]); // Q[0]+Q[1]
+ T[2] = new ECP2(); T[2].copy(T[0]); T[2].add(Q[2]); // Q[0]+Q[2]
+ T[3] = new ECP2(); T[3].copy(T[1]); T[3].add(Q[2]); // Q[0]+Q[1]+Q[2]
+ T[4] = new ECP2(); T[4].copy(T[0]); T[4].add(Q[3]); // Q[0]+Q[3]
+ T[5] = new ECP2(); T[5].copy(T[1]); T[5].add(Q[3]); // Q[0]+Q[1]+Q[3]
+ T[6] = new ECP2(); T[6].copy(T[2]); T[6].add(Q[3]); // Q[0]+Q[2]+Q[3]
+ T[7] = new ECP2(); T[7].copy(T[3]); T[7].add(Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ P.select(T,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ P.dbl();
+ W.select(T,(int)(2*w[i]+s[i]));
+ P.add(W);
+ }
+
+ // apply correction
+ W.copy(P);
+ W.sub(Q[0]);
+ P.cmove(W,pb);
+ P.affine();
+ return P;
+ }
+
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+/*
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb;
+ int[] a=new int[4];
+ ECP2 T=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] W=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ Q[i].affine();
+ }
+
+// precompute table
+
+ W[0]=new ECP2(); W[0].copy(Q[0]); W[0].sub(Q[1]);
+
+ W[1]=new ECP2(); W[1].copy(W[0]);
+ W[2]=new ECP2(); W[2].copy(W[0]);
+ W[3]=new ECP2(); W[3].copy(W[0]);
+ W[4]=new ECP2(); W[4].copy(Q[0]); W[4].add(Q[1]);
+ W[5]=new ECP2(); W[5].copy(W[4]);
+ W[6]=new ECP2(); W[6].copy(W[4]);
+ W[7]=new ECP2(); W[7].copy(W[4]);
+ T.copy(Q[2]); T.sub(Q[3]);
+ W[1].sub(T);
+ W[2].add(T);
+ W[5].sub(T);
+ W[6].add(T);
+ T.copy(Q[2]); T.add(Q[3]);
+ W[0].sub(T);
+ W[3].add(T);
+ W[4].sub(T);
+ W[7].add(T);
+
+// if multiplier is even add 1 to multiplier, and add P to correction
+ mt.zero(); C.inf();
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ C.add(Q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(byte)(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ P.dbl();
+ P.add(T);
+ }
+ P.sub(C); // apply correction
+
+ P.affine();
+ return P;
+ }
+*/
+
+/* needed for SOK */
+ public static ECP2 mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ BIG one=new BIG(1);
+ FP2 X;
+ ECP2 Q;
+ x.mod(q);
+ while (true)
+ {
+ X=new FP2(one,x);
+ Q=new ECP2(X);
+ if (!Q.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+
+ BIG Fra=new BIG(ROM.Fra);
+ BIG Frb=new BIG(ROM.Frb);
+ X=new FP2(Fra,Frb);
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X.inverse();
+ X.norm();
+ }
+
+ x=new BIG(ROM.CURVE_Bnx);
+
+/* Fast Hashing to G2 - Fuentes-Castaneda, Knapp and Rodriguez-Henriquez */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ ECP2 T,K;
+
+ T=new ECP2(); T.copy(Q);
+ T=T.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ T.neg();
+ }
+ K=new ECP2(); K.copy(T);
+ K.dbl(); K.add(T); //K.affine();
+
+ K.frob(X);
+ Q.frob(X); Q.frob(X); Q.frob(X);
+ Q.add(T); Q.add(K);
+ T.frob(X); T.frob(X);
+ Q.add(T);
+
+ }
+
+/* Efficient hash maps to G2 on BLS curves - Budroni, Pintore */
+/* Q -> x2Q -xQ -Q +F(xQ -Q) +F(F(2Q)) */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BLS)
+ {
+ // ECP2 xQ,x2Q;
+ // xQ=new ECP2();
+ // x2Q=new ECP2();
+
+ ECP2 xQ=Q.mul(x);
+ ECP2 x2Q=xQ.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ xQ.neg();
+ }
+
+ x2Q.sub(xQ);
+ x2Q.sub(Q);
+
+ xQ.sub(Q);
+ xQ.frob(X);
+
+ Q.dbl();
+ Q.frob(X);
+ Q.frob(X);
+
+ Q.add(x2Q);
+ Q.add(xQ);
+ }
+ Q.affine();
+ return Q;
+ }
+
+ public static ECP2 generator()
+ {
+ return new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+ }
+
+/*
+ public static void main(String[] args) {
+ BIG r=new BIG(ROM.Modulus);
+
+ BIG Pxa=new BIG(ROM.CURVE_Pxa);
+ BIG Pxb=new BIG(ROM.CURVE_Pxb);
+ BIG Pya=new BIG(ROM.CURVE_Pya);
+ BIG Pyb=new BIG(ROM.CURVE_Pyb);
+
+ BIG Fra=new BIG(ROM.CURVE_Fra);
+ BIG Frb=new BIG(ROM.CURVE_Frb);
+
+ FP2 f=new FP2(Fra,Frb);
+
+ FP2 Px=new FP2(Pxa,Pxb);
+ FP2 Py=new FP2(Pya,Pyb);
+
+ ECP2 P=new ECP2(Px,Py);
+
+ System.out.println("P= "+P.toString());
+
+ P=P.mul(r);
+ System.out.println("P= "+P.toString());
+
+ ECP2 Q=new ECP2(Px,Py);
+ Q.frob(f);
+ System.out.println("Q= "+Q.toString());
+ } */
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/FP.java b/src/main/java/org/apache/milagro/amcl/BN254/FP.java
new file mode 100644
index 0000000..6c3003f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.BN254;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=254; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<26); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/FP12.java b/src/main/java/org/apache/milagro/amcl/BN254/FP12.java
new file mode 100644
index 0000000..0aba532
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/FP12.java
@@ -0,0 +1,907 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Fp^12 functions */
+/* FP12 elements are of the form a+i.b+i^2.c */
+
+package org.apache.milagro.amcl.BN254;
+
+public final class FP12 {
+ private final FP4 a;
+ private final FP4 b;
+ private final FP4 c;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ c.reduce();
+ }
+/* normalise all components of this */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ c.norm();
+ }
+/* test x==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch() && c.iszilch());
+ }
+
+ public void cmove(FP12 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ c.cmove(g.c,d);
+ }
+
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(FP12 g[],int b)
+ {
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(g[0],teq(babs,0)); // conditional move
+ cmove(g[1],teq(babs,1));
+ cmove(g[2],teq(babs,2));
+ cmove(g[3],teq(babs,3));
+ cmove(g[4],teq(babs,4));
+ cmove(g[5],teq(babs,5));
+ cmove(g[6],teq(babs,6));
+ cmove(g[7],teq(babs,7));
+
+ FP12 invf=new FP12(this);
+ invf.conj();
+ cmove(invf,(int)(m&1));
+ }
+
+
+/* test x==1 ? */
+ public boolean isunity() {
+ FP4 one=new FP4(1);
+ return (a.equals(one) && b.iszilch() && c.iszilch());
+ }
+/* return 1 if x==y, else 0 */
+ public boolean equals(FP12 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b) && c.equals(x.c));
+ }
+/* extract a from this */
+ public FP4 geta()
+ {
+ return a;
+ }
+/* extract b */
+ public FP4 getb()
+ {
+ return b;
+ }
+/* extract c */
+ public FP4 getc()
+ {
+ return c;
+ }
+/* copy this=x */
+ public void copy(FP12 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ c.copy(x.c);
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ c.zero();
+ }
+/* this=conj(this) */
+ public void conj()
+ {
+ a.conj();
+ b.nconj();
+ c.conj();
+ }
+/* Constructors */
+ public FP12(FP4 d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(int d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(FP4 d,FP4 e,FP4 f)
+ {
+ a=new FP4(d);
+ b=new FP4(e);
+ c=new FP4(f);
+ }
+
+ public FP12(FP12 x)
+ {
+ a=new FP4(x.a);
+ b=new FP4(x.b);
+ c=new FP4(x.c);
+ }
+
+/* Granger-Scott Unitary Squaring */
+ public void usqr()
+ {
+//System.out.println("Into usqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(c);
+ FP4 C=new FP4(b);
+ FP4 D=new FP4(0);
+
+ a.sqr();
+ D.copy(a); D.add(a);
+ a.add(D);
+
+ a.norm();
+ A.nconj();
+
+ A.add(A);
+ a.add(A);
+ B.sqr();
+ B.times_i();
+
+ D.copy(B); D.add(B);
+ B.add(D);
+ B.norm();
+
+ C.sqr();
+ D.copy(C); D.add(C);
+ C.add(D);
+ C.norm();
+
+ b.conj();
+ b.add(b);
+ c.nconj();
+
+ c.add(c);
+ b.add(B);
+ c.add(C);
+//System.out.println("Out of usqr 1");
+ reduce();
+//System.out.println("Out of usqr 2");
+ }
+
+/* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+ public void sqr()
+ {
+//System.out.println("Into sqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(b);
+ FP4 C=new FP4(c);
+ FP4 D=new FP4(a);
+
+ A.sqr();
+ B.mul(c);
+ B.add(B);
+ B.norm();
+ C.sqr();
+ D.mul(b);
+ D.add(D);
+
+ c.add(a);
+ c.add(b);
+ c.norm();
+ c.sqr();
+
+ a.copy(A);
+
+ A.add(B);
+ A.norm();
+ A.add(C);
+ A.add(D);
+ A.norm();
+
+ A.neg();
+ B.times_i();
+ C.times_i();
+
+ a.add(B);
+
+ b.copy(C); b.add(D);
+ c.add(A);
+//System.out.println("Out of sqr");
+ norm();
+ }
+
+/* FP12 full multiplication this=this*y */
+ public void mul(FP12 y)
+ {
+//System.out.println("Into mul");
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(y.a);
+
+ z0.mul(y.a);
+ z2.mul(y.b);
+
+ t0.add(b);
+ t1.add(y.b);
+
+ t0.norm();
+ t1.norm();
+
+ z1.copy(t0); z1.mul(t1);
+ t0.copy(b); t0.add(c);
+
+ t1.copy(y.b); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ z3.copy(t0); z3.mul(t1);
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ z1.add(t0);
+ //z1.norm();
+ b.copy(z1); b.add(t1);
+
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+t0.norm();
+t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c); t0.mul(y.c);
+ t1.copy(t0); t1.neg();
+
+// z2.norm();
+// z3.norm();
+// b.norm();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ norm();
+//System.out.println("Out of mul");
+ }
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+ public void smul(FP12 y,int type)
+ {
+//System.out.println("Into smul");
+
+ if (type==ECP.D_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(b);
+ FP4 t0=new FP4(0);
+ FP4 t1=new FP4(y.a);
+ z0.mul(y.a);
+ z2.pmul(y.b.real());
+ b.add(a);
+ t1.real().add(y.b.real());
+
+ t1.norm();
+ b.norm();
+ b.mul(t1);
+ z3.add(c);
+ z3.norm();
+ z3.pmul(y.b.real());
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ b.add(t0);
+
+ b.add(t1);
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t0.norm();
+ z3.norm();
+ t0.mul(y.a);
+ c.copy(z2); c.add(t0);
+
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ if (type==ECP.M_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(0);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(0);
+
+ z0.mul(y.a);
+ t0.add(b);
+ t0.norm();
+
+ z1.copy(t0); z1.mul(y.a);
+ t0.copy(b); t0.add(c);
+ t0.norm();
+
+ z3.copy(t0); //z3.mul(y.c);
+ z3.pmul(y.c.getb());
+ z3.times_i();
+
+ t0.copy(z0); t0.neg();
+
+ z1.add(t0);
+ b.copy(z1);
+ z2.copy(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c);
+
+ t0.pmul(y.c.getb());
+ t0.times_i();
+
+ t1.copy(t0); t1.neg();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ norm();
+//System.out.println("Out of smul");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ FP4 f0=new FP4(a);
+ FP4 f1=new FP4(b);
+ FP4 f2=new FP4(a);
+ FP4 f3=new FP4(0);
+
+ norm();
+ f0.sqr();
+ f1.mul(c);
+ f1.times_i();
+ f0.sub(f1);
+ f0.norm();
+
+ f1.copy(c); f1.sqr();
+ f1.times_i();
+ f2.mul(b);
+ f1.sub(f2);
+ f1.norm();
+
+ f2.copy(b); f2.sqr();
+ f3.copy(a); f3.mul(c);
+ f2.sub(f3);
+ f2.norm();
+
+ f3.copy(b); f3.mul(f2);
+ f3.times_i();
+ a.mul(f0);
+ f3.add(a);
+ c.mul(f1);
+ c.times_i();
+
+ f3.add(c);
+ f3.norm();
+ f3.inverse();
+ a.copy(f0); a.mul(f3);
+ b.copy(f1); b.mul(f3);
+ c.copy(f2); c.mul(f3);
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ FP2 f2=new FP2(f);
+ FP2 f3=new FP2(f);
+
+ f2.sqr();
+ f3.mul(f2);
+
+ a.frob(f3);
+ b.frob(f3);
+ c.frob(f3);
+
+ b.pmul(f);
+ c.pmul(f2);
+ }
+
+/* trace function */
+ public FP4 trace()
+ {
+ FP4 t=new FP4(0);
+ t.copy(a);
+ t.imul(3);
+ t.reduce();
+ return t;
+ }
+
+/* convert from byte array to FP12 */
+ public static FP12 fromBytes(byte[] w)
+ {
+ BIG a,b;
+ FP2 c,d;
+ FP4 e,f,g;
+ byte[] t=new byte[BIG.MODBYTES];
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+2*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+3*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ e=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+4*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+5*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+6*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+7*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ f=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+8*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+9*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+10*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+11*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ g=new FP4(c,d);
+
+ return new FP12(e,f,g);
+ }
+
+/* convert this to byte array */
+ public void toBytes(byte[] w)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ a.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i]=t[i];
+ a.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+BIG.MODBYTES]=t[i];
+ a.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+2*BIG.MODBYTES]=t[i];
+ a.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+3*BIG.MODBYTES]=t[i];
+
+ b.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+4*BIG.MODBYTES]=t[i];
+ b.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+5*BIG.MODBYTES]=t[i];
+ b.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+6*BIG.MODBYTES]=t[i];
+ b.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+7*BIG.MODBYTES]=t[i];
+
+ c.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+8*BIG.MODBYTES]=t[i];
+ c.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+9*BIG.MODBYTES]=t[i];
+ c.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+10*BIG.MODBYTES]=t[i];
+ c.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+11*BIG.MODBYTES]=t[i];
+ }
+
+/* convert to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+","+c.toString()+"]");
+ }
+
+/* this=this^e */
+/* Note this is simple square and multiply, so not side-channel safe */
+ public FP12 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ BIG e3=new BIG(e);
+ e3.pmul(3);
+ e3.norm();
+
+ FP12 w=new FP12(this);
+
+ int nb=e3.nbits();
+ for (int i=nb-2;i>=1;i--)
+ {
+ w.usqr();
+ int bt=e3.bit(i)-e.bit(i);
+ if (bt==1)
+ w.mul(this);
+ if (bt==-1)
+ {
+ conj(); w.mul(this); conj();
+ }
+ }
+ w.reduce();
+ return w;
+
+
+/*
+ BIG z=new BIG(e);
+ FP12 r=new FP12(1);
+
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.usqr();
+ }
+ r.reduce();
+ return r; */
+ }
+
+/* constant time powering by small integer of max length bts */
+ public void pinpow(int e,int bts)
+ {
+ int i,b;
+ FP12 [] R=new FP12[2];
+ R[0]=new FP12(1);
+ R[1]=new FP12(this);
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ R[1-b].mul(R[b]);
+ R[b].usqr();
+ }
+ this.copy(R[0]);
+ }
+
+ public FP4 compow(BIG e,BIG r)
+ {
+ FP12 g1=new FP12(0);
+ FP12 g2=new FP12(0);
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.Modulus);
+
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(e);
+ a.mod(m);
+
+ BIG b=new BIG(e);
+ b.div(m);
+
+ g1.copy(this);
+ g2.copy(this);
+
+ FP4 c=g1.trace();
+
+ if (b.iszilch())
+ {
+ c=c.xtr_pow(e);
+ return c;
+ }
+
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+
+ return c;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ FP12 [] g=new FP12[8];
+ FP12 r=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ }
+ g[0]=new FP12(q[0]); // q[0]
+ g[1]=new FP12(g[0]); g[1].mul(q[1]); // q[0].q[1]
+ g[2]=new FP12(g[0]); g[2].mul(q[2]); // q[0].q[2]
+ g[3]=new FP12(g[1]); g[3].mul(q[2]); // q[0].q[1].q[2]
+ g[4]=new FP12(q[0]); g[4].mul(q[3]); // q[0].q[3]
+ g[5]=new FP12(g[1]); g[5].mul(q[3]); // q[0].q[1].q[3]
+ g[6]=new FP12(g[2]); g[6].mul(q[3]); // q[0].q[2].q[3]
+ g[7]=new FP12(g[3]); g[7].mul(q[3]); // q[0].q[1].q[2].q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ p.select(g,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ p.usqr();
+ r.select(g,(int)(2*w[i]+s[i]));
+ p.mul(r);
+ }
+
+ // apply correction
+ r.copy(q[0]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb);
+
+ p.reduce();
+ return p;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+/* Timing attack secure, but not cache attack secure */
+/*
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,m;
+ int[] a=new int[4];
+ FP12 [] g=new FP12[8];
+ FP12 [] s=new FP12[2];
+ FP12 c=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ t[i]=new BIG(u[i]);
+
+ s[0]=new FP12(0);
+ s[1]=new FP12(0);
+
+ g[0]=new FP12(q[0]); s[0].copy(q[1]); s[0].conj(); g[0].mul(s[0]);
+ g[1]=new FP12(g[0]);
+ g[2]=new FP12(g[0]);
+ g[3]=new FP12(g[0]);
+ g[4]=new FP12(q[0]); g[4].mul(q[1]);
+ g[5]=new FP12(g[4]);
+ g[6]=new FP12(g[4]);
+ g[7]=new FP12(g[4]);
+
+ s[1].copy(q[2]); s[0].copy(q[3]); s[0].conj(); s[1].mul(s[0]);
+ s[0].copy(s[1]); s[0].conj(); g[1].mul(s[0]);
+ g[2].mul(s[1]);
+ g[5].mul(s[0]);
+ g[6].mul(s[1]);
+ s[1].copy(q[2]); s[1].mul(q[3]);
+ s[0].copy(s[1]); s[0].conj(); g[0].mul(s[0]);
+ g[3].mul(s[1]);
+ g[4].mul(s[0]);
+ g[7].mul(s[1]);
+
+// if power is even add 1 to power, and add q to correction
+
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ c.mul(q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+ c.conj();
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+ p.copy(g[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ m=w[i]>>7;
+ j=(w[i]^m)-m; // j=abs(w[i])
+ j=(j-1)/2;
+ s[0].copy(g[j]); s[1].copy(g[j]); s[1].conj();
+ p.usqr();
+ p.mul(s[m&1]);
+ }
+ p.mul(c); // apply correction
+ p.reduce();
+ return p;
+ }
+*/
+/*
+ public static void main(String[] args) {
+ BIG p=new BIG(ROM.Modulus);
+ FP2 w0,w1;
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.zero(); b.zero(); a.inc(1); b.inc(2);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(3); b.inc(4);
+ w1=new FP2(a,b);
+ FP4 t0=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(5); b.inc(6);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(7); b.inc(8);
+ w1=new FP2(a,b);
+ FP4 t1=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(9); b.inc(10);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(11); b.inc(12);
+ w1=new FP2(a,b);
+ FP4 t2=new FP4(w0,w1);
+
+ FP12 w=new FP12(t0,t1,t2);
+ FP12 t=new FP12(w);
+
+ System.out.println("w= "+w.toString());
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ w.frob(f);
+ System.out.println("w= "+w.toString());
+
+ w=t.pow(p);
+
+ System.out.println("w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("w= "+w.toString());
+
+ t.copy(w);
+ w.conj();
+ t.inverse();
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)= "+w.toString());
+
+ t.copy(w);
+ w.frob(f);
+ w.frob(f);
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)(p^2+1)= "+w.toString());
+
+ t.copy(w);
+
+ t.inverse();
+ w.conj();
+
+ System.out.println("w= "+w.toString());
+ System.out.println("t= "+t.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/FP2.java b/src/main/java/org/apache/milagro/amcl/BN254/FP2.java
new file mode 100644
index 0000000..d9c4723
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/FP2.java
@@ -0,0 +1,425 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^2 functions */
+
+/* FP2 elements are of the form a+ib, where i is sqrt(-1) */
+
+package org.apache.milagro.amcl.BN254;
+
+public final class FP2 {
+ private final FP a;
+ private final FP b;
+
+/* reduce components mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+
+/* normalise components of w */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+
+/* test this=0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP2 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this=1 ? */
+ public boolean isunity() {
+ FP one=new FP(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test this=x */
+ public boolean equals(FP2 x) {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+
+/* Constructors */
+ public FP2(int c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(FP2 x)
+ {
+ a=new FP(x.a);
+ b=new FP(x.b);
+ }
+
+ public FP2(FP c,FP d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(BIG c,BIG d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(FP c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(BIG c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+/*
+ public BIG geta()
+ {
+ return a.tobig();
+ }
+*/
+/* extract a */
+ public BIG getA()
+ {
+ return a.redc();
+ }
+
+/* extract b */
+ public BIG getB()
+ {
+ return b.redc();
+ }
+
+/* copy this=x */
+ public void copy(FP2 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+
+/* negate this mod Modulus */
+ public void neg()
+ {
+ FP m=new FP(a);
+ FP t=new FP(0);
+
+ m.add(b);
+ m.neg();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ }
+
+/* set to a-ib */
+ public void conj()
+ {
+ b.neg();
+ b.norm();
+ }
+
+/* this+=a */
+ public void add(FP2 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+
+/* this-=a */
+ public void sub(FP2 x)
+ {
+ FP2 m=new FP2(x);
+ m.neg();
+ add(m);
+ }
+
+ public void rsub(FP2 x) // *****
+ {
+ neg();
+ add(x);
+ }
+
+/* this*=s, where s is an FP */
+ public void pmul(FP s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this*=i, where i is an int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+ FP w1=new FP(a);
+ FP w3=new FP(a);
+ FP mb=new FP(b);
+
+ w1.add(b);
+ mb.neg();
+
+ w3.add(a);
+ w3.norm();
+ b.mul(w3);
+
+ a.add(mb);
+
+ w1.norm();
+ a.norm();
+
+ a.mul(w1);
+ }
+
+/* this*=y */
+/* Now uses Lazy reduction */
+ public void mul(FP2 y)
+ {
+ if ((long)(a.XES+b.XES)*(y.a.XES+y.b.XES)>(long)FP.FEXCESS)
+ {
+ if (a.XES>1) a.reduce();
+ if (b.XES>1) b.reduce();
+ }
+
+ DBIG pR=new DBIG(0);
+ BIG C=new BIG(a.x);
+ BIG D=new BIG(y.a.x);
+
+ pR.ucopy(new BIG(ROM.Modulus));
+
+ DBIG A=BIG.mul(a.x,y.a.x);
+ DBIG B=BIG.mul(b.x,y.b.x);
+
+ C.add(b.x); C.norm();
+ D.add(y.b.x); D.norm();
+
+ DBIG E=BIG.mul(C,D);
+ DBIG F=new DBIG(A); F.add(B);
+ B.rsub(pR);
+
+ A.add(B); A.norm();
+ E.sub(F); E.norm();
+
+ a.x.copy(FP.mod(A)); a.XES=3;
+ b.x.copy(FP.mod(E)); b.XES=2;
+ }
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP w1=new FP(b);
+ FP w2=new FP(a);
+ w1.sqr(); w2.sqr(); w1.add(w2);
+ if (w1.jacobi()!=1) { zero(); return false; }
+ w1=w1.sqrt();
+ w2.copy(a); w2.add(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1)
+ {
+ w2.copy(a); w2.sub(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1) { zero(); return false; }
+ }
+ w2=w2.sqrt();
+ a.copy(w2);
+ w2.add(w2);
+ w2.inverse();
+ b.mul(w2);
+ return true;
+ }
+
+/* output to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ norm();
+ FP w1=new FP(a);
+ FP w2=new FP(b);
+
+ w1.sqr();
+ w2.sqr();
+ w1.add(w2);
+ w1.inverse();
+ a.mul(w1);
+ w1.neg();
+ w1.norm();
+ b.mul(w1);
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+/* this*=sqrt(-1) */
+ public void times_i()
+ {
+ FP z=new FP(a);
+ a.copy(b); a.neg();
+ b.copy(z);
+ }
+
+/* w*=(1+sqrt(-1)) */
+/* where X*2-(1+sqrt(-1)) is irreducible for FP4, assumes p=3 mod 8 */
+ public void mul_ip()
+ {
+ FP2 t=new FP2(this);
+ FP z=new FP(a);
+ a.copy(b);
+ a.neg();
+ b.copy(z);
+ add(t);
+ }
+
+ public void div_ip2()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ }
+
+/* w/=(1+sqrt(-1)) */
+ public void div_ip()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ div2();
+ }
+/*
+ public FP2 pow(BIG e)
+ {
+ int bt;
+ FP2 r=new FP2(1);
+ e.norm();
+ norm();
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(this);
+ if (e.iszilch()) break;
+ sqr();
+ }
+
+ r.reduce();
+ return r;
+ }
+
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(27);
+ BIG pp1=new BIG(m);
+ BIG pm1=new BIG(m);
+ BIG a=new BIG(1);
+ BIG b=new BIG(1);
+ FP2 w=new FP2(a,b);
+ FP2 z=new FP2(w);
+
+ byte[] RAW=new byte[100];
+
+ RAND rng=new RAND();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ // for (int i=0;i<100;i++)
+ // {
+ a.randomnum(rng);
+ b.randomnum(rng);
+
+ w=new FP2(a,b);
+ System.out.println("w="+w.toString());
+
+ z=new FP2(w);
+ z.inverse();
+ System.out.println("z="+z.toString());
+
+ z.inverse();
+ if (!z.equals(w)) System.out.println("Error");
+ // }
+
+// System.out.println("m="+m.toString());
+// w.sqr();
+// w.mul(z);
+
+ System.out.println("w="+w.toString());
+
+
+ pp1.inc(1); pp1.norm();
+ pm1.dec(1); pm1.norm();
+ System.out.println("p+1="+pp1.toString());
+ System.out.println("p-1="+pm1.toString());
+ w=w.pow(pp1);
+ w=w.pow(pm1);
+ System.out.println("w="+w.toString());
+ }
+*/
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/FP4.java b/src/main/java/org/apache/milagro/amcl/BN254/FP4.java
new file mode 100644
index 0000000..d535f93
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/FP4.java
@@ -0,0 +1,721 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^4 functions */
+
+/* FP4 elements are of the form a+ib, where i is sqrt(-1+sqrt(-1)) */
+
+package org.apache.milagro.amcl.BN254;
+
+public final class FP4 {
+ private final FP2 a;
+ private final FP2 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP4 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP2 one=new FP2(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP2 real()
+ {
+ return a;
+ }
+
+ public FP2 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP2 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP4 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP4(int c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+
+ public FP4(FP4 x)
+ {
+ a=new FP2(x.a);
+ b=new FP2(x.b);
+ }
+
+ public FP4(FP2 c,FP2 d)
+ {
+ a=new FP2(c);
+ b=new FP2(d);
+ }
+
+ public FP4(FP2 c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+/* copy this=x */
+ public void copy(FP4 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP2 m=new FP2(a);
+ FP2 t=new FP2(0);
+ m.add(b);
+// m.norm();
+ m.neg();
+ // m.norm();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP4 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP4 x)
+ {
+ FP4 m=new FP4(x);
+ m.neg();
+ add(m);
+ }
+
+/* this*=s where s is FP2 */
+ public void pmul(FP2 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this=x-this */
+ public void rsub(FP4 x)
+ {
+ neg();
+ add(x);
+ }
+
+
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.mul_ip();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.mul_ip();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+/* this*=y */
+ public void mul(FP4 y)
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(0);
+ FP2 t4=new FP2(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ // t4.sub(t1);
+ // t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ // b.copy(t4);
+ // b.sub(t2);
+
+ t2.mul_ip();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.mul_ip();
+ t2.norm();
+ t1.sub(t2);
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP2 s=new FP2(b);
+ FP2 t=new FP2(b);
+ s.times_i();
+ t.add(s);
+ // t.norm();
+ b.copy(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ a.conj();
+ b.conj();
+ b.mul(f);
+ }
+
+/* this=this^e */
+ public FP4 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP4 w=new FP4(this);
+ BIG z=new BIG(e);
+ FP4 r=new FP4(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+/* XTR xtr_a function */
+ public void xtr_A(FP4 w,FP4 y,FP4 z)
+ {
+ FP4 r=new FP4(w);
+ FP4 t=new FP4(w);
+ //y.norm();
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP4 w=new FP4(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP4 xtr_pow(BIG n) {
+ FP4 a=new FP4(3);
+ FP4 b=new FP4(this);
+ FP4 c=new FP4(b);
+ c.xtr_D();
+ FP4 t=new FP4(0);
+ FP4 r=new FP4(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP4 xtr_pow2(FP4 ck,FP4 ckml,FP4 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP4 cu=new FP4(ck); // can probably be passed in w/o copying
+ FP4 cv=new FP4(this);
+ FP4 cumv=new FP4(ckml);
+ FP4 cum2v=new FP4(ckm2l);
+ FP4 r=new FP4(0);
+ FP4 t=new FP4(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+ public void div_i()
+ {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip();
+ a.copy(v);
+ b.copy(u);
+ }
+
+ public void div_2i() {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip2();
+ v.add(v); v.norm();
+ a.copy(v);
+ b.copy(u);
+ }
+
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP2 wa=new FP2(a);
+ FP2 ws=new FP2(b);
+ FP2 wt=new FP2(a);
+
+ if (ws.iszilch())
+ {
+ if (wt.sqrt())
+ {
+ a.copy(wt);
+ b.zero();
+ } else {
+ wt.div_ip();
+ wt.sqrt();
+ b.copy(wt);
+ a.zero();
+ }
+ return true;
+ }
+
+ ws.sqr();
+ wa.sqr();
+ ws.mul_ip();
+ ws.norm();
+ wa.sub(ws);
+
+ ws.copy(wa);
+ if (!ws.sqrt()) {
+ return false;
+ }
+
+ wa.copy(wt); wa.add(ws); wa.norm(); wa.div2();
+
+ if (!wa.sqrt()) {
+ wa.copy(wt); wa.sub(ws); wa.norm(); wa.div2();
+ if (!wa.sqrt()) {
+ return false;
+ }
+ }
+ wt.copy(b);
+ ws.copy(wa); ws.add(wa);
+ ws.inverse();
+
+ wt.mul(ws);
+ a.copy(wa);
+ b.copy(wt);
+
+ return true;
+ }
+
+/* this*=s where s is FP */
+ public void qmul(FP s)
+ {
+ a.pmul(s);
+ b.pmul(s);
+ }
+
+
+
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG e=new BIG(12);
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.inc(27); b.inc(45);
+
+ FP2 w0=new FP2(a,b);
+
+ a.zero(); b.zero();
+ a.inc(33); b.inc(54);
+
+ FP2 w1=new FP2(a,b);
+
+
+ FP4 w=new FP4(w0,w1);
+ FP4 t=new FP4(w);
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ System.out.println("w= "+w.toString());
+
+ w=w.pow(m);
+
+ System.out.println("w^p= "+w.toString());
+
+ t.frob(f);
+
+
+ System.out.println("w^p= "+t.toString());
+
+ w=w.pow(m);
+ w=w.pow(m);
+ w=w.pow(m);
+ System.out.println("w^p4= "+w.toString());
+
+
+ System.out.println("Test Inversion");
+
+ w=new FP4(w0,w1);
+
+ w.inverse();
+
+ System.out.println("1/w mod p^4 = "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/(1/w) mod p^4 = "+w.toString());
+
+ FP4 ww=new FP4(w);
+
+ w=w.xtr_pow(e);
+ System.out.println("w^e= "+w.toString());
+
+
+ a.zero(); b.zero();
+ a.inc(37); b.inc(17);
+ w0=new FP2(a,b);
+ a.zero(); b.zero();
+ a.inc(49); b.inc(31);
+ w1=new FP2(a,b);
+
+ FP4 c1=new FP4(w0,w1);
+ FP4 c2=new FP4(w0,w1);
+ FP4 c3=new FP4(w0,w1);
+
+ BIG e1=new BIG(3331);
+ BIG e2=new BIG(3372);
+
+ FP4 cr=w.xtr_pow2(c1,c2,c3,e1,e2);
+
+ System.out.println("c^e= "+cr.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/MPIN.java b/src/main/java/org/apache/milagro/amcl/BN254/MPIN.java
new file mode 100644
index 0000000..dfbeea8
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/MPIN.java
@@ -0,0 +1,823 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* MPIN API Functions */
+
+package org.apache.milagro.amcl.BN254;
+
+import java.util.Date;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public class MPIN
+{
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int PAS=16;
+ public static final int INVALID_POINT=-14;
+ public static final int BAD_PARAMS=-11;
+ public static final int WRONG_ORDER=-18;
+ public static final int BAD_PIN=-19;
+
+/* Configure your PIN here */
+
+ public static final int MAXPIN=10000; /* PIN less than this */
+ public static final int PBLEN=14; /* Number of bits in PIN */
+ public static final int TS=10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+ public static final int TRAP=200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN - approx 2*sqrt(MAXPIN) */
+
+// public static final int HASH_TYPE=SHA256;
+
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,int n,byte[] B,int len)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (n>0) H.process_num(n);
+
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+ byte[] W=new byte[len];
+
+ if (sha>=len)
+ for (int i=0;i<len;i++) W[i]=R[i];
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+len-sha]=R[i];
+ for (int i=0;i<len-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<len;i++) W[i]=0;
+ }
+ return W;
+ }
+
+ /* return time in slots since epoch */
+ public static int today() {
+ Date date=new Date();
+ return (int) (date.getTime()/(1000*60*1440));
+ }
+
+ public static byte[] HASH_ID(int sha,byte[] ID,int len)
+ {
+ return hashit(sha,0,ID,len);
+ }
+
+/* Hash the M-Pin transcript - new */
+
+ public static byte[] HASH_ALL(int sha,byte[] HID,byte[] xID,byte[] xCID,byte[] SEC,byte[] Y,byte[] R,byte[] W,int len)
+ {
+ int i,ilen,tlen=0;
+
+ ilen=HID.length+SEC.length+Y.length+R.length+W.length;
+ if (xCID!=null) ilen+=xCID.length;
+ else ilen+=xID.length;
+
+ byte[] T = new byte[ilen];
+
+ for (i=0;i<HID.length;i++) T[i]=HID[i];
+ tlen+=HID.length;
+ if (xCID!=null)
+ {
+ for (i=0;i<xCID.length;i++) T[i+tlen]=xCID[i];
+ tlen+=xCID.length;
+ }
+ else
+ {
+ for (i=0;i<xID.length;i++) T[i+tlen]=xID[i];
+ tlen+=xID.length;
+ }
+ for (i=0;i<SEC.length;i++) T[i+tlen]=SEC[i];
+ tlen+=SEC.length;
+ for (i=0;i<Y.length;i++) T[i+tlen]=Y[i];
+ tlen+=Y.length;
+ for (i=0;i<R.length;i++) T[i+tlen]=R[i];
+ tlen+=R.length;
+ for (i=0;i<W.length;i++) T[i+tlen]=W[i];
+ tlen+=W.length;
+
+ return hashit(sha,0,T,len);
+ }
+
+/* return time since epoch */
+ public static int GET_TIME() {
+ Date date=new Date();
+ return (int) (date.getTime()/1000);
+ }
+
+ public static byte[] mpin_hash(int sha,FP4 c,ECP U)
+ {
+ byte[] w=new byte[EFS];
+ byte[] t=new byte[6*EFS];
+ byte[] h=null;
+ c.geta().getA().toBytes(w); for (int i=0;i<EFS;i++) t[i]=w[i];
+ c.geta().getB().toBytes(w); for (int i=EFS;i<2*EFS;i++) t[i]=w[i-EFS];
+ c.getb().getA().toBytes(w); for (int i=2*EFS;i<3*EFS;i++) t[i]=w[i-2*EFS];
+ c.getb().getB().toBytes(w); for (int i=3*EFS;i<4*EFS;i++) t[i]=w[i-3*EFS];
+
+ U.getX().toBytes(w); for (int i=4*EFS;i<5*EFS;i++) t[i]=w[i-4*EFS];
+ U.getY().toBytes(w); for (int i=5*EFS;i<6*EFS;i++) t[i]=w[i-5*EFS];
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (h==null) return null;
+ byte[] R=new byte[ECP.AESKEY];
+ for (int i=0;i<ECP.AESKEY;i++) R[i]=h[i];
+ return R;
+ }
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+ public static ECP map(BIG u,int cb)
+ {
+ ECP P;
+ BIG x=new BIG(u);
+ BIG p=new BIG(ROM.Modulus);
+ x.mod(p);
+ while (true)
+ {
+ P=new ECP(x,cb);
+ if (!P.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+ return P;
+ }
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+ public static int unmap(BIG u,ECP P)
+ {
+ int s=P.getS();
+ ECP R;
+ int r=0;
+ BIG x=P.getX();
+ u.copy(x);
+ while (true)
+ {
+ u.dec(1); u.norm();
+ r++;
+ R=new ECP(u,s);
+ if (!R.is_infinity()) break;
+ }
+ return r;
+ }
+
+
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+ public static int ENCODING(RAND rng,byte[] E)
+ {
+ int rn,m,su,sv;
+ byte[] T=new byte[EFS];
+
+ for (int i=0;i<EFS;i++) T[i]=E[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=E[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ ECP P=new ECP(u,v);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG p=new BIG(ROM.Modulus);
+ u=BIG.randomnum(p,rng);
+
+ su=rng.getByte(); /*if (su<0) su=-su;*/ su%=2;
+
+ ECP W=map(u,su);
+ P.sub(W); //P.affine();
+ sv=P.getS();
+ rn=unmap(v,P);
+ m=rng.getByte(); /*if (m<0) m=-m;*/ m%=rn;
+ v.inc(m+1);
+ E[0]=(byte)(su+2*sv);
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+ public static int DECODING(byte[] D)
+ {
+ int su,sv;
+ byte[] T=new byte[EFS];
+
+ if ((D[0]&0x04)!=0) return INVALID_POINT;
+
+ for (int i=0;i<EFS;i++) T[i]=D[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=D[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ su=D[0]&1;
+ sv=(D[0]>>1)&1;
+ ECP W=map(u,su);
+ ECP P=map(v,sv);
+ P.add(W); //P.affine();
+ u=P.getX();
+ v=P.getY();
+ D[0]=0x04;
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+/* R=R1+R2 in group G1 */
+ public static int RECOMBINE_G1(byte[] R1,byte[] R2,byte[] R)
+ {
+ ECP P=ECP.fromBytes(R1);
+ ECP Q=ECP.fromBytes(R2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(R,false);
+ return 0;
+ }
+
+/* W=W1+W2 in group G2 */
+ public static int RECOMBINE_G2(byte[] W1,byte[] W2,byte[] W)
+ {
+ ECP2 P=ECP2.fromBytes(W1);
+ ECP2 Q=ECP2.fromBytes(W2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(W);
+ return 0;
+ }
+
+/* create random secret S */
+ public static int RANDOM_GENERATE(RAND rng,byte[] S)
+ {
+ BIG s;
+ BIG r=new BIG(ROM.CURVE_Order);
+ s=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+ return 0;
+ }
+
+/* Extract PIN from TOKEN for identity CID */
+ public static int EXTRACT_PIN(int sha,byte[] CID,int pin,byte[] TOKEN)
+ {
+ ECP P=ECP.fromBytes(TOKEN);
+ if (P.is_infinity()) return INVALID_POINT;
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R=ECP.mapit(h);
+
+
+ pin%=MAXPIN;
+
+ R=R.pinmul(pin,PBLEN);
+ P.sub(R); //P.affine();
+
+ P.toBytes(TOKEN,false);
+
+ return 0;
+ }
+
+/* Implement step 2 on client side of MPin protocol */
+ public static int CLIENT_2(byte[] X,byte[] Y,byte[] SEC)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ ECP P=ECP.fromBytes(SEC);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG px=BIG.fromBytes(X);
+ BIG py=BIG.fromBytes(Y);
+ px.add(py);
+ px.mod(r);
+ // px.rsub(r);
+
+ P=PAIR.G1mul(P,px);
+ P.neg();
+ P.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Implement step 1 on client side of MPin protocol */
+ public static int CLIENT_1(int sha,int date,byte[] CLIENT_ID,RAND rng,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG x;
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P,T,W;
+ BIG px;
+// byte[] t=new byte[EFS];
+
+ byte[] h=hashit(sha,0,CLIENT_ID,EFS);
+ P=ECP.mapit(h);
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ pin%=MAXPIN;
+ W=P.pinmul(pin,PBLEN);
+ T.add(W);
+ if (date!=0)
+ {
+ W=ECP.fromBytes(PERMIT);
+ if (W.is_infinity()) return INVALID_POINT;
+ T.add(W);
+ h=hashit(sha,date,h,EFS);
+ W=ECP.mapit(h);
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ W=PAIR.G1mul(W,x);
+ P.add(W);
+ //P.affine();
+ }
+ else
+ {
+ P.add(W); //P.affine();
+ P=PAIR.G1mul(P,x);
+ }
+ if (xCID!=null) P.toBytes(xCID,false);
+ }
+ else
+ {
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ }
+ }
+
+ //T.affine();
+ T.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+ public static int GET_SERVER_SECRET(byte[] S,byte[] SST)
+ {
+ ECP2 Q=ECP2.generator();
+ BIG s=BIG.fromBytes(S);
+ Q=PAIR.G2mul(Q,s);
+ Q.toBytes(SST);
+ return 0;
+ }
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+ public static int GET_G1_MULTIPLE(RAND rng, int type,byte[] X,byte[] G,byte[] W)
+ {
+ BIG x;
+ BIG r=new BIG(ROM.CURVE_Order);
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P;
+ if (type==0)
+ {
+ P=ECP.fromBytes(G);
+ if (P.is_infinity()) return INVALID_POINT;
+ }
+ else
+ P=ECP.mapit(G);
+
+ PAIR.G1mul(P,x).toBytes(W,false);
+ return 0;
+ }
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+ public static int GET_CLIENT_SECRET(byte[] S,byte[] CID,byte[] CST)
+ {
+ return GET_G1_MULTIPLE(null,1,S,CID,CST);
+ }
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+ public static int GET_CLIENT_PERMIT(int sha,int date,byte[] S,byte[] CID,byte[] CTT)
+ {
+ byte[] h=hashit(sha,date,CID,EFS);
+ ECP P=ECP.mapit(h);
+
+ BIG s=BIG.fromBytes(S);
+ ECP OP=PAIR.G1mul(P,s);
+
+ OP.toBytes(CTT,false);
+ return 0;
+ }
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+ public static void SERVER_1(int sha,int date,byte[] CID,byte[] HID,byte[] HTID)
+ {
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R,P=ECP.mapit(h);
+
+ P.toBytes(HID,false); // new
+ if (date!=0)
+ {
+ // if (HID!=null) P.toBytes(HID);
+ h=hashit(sha,date,h,EFS);
+ R=ECP.mapit(h);
+ P.add(R); //P.affine();
+ P.toBytes(HTID,false);
+ }
+ // else P.toBytes(HID,false);
+ }
+
+/* Implement step 2 of MPin protocol on server side */
+ public static int SERVER_2(int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] mSEC,byte[] E,byte[] F)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ ECP2 Q=ECP2.generator();
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+
+ ECP R;
+ if (date!=0)
+ R=ECP.fromBytes(xCID);
+ else
+ {
+ if (xID==null) return BAD_PARAMS;
+ R=ECP.fromBytes(xID);
+ }
+ if (R.is_infinity()) return INVALID_POINT;
+
+ BIG y=BIG.fromBytes(Y);
+ ECP P;
+ if (date!=0) P=ECP.fromBytes(HTID);
+ else
+ {
+ if (HID==null) return BAD_PARAMS;
+ P=ECP.fromBytes(HID);
+ }
+
+ if (P.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ R=ECP.fromBytes(mSEC);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ FP12 g;
+
+ g=PAIR.ate2(Q,R,sQ,P);
+ g=PAIR.fexp(g);
+
+ if (!g.isunity())
+ {
+ if (HID!=null && xID!=null && E!=null && F!=null)
+ {
+ g.toBytes(E);
+ if (date!=0)
+ {
+ P=ECP.fromBytes(HID);
+ if (P.is_infinity()) return INVALID_POINT;
+ R=ECP.fromBytes(xID);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ }
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(F);
+ }
+ return BAD_PIN;
+ }
+
+ return 0;
+ }
+
+/* Pollards kangaroos used to return PIN error */
+ public static int KANGAROO(byte[] E,byte[] F)
+ {
+ FP12 ge=FP12.fromBytes(E);
+ FP12 gf=FP12.fromBytes(F);
+ int[] distance = new int[TS];
+ FP12 t=new FP12(gf);
+ FP12[] table=new FP12[TS];
+ int i,j,m,s,dn,dm,res,steps;
+
+ s=1;
+ for (m=0;m<TS;m++)
+ {
+ distance[m]=s;
+ table[m]=new FP12(t);
+ s*=2;
+ t.usqr();
+ }
+ t.one();
+ dn=0;
+ for (j=0;j<TRAP;j++)
+ {
+ i=t.geta().geta().getA().lastbits(20)%TS;
+ t.mul(table[i]);
+ dn+=distance[i];
+ }
+ gf.copy(t); gf.conj();
+ steps=0; dm=0;
+ res=0;
+ while (dm-dn<MAXPIN)
+ {
+ steps++;
+ if (steps>4*TRAP) break;
+ i=ge.geta().geta().getA().lastbits(20)%TS;
+ ge.mul(table[i]);
+ dm+=distance[i];
+ if (ge.equals(t))
+ {
+ res=dm-dn;
+ break;
+ }
+ if (ge.equals(gf))
+ {
+ res=dn-dm;
+ break;
+ }
+
+ }
+ if (steps>4*TRAP || dm-dn>=MAXPIN) {res=0; } // Trap Failed - probable invalid token
+ return res;
+ }
+
+/* Functions to support M-Pin Full */
+
+ public static int PRECOMPUTE(byte[] TOKEN,byte[] CID,byte[] G1,byte[] G2)
+ {
+ ECP P,T;
+ FP12 g;
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ P=ECP.mapit(CID);
+
+ ECP2 Q=ECP2.generator();
+
+ g=PAIR.ate(Q,T);
+ g=PAIR.fexp(g);
+ g.toBytes(G1);
+
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(G2);
+
+ return 0;
+ }
+
+
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+ public static int CLIENT_KEY(int sha,byte[] G1,byte[] G2,int pin,byte[] R,byte[] X,byte[] H,byte[] wCID,byte[] CK)
+ {
+ byte[] t;
+
+ FP12 g1=FP12.fromBytes(G1);
+ FP12 g2=FP12.fromBytes(G2);
+ BIG z=BIG.fromBytes(R);
+ BIG x=BIG.fromBytes(X);
+ BIG h=BIG.fromBytes(H);
+
+ ECP W=ECP.fromBytes(wCID);
+ if (W.is_infinity()) return INVALID_POINT;
+
+ W=PAIR.G1mul(W,x);
+
+// FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG r=new BIG(ROM.CURVE_Order);
+// BIG q=new BIG(ROM.Modulus);
+
+ z.add(h); //new
+ z.mod(r);
+
+ g2.pinpow(pin,PBLEN);
+ g1.mul(g2);
+
+ FP4 c=g1.compow(z,r);
+/*
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(z);
+ a.mod(m);
+
+ BIG b=new BIG(z);
+ b.div(m);
+
+
+ FP4 c=g1.trace();
+ g2.copy(g1);
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+*/
+ t=mpin_hash(sha,c,W);
+
+ for (int i=0;i<ECP.AESKEY;i++) CK[i]=t[i];
+
+ return 0;
+ }
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+ public static int SERVER_KEY(int sha,byte[] Z,byte[] SST,byte[] W,byte[] H,byte[] HID,byte[] xID,byte[] xCID,byte[] SK)
+ {
+ byte[] t;
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+ ECP R=ECP.fromBytes(Z);
+ if (R.is_infinity()) return INVALID_POINT;
+ ECP A=ECP.fromBytes(HID);
+ if (A.is_infinity()) return INVALID_POINT;
+
+ ECP U;
+ if (xCID!=null)
+ U=ECP.fromBytes(xCID);
+ else
+ U=ECP.fromBytes(xID);
+ if (U.is_infinity()) return INVALID_POINT;
+
+ BIG w=BIG.fromBytes(W);
+ BIG h=BIG.fromBytes(H);
+ A=PAIR.G1mul(A,h); // new
+ R.add(A); //R.affine();
+
+ U=PAIR.G1mul(U,w);
+ FP12 g=PAIR.ate(sQ,R);
+ g=PAIR.fexp(g);
+
+ FP4 c=g.trace();
+
+ t=mpin_hash(sha,c,U);
+
+ for (int i=0;i<ECP.AESKEY;i++) SK[i]=t[i];
+
+ return 0;
+ }
+
+/* Generate Y = H(epoch, xCID/xID) */
+ public static void GET_Y(int sha,int TimeValue,byte[] xCID,byte[] Y)
+ {
+ byte[] h = hashit(sha,TimeValue,xCID,EFS);
+ BIG y = BIG.fromBytes(h);
+ BIG q=new BIG(ROM.CURVE_Order);
+ y.mod(q);
+ //if (ROM.AES_S>0)
+ //{
+ // y.mod2m(2*ROM.AES_S);
+ //}
+ y.toBytes(Y);
+ }
+
+/* One pass MPIN Client */
+ public static int CLIENT(int sha,int date,byte[] CLIENT_ID,RAND RNG,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT, int TimeValue, byte[] Y)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ rtn = CLIENT_1(sha,date,CLIENT_ID,RNG,X,pin,TOKEN,SEC,xID,xCID,PERMIT);
+ if (rtn != 0)
+ return rtn;
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+
+/* One pass MPIN Server */
+ public static int SERVER(int sha,int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] SEC,byte[] E,byte[] F,byte[] CID, int TimeValue)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ SERVER_1(sha,date,CID,HID,HTID);
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = SERVER_2(date,HID,HTID,Y,SST,xID,xCID,SEC,E,F);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/PAIR.java b/src/main/java/org/apache/milagro/amcl/BN254/PAIR.java
new file mode 100644
index 0000000..a9619e6
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/PAIR.java
@@ -0,0 +1,817 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BN Curve Pairing functions */
+
+package org.apache.milagro.amcl.BN254;
+
+public final class PAIR {
+
+ public static final boolean USE_GLV =true;
+ public static final boolean USE_GS_G2 =true;
+ public static final boolean USE_GS_GT =true;
+ public static final boolean GT_STRONG=false;
+
+
+/* Line function */
+ public static FP12 line(ECP2 A,ECP2 B,FP Qx,FP Qy)
+ {
+//System.out.println("Into line");
+ FP4 a,b,c; // Edits here
+// c=new FP4(0);
+ if (A==B)
+ { // Doubling
+ FP2 XX=new FP2(A.getx()); //X
+ FP2 YY=new FP2(A.gety()); //Y
+ FP2 ZZ=new FP2(A.getz()); //Z
+ FP2 YZ=new FP2(YY); //Y
+ YZ.mul(ZZ); //YZ
+ XX.sqr(); //X^2
+ YY.sqr(); //Y^2
+ ZZ.sqr(); //Z^2
+
+ YZ.imul(4);
+ YZ.neg(); YZ.norm(); //-2YZ
+ YZ.pmul(Qy); //-2YZ.Ys
+
+ XX.imul(6); //3X^2
+ XX.pmul(Qx); //3X^2.Xs
+
+ int sb=3*ROM.CURVE_B_I;
+ ZZ.imul(sb);
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ ZZ.div_ip2();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ ZZ.mul_ip();
+ ZZ.add(ZZ);
+ YZ.mul_ip();
+ YZ.norm();
+ }
+
+ ZZ.norm(); // 3b.Z^2
+
+ YY.add(YY);
+ ZZ.sub(YY); ZZ.norm(); // 3b.Z^2-Y^2
+
+ a=new FP4(YZ,ZZ); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(XX); // L(0,1) | L(0,0) | L(1,0)
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(XX); c.times_i();
+ }
+ A.dbl();
+ }
+ else
+ { // Addition - assume B is affine
+
+ FP2 X1=new FP2(A.getx()); // X1
+ FP2 Y1=new FP2(A.gety()); // Y1
+ FP2 T1=new FP2(A.getz()); // Z1
+ FP2 T2=new FP2(A.getz()); // Z1
+
+ T1.mul(B.gety()); // T1=Z1.Y2
+ T2.mul(B.getx()); // T2=Z1.X2
+
+ X1.sub(T2); X1.norm(); // X1=X1-Z1.X2
+ Y1.sub(T1); Y1.norm(); // Y1=Y1-Z1.Y2
+
+ T1.copy(X1); // T1=X1-Z1.X2
+ X1.pmul(Qy); // X1=(X1-Z1.X2).Ys
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X1.mul_ip();
+ X1.norm();
+ }
+
+ T1.mul(B.gety()); // T1=(X1-Z1.X2).Y2
+
+ T2.copy(Y1); // T2=Y1-Z1.Y2
+ T2.mul(B.getx()); // T2=(Y1-Z1.Y2).X2
+ T2.sub(T1); T2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+ Y1.pmul(Qx); Y1.neg(); Y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+ a=new FP4(X1,T2); // (X1-Z1.X2).Ys | (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2 | - (Y1-Z1.Y2).Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(Y1);
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(Y1); c.times_i();
+ }
+ A.add(B);
+ }
+//System.out.println("Out of line");
+ return new FP12(a,b,c);
+ }
+
+/* Optimal R-ate pairing */
+ public static FP12 ate(ECP2 P1,ECP Q1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+
+ ECP2 A=new ECP2();
+ FP12 r=new FP12(1);
+ A.copy(P);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ //r.conj();
+ A.neg();
+ }
+ K.copy(P);
+ K.frob(f);
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+ public static FP12 ate2(ECP2 P1,ECP Q1,ECP2 R1,ECP S1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ ECP2 R=new ECP2(R1);
+ ECP S=new ECP(S1);
+
+ R.affine();
+ S.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+ FP Sx=new FP(S.getx());
+ FP Sy=new FP(S.gety());
+
+ ECP2 A=new ECP2();
+ ECP2 B=new ECP2();
+ FP12 r=new FP12(1);
+
+ A.copy(P);
+ B.copy(R);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+ ECP2 MR=new ECP2();
+ MR.copy(R); MR.neg();
+
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ lv=line(B,B,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ lv=line(B,R,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ //R.neg();
+ lv=line(B,MR,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //R.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ // r.conj();
+ A.neg();
+ B.neg();
+ }
+
+ K.copy(P);
+ K.frob(f);
+
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.copy(R);
+ K.frob(f);
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+ public static FP12 fexp(FP12 m)
+ {
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ FP12 r=new FP12(m);
+
+/* Easy part of final exp */
+ FP12 lv=new FP12(r);
+ lv.inverse();
+ r.conj();
+
+ r.mul(lv);
+ lv.copy(r);
+ r.frob(f);
+ r.frob(f);
+ r.mul(lv);
+/* Hard part of final exp */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ FP12 x0,x1,x2,x3,x4,x5;
+ lv.copy(r);
+ lv.frob(f);
+ x0=new FP12(lv);
+ x0.frob(f);
+ lv.mul(r);
+ x0.mul(lv);
+ x0.frob(f);
+ x1=new FP12(r);
+ x1.conj();
+ x4=r.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x4.conj();
+ }
+
+ x3=new FP12(x4);
+ x3.frob(f);
+
+ x2=x4.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x2.conj();
+ }
+ x5=new FP12(x2); x5.conj();
+ lv=x2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ lv.conj();
+ }
+ x2.frob(f);
+ r.copy(x2); r.conj();
+
+ x4.mul(r);
+ x2.frob(f);
+
+ r.copy(lv);
+ r.frob(f);
+ lv.mul(r);
+
+ lv.usqr();
+ lv.mul(x4);
+ lv.mul(x5);
+ r.copy(x3);
+ r.mul(x5);
+ r.mul(lv);
+ lv.mul(x2);
+ r.usqr();
+ r.mul(lv);
+ r.usqr();
+ lv.copy(r);
+ lv.mul(x1);
+ r.mul(x0);
+ lv.usqr();
+ r.mul(lv);
+ r.reduce();
+ }
+ else
+ {
+
+ FP12 y0,y1,y2,y3;
+// Ghamman & Fouotsa Method
+ y0=new FP12(r); y0.usqr();
+ y1=y0.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y1.conj();
+ }
+ x.fshr(1); y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+
+ x.fshl(1);
+ y3=new FP12(r); y3.conj();
+ y1.mul(y3);
+
+ y1.conj();
+ y1.mul(y2);
+
+ y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y3=y2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y3.conj();
+ }
+ y1.conj();
+ y3.mul(y1);
+
+ y1.conj();
+ y1.frob(f); y1.frob(f); y1.frob(f);
+ y2.frob(f); y2.frob(f);
+ y1.mul(y2);
+
+ y2=y3.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y2.mul(y0);
+ y2.mul(r);
+
+ y1.mul(y2);
+ y2.copy(y3); y2.frob(f);
+ y1.mul(y2);
+ r.copy(y1);
+ r.reduce();
+ }
+
+ return r;
+ }
+
+/* GLV method */
+ public static BIG[] glv(BIG e)
+ {
+ BIG[] u=new BIG[2];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+
+ BIG[] v=new BIG[2];
+ for (i=0;i<2;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_W[i])); // why not just t=new BIG(ROM.CURVE_W[i]);
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<2;i++)
+ for (j=0;j<2;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_SB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ { // -(x^2).P = (Beta.x,y)
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG x2=BIG.smul(x,x);
+ u[0]=new BIG(e);
+ u[0].mod(x2);
+ u[1]=new BIG(e);
+ u[1].div(x2);
+ u[1].rsub(q);
+ }
+ return u;
+ }
+
+/* Galbraith & Scott Method */
+ public static BIG[] gs(BIG e)
+ {
+ BIG[] u=new BIG[4];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] v=new BIG[4];
+ for (i=0;i<4;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_WB[i]));
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<4;i++)
+ for (j=0;j<4;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_BB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ {
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG w=new BIG(e);
+ for (int i=0;i<3;i++)
+ {
+ u[i]=new BIG(w);
+ u[i].mod(x);
+ w.div(x);
+ }
+ u[3]=new BIG(w);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ u[1].copy(BIG.modneg(u[1],q));
+ u[3].copy(BIG.modneg(u[3],q));
+ }
+ }
+ return u;
+ }
+
+/* Multiply P by e in group G1 */
+ public static ECP G1mul(ECP P,BIG e)
+ {
+ ECP R;
+ if (USE_GLV)
+ {
+ //P.affine();
+ R=new ECP();
+ R.copy(P);
+ int i,np,nn;
+ ECP Q=new ECP();
+ Q.copy(P); Q.affine();
+ BIG q=new BIG(ROM.CURVE_Order);
+ FP cru=new FP(new BIG(ROM.CURVE_Cru));
+ BIG t=new BIG(0);
+ BIG[] u=glv(e);
+ Q.getx().mul(cru);
+
+ np=u[0].nbits();
+ t.copy(BIG.modneg(u[0],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[0].copy(t);
+ R.neg();
+ }
+
+ np=u[1].nbits();
+ t.copy(BIG.modneg(u[1],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[1].copy(t);
+ Q.neg();
+ }
+ u[0].norm();
+ u[1].norm();
+ R=R.mul2(u[0],Q,u[1]);
+
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* Multiply P by e in group G2 */
+ public static ECP2 G2mul(ECP2 P,BIG e)
+ {
+ ECP2 R;
+ if (USE_GS_G2)
+ {
+ ECP2[] Q=new ECP2[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] u=gs(e);
+
+ BIG t=new BIG(0);
+ int i,np,nn;
+ //P.affine();
+
+ Q[0]=new ECP2(); Q[0].copy(P);
+ for (i=1;i<4;i++)
+ {
+ Q[i]=new ECP2(); Q[i].copy(Q[i-1]);
+ Q[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ Q[i].neg();
+ }
+ u[i].norm();
+ //Q[i].affine();
+ }
+
+ R=ECP2.mul4(Q,u);
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */
+ public static FP12 GTpow(FP12 d,BIG e)
+ {
+ FP12 r;
+ if (USE_GS_GT)
+ {
+ FP12[] g=new FP12[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG t=new BIG(0);
+ int i,np,nn;
+ BIG[] u=gs(e);
+
+ g[0]=new FP12(d);
+ for (i=1;i<4;i++)
+ {
+ g[i]=new FP12(0); g[i].copy(g[i-1]);
+ g[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ g[i].conj();
+ }
+ u[i].norm();
+ }
+ r=FP12.pow4(g,u);
+ }
+ else
+ {
+ r=d.pow(e);
+ }
+ return r;
+ }
+
+/* test group membership - no longer needed */
+/* with GT-Strong curve, now only check that m!=1, conj(m)*m==1, and m.m^{p^4}=m^{p^2} */
+/*
+ public static boolean GTmember(FP12 m)
+ {
+ if (m.isunity()) return false;
+ FP12 r=new FP12(m);
+ r.conj();
+ r.mul(m);
+ if (!r.isunity()) return false;
+
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ r.copy(m); r.frob(f); r.frob(f);
+ FP12 w=new FP12(r); w.frob(f); w.frob(f);
+ w.mul(m);
+ if (!ROM.GT_STRONG)
+ {
+ if (!w.equals(r)) return false;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ r.copy(m); w=r.pow(x); w=w.pow(x);
+ r.copy(w); r.sqr(); r.mul(w); r.sqr();
+ w.copy(m); w.frob(f);
+ }
+ return w.equals(r);
+ }
+*/
+/*
+ public static void main(String[] args) {
+ ECP Q=new ECP(new BIG(ROM.CURVE_Gx),new BIG(ROM.CURVE_Gy));
+ ECP2 P=new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG xa=new BIG(ROM.CURVE_Pxa);
+
+ System.out.println("P= "+P.toString());
+ System.out.println("Q= "+Q.toString());
+
+ BIG m=new BIG(17);
+
+ FP12 e=ate(P,Q);
+ System.out.println("\ne= "+e.toString());
+
+ e=fexp(e);
+
+ for (int i=1;i<1000;i++)
+ {
+ e=ate(P,Q);
+ e=fexp(e);
+ }
+ // e=GTpow(e,m);
+
+ System.out.println("\ne= "+e.toString());
+
+ BIG [] GLV=glv(r);
+
+ System.out.println("GLV[0]= "+GLV[0].toString());
+ System.out.println("GLV[0]= "+GLV[1].toString());
+
+ ECP G=new ECP(); G.copy(Q);
+ ECP2 R=new ECP2(); R.copy(P);
+
+
+ e=ate(R,Q);
+ e=fexp(e);
+
+ e=GTpow(e,xa);
+ System.out.println("\ne= "+e.toString());
+
+
+ R=G2mul(R,xa);
+ e=ate(R,G);
+ e=fexp(e);
+
+ System.out.println("\ne= "+e.toString());
+
+ G=G1mul(G,xa);
+ e=ate(P,G);
+ e=fexp(e);
+ System.out.println("\ne= "+e.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BN254/ROM.java b/src/main/java/org/apache/milagro/amcl/BN254/ROM.java
new file mode 100644
index 0000000..f1927c1
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254/ROM.java
@@ -0,0 +1,55 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.BN254;
+
+public class ROM
+{
+
+ public static final long[] Modulus= {0x13L,0x13A7L,0x80000000086121L,0x40000001BA344DL,0x25236482L};
+ public static final long[] R2modp= {0x2F2A96FF5E7E39L,0x64E8642B96F13CL,0x9926F7B00C7146L,0x8321E7B4DACD24L,0x1D127A2EL};
+ public static final long MConst= 0x435E50D79435E5L;
+
+ public static final int CURVE_Cof_I= 1;
+ public static final int CURVE_A= 0;
+ public static final int CURVE_B_I= 2;
+ public static final long[] CURVE_B= {0x2L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Order= {0xDL,0x800000000010A1L,0x8000000007FF9FL,0x40000001BA344DL,0x25236482L};
+ public static final long[] CURVE_Gx= {0x12L,0x13A7L,0x80000000086121L,0x40000001BA344DL,0x25236482L};
+ public static final long[] CURVE_Gy= {0x1L,0x0L,0x0L,0x0L,0x0L};
+
+ public static final long[] CURVE_Bnx= {0x80000000000001L,0x40L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Cru= {0x80000000000007L,0x6CDL,0x40000000024909L,0x49B362L,0x0L};
+ public static final long[] Fra= {0x7DE6C06F2A6DE9L,0x74924D3F77C2E1L,0x50A846953F8509L,0x212E7C8CB6499BL,0x1B377619L};
+ public static final long[] Frb= {0x82193F90D5922AL,0x8B6DB2C08850C5L,0x2F57B96AC8DC17L,0x1ED1837503EAB2L,0x9EBEE69L};
+ public static final long[] CURVE_Pxa= {0xEE4224C803FB2BL,0x8BBB4898BF0D91L,0x7E8C61EDB6A464L,0x519EB62FEB8D8CL,0x61A10BBL};
+ public static final long[] CURVE_Pxb= {0x8C34C1E7D54CF3L,0x746BAE3784B70DL,0x8C5982AA5B1F4DL,0xBA737833310AA7L,0x516AAF9L};
+ public static final long[] CURVE_Pya= {0xF0E07891CD2B9AL,0xAE6BDBE09BD19L,0x96698C822329BDL,0x6BAF93439A90E0L,0x21897A0L};
+ public static final long[] CURVE_Pyb= {0x2D1AEC6B3ACE9BL,0x6FFD739C9578AL,0x56F5F38D37B090L,0x7C8B15268F6D44L,0xEBB2B0EL};
+ public static final long[][] CURVE_W= {{0x3L,0x80000000000204L,0x6181L,0x0L,0x0L},{0x1L,0x81L,0x0L,0x0L,0x0L}};
+ public static final long[][][] CURVE_SB= {{{0x4L,0x80000000000285L,0x6181L,0x0L,0x0L},{0x1L,0x81L,0x0L,0x0L,0x0L}},{{0x1L,0x81L,0x0L,0x0L,0x0L},{0xAL,0xE9DL,0x80000000079E1EL,0x40000001BA344DL,0x25236482L}}};
+ public static final long[][] CURVE_WB= {{0x80000000000000L,0x80000000000040L,0x2080L,0x0L,0x0L},{0x80000000000005L,0x54AL,0x8000000001C707L,0x312241L,0x0L},{0x80000000000003L,0x800000000002C5L,0xC000000000E383L,0x189120L,0x0L},{0x80000000000001L,0x800000000000C1L,0x2080L,0x0L,0x0L}};
+ public static final long[][][] CURVE_BB= {{{0x8000000000000DL,0x80000000001060L,0x8000000007FF9FL,0x40000001BA344DL,0x25236482L},{0x8000000000000CL,0x80000000001060L,0x8000000007FF9FL,0x40000001BA344DL,0x25236482L},{0x8000000000000CL,0x80000000001060L,0x8000000007FF9FL,0x40000001BA344DL,0x25236482L},{0x2L,0x81L,0x0L,0x0L,0x0L}},{{0x1L,0x81L,0x0L,0x0L,0x0L},{0x8000000000000CL,0x80000000001060L,0x8000000007FF9FL,0x40000001BA344DL,0x25236482L},{0x8000000000000DL,0x80000000001060L,0x8000000007FF9FL,0x40000001BA344DL,0x25236482L},{0x8000000000000CL,0x80000000001060L,0x8000000007FF9FL,0x40000001BA344DL,0x25236482L}},{{0x2L,0x81L,0x0L,0x0L,0x0L},{0x1L,0x81L,0x0L,0x0L,0x0L},{0x1L,0x81L,0x0L,0x0L,0x0L},{0x1L,0x81L,0x0L,0x0L,0x0L}},{{0x80000000000002L,0x40L,0x0L,0x0L,0x0L},{0x2L,0x102L,0x0L,0x0L,0x0L},{0xAL,0x80000000001020L,0x8000000007FF9FL,0x40000001BA344DL,0x25236482L},{0x80000000000002L,0x40L,0x0L,0x0L,0x0L}}};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/BIG.java b/src/main/java/org/apache/milagro/amcl/BN254CX/BIG.java
new file mode 100644
index 0000000..ff8ac77
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.BN254CX;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=32; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/DBIG.java b/src/main/java/org/apache/milagro/amcl/BN254CX/DBIG.java
new file mode 100644
index 0000000..efc3777
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.BN254CX;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/ECDH.java b/src/main/java/org/apache/milagro/amcl/BN254CX/ECDH.java
new file mode 100644
index 0000000..721eaf9
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.BN254CX;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/ECP.java b/src/main/java/org/apache/milagro/amcl/BN254CX/ECP.java
new file mode 100644
index 0000000..660b808
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.BN254CX;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=BN;
+ public static final int SEXTIC_TWIST=D_TYPE;
+ public static final int SIGN_OF_X=NEGATIVEX;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/ECP2.java b/src/main/java/org/apache/milagro/amcl/BN254CX/ECP2.java
new file mode 100644
index 0000000..cb8f663
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/ECP2.java
@@ -0,0 +1,796 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Weierstrass elliptic curve functions over FP2 */
+
+package org.apache.milagro.amcl.BN254CX;
+
+public final class ECP2 {
+ private FP2 x;
+ private FP2 y;
+ private FP2 z;
+// private boolean INF;
+
+/* Constructor - set this=O */
+ public ECP2() {
+// INF=true;
+ x=new FP2(0);
+ y=new FP2(1);
+ z=new FP2(0);
+ }
+
+ public ECP2(ECP2 e) {
+ this.x = new FP2(e.x);
+ this.y = new FP2(e.y);
+ this.z = new FP2(e.z);
+ }
+
+/* Test this=O? */
+ public boolean is_infinity() {
+// if (INF) return true; //******
+ return (x.iszilch() && z.iszilch());
+ }
+/* copy this=P */
+ public void copy(ECP2 P)
+ {
+ x.copy(P.x);
+ y.copy(P.y);
+ z.copy(P.z);
+// INF=P.INF;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ y.one();
+ z.zero();
+ }
+
+/* Conditional move of Q to P dependant on d */
+ public void cmove(ECP2 Q,int d)
+ {
+ x.cmove(Q.x,d);
+ y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ }
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(ECP2 W[],int b)
+ {
+ ECP2 MP=new ECP2();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test if P == Q */
+ public boolean equals(ECP2 Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+
+ FP2 a=new FP2(x); // *****
+ FP2 b=new FP2(Q.x);
+ a.mul(Q.z);
+ b.mul(z);
+ if (!a.equals(b)) return false;
+
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+
+ return true;
+ }
+/* set this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ y.norm();
+ y.neg(); y.norm();
+ return;
+ }
+/* set to Affine - (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return;
+ FP2 one=new FP2(1);
+ if (z.equals(one))
+ {
+ x.reduce();
+ y.reduce();
+ return;
+ }
+ z.inverse();
+
+ x.mul(z); x.reduce(); // *****
+ y.mul(z); y.reduce();
+ z.copy(one);
+ }
+/* extract affine x as FP2 */
+ public FP2 getX()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.x;
+ }
+/* extract affine y as FP2 */
+ public FP2 getY()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.y;
+ }
+/* extract projective x */
+ public FP2 getx()
+ {
+ return x;
+ }
+/* extract projective y */
+ public FP2 gety()
+ {
+ return y;
+ }
+/* extract projective z */
+ public FP2 getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP2 W=new ECP2(this);
+ W.affine();
+ W.x.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i]=t[i];
+ W.x.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+BIG.MODBYTES]=t[i];
+
+ W.y.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+2*BIG.MODBYTES]=t[i];
+ W.y.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+3*BIG.MODBYTES]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP2 fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG ra;
+ BIG rb;
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 rx=new FP2(ra,rb);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+2*BIG.MODBYTES];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+3*BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 ry=new FP2(ra,rb);
+
+ return new ECP2(rx,ry);
+ }
+/* convert this to hex string */
+ public String toString() {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ return "("+W.x.toString()+","+W.y.toString()+")";
+ }
+
+/* Calculate RHS of twisted curve equation x^3+B/i */
+ public static FP2 RHS(FP2 x) {
+ x.norm();
+ FP2 r=new FP2(x);
+ r.sqr();
+ FP2 b=new FP2(new BIG(ROM.CURVE_B));
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b.div_ip();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b.norm();
+ b.mul_ip();
+ b.norm();
+ }
+
+
+ r.mul(x);
+ r.add(b);
+
+ r.reduce();
+ return r;
+ }
+
+/* construct this from (x,y) - but set to O if not on curve */
+ public ECP2(FP2 ix,FP2 iy) {
+ x=new FP2(ix);
+ y=new FP2(iy);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ FP2 y2=new FP2(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+// if (y2.equals(rhs)) INF=false;
+// else {x.zero();INF=true;}
+ }
+
+/* construct this from x - but set to O if not on curve */
+ public ECP2(FP2 ix) {
+ x=new FP2(ix);
+ y=new FP2(1);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ if (rhs.sqrt())
+ {
+ y.copy(rhs);
+ //INF=false;
+ }
+ else {/*x.zero();INF=true;*/ inf();}
+ }
+
+/* this+=this */
+ public int dbl() {
+// if (INF) return -1;
+//System.out.println("Into dbl");
+ FP2 iy=new FP2(y);
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ iy.mul_ip(); iy.norm();
+ }
+ FP2 t0=new FP2(y); //***** Change
+ t0.sqr();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip();
+ }
+ FP2 t1=new FP2(iy);
+ t1.mul(z);
+ FP2 t2=new FP2(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z);
+ z.add(z);
+ z.norm();
+
+ t2.imul(3*ROM.CURVE_B_I);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip();
+ t2.norm();
+ }
+
+ FP2 x3=new FP2(t2);
+ x3.mul(z);
+
+ FP2 y3=new FP2(t0);
+
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1); t2.norm();
+ t0.sub(t2); t0.norm(); //y^2-9bz^2
+ y3.mul(t0); y3.add(x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+ t1.copy(x); t1.mul(iy); //
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x); //(y^2-9bz^2)xy2
+
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ return 1;
+ }
+
+/* this+=Q - return 0 for add, 1 for double, -1 for O */
+ public int add(ECP2 Q) {
+// if (INF)
+// {
+// copy(Q);
+// return -1;
+// }
+// if (Q.INF) return -1;
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP2 t0=new FP2(x);
+ t0.mul(Q.x); // x.Q.x
+ FP2 t1=new FP2(y);
+ t1.mul(Q.y); // y.Q.y
+
+ FP2 t2=new FP2(z);
+ t2.mul(Q.z);
+ FP2 t3=new FP2(x);
+ t3.add(y); t3.norm(); //t3=X1+Y1
+ FP2 t4=new FP2(Q.x);
+ t4.add(Q.y); t4.norm(); //t4=X2+Y2
+ t3.mul(t4); //t3=(X1+Y1)(X2+Y2)
+ t4.copy(t0); t4.add(t1); //t4=X1.X2+Y1.Y2
+
+ t3.sub(t4); t3.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t3.mul_ip(); t3.norm(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+ }
+ t4.copy(y);
+ t4.add(z); t4.norm(); //t4=Y1+Z1
+ FP2 x3=new FP2(Q.y);
+ x3.add(Q.z); x3.norm(); //x3=Y2+Z2
+
+ t4.mul(x3); //t4=(Y1+Z1)(Y2+Z2)
+ x3.copy(t1); //
+ x3.add(t2); //X3=Y1.Y2+Z1.Z2
+
+ t4.sub(x3); t4.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t4.mul_ip(); t4.norm(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+ }
+ x3.copy(x); x3.add(z); x3.norm(); // x3=X1+Z1
+ FP2 y3=new FP2(Q.x);
+ y3.add(Q.z); y3.norm(); // y3=X2+Z2
+ x3.mul(y3); // x3=(X1+Z1)(X2+Z2)
+ y3.copy(t0);
+ y3.add(t2); // y3=X1.X2+Z1+Z2
+ y3.rsub(x3); y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip(); t0.norm(); // x.Q.x
+ t1.mul_ip(); t1.norm(); // y.Q.y
+ }
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip(); t2.norm();
+ }
+ FP2 z3=new FP2(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ y3.mul_ip();
+ y3.norm();
+ }
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ return 0;
+ }
+
+/* set this-=Q */
+ public int sub(ECP2 Q) {
+ ECP2 NQ=new ECP2(Q);
+ NQ.neg();
+ int D=add(NQ);
+ //Q.neg();
+ //int D=add(Q);
+ //Q.neg();
+ return D;
+ }
+/* set this*=q, where q is Modulus, using Frobenius */
+ public void frob(FP2 X)
+ {
+// if (INF) return;
+ FP2 X2=new FP2(X);
+
+ X2.sqr();
+ x.conj();
+ y.conj();
+ z.conj();
+ z.reduce();
+ x.mul(X2);
+
+ y.mul(X2);
+ y.mul(X);
+ }
+
+/* P*=e */
+ public ECP2 mul(BIG e)
+ {
+/* fixed size windows */
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP2 P=new ECP2();
+ ECP2 Q=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2[] W=new ECP2[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ if (is_infinity()) return new ECP2();
+
+ //affine();
+
+/* precompute table */
+ Q.copy(this);
+ Q.dbl();
+ W[0]=new ECP2();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP2();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+/* make exponent odd - add 2P if even, P if odd */
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+/* convert exponent to signed 4-bit window */
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C);
+ P.affine();
+ return P;
+ }
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ ECP2 W=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] T=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ //Q[i].affine();
+ }
+
+ T[0] = new ECP2(); T[0].copy(Q[0]); // Q[0]
+ T[1] = new ECP2(); T[1].copy(T[0]); T[1].add(Q[1]); // Q[0]+Q[1]
+ T[2] = new ECP2(); T[2].copy(T[0]); T[2].add(Q[2]); // Q[0]+Q[2]
+ T[3] = new ECP2(); T[3].copy(T[1]); T[3].add(Q[2]); // Q[0]+Q[1]+Q[2]
+ T[4] = new ECP2(); T[4].copy(T[0]); T[4].add(Q[3]); // Q[0]+Q[3]
+ T[5] = new ECP2(); T[5].copy(T[1]); T[5].add(Q[3]); // Q[0]+Q[1]+Q[3]
+ T[6] = new ECP2(); T[6].copy(T[2]); T[6].add(Q[3]); // Q[0]+Q[2]+Q[3]
+ T[7] = new ECP2(); T[7].copy(T[3]); T[7].add(Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ P.select(T,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ P.dbl();
+ W.select(T,(int)(2*w[i]+s[i]));
+ P.add(W);
+ }
+
+ // apply correction
+ W.copy(P);
+ W.sub(Q[0]);
+ P.cmove(W,pb);
+ P.affine();
+ return P;
+ }
+
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+/*
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb;
+ int[] a=new int[4];
+ ECP2 T=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] W=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ Q[i].affine();
+ }
+
+// precompute table
+
+ W[0]=new ECP2(); W[0].copy(Q[0]); W[0].sub(Q[1]);
+
+ W[1]=new ECP2(); W[1].copy(W[0]);
+ W[2]=new ECP2(); W[2].copy(W[0]);
+ W[3]=new ECP2(); W[3].copy(W[0]);
+ W[4]=new ECP2(); W[4].copy(Q[0]); W[4].add(Q[1]);
+ W[5]=new ECP2(); W[5].copy(W[4]);
+ W[6]=new ECP2(); W[6].copy(W[4]);
+ W[7]=new ECP2(); W[7].copy(W[4]);
+ T.copy(Q[2]); T.sub(Q[3]);
+ W[1].sub(T);
+ W[2].add(T);
+ W[5].sub(T);
+ W[6].add(T);
+ T.copy(Q[2]); T.add(Q[3]);
+ W[0].sub(T);
+ W[3].add(T);
+ W[4].sub(T);
+ W[7].add(T);
+
+// if multiplier is even add 1 to multiplier, and add P to correction
+ mt.zero(); C.inf();
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ C.add(Q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(byte)(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ P.dbl();
+ P.add(T);
+ }
+ P.sub(C); // apply correction
+
+ P.affine();
+ return P;
+ }
+*/
+
+/* needed for SOK */
+ public static ECP2 mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ BIG one=new BIG(1);
+ FP2 X;
+ ECP2 Q;
+ x.mod(q);
+ while (true)
+ {
+ X=new FP2(one,x);
+ Q=new ECP2(X);
+ if (!Q.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+
+ BIG Fra=new BIG(ROM.Fra);
+ BIG Frb=new BIG(ROM.Frb);
+ X=new FP2(Fra,Frb);
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X.inverse();
+ X.norm();
+ }
+
+ x=new BIG(ROM.CURVE_Bnx);
+
+/* Fast Hashing to G2 - Fuentes-Castaneda, Knapp and Rodriguez-Henriquez */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ ECP2 T,K;
+
+ T=new ECP2(); T.copy(Q);
+ T=T.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ T.neg();
+ }
+ K=new ECP2(); K.copy(T);
+ K.dbl(); K.add(T); //K.affine();
+
+ K.frob(X);
+ Q.frob(X); Q.frob(X); Q.frob(X);
+ Q.add(T); Q.add(K);
+ T.frob(X); T.frob(X);
+ Q.add(T);
+
+ }
+
+/* Efficient hash maps to G2 on BLS curves - Budroni, Pintore */
+/* Q -> x2Q -xQ -Q +F(xQ -Q) +F(F(2Q)) */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BLS)
+ {
+ // ECP2 xQ,x2Q;
+ // xQ=new ECP2();
+ // x2Q=new ECP2();
+
+ ECP2 xQ=Q.mul(x);
+ ECP2 x2Q=xQ.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ xQ.neg();
+ }
+
+ x2Q.sub(xQ);
+ x2Q.sub(Q);
+
+ xQ.sub(Q);
+ xQ.frob(X);
+
+ Q.dbl();
+ Q.frob(X);
+ Q.frob(X);
+
+ Q.add(x2Q);
+ Q.add(xQ);
+ }
+ Q.affine();
+ return Q;
+ }
+
+ public static ECP2 generator()
+ {
+ return new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+ }
+
+/*
+ public static void main(String[] args) {
+ BIG r=new BIG(ROM.Modulus);
+
+ BIG Pxa=new BIG(ROM.CURVE_Pxa);
+ BIG Pxb=new BIG(ROM.CURVE_Pxb);
+ BIG Pya=new BIG(ROM.CURVE_Pya);
+ BIG Pyb=new BIG(ROM.CURVE_Pyb);
+
+ BIG Fra=new BIG(ROM.CURVE_Fra);
+ BIG Frb=new BIG(ROM.CURVE_Frb);
+
+ FP2 f=new FP2(Fra,Frb);
+
+ FP2 Px=new FP2(Pxa,Pxb);
+ FP2 Py=new FP2(Pya,Pyb);
+
+ ECP2 P=new ECP2(Px,Py);
+
+ System.out.println("P= "+P.toString());
+
+ P=P.mul(r);
+ System.out.println("P= "+P.toString());
+
+ ECP2 Q=new ECP2(Px,Py);
+ Q.frob(f);
+ System.out.println("Q= "+Q.toString());
+ } */
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/FP.java b/src/main/java/org/apache/milagro/amcl/BN254CX/FP.java
new file mode 100644
index 0000000..474343e
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.BN254CX;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=254; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<26); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/FP12.java b/src/main/java/org/apache/milagro/amcl/BN254CX/FP12.java
new file mode 100644
index 0000000..481a544
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/FP12.java
@@ -0,0 +1,907 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Fp^12 functions */
+/* FP12 elements are of the form a+i.b+i^2.c */
+
+package org.apache.milagro.amcl.BN254CX;
+
+public final class FP12 {
+ private final FP4 a;
+ private final FP4 b;
+ private final FP4 c;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ c.reduce();
+ }
+/* normalise all components of this */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ c.norm();
+ }
+/* test x==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch() && c.iszilch());
+ }
+
+ public void cmove(FP12 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ c.cmove(g.c,d);
+ }
+
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(FP12 g[],int b)
+ {
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(g[0],teq(babs,0)); // conditional move
+ cmove(g[1],teq(babs,1));
+ cmove(g[2],teq(babs,2));
+ cmove(g[3],teq(babs,3));
+ cmove(g[4],teq(babs,4));
+ cmove(g[5],teq(babs,5));
+ cmove(g[6],teq(babs,6));
+ cmove(g[7],teq(babs,7));
+
+ FP12 invf=new FP12(this);
+ invf.conj();
+ cmove(invf,(int)(m&1));
+ }
+
+
+/* test x==1 ? */
+ public boolean isunity() {
+ FP4 one=new FP4(1);
+ return (a.equals(one) && b.iszilch() && c.iszilch());
+ }
+/* return 1 if x==y, else 0 */
+ public boolean equals(FP12 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b) && c.equals(x.c));
+ }
+/* extract a from this */
+ public FP4 geta()
+ {
+ return a;
+ }
+/* extract b */
+ public FP4 getb()
+ {
+ return b;
+ }
+/* extract c */
+ public FP4 getc()
+ {
+ return c;
+ }
+/* copy this=x */
+ public void copy(FP12 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ c.copy(x.c);
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ c.zero();
+ }
+/* this=conj(this) */
+ public void conj()
+ {
+ a.conj();
+ b.nconj();
+ c.conj();
+ }
+/* Constructors */
+ public FP12(FP4 d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(int d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(FP4 d,FP4 e,FP4 f)
+ {
+ a=new FP4(d);
+ b=new FP4(e);
+ c=new FP4(f);
+ }
+
+ public FP12(FP12 x)
+ {
+ a=new FP4(x.a);
+ b=new FP4(x.b);
+ c=new FP4(x.c);
+ }
+
+/* Granger-Scott Unitary Squaring */
+ public void usqr()
+ {
+//System.out.println("Into usqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(c);
+ FP4 C=new FP4(b);
+ FP4 D=new FP4(0);
+
+ a.sqr();
+ D.copy(a); D.add(a);
+ a.add(D);
+
+ a.norm();
+ A.nconj();
+
+ A.add(A);
+ a.add(A);
+ B.sqr();
+ B.times_i();
+
+ D.copy(B); D.add(B);
+ B.add(D);
+ B.norm();
+
+ C.sqr();
+ D.copy(C); D.add(C);
+ C.add(D);
+ C.norm();
+
+ b.conj();
+ b.add(b);
+ c.nconj();
+
+ c.add(c);
+ b.add(B);
+ c.add(C);
+//System.out.println("Out of usqr 1");
+ reduce();
+//System.out.println("Out of usqr 2");
+ }
+
+/* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+ public void sqr()
+ {
+//System.out.println("Into sqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(b);
+ FP4 C=new FP4(c);
+ FP4 D=new FP4(a);
+
+ A.sqr();
+ B.mul(c);
+ B.add(B);
+ B.norm();
+ C.sqr();
+ D.mul(b);
+ D.add(D);
+
+ c.add(a);
+ c.add(b);
+ c.norm();
+ c.sqr();
+
+ a.copy(A);
+
+ A.add(B);
+ A.norm();
+ A.add(C);
+ A.add(D);
+ A.norm();
+
+ A.neg();
+ B.times_i();
+ C.times_i();
+
+ a.add(B);
+
+ b.copy(C); b.add(D);
+ c.add(A);
+//System.out.println("Out of sqr");
+ norm();
+ }
+
+/* FP12 full multiplication this=this*y */
+ public void mul(FP12 y)
+ {
+//System.out.println("Into mul");
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(y.a);
+
+ z0.mul(y.a);
+ z2.mul(y.b);
+
+ t0.add(b);
+ t1.add(y.b);
+
+ t0.norm();
+ t1.norm();
+
+ z1.copy(t0); z1.mul(t1);
+ t0.copy(b); t0.add(c);
+
+ t1.copy(y.b); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ z3.copy(t0); z3.mul(t1);
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ z1.add(t0);
+ //z1.norm();
+ b.copy(z1); b.add(t1);
+
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+t0.norm();
+t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c); t0.mul(y.c);
+ t1.copy(t0); t1.neg();
+
+// z2.norm();
+// z3.norm();
+// b.norm();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ norm();
+//System.out.println("Out of mul");
+ }
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+ public void smul(FP12 y,int type)
+ {
+//System.out.println("Into smul");
+
+ if (type==ECP.D_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(b);
+ FP4 t0=new FP4(0);
+ FP4 t1=new FP4(y.a);
+ z0.mul(y.a);
+ z2.pmul(y.b.real());
+ b.add(a);
+ t1.real().add(y.b.real());
+
+ t1.norm();
+ b.norm();
+ b.mul(t1);
+ z3.add(c);
+ z3.norm();
+ z3.pmul(y.b.real());
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ b.add(t0);
+
+ b.add(t1);
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t0.norm();
+ z3.norm();
+ t0.mul(y.a);
+ c.copy(z2); c.add(t0);
+
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ if (type==ECP.M_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(0);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(0);
+
+ z0.mul(y.a);
+ t0.add(b);
+ t0.norm();
+
+ z1.copy(t0); z1.mul(y.a);
+ t0.copy(b); t0.add(c);
+ t0.norm();
+
+ z3.copy(t0); //z3.mul(y.c);
+ z3.pmul(y.c.getb());
+ z3.times_i();
+
+ t0.copy(z0); t0.neg();
+
+ z1.add(t0);
+ b.copy(z1);
+ z2.copy(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c);
+
+ t0.pmul(y.c.getb());
+ t0.times_i();
+
+ t1.copy(t0); t1.neg();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ norm();
+//System.out.println("Out of smul");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ FP4 f0=new FP4(a);
+ FP4 f1=new FP4(b);
+ FP4 f2=new FP4(a);
+ FP4 f3=new FP4(0);
+
+ norm();
+ f0.sqr();
+ f1.mul(c);
+ f1.times_i();
+ f0.sub(f1);
+ f0.norm();
+
+ f1.copy(c); f1.sqr();
+ f1.times_i();
+ f2.mul(b);
+ f1.sub(f2);
+ f1.norm();
+
+ f2.copy(b); f2.sqr();
+ f3.copy(a); f3.mul(c);
+ f2.sub(f3);
+ f2.norm();
+
+ f3.copy(b); f3.mul(f2);
+ f3.times_i();
+ a.mul(f0);
+ f3.add(a);
+ c.mul(f1);
+ c.times_i();
+
+ f3.add(c);
+ f3.norm();
+ f3.inverse();
+ a.copy(f0); a.mul(f3);
+ b.copy(f1); b.mul(f3);
+ c.copy(f2); c.mul(f3);
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ FP2 f2=new FP2(f);
+ FP2 f3=new FP2(f);
+
+ f2.sqr();
+ f3.mul(f2);
+
+ a.frob(f3);
+ b.frob(f3);
+ c.frob(f3);
+
+ b.pmul(f);
+ c.pmul(f2);
+ }
+
+/* trace function */
+ public FP4 trace()
+ {
+ FP4 t=new FP4(0);
+ t.copy(a);
+ t.imul(3);
+ t.reduce();
+ return t;
+ }
+
+/* convert from byte array to FP12 */
+ public static FP12 fromBytes(byte[] w)
+ {
+ BIG a,b;
+ FP2 c,d;
+ FP4 e,f,g;
+ byte[] t=new byte[BIG.MODBYTES];
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+2*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+3*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ e=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+4*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+5*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+6*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+7*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ f=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+8*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+9*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+10*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+11*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ g=new FP4(c,d);
+
+ return new FP12(e,f,g);
+ }
+
+/* convert this to byte array */
+ public void toBytes(byte[] w)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ a.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i]=t[i];
+ a.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+BIG.MODBYTES]=t[i];
+ a.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+2*BIG.MODBYTES]=t[i];
+ a.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+3*BIG.MODBYTES]=t[i];
+
+ b.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+4*BIG.MODBYTES]=t[i];
+ b.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+5*BIG.MODBYTES]=t[i];
+ b.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+6*BIG.MODBYTES]=t[i];
+ b.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+7*BIG.MODBYTES]=t[i];
+
+ c.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+8*BIG.MODBYTES]=t[i];
+ c.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+9*BIG.MODBYTES]=t[i];
+ c.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+10*BIG.MODBYTES]=t[i];
+ c.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+11*BIG.MODBYTES]=t[i];
+ }
+
+/* convert to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+","+c.toString()+"]");
+ }
+
+/* this=this^e */
+/* Note this is simple square and multiply, so not side-channel safe */
+ public FP12 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ BIG e3=new BIG(e);
+ e3.pmul(3);
+ e3.norm();
+
+ FP12 w=new FP12(this);
+
+ int nb=e3.nbits();
+ for (int i=nb-2;i>=1;i--)
+ {
+ w.usqr();
+ int bt=e3.bit(i)-e.bit(i);
+ if (bt==1)
+ w.mul(this);
+ if (bt==-1)
+ {
+ conj(); w.mul(this); conj();
+ }
+ }
+ w.reduce();
+ return w;
+
+
+/*
+ BIG z=new BIG(e);
+ FP12 r=new FP12(1);
+
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.usqr();
+ }
+ r.reduce();
+ return r; */
+ }
+
+/* constant time powering by small integer of max length bts */
+ public void pinpow(int e,int bts)
+ {
+ int i,b;
+ FP12 [] R=new FP12[2];
+ R[0]=new FP12(1);
+ R[1]=new FP12(this);
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ R[1-b].mul(R[b]);
+ R[b].usqr();
+ }
+ this.copy(R[0]);
+ }
+
+ public FP4 compow(BIG e,BIG r)
+ {
+ FP12 g1=new FP12(0);
+ FP12 g2=new FP12(0);
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.Modulus);
+
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(e);
+ a.mod(m);
+
+ BIG b=new BIG(e);
+ b.div(m);
+
+ g1.copy(this);
+ g2.copy(this);
+
+ FP4 c=g1.trace();
+
+ if (b.iszilch())
+ {
+ c=c.xtr_pow(e);
+ return c;
+ }
+
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+
+ return c;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ FP12 [] g=new FP12[8];
+ FP12 r=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ }
+ g[0]=new FP12(q[0]); // q[0]
+ g[1]=new FP12(g[0]); g[1].mul(q[1]); // q[0].q[1]
+ g[2]=new FP12(g[0]); g[2].mul(q[2]); // q[0].q[2]
+ g[3]=new FP12(g[1]); g[3].mul(q[2]); // q[0].q[1].q[2]
+ g[4]=new FP12(q[0]); g[4].mul(q[3]); // q[0].q[3]
+ g[5]=new FP12(g[1]); g[5].mul(q[3]); // q[0].q[1].q[3]
+ g[6]=new FP12(g[2]); g[6].mul(q[3]); // q[0].q[2].q[3]
+ g[7]=new FP12(g[3]); g[7].mul(q[3]); // q[0].q[1].q[2].q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ p.select(g,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ p.usqr();
+ r.select(g,(int)(2*w[i]+s[i]));
+ p.mul(r);
+ }
+
+ // apply correction
+ r.copy(q[0]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb);
+
+ p.reduce();
+ return p;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+/* Timing attack secure, but not cache attack secure */
+/*
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,m;
+ int[] a=new int[4];
+ FP12 [] g=new FP12[8];
+ FP12 [] s=new FP12[2];
+ FP12 c=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ t[i]=new BIG(u[i]);
+
+ s[0]=new FP12(0);
+ s[1]=new FP12(0);
+
+ g[0]=new FP12(q[0]); s[0].copy(q[1]); s[0].conj(); g[0].mul(s[0]);
+ g[1]=new FP12(g[0]);
+ g[2]=new FP12(g[0]);
+ g[3]=new FP12(g[0]);
+ g[4]=new FP12(q[0]); g[4].mul(q[1]);
+ g[5]=new FP12(g[4]);
+ g[6]=new FP12(g[4]);
+ g[7]=new FP12(g[4]);
+
+ s[1].copy(q[2]); s[0].copy(q[3]); s[0].conj(); s[1].mul(s[0]);
+ s[0].copy(s[1]); s[0].conj(); g[1].mul(s[0]);
+ g[2].mul(s[1]);
+ g[5].mul(s[0]);
+ g[6].mul(s[1]);
+ s[1].copy(q[2]); s[1].mul(q[3]);
+ s[0].copy(s[1]); s[0].conj(); g[0].mul(s[0]);
+ g[3].mul(s[1]);
+ g[4].mul(s[0]);
+ g[7].mul(s[1]);
+
+// if power is even add 1 to power, and add q to correction
+
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ c.mul(q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+ c.conj();
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+ p.copy(g[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ m=w[i]>>7;
+ j=(w[i]^m)-m; // j=abs(w[i])
+ j=(j-1)/2;
+ s[0].copy(g[j]); s[1].copy(g[j]); s[1].conj();
+ p.usqr();
+ p.mul(s[m&1]);
+ }
+ p.mul(c); // apply correction
+ p.reduce();
+ return p;
+ }
+*/
+/*
+ public static void main(String[] args) {
+ BIG p=new BIG(ROM.Modulus);
+ FP2 w0,w1;
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.zero(); b.zero(); a.inc(1); b.inc(2);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(3); b.inc(4);
+ w1=new FP2(a,b);
+ FP4 t0=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(5); b.inc(6);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(7); b.inc(8);
+ w1=new FP2(a,b);
+ FP4 t1=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(9); b.inc(10);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(11); b.inc(12);
+ w1=new FP2(a,b);
+ FP4 t2=new FP4(w0,w1);
+
+ FP12 w=new FP12(t0,t1,t2);
+ FP12 t=new FP12(w);
+
+ System.out.println("w= "+w.toString());
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ w.frob(f);
+ System.out.println("w= "+w.toString());
+
+ w=t.pow(p);
+
+ System.out.println("w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("w= "+w.toString());
+
+ t.copy(w);
+ w.conj();
+ t.inverse();
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)= "+w.toString());
+
+ t.copy(w);
+ w.frob(f);
+ w.frob(f);
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)(p^2+1)= "+w.toString());
+
+ t.copy(w);
+
+ t.inverse();
+ w.conj();
+
+ System.out.println("w= "+w.toString());
+ System.out.println("t= "+t.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/FP2.java b/src/main/java/org/apache/milagro/amcl/BN254CX/FP2.java
new file mode 100644
index 0000000..7848512
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/FP2.java
@@ -0,0 +1,425 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^2 functions */
+
+/* FP2 elements are of the form a+ib, where i is sqrt(-1) */
+
+package org.apache.milagro.amcl.BN254CX;
+
+public final class FP2 {
+ private final FP a;
+ private final FP b;
+
+/* reduce components mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+
+/* normalise components of w */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+
+/* test this=0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP2 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this=1 ? */
+ public boolean isunity() {
+ FP one=new FP(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test this=x */
+ public boolean equals(FP2 x) {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+
+/* Constructors */
+ public FP2(int c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(FP2 x)
+ {
+ a=new FP(x.a);
+ b=new FP(x.b);
+ }
+
+ public FP2(FP c,FP d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(BIG c,BIG d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(FP c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(BIG c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+/*
+ public BIG geta()
+ {
+ return a.tobig();
+ }
+*/
+/* extract a */
+ public BIG getA()
+ {
+ return a.redc();
+ }
+
+/* extract b */
+ public BIG getB()
+ {
+ return b.redc();
+ }
+
+/* copy this=x */
+ public void copy(FP2 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+
+/* negate this mod Modulus */
+ public void neg()
+ {
+ FP m=new FP(a);
+ FP t=new FP(0);
+
+ m.add(b);
+ m.neg();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ }
+
+/* set to a-ib */
+ public void conj()
+ {
+ b.neg();
+ b.norm();
+ }
+
+/* this+=a */
+ public void add(FP2 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+
+/* this-=a */
+ public void sub(FP2 x)
+ {
+ FP2 m=new FP2(x);
+ m.neg();
+ add(m);
+ }
+
+ public void rsub(FP2 x) // *****
+ {
+ neg();
+ add(x);
+ }
+
+/* this*=s, where s is an FP */
+ public void pmul(FP s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this*=i, where i is an int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+ FP w1=new FP(a);
+ FP w3=new FP(a);
+ FP mb=new FP(b);
+
+ w1.add(b);
+ mb.neg();
+
+ w3.add(a);
+ w3.norm();
+ b.mul(w3);
+
+ a.add(mb);
+
+ w1.norm();
+ a.norm();
+
+ a.mul(w1);
+ }
+
+/* this*=y */
+/* Now uses Lazy reduction */
+ public void mul(FP2 y)
+ {
+ if ((long)(a.XES+b.XES)*(y.a.XES+y.b.XES)>(long)FP.FEXCESS)
+ {
+ if (a.XES>1) a.reduce();
+ if (b.XES>1) b.reduce();
+ }
+
+ DBIG pR=new DBIG(0);
+ BIG C=new BIG(a.x);
+ BIG D=new BIG(y.a.x);
+
+ pR.ucopy(new BIG(ROM.Modulus));
+
+ DBIG A=BIG.mul(a.x,y.a.x);
+ DBIG B=BIG.mul(b.x,y.b.x);
+
+ C.add(b.x); C.norm();
+ D.add(y.b.x); D.norm();
+
+ DBIG E=BIG.mul(C,D);
+ DBIG F=new DBIG(A); F.add(B);
+ B.rsub(pR);
+
+ A.add(B); A.norm();
+ E.sub(F); E.norm();
+
+ a.x.copy(FP.mod(A)); a.XES=3;
+ b.x.copy(FP.mod(E)); b.XES=2;
+ }
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP w1=new FP(b);
+ FP w2=new FP(a);
+ w1.sqr(); w2.sqr(); w1.add(w2);
+ if (w1.jacobi()!=1) { zero(); return false; }
+ w1=w1.sqrt();
+ w2.copy(a); w2.add(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1)
+ {
+ w2.copy(a); w2.sub(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1) { zero(); return false; }
+ }
+ w2=w2.sqrt();
+ a.copy(w2);
+ w2.add(w2);
+ w2.inverse();
+ b.mul(w2);
+ return true;
+ }
+
+/* output to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ norm();
+ FP w1=new FP(a);
+ FP w2=new FP(b);
+
+ w1.sqr();
+ w2.sqr();
+ w1.add(w2);
+ w1.inverse();
+ a.mul(w1);
+ w1.neg();
+ w1.norm();
+ b.mul(w1);
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+/* this*=sqrt(-1) */
+ public void times_i()
+ {
+ FP z=new FP(a);
+ a.copy(b); a.neg();
+ b.copy(z);
+ }
+
+/* w*=(1+sqrt(-1)) */
+/* where X*2-(1+sqrt(-1)) is irreducible for FP4, assumes p=3 mod 8 */
+ public void mul_ip()
+ {
+ FP2 t=new FP2(this);
+ FP z=new FP(a);
+ a.copy(b);
+ a.neg();
+ b.copy(z);
+ add(t);
+ }
+
+ public void div_ip2()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ }
+
+/* w/=(1+sqrt(-1)) */
+ public void div_ip()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ div2();
+ }
+/*
+ public FP2 pow(BIG e)
+ {
+ int bt;
+ FP2 r=new FP2(1);
+ e.norm();
+ norm();
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(this);
+ if (e.iszilch()) break;
+ sqr();
+ }
+
+ r.reduce();
+ return r;
+ }
+
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(27);
+ BIG pp1=new BIG(m);
+ BIG pm1=new BIG(m);
+ BIG a=new BIG(1);
+ BIG b=new BIG(1);
+ FP2 w=new FP2(a,b);
+ FP2 z=new FP2(w);
+
+ byte[] RAW=new byte[100];
+
+ RAND rng=new RAND();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ // for (int i=0;i<100;i++)
+ // {
+ a.randomnum(rng);
+ b.randomnum(rng);
+
+ w=new FP2(a,b);
+ System.out.println("w="+w.toString());
+
+ z=new FP2(w);
+ z.inverse();
+ System.out.println("z="+z.toString());
+
+ z.inverse();
+ if (!z.equals(w)) System.out.println("Error");
+ // }
+
+// System.out.println("m="+m.toString());
+// w.sqr();
+// w.mul(z);
+
+ System.out.println("w="+w.toString());
+
+
+ pp1.inc(1); pp1.norm();
+ pm1.dec(1); pm1.norm();
+ System.out.println("p+1="+pp1.toString());
+ System.out.println("p-1="+pm1.toString());
+ w=w.pow(pp1);
+ w=w.pow(pm1);
+ System.out.println("w="+w.toString());
+ }
+*/
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/FP4.java b/src/main/java/org/apache/milagro/amcl/BN254CX/FP4.java
new file mode 100644
index 0000000..d7b47d7
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/FP4.java
@@ -0,0 +1,721 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^4 functions */
+
+/* FP4 elements are of the form a+ib, where i is sqrt(-1+sqrt(-1)) */
+
+package org.apache.milagro.amcl.BN254CX;
+
+public final class FP4 {
+ private final FP2 a;
+ private final FP2 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP4 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP2 one=new FP2(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP2 real()
+ {
+ return a;
+ }
+
+ public FP2 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP2 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP4 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP4(int c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+
+ public FP4(FP4 x)
+ {
+ a=new FP2(x.a);
+ b=new FP2(x.b);
+ }
+
+ public FP4(FP2 c,FP2 d)
+ {
+ a=new FP2(c);
+ b=new FP2(d);
+ }
+
+ public FP4(FP2 c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+/* copy this=x */
+ public void copy(FP4 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP2 m=new FP2(a);
+ FP2 t=new FP2(0);
+ m.add(b);
+// m.norm();
+ m.neg();
+ // m.norm();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP4 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP4 x)
+ {
+ FP4 m=new FP4(x);
+ m.neg();
+ add(m);
+ }
+
+/* this*=s where s is FP2 */
+ public void pmul(FP2 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this=x-this */
+ public void rsub(FP4 x)
+ {
+ neg();
+ add(x);
+ }
+
+
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.mul_ip();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.mul_ip();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+/* this*=y */
+ public void mul(FP4 y)
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(0);
+ FP2 t4=new FP2(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ // t4.sub(t1);
+ // t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ // b.copy(t4);
+ // b.sub(t2);
+
+ t2.mul_ip();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.mul_ip();
+ t2.norm();
+ t1.sub(t2);
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP2 s=new FP2(b);
+ FP2 t=new FP2(b);
+ s.times_i();
+ t.add(s);
+ // t.norm();
+ b.copy(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ a.conj();
+ b.conj();
+ b.mul(f);
+ }
+
+/* this=this^e */
+ public FP4 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP4 w=new FP4(this);
+ BIG z=new BIG(e);
+ FP4 r=new FP4(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+/* XTR xtr_a function */
+ public void xtr_A(FP4 w,FP4 y,FP4 z)
+ {
+ FP4 r=new FP4(w);
+ FP4 t=new FP4(w);
+ //y.norm();
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP4 w=new FP4(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP4 xtr_pow(BIG n) {
+ FP4 a=new FP4(3);
+ FP4 b=new FP4(this);
+ FP4 c=new FP4(b);
+ c.xtr_D();
+ FP4 t=new FP4(0);
+ FP4 r=new FP4(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP4 xtr_pow2(FP4 ck,FP4 ckml,FP4 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP4 cu=new FP4(ck); // can probably be passed in w/o copying
+ FP4 cv=new FP4(this);
+ FP4 cumv=new FP4(ckml);
+ FP4 cum2v=new FP4(ckm2l);
+ FP4 r=new FP4(0);
+ FP4 t=new FP4(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+ public void div_i()
+ {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip();
+ a.copy(v);
+ b.copy(u);
+ }
+
+ public void div_2i() {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip2();
+ v.add(v); v.norm();
+ a.copy(v);
+ b.copy(u);
+ }
+
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP2 wa=new FP2(a);
+ FP2 ws=new FP2(b);
+ FP2 wt=new FP2(a);
+
+ if (ws.iszilch())
+ {
+ if (wt.sqrt())
+ {
+ a.copy(wt);
+ b.zero();
+ } else {
+ wt.div_ip();
+ wt.sqrt();
+ b.copy(wt);
+ a.zero();
+ }
+ return true;
+ }
+
+ ws.sqr();
+ wa.sqr();
+ ws.mul_ip();
+ ws.norm();
+ wa.sub(ws);
+
+ ws.copy(wa);
+ if (!ws.sqrt()) {
+ return false;
+ }
+
+ wa.copy(wt); wa.add(ws); wa.norm(); wa.div2();
+
+ if (!wa.sqrt()) {
+ wa.copy(wt); wa.sub(ws); wa.norm(); wa.div2();
+ if (!wa.sqrt()) {
+ return false;
+ }
+ }
+ wt.copy(b);
+ ws.copy(wa); ws.add(wa);
+ ws.inverse();
+
+ wt.mul(ws);
+ a.copy(wa);
+ b.copy(wt);
+
+ return true;
+ }
+
+/* this*=s where s is FP */
+ public void qmul(FP s)
+ {
+ a.pmul(s);
+ b.pmul(s);
+ }
+
+
+
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG e=new BIG(12);
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.inc(27); b.inc(45);
+
+ FP2 w0=new FP2(a,b);
+
+ a.zero(); b.zero();
+ a.inc(33); b.inc(54);
+
+ FP2 w1=new FP2(a,b);
+
+
+ FP4 w=new FP4(w0,w1);
+ FP4 t=new FP4(w);
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ System.out.println("w= "+w.toString());
+
+ w=w.pow(m);
+
+ System.out.println("w^p= "+w.toString());
+
+ t.frob(f);
+
+
+ System.out.println("w^p= "+t.toString());
+
+ w=w.pow(m);
+ w=w.pow(m);
+ w=w.pow(m);
+ System.out.println("w^p4= "+w.toString());
+
+
+ System.out.println("Test Inversion");
+
+ w=new FP4(w0,w1);
+
+ w.inverse();
+
+ System.out.println("1/w mod p^4 = "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/(1/w) mod p^4 = "+w.toString());
+
+ FP4 ww=new FP4(w);
+
+ w=w.xtr_pow(e);
+ System.out.println("w^e= "+w.toString());
+
+
+ a.zero(); b.zero();
+ a.inc(37); b.inc(17);
+ w0=new FP2(a,b);
+ a.zero(); b.zero();
+ a.inc(49); b.inc(31);
+ w1=new FP2(a,b);
+
+ FP4 c1=new FP4(w0,w1);
+ FP4 c2=new FP4(w0,w1);
+ FP4 c3=new FP4(w0,w1);
+
+ BIG e1=new BIG(3331);
+ BIG e2=new BIG(3372);
+
+ FP4 cr=w.xtr_pow2(c1,c2,c3,e1,e2);
+
+ System.out.println("c^e= "+cr.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/MPIN.java b/src/main/java/org/apache/milagro/amcl/BN254CX/MPIN.java
new file mode 100644
index 0000000..7317e3d
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/MPIN.java
@@ -0,0 +1,823 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* MPIN API Functions */
+
+package org.apache.milagro.amcl.BN254CX;
+
+import java.util.Date;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public class MPIN
+{
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int PAS=16;
+ public static final int INVALID_POINT=-14;
+ public static final int BAD_PARAMS=-11;
+ public static final int WRONG_ORDER=-18;
+ public static final int BAD_PIN=-19;
+
+/* Configure your PIN here */
+
+ public static final int MAXPIN=10000; /* PIN less than this */
+ public static final int PBLEN=14; /* Number of bits in PIN */
+ public static final int TS=10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+ public static final int TRAP=200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN - approx 2*sqrt(MAXPIN) */
+
+// public static final int HASH_TYPE=SHA256;
+
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,int n,byte[] B,int len)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (n>0) H.process_num(n);
+
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+ byte[] W=new byte[len];
+
+ if (sha>=len)
+ for (int i=0;i<len;i++) W[i]=R[i];
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+len-sha]=R[i];
+ for (int i=0;i<len-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<len;i++) W[i]=0;
+ }
+ return W;
+ }
+
+ /* return time in slots since epoch */
+ public static int today() {
+ Date date=new Date();
+ return (int) (date.getTime()/(1000*60*1440));
+ }
+
+ public static byte[] HASH_ID(int sha,byte[] ID,int len)
+ {
+ return hashit(sha,0,ID,len);
+ }
+
+/* Hash the M-Pin transcript - new */
+
+ public static byte[] HASH_ALL(int sha,byte[] HID,byte[] xID,byte[] xCID,byte[] SEC,byte[] Y,byte[] R,byte[] W,int len)
+ {
+ int i,ilen,tlen=0;
+
+ ilen=HID.length+SEC.length+Y.length+R.length+W.length;
+ if (xCID!=null) ilen+=xCID.length;
+ else ilen+=xID.length;
+
+ byte[] T = new byte[ilen];
+
+ for (i=0;i<HID.length;i++) T[i]=HID[i];
+ tlen+=HID.length;
+ if (xCID!=null)
+ {
+ for (i=0;i<xCID.length;i++) T[i+tlen]=xCID[i];
+ tlen+=xCID.length;
+ }
+ else
+ {
+ for (i=0;i<xID.length;i++) T[i+tlen]=xID[i];
+ tlen+=xID.length;
+ }
+ for (i=0;i<SEC.length;i++) T[i+tlen]=SEC[i];
+ tlen+=SEC.length;
+ for (i=0;i<Y.length;i++) T[i+tlen]=Y[i];
+ tlen+=Y.length;
+ for (i=0;i<R.length;i++) T[i+tlen]=R[i];
+ tlen+=R.length;
+ for (i=0;i<W.length;i++) T[i+tlen]=W[i];
+ tlen+=W.length;
+
+ return hashit(sha,0,T,len);
+ }
+
+/* return time since epoch */
+ public static int GET_TIME() {
+ Date date=new Date();
+ return (int) (date.getTime()/1000);
+ }
+
+ public static byte[] mpin_hash(int sha,FP4 c,ECP U)
+ {
+ byte[] w=new byte[EFS];
+ byte[] t=new byte[6*EFS];
+ byte[] h=null;
+ c.geta().getA().toBytes(w); for (int i=0;i<EFS;i++) t[i]=w[i];
+ c.geta().getB().toBytes(w); for (int i=EFS;i<2*EFS;i++) t[i]=w[i-EFS];
+ c.getb().getA().toBytes(w); for (int i=2*EFS;i<3*EFS;i++) t[i]=w[i-2*EFS];
+ c.getb().getB().toBytes(w); for (int i=3*EFS;i<4*EFS;i++) t[i]=w[i-3*EFS];
+
+ U.getX().toBytes(w); for (int i=4*EFS;i<5*EFS;i++) t[i]=w[i-4*EFS];
+ U.getY().toBytes(w); for (int i=5*EFS;i<6*EFS;i++) t[i]=w[i-5*EFS];
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (h==null) return null;
+ byte[] R=new byte[ECP.AESKEY];
+ for (int i=0;i<ECP.AESKEY;i++) R[i]=h[i];
+ return R;
+ }
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+ public static ECP map(BIG u,int cb)
+ {
+ ECP P;
+ BIG x=new BIG(u);
+ BIG p=new BIG(ROM.Modulus);
+ x.mod(p);
+ while (true)
+ {
+ P=new ECP(x,cb);
+ if (!P.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+ return P;
+ }
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+ public static int unmap(BIG u,ECP P)
+ {
+ int s=P.getS();
+ ECP R;
+ int r=0;
+ BIG x=P.getX();
+ u.copy(x);
+ while (true)
+ {
+ u.dec(1); u.norm();
+ r++;
+ R=new ECP(u,s);
+ if (!R.is_infinity()) break;
+ }
+ return r;
+ }
+
+
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+ public static int ENCODING(RAND rng,byte[] E)
+ {
+ int rn,m,su,sv;
+ byte[] T=new byte[EFS];
+
+ for (int i=0;i<EFS;i++) T[i]=E[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=E[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ ECP P=new ECP(u,v);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG p=new BIG(ROM.Modulus);
+ u=BIG.randomnum(p,rng);
+
+ su=rng.getByte(); /*if (su<0) su=-su;*/ su%=2;
+
+ ECP W=map(u,su);
+ P.sub(W); //P.affine();
+ sv=P.getS();
+ rn=unmap(v,P);
+ m=rng.getByte(); /*if (m<0) m=-m;*/ m%=rn;
+ v.inc(m+1);
+ E[0]=(byte)(su+2*sv);
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+ public static int DECODING(byte[] D)
+ {
+ int su,sv;
+ byte[] T=new byte[EFS];
+
+ if ((D[0]&0x04)!=0) return INVALID_POINT;
+
+ for (int i=0;i<EFS;i++) T[i]=D[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=D[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ su=D[0]&1;
+ sv=(D[0]>>1)&1;
+ ECP W=map(u,su);
+ ECP P=map(v,sv);
+ P.add(W); //P.affine();
+ u=P.getX();
+ v=P.getY();
+ D[0]=0x04;
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+/* R=R1+R2 in group G1 */
+ public static int RECOMBINE_G1(byte[] R1,byte[] R2,byte[] R)
+ {
+ ECP P=ECP.fromBytes(R1);
+ ECP Q=ECP.fromBytes(R2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(R,false);
+ return 0;
+ }
+
+/* W=W1+W2 in group G2 */
+ public static int RECOMBINE_G2(byte[] W1,byte[] W2,byte[] W)
+ {
+ ECP2 P=ECP2.fromBytes(W1);
+ ECP2 Q=ECP2.fromBytes(W2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(W);
+ return 0;
+ }
+
+/* create random secret S */
+ public static int RANDOM_GENERATE(RAND rng,byte[] S)
+ {
+ BIG s;
+ BIG r=new BIG(ROM.CURVE_Order);
+ s=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+ return 0;
+ }
+
+/* Extract PIN from TOKEN for identity CID */
+ public static int EXTRACT_PIN(int sha,byte[] CID,int pin,byte[] TOKEN)
+ {
+ ECP P=ECP.fromBytes(TOKEN);
+ if (P.is_infinity()) return INVALID_POINT;
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R=ECP.mapit(h);
+
+
+ pin%=MAXPIN;
+
+ R=R.pinmul(pin,PBLEN);
+ P.sub(R); //P.affine();
+
+ P.toBytes(TOKEN,false);
+
+ return 0;
+ }
+
+/* Implement step 2 on client side of MPin protocol */
+ public static int CLIENT_2(byte[] X,byte[] Y,byte[] SEC)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ ECP P=ECP.fromBytes(SEC);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG px=BIG.fromBytes(X);
+ BIG py=BIG.fromBytes(Y);
+ px.add(py);
+ px.mod(r);
+ // px.rsub(r);
+
+ P=PAIR.G1mul(P,px);
+ P.neg();
+ P.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Implement step 1 on client side of MPin protocol */
+ public static int CLIENT_1(int sha,int date,byte[] CLIENT_ID,RAND rng,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG x;
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P,T,W;
+ BIG px;
+// byte[] t=new byte[EFS];
+
+ byte[] h=hashit(sha,0,CLIENT_ID,EFS);
+ P=ECP.mapit(h);
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ pin%=MAXPIN;
+ W=P.pinmul(pin,PBLEN);
+ T.add(W);
+ if (date!=0)
+ {
+ W=ECP.fromBytes(PERMIT);
+ if (W.is_infinity()) return INVALID_POINT;
+ T.add(W);
+ h=hashit(sha,date,h,EFS);
+ W=ECP.mapit(h);
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ W=PAIR.G1mul(W,x);
+ P.add(W);
+ //P.affine();
+ }
+ else
+ {
+ P.add(W); //P.affine();
+ P=PAIR.G1mul(P,x);
+ }
+ if (xCID!=null) P.toBytes(xCID,false);
+ }
+ else
+ {
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ }
+ }
+
+ //T.affine();
+ T.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+ public static int GET_SERVER_SECRET(byte[] S,byte[] SST)
+ {
+ ECP2 Q=ECP2.generator();
+ BIG s=BIG.fromBytes(S);
+ Q=PAIR.G2mul(Q,s);
+ Q.toBytes(SST);
+ return 0;
+ }
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+ public static int GET_G1_MULTIPLE(RAND rng, int type,byte[] X,byte[] G,byte[] W)
+ {
+ BIG x;
+ BIG r=new BIG(ROM.CURVE_Order);
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P;
+ if (type==0)
+ {
+ P=ECP.fromBytes(G);
+ if (P.is_infinity()) return INVALID_POINT;
+ }
+ else
+ P=ECP.mapit(G);
+
+ PAIR.G1mul(P,x).toBytes(W,false);
+ return 0;
+ }
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+ public static int GET_CLIENT_SECRET(byte[] S,byte[] CID,byte[] CST)
+ {
+ return GET_G1_MULTIPLE(null,1,S,CID,CST);
+ }
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+ public static int GET_CLIENT_PERMIT(int sha,int date,byte[] S,byte[] CID,byte[] CTT)
+ {
+ byte[] h=hashit(sha,date,CID,EFS);
+ ECP P=ECP.mapit(h);
+
+ BIG s=BIG.fromBytes(S);
+ ECP OP=PAIR.G1mul(P,s);
+
+ OP.toBytes(CTT,false);
+ return 0;
+ }
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+ public static void SERVER_1(int sha,int date,byte[] CID,byte[] HID,byte[] HTID)
+ {
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R,P=ECP.mapit(h);
+
+ P.toBytes(HID,false); // new
+ if (date!=0)
+ {
+ // if (HID!=null) P.toBytes(HID);
+ h=hashit(sha,date,h,EFS);
+ R=ECP.mapit(h);
+ P.add(R); //P.affine();
+ P.toBytes(HTID,false);
+ }
+ // else P.toBytes(HID,false);
+ }
+
+/* Implement step 2 of MPin protocol on server side */
+ public static int SERVER_2(int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] mSEC,byte[] E,byte[] F)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ ECP2 Q=ECP2.generator();
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+
+ ECP R;
+ if (date!=0)
+ R=ECP.fromBytes(xCID);
+ else
+ {
+ if (xID==null) return BAD_PARAMS;
+ R=ECP.fromBytes(xID);
+ }
+ if (R.is_infinity()) return INVALID_POINT;
+
+ BIG y=BIG.fromBytes(Y);
+ ECP P;
+ if (date!=0) P=ECP.fromBytes(HTID);
+ else
+ {
+ if (HID==null) return BAD_PARAMS;
+ P=ECP.fromBytes(HID);
+ }
+
+ if (P.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ R=ECP.fromBytes(mSEC);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ FP12 g;
+
+ g=PAIR.ate2(Q,R,sQ,P);
+ g=PAIR.fexp(g);
+
+ if (!g.isunity())
+ {
+ if (HID!=null && xID!=null && E!=null && F!=null)
+ {
+ g.toBytes(E);
+ if (date!=0)
+ {
+ P=ECP.fromBytes(HID);
+ if (P.is_infinity()) return INVALID_POINT;
+ R=ECP.fromBytes(xID);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ }
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(F);
+ }
+ return BAD_PIN;
+ }
+
+ return 0;
+ }
+
+/* Pollards kangaroos used to return PIN error */
+ public static int KANGAROO(byte[] E,byte[] F)
+ {
+ FP12 ge=FP12.fromBytes(E);
+ FP12 gf=FP12.fromBytes(F);
+ int[] distance = new int[TS];
+ FP12 t=new FP12(gf);
+ FP12[] table=new FP12[TS];
+ int i,j,m,s,dn,dm,res,steps;
+
+ s=1;
+ for (m=0;m<TS;m++)
+ {
+ distance[m]=s;
+ table[m]=new FP12(t);
+ s*=2;
+ t.usqr();
+ }
+ t.one();
+ dn=0;
+ for (j=0;j<TRAP;j++)
+ {
+ i=t.geta().geta().getA().lastbits(20)%TS;
+ t.mul(table[i]);
+ dn+=distance[i];
+ }
+ gf.copy(t); gf.conj();
+ steps=0; dm=0;
+ res=0;
+ while (dm-dn<MAXPIN)
+ {
+ steps++;
+ if (steps>4*TRAP) break;
+ i=ge.geta().geta().getA().lastbits(20)%TS;
+ ge.mul(table[i]);
+ dm+=distance[i];
+ if (ge.equals(t))
+ {
+ res=dm-dn;
+ break;
+ }
+ if (ge.equals(gf))
+ {
+ res=dn-dm;
+ break;
+ }
+
+ }
+ if (steps>4*TRAP || dm-dn>=MAXPIN) {res=0; } // Trap Failed - probable invalid token
+ return res;
+ }
+
+/* Functions to support M-Pin Full */
+
+ public static int PRECOMPUTE(byte[] TOKEN,byte[] CID,byte[] G1,byte[] G2)
+ {
+ ECP P,T;
+ FP12 g;
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ P=ECP.mapit(CID);
+
+ ECP2 Q=ECP2.generator();
+
+ g=PAIR.ate(Q,T);
+ g=PAIR.fexp(g);
+ g.toBytes(G1);
+
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(G2);
+
+ return 0;
+ }
+
+
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+ public static int CLIENT_KEY(int sha,byte[] G1,byte[] G2,int pin,byte[] R,byte[] X,byte[] H,byte[] wCID,byte[] CK)
+ {
+ byte[] t;
+
+ FP12 g1=FP12.fromBytes(G1);
+ FP12 g2=FP12.fromBytes(G2);
+ BIG z=BIG.fromBytes(R);
+ BIG x=BIG.fromBytes(X);
+ BIG h=BIG.fromBytes(H);
+
+ ECP W=ECP.fromBytes(wCID);
+ if (W.is_infinity()) return INVALID_POINT;
+
+ W=PAIR.G1mul(W,x);
+
+// FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG r=new BIG(ROM.CURVE_Order);
+// BIG q=new BIG(ROM.Modulus);
+
+ z.add(h); //new
+ z.mod(r);
+
+ g2.pinpow(pin,PBLEN);
+ g1.mul(g2);
+
+ FP4 c=g1.compow(z,r);
+/*
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(z);
+ a.mod(m);
+
+ BIG b=new BIG(z);
+ b.div(m);
+
+
+ FP4 c=g1.trace();
+ g2.copy(g1);
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+*/
+ t=mpin_hash(sha,c,W);
+
+ for (int i=0;i<ECP.AESKEY;i++) CK[i]=t[i];
+
+ return 0;
+ }
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+ public static int SERVER_KEY(int sha,byte[] Z,byte[] SST,byte[] W,byte[] H,byte[] HID,byte[] xID,byte[] xCID,byte[] SK)
+ {
+ byte[] t;
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+ ECP R=ECP.fromBytes(Z);
+ if (R.is_infinity()) return INVALID_POINT;
+ ECP A=ECP.fromBytes(HID);
+ if (A.is_infinity()) return INVALID_POINT;
+
+ ECP U;
+ if (xCID!=null)
+ U=ECP.fromBytes(xCID);
+ else
+ U=ECP.fromBytes(xID);
+ if (U.is_infinity()) return INVALID_POINT;
+
+ BIG w=BIG.fromBytes(W);
+ BIG h=BIG.fromBytes(H);
+ A=PAIR.G1mul(A,h); // new
+ R.add(A); //R.affine();
+
+ U=PAIR.G1mul(U,w);
+ FP12 g=PAIR.ate(sQ,R);
+ g=PAIR.fexp(g);
+
+ FP4 c=g.trace();
+
+ t=mpin_hash(sha,c,U);
+
+ for (int i=0;i<ECP.AESKEY;i++) SK[i]=t[i];
+
+ return 0;
+ }
+
+/* Generate Y = H(epoch, xCID/xID) */
+ public static void GET_Y(int sha,int TimeValue,byte[] xCID,byte[] Y)
+ {
+ byte[] h = hashit(sha,TimeValue,xCID,EFS);
+ BIG y = BIG.fromBytes(h);
+ BIG q=new BIG(ROM.CURVE_Order);
+ y.mod(q);
+ //if (ROM.AES_S>0)
+ //{
+ // y.mod2m(2*ROM.AES_S);
+ //}
+ y.toBytes(Y);
+ }
+
+/* One pass MPIN Client */
+ public static int CLIENT(int sha,int date,byte[] CLIENT_ID,RAND RNG,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT, int TimeValue, byte[] Y)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ rtn = CLIENT_1(sha,date,CLIENT_ID,RNG,X,pin,TOKEN,SEC,xID,xCID,PERMIT);
+ if (rtn != 0)
+ return rtn;
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+
+/* One pass MPIN Server */
+ public static int SERVER(int sha,int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] SEC,byte[] E,byte[] F,byte[] CID, int TimeValue)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ SERVER_1(sha,date,CID,HID,HTID);
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = SERVER_2(date,HID,HTID,Y,SST,xID,xCID,SEC,E,F);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/PAIR.java b/src/main/java/org/apache/milagro/amcl/BN254CX/PAIR.java
new file mode 100644
index 0000000..6c587bf
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/PAIR.java
@@ -0,0 +1,817 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BN Curve Pairing functions */
+
+package org.apache.milagro.amcl.BN254CX;
+
+public final class PAIR {
+
+ public static final boolean USE_GLV =true;
+ public static final boolean USE_GS_G2 =true;
+ public static final boolean USE_GS_GT =true;
+ public static final boolean GT_STRONG=false;
+
+
+/* Line function */
+ public static FP12 line(ECP2 A,ECP2 B,FP Qx,FP Qy)
+ {
+//System.out.println("Into line");
+ FP4 a,b,c; // Edits here
+// c=new FP4(0);
+ if (A==B)
+ { // Doubling
+ FP2 XX=new FP2(A.getx()); //X
+ FP2 YY=new FP2(A.gety()); //Y
+ FP2 ZZ=new FP2(A.getz()); //Z
+ FP2 YZ=new FP2(YY); //Y
+ YZ.mul(ZZ); //YZ
+ XX.sqr(); //X^2
+ YY.sqr(); //Y^2
+ ZZ.sqr(); //Z^2
+
+ YZ.imul(4);
+ YZ.neg(); YZ.norm(); //-2YZ
+ YZ.pmul(Qy); //-2YZ.Ys
+
+ XX.imul(6); //3X^2
+ XX.pmul(Qx); //3X^2.Xs
+
+ int sb=3*ROM.CURVE_B_I;
+ ZZ.imul(sb);
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ ZZ.div_ip2();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ ZZ.mul_ip();
+ ZZ.add(ZZ);
+ YZ.mul_ip();
+ YZ.norm();
+ }
+
+ ZZ.norm(); // 3b.Z^2
+
+ YY.add(YY);
+ ZZ.sub(YY); ZZ.norm(); // 3b.Z^2-Y^2
+
+ a=new FP4(YZ,ZZ); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(XX); // L(0,1) | L(0,0) | L(1,0)
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(XX); c.times_i();
+ }
+ A.dbl();
+ }
+ else
+ { // Addition - assume B is affine
+
+ FP2 X1=new FP2(A.getx()); // X1
+ FP2 Y1=new FP2(A.gety()); // Y1
+ FP2 T1=new FP2(A.getz()); // Z1
+ FP2 T2=new FP2(A.getz()); // Z1
+
+ T1.mul(B.gety()); // T1=Z1.Y2
+ T2.mul(B.getx()); // T2=Z1.X2
+
+ X1.sub(T2); X1.norm(); // X1=X1-Z1.X2
+ Y1.sub(T1); Y1.norm(); // Y1=Y1-Z1.Y2
+
+ T1.copy(X1); // T1=X1-Z1.X2
+ X1.pmul(Qy); // X1=(X1-Z1.X2).Ys
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X1.mul_ip();
+ X1.norm();
+ }
+
+ T1.mul(B.gety()); // T1=(X1-Z1.X2).Y2
+
+ T2.copy(Y1); // T2=Y1-Z1.Y2
+ T2.mul(B.getx()); // T2=(Y1-Z1.Y2).X2
+ T2.sub(T1); T2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+ Y1.pmul(Qx); Y1.neg(); Y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+ a=new FP4(X1,T2); // (X1-Z1.X2).Ys | (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2 | - (Y1-Z1.Y2).Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(Y1);
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(Y1); c.times_i();
+ }
+ A.add(B);
+ }
+//System.out.println("Out of line");
+ return new FP12(a,b,c);
+ }
+
+/* Optimal R-ate pairing */
+ public static FP12 ate(ECP2 P1,ECP Q1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+
+ ECP2 A=new ECP2();
+ FP12 r=new FP12(1);
+ A.copy(P);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ //r.conj();
+ A.neg();
+ }
+ K.copy(P);
+ K.frob(f);
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+ public static FP12 ate2(ECP2 P1,ECP Q1,ECP2 R1,ECP S1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ ECP2 R=new ECP2(R1);
+ ECP S=new ECP(S1);
+
+ R.affine();
+ S.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+ FP Sx=new FP(S.getx());
+ FP Sy=new FP(S.gety());
+
+ ECP2 A=new ECP2();
+ ECP2 B=new ECP2();
+ FP12 r=new FP12(1);
+
+ A.copy(P);
+ B.copy(R);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+ ECP2 MR=new ECP2();
+ MR.copy(R); MR.neg();
+
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ lv=line(B,B,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ lv=line(B,R,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ //R.neg();
+ lv=line(B,MR,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //R.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ // r.conj();
+ A.neg();
+ B.neg();
+ }
+
+ K.copy(P);
+ K.frob(f);
+
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.copy(R);
+ K.frob(f);
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+ public static FP12 fexp(FP12 m)
+ {
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ FP12 r=new FP12(m);
+
+/* Easy part of final exp */
+ FP12 lv=new FP12(r);
+ lv.inverse();
+ r.conj();
+
+ r.mul(lv);
+ lv.copy(r);
+ r.frob(f);
+ r.frob(f);
+ r.mul(lv);
+/* Hard part of final exp */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ FP12 x0,x1,x2,x3,x4,x5;
+ lv.copy(r);
+ lv.frob(f);
+ x0=new FP12(lv);
+ x0.frob(f);
+ lv.mul(r);
+ x0.mul(lv);
+ x0.frob(f);
+ x1=new FP12(r);
+ x1.conj();
+ x4=r.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x4.conj();
+ }
+
+ x3=new FP12(x4);
+ x3.frob(f);
+
+ x2=x4.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x2.conj();
+ }
+ x5=new FP12(x2); x5.conj();
+ lv=x2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ lv.conj();
+ }
+ x2.frob(f);
+ r.copy(x2); r.conj();
+
+ x4.mul(r);
+ x2.frob(f);
+
+ r.copy(lv);
+ r.frob(f);
+ lv.mul(r);
+
+ lv.usqr();
+ lv.mul(x4);
+ lv.mul(x5);
+ r.copy(x3);
+ r.mul(x5);
+ r.mul(lv);
+ lv.mul(x2);
+ r.usqr();
+ r.mul(lv);
+ r.usqr();
+ lv.copy(r);
+ lv.mul(x1);
+ r.mul(x0);
+ lv.usqr();
+ r.mul(lv);
+ r.reduce();
+ }
+ else
+ {
+
+ FP12 y0,y1,y2,y3;
+// Ghamman & Fouotsa Method
+ y0=new FP12(r); y0.usqr();
+ y1=y0.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y1.conj();
+ }
+ x.fshr(1); y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+
+ x.fshl(1);
+ y3=new FP12(r); y3.conj();
+ y1.mul(y3);
+
+ y1.conj();
+ y1.mul(y2);
+
+ y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y3=y2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y3.conj();
+ }
+ y1.conj();
+ y3.mul(y1);
+
+ y1.conj();
+ y1.frob(f); y1.frob(f); y1.frob(f);
+ y2.frob(f); y2.frob(f);
+ y1.mul(y2);
+
+ y2=y3.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y2.mul(y0);
+ y2.mul(r);
+
+ y1.mul(y2);
+ y2.copy(y3); y2.frob(f);
+ y1.mul(y2);
+ r.copy(y1);
+ r.reduce();
+ }
+
+ return r;
+ }
+
+/* GLV method */
+ public static BIG[] glv(BIG e)
+ {
+ BIG[] u=new BIG[2];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+
+ BIG[] v=new BIG[2];
+ for (i=0;i<2;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_W[i])); // why not just t=new BIG(ROM.CURVE_W[i]);
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<2;i++)
+ for (j=0;j<2;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_SB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ { // -(x^2).P = (Beta.x,y)
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG x2=BIG.smul(x,x);
+ u[0]=new BIG(e);
+ u[0].mod(x2);
+ u[1]=new BIG(e);
+ u[1].div(x2);
+ u[1].rsub(q);
+ }
+ return u;
+ }
+
+/* Galbraith & Scott Method */
+ public static BIG[] gs(BIG e)
+ {
+ BIG[] u=new BIG[4];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] v=new BIG[4];
+ for (i=0;i<4;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_WB[i]));
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<4;i++)
+ for (j=0;j<4;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_BB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ {
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG w=new BIG(e);
+ for (int i=0;i<3;i++)
+ {
+ u[i]=new BIG(w);
+ u[i].mod(x);
+ w.div(x);
+ }
+ u[3]=new BIG(w);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ u[1].copy(BIG.modneg(u[1],q));
+ u[3].copy(BIG.modneg(u[3],q));
+ }
+ }
+ return u;
+ }
+
+/* Multiply P by e in group G1 */
+ public static ECP G1mul(ECP P,BIG e)
+ {
+ ECP R;
+ if (USE_GLV)
+ {
+ //P.affine();
+ R=new ECP();
+ R.copy(P);
+ int i,np,nn;
+ ECP Q=new ECP();
+ Q.copy(P); Q.affine();
+ BIG q=new BIG(ROM.CURVE_Order);
+ FP cru=new FP(new BIG(ROM.CURVE_Cru));
+ BIG t=new BIG(0);
+ BIG[] u=glv(e);
+ Q.getx().mul(cru);
+
+ np=u[0].nbits();
+ t.copy(BIG.modneg(u[0],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[0].copy(t);
+ R.neg();
+ }
+
+ np=u[1].nbits();
+ t.copy(BIG.modneg(u[1],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[1].copy(t);
+ Q.neg();
+ }
+ u[0].norm();
+ u[1].norm();
+ R=R.mul2(u[0],Q,u[1]);
+
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* Multiply P by e in group G2 */
+ public static ECP2 G2mul(ECP2 P,BIG e)
+ {
+ ECP2 R;
+ if (USE_GS_G2)
+ {
+ ECP2[] Q=new ECP2[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] u=gs(e);
+
+ BIG t=new BIG(0);
+ int i,np,nn;
+ //P.affine();
+
+ Q[0]=new ECP2(); Q[0].copy(P);
+ for (i=1;i<4;i++)
+ {
+ Q[i]=new ECP2(); Q[i].copy(Q[i-1]);
+ Q[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ Q[i].neg();
+ }
+ u[i].norm();
+ //Q[i].affine();
+ }
+
+ R=ECP2.mul4(Q,u);
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */
+ public static FP12 GTpow(FP12 d,BIG e)
+ {
+ FP12 r;
+ if (USE_GS_GT)
+ {
+ FP12[] g=new FP12[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG t=new BIG(0);
+ int i,np,nn;
+ BIG[] u=gs(e);
+
+ g[0]=new FP12(d);
+ for (i=1;i<4;i++)
+ {
+ g[i]=new FP12(0); g[i].copy(g[i-1]);
+ g[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ g[i].conj();
+ }
+ u[i].norm();
+ }
+ r=FP12.pow4(g,u);
+ }
+ else
+ {
+ r=d.pow(e);
+ }
+ return r;
+ }
+
+/* test group membership - no longer needed */
+/* with GT-Strong curve, now only check that m!=1, conj(m)*m==1, and m.m^{p^4}=m^{p^2} */
+/*
+ public static boolean GTmember(FP12 m)
+ {
+ if (m.isunity()) return false;
+ FP12 r=new FP12(m);
+ r.conj();
+ r.mul(m);
+ if (!r.isunity()) return false;
+
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ r.copy(m); r.frob(f); r.frob(f);
+ FP12 w=new FP12(r); w.frob(f); w.frob(f);
+ w.mul(m);
+ if (!ROM.GT_STRONG)
+ {
+ if (!w.equals(r)) return false;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ r.copy(m); w=r.pow(x); w=w.pow(x);
+ r.copy(w); r.sqr(); r.mul(w); r.sqr();
+ w.copy(m); w.frob(f);
+ }
+ return w.equals(r);
+ }
+*/
+/*
+ public static void main(String[] args) {
+ ECP Q=new ECP(new BIG(ROM.CURVE_Gx),new BIG(ROM.CURVE_Gy));
+ ECP2 P=new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG xa=new BIG(ROM.CURVE_Pxa);
+
+ System.out.println("P= "+P.toString());
+ System.out.println("Q= "+Q.toString());
+
+ BIG m=new BIG(17);
+
+ FP12 e=ate(P,Q);
+ System.out.println("\ne= "+e.toString());
+
+ e=fexp(e);
+
+ for (int i=1;i<1000;i++)
+ {
+ e=ate(P,Q);
+ e=fexp(e);
+ }
+ // e=GTpow(e,m);
+
+ System.out.println("\ne= "+e.toString());
+
+ BIG [] GLV=glv(r);
+
+ System.out.println("GLV[0]= "+GLV[0].toString());
+ System.out.println("GLV[0]= "+GLV[1].toString());
+
+ ECP G=new ECP(); G.copy(Q);
+ ECP2 R=new ECP2(); R.copy(P);
+
+
+ e=ate(R,Q);
+ e=fexp(e);
+
+ e=GTpow(e,xa);
+ System.out.println("\ne= "+e.toString());
+
+
+ R=G2mul(R,xa);
+ e=ate(R,G);
+ e=fexp(e);
+
+ System.out.println("\ne= "+e.toString());
+
+ G=G1mul(G,xa);
+ e=ate(P,G);
+ e=fexp(e);
+ System.out.println("\ne= "+e.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BN254CX/ROM.java b/src/main/java/org/apache/milagro/amcl/BN254CX/ROM.java
new file mode 100644
index 0000000..0e837f0
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BN254CX/ROM.java
@@ -0,0 +1,58 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.BN254CX;
+
+public class ROM
+{
+// Base Bits= 56
+ public static final long[] Modulus= {0x6623EF5C1B55B3L,0xD6EE18093EE1BEL,0x647A6366D3243FL,0x8702A0DB0BDDFL,0x24000000L};
+ public static final long[] R2modp= {0x466A0618A0800AL,0x2B3A22543056A3L,0x148515B09C6600L,0xEC9EA5606BDF50L,0x1C992E66L};
+ public static final long MConst= 0x4E205BF9789E85L;
+
+ public static final int CURVE_A= 0;
+ public static final int CURVE_B_I= 2;
+ public static final int CURVE_Cof_I= 1;
+ public static final long[] CURVE_B= {0x2L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Order= {0x11C0A636EB1F6DL,0xD6EE0CC906CEBEL,0x647A6366D2C43FL,0x8702A0DB0BDDFL,0x24000000L};
+ public static final long[] CURVE_Gx= {0x6623EF5C1B55B2L,0xD6EE18093EE1BEL,0x647A6366D3243FL,0x8702A0DB0BDDFL,0x24000000L};
+ public static final long[] CURVE_Gy= {0x1L,0x0L,0x0L,0x0L,0x0L};
+
+ public static final long[] CURVE_Bnx= {0x3C012B1L,0x40L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Cru= {0xE0931794235C97L,0xDF6471EF875631L,0xCA83F1440BDL,0x480000L,0x0L};
+ public static final long[] Fra= {0xD9083355C80EA3L,0x7326F173F8215BL,0x8AACA718986867L,0xA63A0164AFE18BL,0x1359082FL};
+ public static final long[] Frb= {0x8D1BBC06534710L,0x63C7269546C062L,0xD9CDBC4E3ABBD8L,0x623628A900DC53L,0x10A6F7D0L};
+
+ public static final long[] CURVE_Pxa= {0x851CEEE4D2EC74L,0x85BFA03E2726C0L,0xF5C34BBB907CL,0x7053B256358B25L,0x19682D2CL};
+ public static final long[] CURVE_Pxb= {0xA58E8B2E29CFE1L,0x97B0C209C30F47L,0x37A8E99743F81BL,0x3E19F64AA011C9L,0x1466B9ECL};
+ public static final long[] CURVE_Pya= {0xFBFCEBCF0BE09FL,0xB33D847EC1B30CL,0x157DAEE2096361L,0x72332B8DD81E22L,0xA79EDD9L};
+ public static final long[] CURVE_Pyb= {0x904B228898EE9DL,0x4EA569D2EDEBEDL,0x512D8D3461C286L,0xECC4C09035C6E4L,0x6160C39L};
+
+
+ public static final long[][] CURVE_W= {{0x546349162FEB83L,0xB40381200L,0x6000L,0x0L,0x0L},{0x7802561L,0x80L,0x0L,0x0L,0x0L}};
+ public static final long[][][] CURVE_SB= {{{0x5463491DB010E4L,0xB40381280L,0x6000L,0x0L,0x0L},{0x7802561L,0x80L,0x0L,0x0L,0x0L}},{{0x7802561L,0x80L,0x0L,0x0L,0x0L},{0xBD5D5D20BB33EAL,0xD6EE0188CEBCBDL,0x647A6366D2643FL,0x8702A0DB0BDDFL,0x24000000L}}};
+ public static final long[][] CURVE_WB= {{0x1C2118567A84B0L,0x3C012B040L,0x2000L,0x0L,0x0L},{0xCDF995BE220475L,0x94EDA8CA7F9A36L,0x8702A0DC07EL,0x300000L,0x0L},{0x66FCCAE0F10B93L,0x4A76D4653FCD3BL,0x4381506E03FL,0x180000L,0x0L},{0x1C21185DFAAA11L,0x3C012B0C0L,0x2000L,0x0L,0x0L}};
+ public static final long[][][] CURVE_BB= {{{0x11C0A6332B0CBDL,0xD6EE0CC906CE7EL,0x647A6366D2C43FL,0x8702A0DB0BDDFL,0x24000000L},{0x11C0A6332B0CBCL,0xD6EE0CC906CE7EL,0x647A6366D2C43FL,0x8702A0DB0BDDFL,0x24000000L},{0x11C0A6332B0CBCL,0xD6EE0CC906CE7EL,0x647A6366D2C43FL,0x8702A0DB0BDDFL,0x24000000L},{0x7802562L,0x80L,0x0L,0x0L,0x0L}},{{0x7802561L,0x80L,0x0L,0x0L,0x0L},{0x11C0A6332B0CBCL,0xD6EE0CC906CE7EL,0x647A6366D2C43FL,0x8702A0DB0BDDFL,0x24000000L},{0x11C0A6332B0CBDL,0xD6EE0CC906CE7EL,0x647A6366D2C43FL,0x8702A0DB0BDDFL,0x24000000L},{0x11C0A6332B0CBCL,0xD6EE0CC906CE7EL,0x647A6366D2C43FL,0x8702A0DB0BDDFL,0x24000000L}},{{0x7802562L,0x80L,0x0L,0x0L,0x0L},{0x7802561L,0x80L,0x0L,0x0L,0x0L},{0x7802561L,0x80L,0x0L,0x0L,0x0L},{0x7802561L,0x80L,0x0L,0x0L,0x0L}},{{0x3C012B2L,0x40L,0x0L,0x0L,0x0L},{0xF004AC2L,0x100L,0x0L,0x0L,0x0L},{0x11C0A62F6AFA0AL,0xD6EE0CC906CE3EL,0x647A6366D2C43FL,0x8702A0DB0BDDFL,0x24000000L},{0x3C012B2L,0x40L,0x0L,0x0L,0x0L}}};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BRAINPOOL/BIG.java b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/BIG.java
new file mode 100644
index 0000000..575028b
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.BRAINPOOL;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=32; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BRAINPOOL/DBIG.java b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/DBIG.java
new file mode 100644
index 0000000..45d1ffe
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.BRAINPOOL;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BRAINPOOL/ECDH.java b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/ECDH.java
new file mode 100644
index 0000000..2c164cb
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.BRAINPOOL;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BRAINPOOL/ECP.java b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/ECP.java
new file mode 100644
index 0000000..46df5b7
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.BRAINPOOL;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/BRAINPOOL/FP.java b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/FP.java
new file mode 100644
index 0000000..35adafa
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.BRAINPOOL;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=256; /* Number of bits in Modulus */
+ public static final int MOD8=7; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<24); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/BRAINPOOL/ROM.java b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/ROM.java
new file mode 100644
index 0000000..e234556
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/BRAINPOOL/ROM.java
@@ -0,0 +1,43 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+/* Note that the original curve has been transformed to an isomorphic curve with A=-3 */
+
+package org.apache.milagro.amcl.BRAINPOOL;
+
+public class ROM
+{
+
+// Base Bits= 56
+ public static final long[] Modulus= {0x13481D1F6E5377L,0xF623D526202820L,0x909D838D726E3BL,0xA1EEA9BC3E660AL,0xA9FB57DBL};
+ public static final long[] R2modp= {0x9E04F49B9A3787L,0x29317218F3CF49L,0x54E8C3CF1DBC89L,0xBB411A3F7559CAL,0x9773E15FL};
+ public static final long MConst= 0xA75590CEFD89B9L;
+
+ public static final int CURVE_Cof_I= 1;
+ public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= -3;
+ public static final int CURVE_B_I= 0;
+ public static final long[] CURVE_B= {0xE58101FEE92B04L,0xEBC4AF2F49256AL,0x733D0B76B7BF93L,0x30D84EA4FE66A7L,0x662C61C4L};
+ public static final long[] CURVE_Order= {0x1E0E82974856A7L,0x7AA3B561A6F790L,0x909D838D718C39L,0xA1EEA9BC3E660AL,0xA9FB57DBL};
+ public static final long[] CURVE_Gx= {0xA191562E1305F4L,0x42C47AAFBC2B79L,0xB23A656149AFA1L,0xC1CFE7B7732213L,0xA3E8EB3CL};
+ public static final long[] CURVE_Gy= {0xABE8F35B25C9BEL,0xB6DE39D027001DL,0xE14644417E69BCL,0x3439C56D7F7B22L,0x2D996C82L};
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/C25519/BIG.java b/src/main/java/org/apache/milagro/amcl/C25519/BIG.java
new file mode 100644
index 0000000..a661762
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C25519/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.C25519;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=32; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/C25519/DBIG.java b/src/main/java/org/apache/milagro/amcl/C25519/DBIG.java
new file mode 100644
index 0000000..1e27a1d
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C25519/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.C25519;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/C25519/ECDH.java b/src/main/java/org/apache/milagro/amcl/C25519/ECDH.java
new file mode 100644
index 0000000..5532f65
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C25519/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.C25519;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/C25519/ECP.java b/src/main/java/org/apache/milagro/amcl/C25519/ECP.java
new file mode 100644
index 0000000..aeb3526
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C25519/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.C25519;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=MONTGOMERY;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/C25519/FP.java b/src/main/java/org/apache/milagro/amcl/C25519/FP.java
new file mode 100644
index 0000000..3a78107
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C25519/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.C25519;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=255; /* Number of bits in Modulus */
+ public static final int MOD8=5; /* Modulus mod 8 */
+ public static final int MODTYPE=PSEUDO_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<25); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/C25519/ROM.java b/src/main/java/org/apache/milagro/amcl/C25519/ROM.java
new file mode 100644
index 0000000..dc3c95a
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C25519/ROM.java
@@ -0,0 +1,42 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.C25519;
+
+public class ROM
+{
+
+// Base Bits= 56
+public static final long[] Modulus= {0xFFFFFFFFFFFFEDL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0x7FFFFFFFL};
+public static final long[] R2modp= {0xA4000000000000L,0x5L,0x0L,0x0L,0x0L};
+public static final long MConst= 0x13L;
+
+public static final int CURVE_Cof_I= 8;
+public static final long[] CURVE_Cof= {0x8L,0x0L,0x0L,0x0L,0x0L};
+public static final int CURVE_A= 486662;
+public static final int CURVE_B_I= 0;
+public static final long[] CURVE_B= {0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Order= {0x12631A5CF5D3EDL,0xF9DEA2F79CD658L,0x14DEL,0x0L,0x10000000L};
+public static final long[] CURVE_Gx= {0x9L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Gy= {0x0L,0x0L,0x0L,0x0L,0x0L};
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/C41417/BIG.java b/src/main/java/org/apache/milagro/amcl/C41417/BIG.java
new file mode 100644
index 0000000..145342f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C41417/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.C41417;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=52; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=60;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/C41417/DBIG.java b/src/main/java/org/apache/milagro/amcl/C41417/DBIG.java
new file mode 100644
index 0000000..2dbd26a
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C41417/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.C41417;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/C41417/ECDH.java b/src/main/java/org/apache/milagro/amcl/C41417/ECDH.java
new file mode 100644
index 0000000..60a340e
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C41417/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.C41417;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/C41417/ECP.java b/src/main/java/org/apache/milagro/amcl/C41417/ECP.java
new file mode 100644
index 0000000..61b523c
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C41417/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.C41417;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=EDWARDS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=64;
+ public static final int AESKEY=32;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/C41417/FP.java b/src/main/java/org/apache/milagro/amcl/C41417/FP.java
new file mode 100644
index 0000000..673d16f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C41417/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.C41417;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=414; /* Number of bits in Modulus */
+ public static final int MOD8=7; /* Modulus mod 8 */
+ public static final int MODTYPE=PSEUDO_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<6); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/C41417/ROM.java b/src/main/java/org/apache/milagro/amcl/C41417/ROM.java
new file mode 100644
index 0000000..f23f20f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/C41417/ROM.java
@@ -0,0 +1,44 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.C41417;
+
+public class ROM
+{
+
+// Base Bits= 60
+ public static final long[] Modulus= {0xFFFFFFFFFFFFFEFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFL};
+ public static final long[] R2modp= {0x121000L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long MConst= 0x11L;
+
+
+ public static final int CURVE_Cof_I= 8;
+ public static final long[] CURVE_Cof= {0x8L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= 1;
+ public static final int CURVE_B_I= 3617;
+ public static final long[] CURVE_B= {0xE21L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Order= {0xB0E71A5E106AF79L,0x1C0338AD63CF181L,0x414CF706022B36FL,0xFFFFFFFFEB3CC92L,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0x7FFFFFFFFFFFFL};
+ public static final long[] CURVE_Gx= {0x4FD3812F3CBC595L,0x1A73FAA8537C64CL,0x4AB4D6D6BA11130L,0x3EC7F57FF35498AL,0xE5FCD46369F44C0L,0x300218C0631C326L,0x1A334905141443L};
+ public static final long[] CURVE_Gy= {0x22L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/ED25519/BIG.java b/src/main/java/org/apache/milagro/amcl/ED25519/BIG.java
new file mode 100644
index 0000000..8fa4f59
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ED25519/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.ED25519;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=32; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/ED25519/DBIG.java b/src/main/java/org/apache/milagro/amcl/ED25519/DBIG.java
new file mode 100644
index 0000000..18d7453
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ED25519/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.ED25519;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/ED25519/ECDH.java b/src/main/java/org/apache/milagro/amcl/ED25519/ECDH.java
new file mode 100644
index 0000000..55dcaeb
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ED25519/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.ED25519;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/ED25519/ECP.java b/src/main/java/org/apache/milagro/amcl/ED25519/ECP.java
new file mode 100644
index 0000000..65f151a
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ED25519/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.ED25519;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=EDWARDS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/ED25519/FP.java b/src/main/java/org/apache/milagro/amcl/ED25519/FP.java
new file mode 100644
index 0000000..cab29f7
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ED25519/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.ED25519;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=255; /* Number of bits in Modulus */
+ public static final int MOD8=5; /* Modulus mod 8 */
+ public static final int MODTYPE=PSEUDO_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<25); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/ED25519/ROM.java b/src/main/java/org/apache/milagro/amcl/ED25519/ROM.java
new file mode 100644
index 0000000..a50866a
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/ED25519/ROM.java
@@ -0,0 +1,43 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.ED25519;
+
+public class ROM
+{
+
+public static final long[] Modulus= {0xFFFFFFFFFFFFEDL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0x7FFFFFFFL};
+public static final long[] R2modp= {0xA4000000000000L,0x5L,0x0L,0x0L,0x0L};
+public static final long MConst= 0x13;
+
+
+public static final int CURVE_Cof_I= 8;
+public static final long[] CURVE_Cof= {0x8L,0x0L,0x0L,0x0L,0x0L};
+public static final int CURVE_A= -1;
+public static final int CURVE_B_I= 0;
+public static final long[] CURVE_B= {0xEB4DCA135978A3L,0xA4D4141D8AB75L,0x797779E8980070L,0x2B6FFE738CC740L,0x52036CEEL};
+public static final long[] CURVE_Order= {0x12631A5CF5D3EDL,0xF9DEA2F79CD658L,0x14DEL,0x0L,0x10000000L};
+public static final long[] CURVE_Gx= {0x562D608F25D51AL,0xC7609525A7B2C9L,0x31FDD6DC5C692CL,0xCD6E53FEC0A4E2L,0x216936D3L};
+public static final long[] CURVE_Gy= {0x66666666666658L,0x66666666666666L,0x66666666666666L,0x66666666666666L,0x66666666L};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/BIG.java b/src/main/java/org/apache/milagro/amcl/FP256BN/BIG.java
new file mode 100644
index 0000000..80cee96
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.FP256BN;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=32; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/DBIG.java b/src/main/java/org/apache/milagro/amcl/FP256BN/DBIG.java
new file mode 100644
index 0000000..88d39eb
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.FP256BN;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/ECDH.java b/src/main/java/org/apache/milagro/amcl/FP256BN/ECDH.java
new file mode 100644
index 0000000..9d9de60
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.FP256BN;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/ECP.java b/src/main/java/org/apache/milagro/amcl/FP256BN/ECP.java
new file mode 100644
index 0000000..4c45abb
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.FP256BN;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=BN;
+ public static final int SEXTIC_TWIST=M_TYPE;
+ public static final int SIGN_OF_X=NEGATIVEX;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/ECP2.java b/src/main/java/org/apache/milagro/amcl/FP256BN/ECP2.java
new file mode 100644
index 0000000..43499d3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/ECP2.java
@@ -0,0 +1,796 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Weierstrass elliptic curve functions over FP2 */
+
+package org.apache.milagro.amcl.FP256BN;
+
+public final class ECP2 {
+ private FP2 x;
+ private FP2 y;
+ private FP2 z;
+// private boolean INF;
+
+/* Constructor - set this=O */
+ public ECP2() {
+// INF=true;
+ x=new FP2(0);
+ y=new FP2(1);
+ z=new FP2(0);
+ }
+
+ public ECP2(ECP2 e) {
+ this.x = new FP2(e.x);
+ this.y = new FP2(e.y);
+ this.z = new FP2(e.z);
+ }
+
+/* Test this=O? */
+ public boolean is_infinity() {
+// if (INF) return true; //******
+ return (x.iszilch() && z.iszilch());
+ }
+/* copy this=P */
+ public void copy(ECP2 P)
+ {
+ x.copy(P.x);
+ y.copy(P.y);
+ z.copy(P.z);
+// INF=P.INF;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ y.one();
+ z.zero();
+ }
+
+/* Conditional move of Q to P dependant on d */
+ public void cmove(ECP2 Q,int d)
+ {
+ x.cmove(Q.x,d);
+ y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ }
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(ECP2 W[],int b)
+ {
+ ECP2 MP=new ECP2();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test if P == Q */
+ public boolean equals(ECP2 Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+
+ FP2 a=new FP2(x); // *****
+ FP2 b=new FP2(Q.x);
+ a.mul(Q.z);
+ b.mul(z);
+ if (!a.equals(b)) return false;
+
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+
+ return true;
+ }
+/* set this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ y.norm();
+ y.neg(); y.norm();
+ return;
+ }
+/* set to Affine - (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return;
+ FP2 one=new FP2(1);
+ if (z.equals(one))
+ {
+ x.reduce();
+ y.reduce();
+ return;
+ }
+ z.inverse();
+
+ x.mul(z); x.reduce(); // *****
+ y.mul(z); y.reduce();
+ z.copy(one);
+ }
+/* extract affine x as FP2 */
+ public FP2 getX()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.x;
+ }
+/* extract affine y as FP2 */
+ public FP2 getY()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.y;
+ }
+/* extract projective x */
+ public FP2 getx()
+ {
+ return x;
+ }
+/* extract projective y */
+ public FP2 gety()
+ {
+ return y;
+ }
+/* extract projective z */
+ public FP2 getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP2 W=new ECP2(this);
+ W.affine();
+ W.x.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i]=t[i];
+ W.x.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+BIG.MODBYTES]=t[i];
+
+ W.y.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+2*BIG.MODBYTES]=t[i];
+ W.y.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+3*BIG.MODBYTES]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP2 fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG ra;
+ BIG rb;
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 rx=new FP2(ra,rb);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+2*BIG.MODBYTES];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+3*BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 ry=new FP2(ra,rb);
+
+ return new ECP2(rx,ry);
+ }
+/* convert this to hex string */
+ public String toString() {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ return "("+W.x.toString()+","+W.y.toString()+")";
+ }
+
+/* Calculate RHS of twisted curve equation x^3+B/i */
+ public static FP2 RHS(FP2 x) {
+ x.norm();
+ FP2 r=new FP2(x);
+ r.sqr();
+ FP2 b=new FP2(new BIG(ROM.CURVE_B));
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b.div_ip();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b.norm();
+ b.mul_ip();
+ b.norm();
+ }
+
+
+ r.mul(x);
+ r.add(b);
+
+ r.reduce();
+ return r;
+ }
+
+/* construct this from (x,y) - but set to O if not on curve */
+ public ECP2(FP2 ix,FP2 iy) {
+ x=new FP2(ix);
+ y=new FP2(iy);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ FP2 y2=new FP2(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+// if (y2.equals(rhs)) INF=false;
+// else {x.zero();INF=true;}
+ }
+
+/* construct this from x - but set to O if not on curve */
+ public ECP2(FP2 ix) {
+ x=new FP2(ix);
+ y=new FP2(1);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ if (rhs.sqrt())
+ {
+ y.copy(rhs);
+ //INF=false;
+ }
+ else {/*x.zero();INF=true;*/ inf();}
+ }
+
+/* this+=this */
+ public int dbl() {
+// if (INF) return -1;
+//System.out.println("Into dbl");
+ FP2 iy=new FP2(y);
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ iy.mul_ip(); iy.norm();
+ }
+ FP2 t0=new FP2(y); //***** Change
+ t0.sqr();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip();
+ }
+ FP2 t1=new FP2(iy);
+ t1.mul(z);
+ FP2 t2=new FP2(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z);
+ z.add(z);
+ z.norm();
+
+ t2.imul(3*ROM.CURVE_B_I);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip();
+ t2.norm();
+ }
+
+ FP2 x3=new FP2(t2);
+ x3.mul(z);
+
+ FP2 y3=new FP2(t0);
+
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1); t2.norm();
+ t0.sub(t2); t0.norm(); //y^2-9bz^2
+ y3.mul(t0); y3.add(x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+ t1.copy(x); t1.mul(iy); //
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x); //(y^2-9bz^2)xy2
+
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ return 1;
+ }
+
+/* this+=Q - return 0 for add, 1 for double, -1 for O */
+ public int add(ECP2 Q) {
+// if (INF)
+// {
+// copy(Q);
+// return -1;
+// }
+// if (Q.INF) return -1;
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP2 t0=new FP2(x);
+ t0.mul(Q.x); // x.Q.x
+ FP2 t1=new FP2(y);
+ t1.mul(Q.y); // y.Q.y
+
+ FP2 t2=new FP2(z);
+ t2.mul(Q.z);
+ FP2 t3=new FP2(x);
+ t3.add(y); t3.norm(); //t3=X1+Y1
+ FP2 t4=new FP2(Q.x);
+ t4.add(Q.y); t4.norm(); //t4=X2+Y2
+ t3.mul(t4); //t3=(X1+Y1)(X2+Y2)
+ t4.copy(t0); t4.add(t1); //t4=X1.X2+Y1.Y2
+
+ t3.sub(t4); t3.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t3.mul_ip(); t3.norm(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+ }
+ t4.copy(y);
+ t4.add(z); t4.norm(); //t4=Y1+Z1
+ FP2 x3=new FP2(Q.y);
+ x3.add(Q.z); x3.norm(); //x3=Y2+Z2
+
+ t4.mul(x3); //t4=(Y1+Z1)(Y2+Z2)
+ x3.copy(t1); //
+ x3.add(t2); //X3=Y1.Y2+Z1.Z2
+
+ t4.sub(x3); t4.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t4.mul_ip(); t4.norm(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+ }
+ x3.copy(x); x3.add(z); x3.norm(); // x3=X1+Z1
+ FP2 y3=new FP2(Q.x);
+ y3.add(Q.z); y3.norm(); // y3=X2+Z2
+ x3.mul(y3); // x3=(X1+Z1)(X2+Z2)
+ y3.copy(t0);
+ y3.add(t2); // y3=X1.X2+Z1+Z2
+ y3.rsub(x3); y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip(); t0.norm(); // x.Q.x
+ t1.mul_ip(); t1.norm(); // y.Q.y
+ }
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip(); t2.norm();
+ }
+ FP2 z3=new FP2(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ y3.mul_ip();
+ y3.norm();
+ }
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ return 0;
+ }
+
+/* set this-=Q */
+ public int sub(ECP2 Q) {
+ ECP2 NQ=new ECP2(Q);
+ NQ.neg();
+ int D=add(NQ);
+ //Q.neg();
+ //int D=add(Q);
+ //Q.neg();
+ return D;
+ }
+/* set this*=q, where q is Modulus, using Frobenius */
+ public void frob(FP2 X)
+ {
+// if (INF) return;
+ FP2 X2=new FP2(X);
+
+ X2.sqr();
+ x.conj();
+ y.conj();
+ z.conj();
+ z.reduce();
+ x.mul(X2);
+
+ y.mul(X2);
+ y.mul(X);
+ }
+
+/* P*=e */
+ public ECP2 mul(BIG e)
+ {
+/* fixed size windows */
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP2 P=new ECP2();
+ ECP2 Q=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2[] W=new ECP2[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ if (is_infinity()) return new ECP2();
+
+ //affine();
+
+/* precompute table */
+ Q.copy(this);
+ Q.dbl();
+ W[0]=new ECP2();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP2();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+/* make exponent odd - add 2P if even, P if odd */
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+/* convert exponent to signed 4-bit window */
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C);
+ P.affine();
+ return P;
+ }
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ ECP2 W=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] T=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ //Q[i].affine();
+ }
+
+ T[0] = new ECP2(); T[0].copy(Q[0]); // Q[0]
+ T[1] = new ECP2(); T[1].copy(T[0]); T[1].add(Q[1]); // Q[0]+Q[1]
+ T[2] = new ECP2(); T[2].copy(T[0]); T[2].add(Q[2]); // Q[0]+Q[2]
+ T[3] = new ECP2(); T[3].copy(T[1]); T[3].add(Q[2]); // Q[0]+Q[1]+Q[2]
+ T[4] = new ECP2(); T[4].copy(T[0]); T[4].add(Q[3]); // Q[0]+Q[3]
+ T[5] = new ECP2(); T[5].copy(T[1]); T[5].add(Q[3]); // Q[0]+Q[1]+Q[3]
+ T[6] = new ECP2(); T[6].copy(T[2]); T[6].add(Q[3]); // Q[0]+Q[2]+Q[3]
+ T[7] = new ECP2(); T[7].copy(T[3]); T[7].add(Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ P.select(T,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ P.dbl();
+ W.select(T,(int)(2*w[i]+s[i]));
+ P.add(W);
+ }
+
+ // apply correction
+ W.copy(P);
+ W.sub(Q[0]);
+ P.cmove(W,pb);
+ P.affine();
+ return P;
+ }
+
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+/*
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb;
+ int[] a=new int[4];
+ ECP2 T=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] W=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ Q[i].affine();
+ }
+
+// precompute table
+
+ W[0]=new ECP2(); W[0].copy(Q[0]); W[0].sub(Q[1]);
+
+ W[1]=new ECP2(); W[1].copy(W[0]);
+ W[2]=new ECP2(); W[2].copy(W[0]);
+ W[3]=new ECP2(); W[3].copy(W[0]);
+ W[4]=new ECP2(); W[4].copy(Q[0]); W[4].add(Q[1]);
+ W[5]=new ECP2(); W[5].copy(W[4]);
+ W[6]=new ECP2(); W[6].copy(W[4]);
+ W[7]=new ECP2(); W[7].copy(W[4]);
+ T.copy(Q[2]); T.sub(Q[3]);
+ W[1].sub(T);
+ W[2].add(T);
+ W[5].sub(T);
+ W[6].add(T);
+ T.copy(Q[2]); T.add(Q[3]);
+ W[0].sub(T);
+ W[3].add(T);
+ W[4].sub(T);
+ W[7].add(T);
+
+// if multiplier is even add 1 to multiplier, and add P to correction
+ mt.zero(); C.inf();
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ C.add(Q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(byte)(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ P.dbl();
+ P.add(T);
+ }
+ P.sub(C); // apply correction
+
+ P.affine();
+ return P;
+ }
+*/
+
+/* needed for SOK */
+ public static ECP2 mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ BIG one=new BIG(1);
+ FP2 X;
+ ECP2 Q;
+ x.mod(q);
+ while (true)
+ {
+ X=new FP2(one,x);
+ Q=new ECP2(X);
+ if (!Q.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+
+ BIG Fra=new BIG(ROM.Fra);
+ BIG Frb=new BIG(ROM.Frb);
+ X=new FP2(Fra,Frb);
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X.inverse();
+ X.norm();
+ }
+
+ x=new BIG(ROM.CURVE_Bnx);
+
+/* Fast Hashing to G2 - Fuentes-Castaneda, Knapp and Rodriguez-Henriquez */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ ECP2 T,K;
+
+ T=new ECP2(); T.copy(Q);
+ T=T.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ T.neg();
+ }
+ K=new ECP2(); K.copy(T);
+ K.dbl(); K.add(T); //K.affine();
+
+ K.frob(X);
+ Q.frob(X); Q.frob(X); Q.frob(X);
+ Q.add(T); Q.add(K);
+ T.frob(X); T.frob(X);
+ Q.add(T);
+
+ }
+
+/* Efficient hash maps to G2 on BLS curves - Budroni, Pintore */
+/* Q -> x2Q -xQ -Q +F(xQ -Q) +F(F(2Q)) */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BLS)
+ {
+ // ECP2 xQ,x2Q;
+ // xQ=new ECP2();
+ // x2Q=new ECP2();
+
+ ECP2 xQ=Q.mul(x);
+ ECP2 x2Q=xQ.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ xQ.neg();
+ }
+
+ x2Q.sub(xQ);
+ x2Q.sub(Q);
+
+ xQ.sub(Q);
+ xQ.frob(X);
+
+ Q.dbl();
+ Q.frob(X);
+ Q.frob(X);
+
+ Q.add(x2Q);
+ Q.add(xQ);
+ }
+ Q.affine();
+ return Q;
+ }
+
+ public static ECP2 generator()
+ {
+ return new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+ }
+
+/*
+ public static void main(String[] args) {
+ BIG r=new BIG(ROM.Modulus);
+
+ BIG Pxa=new BIG(ROM.CURVE_Pxa);
+ BIG Pxb=new BIG(ROM.CURVE_Pxb);
+ BIG Pya=new BIG(ROM.CURVE_Pya);
+ BIG Pyb=new BIG(ROM.CURVE_Pyb);
+
+ BIG Fra=new BIG(ROM.CURVE_Fra);
+ BIG Frb=new BIG(ROM.CURVE_Frb);
+
+ FP2 f=new FP2(Fra,Frb);
+
+ FP2 Px=new FP2(Pxa,Pxb);
+ FP2 Py=new FP2(Pya,Pyb);
+
+ ECP2 P=new ECP2(Px,Py);
+
+ System.out.println("P= "+P.toString());
+
+ P=P.mul(r);
+ System.out.println("P= "+P.toString());
+
+ ECP2 Q=new ECP2(Px,Py);
+ Q.frob(f);
+ System.out.println("Q= "+Q.toString());
+ } */
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/FP.java b/src/main/java/org/apache/milagro/amcl/FP256BN/FP.java
new file mode 100644
index 0000000..d24323b
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.FP256BN;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=256; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<24); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/FP12.java b/src/main/java/org/apache/milagro/amcl/FP256BN/FP12.java
new file mode 100644
index 0000000..0e6ee71
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/FP12.java
@@ -0,0 +1,907 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Fp^12 functions */
+/* FP12 elements are of the form a+i.b+i^2.c */
+
+package org.apache.milagro.amcl.FP256BN;
+
+public final class FP12 {
+ private final FP4 a;
+ private final FP4 b;
+ private final FP4 c;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ c.reduce();
+ }
+/* normalise all components of this */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ c.norm();
+ }
+/* test x==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch() && c.iszilch());
+ }
+
+ public void cmove(FP12 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ c.cmove(g.c,d);
+ }
+
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(FP12 g[],int b)
+ {
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(g[0],teq(babs,0)); // conditional move
+ cmove(g[1],teq(babs,1));
+ cmove(g[2],teq(babs,2));
+ cmove(g[3],teq(babs,3));
+ cmove(g[4],teq(babs,4));
+ cmove(g[5],teq(babs,5));
+ cmove(g[6],teq(babs,6));
+ cmove(g[7],teq(babs,7));
+
+ FP12 invf=new FP12(this);
+ invf.conj();
+ cmove(invf,(int)(m&1));
+ }
+
+
+/* test x==1 ? */
+ public boolean isunity() {
+ FP4 one=new FP4(1);
+ return (a.equals(one) && b.iszilch() && c.iszilch());
+ }
+/* return 1 if x==y, else 0 */
+ public boolean equals(FP12 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b) && c.equals(x.c));
+ }
+/* extract a from this */
+ public FP4 geta()
+ {
+ return a;
+ }
+/* extract b */
+ public FP4 getb()
+ {
+ return b;
+ }
+/* extract c */
+ public FP4 getc()
+ {
+ return c;
+ }
+/* copy this=x */
+ public void copy(FP12 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ c.copy(x.c);
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ c.zero();
+ }
+/* this=conj(this) */
+ public void conj()
+ {
+ a.conj();
+ b.nconj();
+ c.conj();
+ }
+/* Constructors */
+ public FP12(FP4 d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(int d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(FP4 d,FP4 e,FP4 f)
+ {
+ a=new FP4(d);
+ b=new FP4(e);
+ c=new FP4(f);
+ }
+
+ public FP12(FP12 x)
+ {
+ a=new FP4(x.a);
+ b=new FP4(x.b);
+ c=new FP4(x.c);
+ }
+
+/* Granger-Scott Unitary Squaring */
+ public void usqr()
+ {
+//System.out.println("Into usqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(c);
+ FP4 C=new FP4(b);
+ FP4 D=new FP4(0);
+
+ a.sqr();
+ D.copy(a); D.add(a);
+ a.add(D);
+
+ a.norm();
+ A.nconj();
+
+ A.add(A);
+ a.add(A);
+ B.sqr();
+ B.times_i();
+
+ D.copy(B); D.add(B);
+ B.add(D);
+ B.norm();
+
+ C.sqr();
+ D.copy(C); D.add(C);
+ C.add(D);
+ C.norm();
+
+ b.conj();
+ b.add(b);
+ c.nconj();
+
+ c.add(c);
+ b.add(B);
+ c.add(C);
+//System.out.println("Out of usqr 1");
+ reduce();
+//System.out.println("Out of usqr 2");
+ }
+
+/* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+ public void sqr()
+ {
+//System.out.println("Into sqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(b);
+ FP4 C=new FP4(c);
+ FP4 D=new FP4(a);
+
+ A.sqr();
+ B.mul(c);
+ B.add(B);
+ B.norm();
+ C.sqr();
+ D.mul(b);
+ D.add(D);
+
+ c.add(a);
+ c.add(b);
+ c.norm();
+ c.sqr();
+
+ a.copy(A);
+
+ A.add(B);
+ A.norm();
+ A.add(C);
+ A.add(D);
+ A.norm();
+
+ A.neg();
+ B.times_i();
+ C.times_i();
+
+ a.add(B);
+
+ b.copy(C); b.add(D);
+ c.add(A);
+//System.out.println("Out of sqr");
+ norm();
+ }
+
+/* FP12 full multiplication this=this*y */
+ public void mul(FP12 y)
+ {
+//System.out.println("Into mul");
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(y.a);
+
+ z0.mul(y.a);
+ z2.mul(y.b);
+
+ t0.add(b);
+ t1.add(y.b);
+
+ t0.norm();
+ t1.norm();
+
+ z1.copy(t0); z1.mul(t1);
+ t0.copy(b); t0.add(c);
+
+ t1.copy(y.b); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ z3.copy(t0); z3.mul(t1);
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ z1.add(t0);
+ //z1.norm();
+ b.copy(z1); b.add(t1);
+
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+t0.norm();
+t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c); t0.mul(y.c);
+ t1.copy(t0); t1.neg();
+
+// z2.norm();
+// z3.norm();
+// b.norm();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ norm();
+//System.out.println("Out of mul");
+ }
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+ public void smul(FP12 y,int type)
+ {
+//System.out.println("Into smul");
+
+ if (type==ECP.D_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(b);
+ FP4 t0=new FP4(0);
+ FP4 t1=new FP4(y.a);
+ z0.mul(y.a);
+ z2.pmul(y.b.real());
+ b.add(a);
+ t1.real().add(y.b.real());
+
+ t1.norm();
+ b.norm();
+ b.mul(t1);
+ z3.add(c);
+ z3.norm();
+ z3.pmul(y.b.real());
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ b.add(t0);
+
+ b.add(t1);
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t0.norm();
+ z3.norm();
+ t0.mul(y.a);
+ c.copy(z2); c.add(t0);
+
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ if (type==ECP.M_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(0);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(0);
+
+ z0.mul(y.a);
+ t0.add(b);
+ t0.norm();
+
+ z1.copy(t0); z1.mul(y.a);
+ t0.copy(b); t0.add(c);
+ t0.norm();
+
+ z3.copy(t0); //z3.mul(y.c);
+ z3.pmul(y.c.getb());
+ z3.times_i();
+
+ t0.copy(z0); t0.neg();
+
+ z1.add(t0);
+ b.copy(z1);
+ z2.copy(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c);
+
+ t0.pmul(y.c.getb());
+ t0.times_i();
+
+ t1.copy(t0); t1.neg();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ norm();
+//System.out.println("Out of smul");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ FP4 f0=new FP4(a);
+ FP4 f1=new FP4(b);
+ FP4 f2=new FP4(a);
+ FP4 f3=new FP4(0);
+
+ norm();
+ f0.sqr();
+ f1.mul(c);
+ f1.times_i();
+ f0.sub(f1);
+ f0.norm();
+
+ f1.copy(c); f1.sqr();
+ f1.times_i();
+ f2.mul(b);
+ f1.sub(f2);
+ f1.norm();
+
+ f2.copy(b); f2.sqr();
+ f3.copy(a); f3.mul(c);
+ f2.sub(f3);
+ f2.norm();
+
+ f3.copy(b); f3.mul(f2);
+ f3.times_i();
+ a.mul(f0);
+ f3.add(a);
+ c.mul(f1);
+ c.times_i();
+
+ f3.add(c);
+ f3.norm();
+ f3.inverse();
+ a.copy(f0); a.mul(f3);
+ b.copy(f1); b.mul(f3);
+ c.copy(f2); c.mul(f3);
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ FP2 f2=new FP2(f);
+ FP2 f3=new FP2(f);
+
+ f2.sqr();
+ f3.mul(f2);
+
+ a.frob(f3);
+ b.frob(f3);
+ c.frob(f3);
+
+ b.pmul(f);
+ c.pmul(f2);
+ }
+
+/* trace function */
+ public FP4 trace()
+ {
+ FP4 t=new FP4(0);
+ t.copy(a);
+ t.imul(3);
+ t.reduce();
+ return t;
+ }
+
+/* convert from byte array to FP12 */
+ public static FP12 fromBytes(byte[] w)
+ {
+ BIG a,b;
+ FP2 c,d;
+ FP4 e,f,g;
+ byte[] t=new byte[BIG.MODBYTES];
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+2*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+3*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ e=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+4*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+5*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+6*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+7*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ f=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+8*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+9*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+10*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+11*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ g=new FP4(c,d);
+
+ return new FP12(e,f,g);
+ }
+
+/* convert this to byte array */
+ public void toBytes(byte[] w)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ a.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i]=t[i];
+ a.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+BIG.MODBYTES]=t[i];
+ a.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+2*BIG.MODBYTES]=t[i];
+ a.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+3*BIG.MODBYTES]=t[i];
+
+ b.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+4*BIG.MODBYTES]=t[i];
+ b.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+5*BIG.MODBYTES]=t[i];
+ b.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+6*BIG.MODBYTES]=t[i];
+ b.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+7*BIG.MODBYTES]=t[i];
+
+ c.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+8*BIG.MODBYTES]=t[i];
+ c.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+9*BIG.MODBYTES]=t[i];
+ c.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+10*BIG.MODBYTES]=t[i];
+ c.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+11*BIG.MODBYTES]=t[i];
+ }
+
+/* convert to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+","+c.toString()+"]");
+ }
+
+/* this=this^e */
+/* Note this is simple square and multiply, so not side-channel safe */
+ public FP12 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ BIG e3=new BIG(e);
+ e3.pmul(3);
+ e3.norm();
+
+ FP12 w=new FP12(this);
+
+ int nb=e3.nbits();
+ for (int i=nb-2;i>=1;i--)
+ {
+ w.usqr();
+ int bt=e3.bit(i)-e.bit(i);
+ if (bt==1)
+ w.mul(this);
+ if (bt==-1)
+ {
+ conj(); w.mul(this); conj();
+ }
+ }
+ w.reduce();
+ return w;
+
+
+/*
+ BIG z=new BIG(e);
+ FP12 r=new FP12(1);
+
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.usqr();
+ }
+ r.reduce();
+ return r; */
+ }
+
+/* constant time powering by small integer of max length bts */
+ public void pinpow(int e,int bts)
+ {
+ int i,b;
+ FP12 [] R=new FP12[2];
+ R[0]=new FP12(1);
+ R[1]=new FP12(this);
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ R[1-b].mul(R[b]);
+ R[b].usqr();
+ }
+ this.copy(R[0]);
+ }
+
+ public FP4 compow(BIG e,BIG r)
+ {
+ FP12 g1=new FP12(0);
+ FP12 g2=new FP12(0);
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.Modulus);
+
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(e);
+ a.mod(m);
+
+ BIG b=new BIG(e);
+ b.div(m);
+
+ g1.copy(this);
+ g2.copy(this);
+
+ FP4 c=g1.trace();
+
+ if (b.iszilch())
+ {
+ c=c.xtr_pow(e);
+ return c;
+ }
+
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+
+ return c;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ FP12 [] g=new FP12[8];
+ FP12 r=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ }
+ g[0]=new FP12(q[0]); // q[0]
+ g[1]=new FP12(g[0]); g[1].mul(q[1]); // q[0].q[1]
+ g[2]=new FP12(g[0]); g[2].mul(q[2]); // q[0].q[2]
+ g[3]=new FP12(g[1]); g[3].mul(q[2]); // q[0].q[1].q[2]
+ g[4]=new FP12(q[0]); g[4].mul(q[3]); // q[0].q[3]
+ g[5]=new FP12(g[1]); g[5].mul(q[3]); // q[0].q[1].q[3]
+ g[6]=new FP12(g[2]); g[6].mul(q[3]); // q[0].q[2].q[3]
+ g[7]=new FP12(g[3]); g[7].mul(q[3]); // q[0].q[1].q[2].q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ p.select(g,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ p.usqr();
+ r.select(g,(int)(2*w[i]+s[i]));
+ p.mul(r);
+ }
+
+ // apply correction
+ r.copy(q[0]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb);
+
+ p.reduce();
+ return p;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+/* Timing attack secure, but not cache attack secure */
+/*
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,m;
+ int[] a=new int[4];
+ FP12 [] g=new FP12[8];
+ FP12 [] s=new FP12[2];
+ FP12 c=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ t[i]=new BIG(u[i]);
+
+ s[0]=new FP12(0);
+ s[1]=new FP12(0);
+
+ g[0]=new FP12(q[0]); s[0].copy(q[1]); s[0].conj(); g[0].mul(s[0]);
+ g[1]=new FP12(g[0]);
+ g[2]=new FP12(g[0]);
+ g[3]=new FP12(g[0]);
+ g[4]=new FP12(q[0]); g[4].mul(q[1]);
+ g[5]=new FP12(g[4]);
+ g[6]=new FP12(g[4]);
+ g[7]=new FP12(g[4]);
+
+ s[1].copy(q[2]); s[0].copy(q[3]); s[0].conj(); s[1].mul(s[0]);
+ s[0].copy(s[1]); s[0].conj(); g[1].mul(s[0]);
+ g[2].mul(s[1]);
+ g[5].mul(s[0]);
+ g[6].mul(s[1]);
+ s[1].copy(q[2]); s[1].mul(q[3]);
+ s[0].copy(s[1]); s[0].conj(); g[0].mul(s[0]);
+ g[3].mul(s[1]);
+ g[4].mul(s[0]);
+ g[7].mul(s[1]);
+
+// if power is even add 1 to power, and add q to correction
+
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ c.mul(q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+ c.conj();
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+ p.copy(g[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ m=w[i]>>7;
+ j=(w[i]^m)-m; // j=abs(w[i])
+ j=(j-1)/2;
+ s[0].copy(g[j]); s[1].copy(g[j]); s[1].conj();
+ p.usqr();
+ p.mul(s[m&1]);
+ }
+ p.mul(c); // apply correction
+ p.reduce();
+ return p;
+ }
+*/
+/*
+ public static void main(String[] args) {
+ BIG p=new BIG(ROM.Modulus);
+ FP2 w0,w1;
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.zero(); b.zero(); a.inc(1); b.inc(2);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(3); b.inc(4);
+ w1=new FP2(a,b);
+ FP4 t0=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(5); b.inc(6);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(7); b.inc(8);
+ w1=new FP2(a,b);
+ FP4 t1=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(9); b.inc(10);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(11); b.inc(12);
+ w1=new FP2(a,b);
+ FP4 t2=new FP4(w0,w1);
+
+ FP12 w=new FP12(t0,t1,t2);
+ FP12 t=new FP12(w);
+
+ System.out.println("w= "+w.toString());
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ w.frob(f);
+ System.out.println("w= "+w.toString());
+
+ w=t.pow(p);
+
+ System.out.println("w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("w= "+w.toString());
+
+ t.copy(w);
+ w.conj();
+ t.inverse();
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)= "+w.toString());
+
+ t.copy(w);
+ w.frob(f);
+ w.frob(f);
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)(p^2+1)= "+w.toString());
+
+ t.copy(w);
+
+ t.inverse();
+ w.conj();
+
+ System.out.println("w= "+w.toString());
+ System.out.println("t= "+t.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/FP2.java b/src/main/java/org/apache/milagro/amcl/FP256BN/FP2.java
new file mode 100644
index 0000000..e9cc7f9
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/FP2.java
@@ -0,0 +1,425 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^2 functions */
+
+/* FP2 elements are of the form a+ib, where i is sqrt(-1) */
+
+package org.apache.milagro.amcl.FP256BN;
+
+public final class FP2 {
+ private final FP a;
+ private final FP b;
+
+/* reduce components mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+
+/* normalise components of w */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+
+/* test this=0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP2 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this=1 ? */
+ public boolean isunity() {
+ FP one=new FP(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test this=x */
+ public boolean equals(FP2 x) {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+
+/* Constructors */
+ public FP2(int c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(FP2 x)
+ {
+ a=new FP(x.a);
+ b=new FP(x.b);
+ }
+
+ public FP2(FP c,FP d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(BIG c,BIG d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(FP c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(BIG c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+/*
+ public BIG geta()
+ {
+ return a.tobig();
+ }
+*/
+/* extract a */
+ public BIG getA()
+ {
+ return a.redc();
+ }
+
+/* extract b */
+ public BIG getB()
+ {
+ return b.redc();
+ }
+
+/* copy this=x */
+ public void copy(FP2 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+
+/* negate this mod Modulus */
+ public void neg()
+ {
+ FP m=new FP(a);
+ FP t=new FP(0);
+
+ m.add(b);
+ m.neg();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ }
+
+/* set to a-ib */
+ public void conj()
+ {
+ b.neg();
+ b.norm();
+ }
+
+/* this+=a */
+ public void add(FP2 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+
+/* this-=a */
+ public void sub(FP2 x)
+ {
+ FP2 m=new FP2(x);
+ m.neg();
+ add(m);
+ }
+
+ public void rsub(FP2 x) // *****
+ {
+ neg();
+ add(x);
+ }
+
+/* this*=s, where s is an FP */
+ public void pmul(FP s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this*=i, where i is an int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+ FP w1=new FP(a);
+ FP w3=new FP(a);
+ FP mb=new FP(b);
+
+ w1.add(b);
+ mb.neg();
+
+ w3.add(a);
+ w3.norm();
+ b.mul(w3);
+
+ a.add(mb);
+
+ w1.norm();
+ a.norm();
+
+ a.mul(w1);
+ }
+
+/* this*=y */
+/* Now uses Lazy reduction */
+ public void mul(FP2 y)
+ {
+ if ((long)(a.XES+b.XES)*(y.a.XES+y.b.XES)>(long)FP.FEXCESS)
+ {
+ if (a.XES>1) a.reduce();
+ if (b.XES>1) b.reduce();
+ }
+
+ DBIG pR=new DBIG(0);
+ BIG C=new BIG(a.x);
+ BIG D=new BIG(y.a.x);
+
+ pR.ucopy(new BIG(ROM.Modulus));
+
+ DBIG A=BIG.mul(a.x,y.a.x);
+ DBIG B=BIG.mul(b.x,y.b.x);
+
+ C.add(b.x); C.norm();
+ D.add(y.b.x); D.norm();
+
+ DBIG E=BIG.mul(C,D);
+ DBIG F=new DBIG(A); F.add(B);
+ B.rsub(pR);
+
+ A.add(B); A.norm();
+ E.sub(F); E.norm();
+
+ a.x.copy(FP.mod(A)); a.XES=3;
+ b.x.copy(FP.mod(E)); b.XES=2;
+ }
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP w1=new FP(b);
+ FP w2=new FP(a);
+ w1.sqr(); w2.sqr(); w1.add(w2);
+ if (w1.jacobi()!=1) { zero(); return false; }
+ w1=w1.sqrt();
+ w2.copy(a); w2.add(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1)
+ {
+ w2.copy(a); w2.sub(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1) { zero(); return false; }
+ }
+ w2=w2.sqrt();
+ a.copy(w2);
+ w2.add(w2);
+ w2.inverse();
+ b.mul(w2);
+ return true;
+ }
+
+/* output to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ norm();
+ FP w1=new FP(a);
+ FP w2=new FP(b);
+
+ w1.sqr();
+ w2.sqr();
+ w1.add(w2);
+ w1.inverse();
+ a.mul(w1);
+ w1.neg();
+ w1.norm();
+ b.mul(w1);
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+/* this*=sqrt(-1) */
+ public void times_i()
+ {
+ FP z=new FP(a);
+ a.copy(b); a.neg();
+ b.copy(z);
+ }
+
+/* w*=(1+sqrt(-1)) */
+/* where X*2-(1+sqrt(-1)) is irreducible for FP4, assumes p=3 mod 8 */
+ public void mul_ip()
+ {
+ FP2 t=new FP2(this);
+ FP z=new FP(a);
+ a.copy(b);
+ a.neg();
+ b.copy(z);
+ add(t);
+ }
+
+ public void div_ip2()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ }
+
+/* w/=(1+sqrt(-1)) */
+ public void div_ip()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ div2();
+ }
+/*
+ public FP2 pow(BIG e)
+ {
+ int bt;
+ FP2 r=new FP2(1);
+ e.norm();
+ norm();
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(this);
+ if (e.iszilch()) break;
+ sqr();
+ }
+
+ r.reduce();
+ return r;
+ }
+
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(27);
+ BIG pp1=new BIG(m);
+ BIG pm1=new BIG(m);
+ BIG a=new BIG(1);
+ BIG b=new BIG(1);
+ FP2 w=new FP2(a,b);
+ FP2 z=new FP2(w);
+
+ byte[] RAW=new byte[100];
+
+ RAND rng=new RAND();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ // for (int i=0;i<100;i++)
+ // {
+ a.randomnum(rng);
+ b.randomnum(rng);
+
+ w=new FP2(a,b);
+ System.out.println("w="+w.toString());
+
+ z=new FP2(w);
+ z.inverse();
+ System.out.println("z="+z.toString());
+
+ z.inverse();
+ if (!z.equals(w)) System.out.println("Error");
+ // }
+
+// System.out.println("m="+m.toString());
+// w.sqr();
+// w.mul(z);
+
+ System.out.println("w="+w.toString());
+
+
+ pp1.inc(1); pp1.norm();
+ pm1.dec(1); pm1.norm();
+ System.out.println("p+1="+pp1.toString());
+ System.out.println("p-1="+pm1.toString());
+ w=w.pow(pp1);
+ w=w.pow(pm1);
+ System.out.println("w="+w.toString());
+ }
+*/
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/FP4.java b/src/main/java/org/apache/milagro/amcl/FP256BN/FP4.java
new file mode 100644
index 0000000..2f4cc41
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/FP4.java
@@ -0,0 +1,721 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^4 functions */
+
+/* FP4 elements are of the form a+ib, where i is sqrt(-1+sqrt(-1)) */
+
+package org.apache.milagro.amcl.FP256BN;
+
+public final class FP4 {
+ private final FP2 a;
+ private final FP2 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP4 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP2 one=new FP2(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP2 real()
+ {
+ return a;
+ }
+
+ public FP2 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP2 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP4 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP4(int c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+
+ public FP4(FP4 x)
+ {
+ a=new FP2(x.a);
+ b=new FP2(x.b);
+ }
+
+ public FP4(FP2 c,FP2 d)
+ {
+ a=new FP2(c);
+ b=new FP2(d);
+ }
+
+ public FP4(FP2 c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+/* copy this=x */
+ public void copy(FP4 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP2 m=new FP2(a);
+ FP2 t=new FP2(0);
+ m.add(b);
+// m.norm();
+ m.neg();
+ // m.norm();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP4 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP4 x)
+ {
+ FP4 m=new FP4(x);
+ m.neg();
+ add(m);
+ }
+
+/* this*=s where s is FP2 */
+ public void pmul(FP2 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this=x-this */
+ public void rsub(FP4 x)
+ {
+ neg();
+ add(x);
+ }
+
+
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.mul_ip();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.mul_ip();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+/* this*=y */
+ public void mul(FP4 y)
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(0);
+ FP2 t4=new FP2(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ // t4.sub(t1);
+ // t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ // b.copy(t4);
+ // b.sub(t2);
+
+ t2.mul_ip();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.mul_ip();
+ t2.norm();
+ t1.sub(t2);
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP2 s=new FP2(b);
+ FP2 t=new FP2(b);
+ s.times_i();
+ t.add(s);
+ // t.norm();
+ b.copy(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ a.conj();
+ b.conj();
+ b.mul(f);
+ }
+
+/* this=this^e */
+ public FP4 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP4 w=new FP4(this);
+ BIG z=new BIG(e);
+ FP4 r=new FP4(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+/* XTR xtr_a function */
+ public void xtr_A(FP4 w,FP4 y,FP4 z)
+ {
+ FP4 r=new FP4(w);
+ FP4 t=new FP4(w);
+ //y.norm();
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP4 w=new FP4(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP4 xtr_pow(BIG n) {
+ FP4 a=new FP4(3);
+ FP4 b=new FP4(this);
+ FP4 c=new FP4(b);
+ c.xtr_D();
+ FP4 t=new FP4(0);
+ FP4 r=new FP4(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP4 xtr_pow2(FP4 ck,FP4 ckml,FP4 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP4 cu=new FP4(ck); // can probably be passed in w/o copying
+ FP4 cv=new FP4(this);
+ FP4 cumv=new FP4(ckml);
+ FP4 cum2v=new FP4(ckm2l);
+ FP4 r=new FP4(0);
+ FP4 t=new FP4(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+ public void div_i()
+ {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip();
+ a.copy(v);
+ b.copy(u);
+ }
+
+ public void div_2i() {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip2();
+ v.add(v); v.norm();
+ a.copy(v);
+ b.copy(u);
+ }
+
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP2 wa=new FP2(a);
+ FP2 ws=new FP2(b);
+ FP2 wt=new FP2(a);
+
+ if (ws.iszilch())
+ {
+ if (wt.sqrt())
+ {
+ a.copy(wt);
+ b.zero();
+ } else {
+ wt.div_ip();
+ wt.sqrt();
+ b.copy(wt);
+ a.zero();
+ }
+ return true;
+ }
+
+ ws.sqr();
+ wa.sqr();
+ ws.mul_ip();
+ ws.norm();
+ wa.sub(ws);
+
+ ws.copy(wa);
+ if (!ws.sqrt()) {
+ return false;
+ }
+
+ wa.copy(wt); wa.add(ws); wa.norm(); wa.div2();
+
+ if (!wa.sqrt()) {
+ wa.copy(wt); wa.sub(ws); wa.norm(); wa.div2();
+ if (!wa.sqrt()) {
+ return false;
+ }
+ }
+ wt.copy(b);
+ ws.copy(wa); ws.add(wa);
+ ws.inverse();
+
+ wt.mul(ws);
+ a.copy(wa);
+ b.copy(wt);
+
+ return true;
+ }
+
+/* this*=s where s is FP */
+ public void qmul(FP s)
+ {
+ a.pmul(s);
+ b.pmul(s);
+ }
+
+
+
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG e=new BIG(12);
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.inc(27); b.inc(45);
+
+ FP2 w0=new FP2(a,b);
+
+ a.zero(); b.zero();
+ a.inc(33); b.inc(54);
+
+ FP2 w1=new FP2(a,b);
+
+
+ FP4 w=new FP4(w0,w1);
+ FP4 t=new FP4(w);
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ System.out.println("w= "+w.toString());
+
+ w=w.pow(m);
+
+ System.out.println("w^p= "+w.toString());
+
+ t.frob(f);
+
+
+ System.out.println("w^p= "+t.toString());
+
+ w=w.pow(m);
+ w=w.pow(m);
+ w=w.pow(m);
+ System.out.println("w^p4= "+w.toString());
+
+
+ System.out.println("Test Inversion");
+
+ w=new FP4(w0,w1);
+
+ w.inverse();
+
+ System.out.println("1/w mod p^4 = "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/(1/w) mod p^4 = "+w.toString());
+
+ FP4 ww=new FP4(w);
+
+ w=w.xtr_pow(e);
+ System.out.println("w^e= "+w.toString());
+
+
+ a.zero(); b.zero();
+ a.inc(37); b.inc(17);
+ w0=new FP2(a,b);
+ a.zero(); b.zero();
+ a.inc(49); b.inc(31);
+ w1=new FP2(a,b);
+
+ FP4 c1=new FP4(w0,w1);
+ FP4 c2=new FP4(w0,w1);
+ FP4 c3=new FP4(w0,w1);
+
+ BIG e1=new BIG(3331);
+ BIG e2=new BIG(3372);
+
+ FP4 cr=w.xtr_pow2(c1,c2,c3,e1,e2);
+
+ System.out.println("c^e= "+cr.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/MPIN.java b/src/main/java/org/apache/milagro/amcl/FP256BN/MPIN.java
new file mode 100644
index 0000000..369638c
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/MPIN.java
@@ -0,0 +1,823 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* MPIN API Functions */
+
+package org.apache.milagro.amcl.FP256BN;
+
+import java.util.Date;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public class MPIN
+{
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int PAS=16;
+ public static final int INVALID_POINT=-14;
+ public static final int BAD_PARAMS=-11;
+ public static final int WRONG_ORDER=-18;
+ public static final int BAD_PIN=-19;
+
+/* Configure your PIN here */
+
+ public static final int MAXPIN=10000; /* PIN less than this */
+ public static final int PBLEN=14; /* Number of bits in PIN */
+ public static final int TS=10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+ public static final int TRAP=200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN - approx 2*sqrt(MAXPIN) */
+
+// public static final int HASH_TYPE=SHA256;
+
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,int n,byte[] B,int len)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (n>0) H.process_num(n);
+
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+ byte[] W=new byte[len];
+
+ if (sha>=len)
+ for (int i=0;i<len;i++) W[i]=R[i];
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+len-sha]=R[i];
+ for (int i=0;i<len-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<len;i++) W[i]=0;
+ }
+ return W;
+ }
+
+ /* return time in slots since epoch */
+ public static int today() {
+ Date date=new Date();
+ return (int) (date.getTime()/(1000*60*1440));
+ }
+
+ public static byte[] HASH_ID(int sha,byte[] ID,int len)
+ {
+ return hashit(sha,0,ID,len);
+ }
+
+/* Hash the M-Pin transcript - new */
+
+ public static byte[] HASH_ALL(int sha,byte[] HID,byte[] xID,byte[] xCID,byte[] SEC,byte[] Y,byte[] R,byte[] W,int len)
+ {
+ int i,ilen,tlen=0;
+
+ ilen=HID.length+SEC.length+Y.length+R.length+W.length;
+ if (xCID!=null) ilen+=xCID.length;
+ else ilen+=xID.length;
+
+ byte[] T = new byte[ilen];
+
+ for (i=0;i<HID.length;i++) T[i]=HID[i];
+ tlen+=HID.length;
+ if (xCID!=null)
+ {
+ for (i=0;i<xCID.length;i++) T[i+tlen]=xCID[i];
+ tlen+=xCID.length;
+ }
+ else
+ {
+ for (i=0;i<xID.length;i++) T[i+tlen]=xID[i];
+ tlen+=xID.length;
+ }
+ for (i=0;i<SEC.length;i++) T[i+tlen]=SEC[i];
+ tlen+=SEC.length;
+ for (i=0;i<Y.length;i++) T[i+tlen]=Y[i];
+ tlen+=Y.length;
+ for (i=0;i<R.length;i++) T[i+tlen]=R[i];
+ tlen+=R.length;
+ for (i=0;i<W.length;i++) T[i+tlen]=W[i];
+ tlen+=W.length;
+
+ return hashit(sha,0,T,len);
+ }
+
+/* return time since epoch */
+ public static int GET_TIME() {
+ Date date=new Date();
+ return (int) (date.getTime()/1000);
+ }
+
+ public static byte[] mpin_hash(int sha,FP4 c,ECP U)
+ {
+ byte[] w=new byte[EFS];
+ byte[] t=new byte[6*EFS];
+ byte[] h=null;
+ c.geta().getA().toBytes(w); for (int i=0;i<EFS;i++) t[i]=w[i];
+ c.geta().getB().toBytes(w); for (int i=EFS;i<2*EFS;i++) t[i]=w[i-EFS];
+ c.getb().getA().toBytes(w); for (int i=2*EFS;i<3*EFS;i++) t[i]=w[i-2*EFS];
+ c.getb().getB().toBytes(w); for (int i=3*EFS;i<4*EFS;i++) t[i]=w[i-3*EFS];
+
+ U.getX().toBytes(w); for (int i=4*EFS;i<5*EFS;i++) t[i]=w[i-4*EFS];
+ U.getY().toBytes(w); for (int i=5*EFS;i<6*EFS;i++) t[i]=w[i-5*EFS];
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (h==null) return null;
+ byte[] R=new byte[ECP.AESKEY];
+ for (int i=0;i<ECP.AESKEY;i++) R[i]=h[i];
+ return R;
+ }
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+ public static ECP map(BIG u,int cb)
+ {
+ ECP P;
+ BIG x=new BIG(u);
+ BIG p=new BIG(ROM.Modulus);
+ x.mod(p);
+ while (true)
+ {
+ P=new ECP(x,cb);
+ if (!P.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+ return P;
+ }
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+ public static int unmap(BIG u,ECP P)
+ {
+ int s=P.getS();
+ ECP R;
+ int r=0;
+ BIG x=P.getX();
+ u.copy(x);
+ while (true)
+ {
+ u.dec(1); u.norm();
+ r++;
+ R=new ECP(u,s);
+ if (!R.is_infinity()) break;
+ }
+ return r;
+ }
+
+
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+ public static int ENCODING(RAND rng,byte[] E)
+ {
+ int rn,m,su,sv;
+ byte[] T=new byte[EFS];
+
+ for (int i=0;i<EFS;i++) T[i]=E[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=E[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ ECP P=new ECP(u,v);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG p=new BIG(ROM.Modulus);
+ u=BIG.randomnum(p,rng);
+
+ su=rng.getByte(); /*if (su<0) su=-su;*/ su%=2;
+
+ ECP W=map(u,su);
+ P.sub(W); //P.affine();
+ sv=P.getS();
+ rn=unmap(v,P);
+ m=rng.getByte(); /*if (m<0) m=-m;*/ m%=rn;
+ v.inc(m+1);
+ E[0]=(byte)(su+2*sv);
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+ public static int DECODING(byte[] D)
+ {
+ int su,sv;
+ byte[] T=new byte[EFS];
+
+ if ((D[0]&0x04)!=0) return INVALID_POINT;
+
+ for (int i=0;i<EFS;i++) T[i]=D[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=D[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ su=D[0]&1;
+ sv=(D[0]>>1)&1;
+ ECP W=map(u,su);
+ ECP P=map(v,sv);
+ P.add(W); //P.affine();
+ u=P.getX();
+ v=P.getY();
+ D[0]=0x04;
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+/* R=R1+R2 in group G1 */
+ public static int RECOMBINE_G1(byte[] R1,byte[] R2,byte[] R)
+ {
+ ECP P=ECP.fromBytes(R1);
+ ECP Q=ECP.fromBytes(R2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(R,false);
+ return 0;
+ }
+
+/* W=W1+W2 in group G2 */
+ public static int RECOMBINE_G2(byte[] W1,byte[] W2,byte[] W)
+ {
+ ECP2 P=ECP2.fromBytes(W1);
+ ECP2 Q=ECP2.fromBytes(W2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(W);
+ return 0;
+ }
+
+/* create random secret S */
+ public static int RANDOM_GENERATE(RAND rng,byte[] S)
+ {
+ BIG s;
+ BIG r=new BIG(ROM.CURVE_Order);
+ s=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+ return 0;
+ }
+
+/* Extract PIN from TOKEN for identity CID */
+ public static int EXTRACT_PIN(int sha,byte[] CID,int pin,byte[] TOKEN)
+ {
+ ECP P=ECP.fromBytes(TOKEN);
+ if (P.is_infinity()) return INVALID_POINT;
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R=ECP.mapit(h);
+
+
+ pin%=MAXPIN;
+
+ R=R.pinmul(pin,PBLEN);
+ P.sub(R); //P.affine();
+
+ P.toBytes(TOKEN,false);
+
+ return 0;
+ }
+
+/* Implement step 2 on client side of MPin protocol */
+ public static int CLIENT_2(byte[] X,byte[] Y,byte[] SEC)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ ECP P=ECP.fromBytes(SEC);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG px=BIG.fromBytes(X);
+ BIG py=BIG.fromBytes(Y);
+ px.add(py);
+ px.mod(r);
+ // px.rsub(r);
+
+ P=PAIR.G1mul(P,px);
+ P.neg();
+ P.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Implement step 1 on client side of MPin protocol */
+ public static int CLIENT_1(int sha,int date,byte[] CLIENT_ID,RAND rng,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG x;
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P,T,W;
+ BIG px;
+// byte[] t=new byte[EFS];
+
+ byte[] h=hashit(sha,0,CLIENT_ID,EFS);
+ P=ECP.mapit(h);
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ pin%=MAXPIN;
+ W=P.pinmul(pin,PBLEN);
+ T.add(W);
+ if (date!=0)
+ {
+ W=ECP.fromBytes(PERMIT);
+ if (W.is_infinity()) return INVALID_POINT;
+ T.add(W);
+ h=hashit(sha,date,h,EFS);
+ W=ECP.mapit(h);
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ W=PAIR.G1mul(W,x);
+ P.add(W);
+ //P.affine();
+ }
+ else
+ {
+ P.add(W); //P.affine();
+ P=PAIR.G1mul(P,x);
+ }
+ if (xCID!=null) P.toBytes(xCID,false);
+ }
+ else
+ {
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ }
+ }
+
+ //T.affine();
+ T.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+ public static int GET_SERVER_SECRET(byte[] S,byte[] SST)
+ {
+ ECP2 Q=ECP2.generator();
+ BIG s=BIG.fromBytes(S);
+ Q=PAIR.G2mul(Q,s);
+ Q.toBytes(SST);
+ return 0;
+ }
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+ public static int GET_G1_MULTIPLE(RAND rng, int type,byte[] X,byte[] G,byte[] W)
+ {
+ BIG x;
+ BIG r=new BIG(ROM.CURVE_Order);
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P;
+ if (type==0)
+ {
+ P=ECP.fromBytes(G);
+ if (P.is_infinity()) return INVALID_POINT;
+ }
+ else
+ P=ECP.mapit(G);
+
+ PAIR.G1mul(P,x).toBytes(W,false);
+ return 0;
+ }
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+ public static int GET_CLIENT_SECRET(byte[] S,byte[] CID,byte[] CST)
+ {
+ return GET_G1_MULTIPLE(null,1,S,CID,CST);
+ }
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+ public static int GET_CLIENT_PERMIT(int sha,int date,byte[] S,byte[] CID,byte[] CTT)
+ {
+ byte[] h=hashit(sha,date,CID,EFS);
+ ECP P=ECP.mapit(h);
+
+ BIG s=BIG.fromBytes(S);
+ ECP OP=PAIR.G1mul(P,s);
+
+ OP.toBytes(CTT,false);
+ return 0;
+ }
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+ public static void SERVER_1(int sha,int date,byte[] CID,byte[] HID,byte[] HTID)
+ {
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R,P=ECP.mapit(h);
+
+ P.toBytes(HID,false); // new
+ if (date!=0)
+ {
+ // if (HID!=null) P.toBytes(HID);
+ h=hashit(sha,date,h,EFS);
+ R=ECP.mapit(h);
+ P.add(R); //P.affine();
+ P.toBytes(HTID,false);
+ }
+ // else P.toBytes(HID,false);
+ }
+
+/* Implement step 2 of MPin protocol on server side */
+ public static int SERVER_2(int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] mSEC,byte[] E,byte[] F)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ ECP2 Q=ECP2.generator();
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+
+ ECP R;
+ if (date!=0)
+ R=ECP.fromBytes(xCID);
+ else
+ {
+ if (xID==null) return BAD_PARAMS;
+ R=ECP.fromBytes(xID);
+ }
+ if (R.is_infinity()) return INVALID_POINT;
+
+ BIG y=BIG.fromBytes(Y);
+ ECP P;
+ if (date!=0) P=ECP.fromBytes(HTID);
+ else
+ {
+ if (HID==null) return BAD_PARAMS;
+ P=ECP.fromBytes(HID);
+ }
+
+ if (P.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ R=ECP.fromBytes(mSEC);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ FP12 g;
+
+ g=PAIR.ate2(Q,R,sQ,P);
+ g=PAIR.fexp(g);
+
+ if (!g.isunity())
+ {
+ if (HID!=null && xID!=null && E!=null && F!=null)
+ {
+ g.toBytes(E);
+ if (date!=0)
+ {
+ P=ECP.fromBytes(HID);
+ if (P.is_infinity()) return INVALID_POINT;
+ R=ECP.fromBytes(xID);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ }
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(F);
+ }
+ return BAD_PIN;
+ }
+
+ return 0;
+ }
+
+/* Pollards kangaroos used to return PIN error */
+ public static int KANGAROO(byte[] E,byte[] F)
+ {
+ FP12 ge=FP12.fromBytes(E);
+ FP12 gf=FP12.fromBytes(F);
+ int[] distance = new int[TS];
+ FP12 t=new FP12(gf);
+ FP12[] table=new FP12[TS];
+ int i,j,m,s,dn,dm,res,steps;
+
+ s=1;
+ for (m=0;m<TS;m++)
+ {
+ distance[m]=s;
+ table[m]=new FP12(t);
+ s*=2;
+ t.usqr();
+ }
+ t.one();
+ dn=0;
+ for (j=0;j<TRAP;j++)
+ {
+ i=t.geta().geta().getA().lastbits(20)%TS;
+ t.mul(table[i]);
+ dn+=distance[i];
+ }
+ gf.copy(t); gf.conj();
+ steps=0; dm=0;
+ res=0;
+ while (dm-dn<MAXPIN)
+ {
+ steps++;
+ if (steps>4*TRAP) break;
+ i=ge.geta().geta().getA().lastbits(20)%TS;
+ ge.mul(table[i]);
+ dm+=distance[i];
+ if (ge.equals(t))
+ {
+ res=dm-dn;
+ break;
+ }
+ if (ge.equals(gf))
+ {
+ res=dn-dm;
+ break;
+ }
+
+ }
+ if (steps>4*TRAP || dm-dn>=MAXPIN) {res=0; } // Trap Failed - probable invalid token
+ return res;
+ }
+
+/* Functions to support M-Pin Full */
+
+ public static int PRECOMPUTE(byte[] TOKEN,byte[] CID,byte[] G1,byte[] G2)
+ {
+ ECP P,T;
+ FP12 g;
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ P=ECP.mapit(CID);
+
+ ECP2 Q=ECP2.generator();
+
+ g=PAIR.ate(Q,T);
+ g=PAIR.fexp(g);
+ g.toBytes(G1);
+
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(G2);
+
+ return 0;
+ }
+
+
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+ public static int CLIENT_KEY(int sha,byte[] G1,byte[] G2,int pin,byte[] R,byte[] X,byte[] H,byte[] wCID,byte[] CK)
+ {
+ byte[] t;
+
+ FP12 g1=FP12.fromBytes(G1);
+ FP12 g2=FP12.fromBytes(G2);
+ BIG z=BIG.fromBytes(R);
+ BIG x=BIG.fromBytes(X);
+ BIG h=BIG.fromBytes(H);
+
+ ECP W=ECP.fromBytes(wCID);
+ if (W.is_infinity()) return INVALID_POINT;
+
+ W=PAIR.G1mul(W,x);
+
+// FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG r=new BIG(ROM.CURVE_Order);
+// BIG q=new BIG(ROM.Modulus);
+
+ z.add(h); //new
+ z.mod(r);
+
+ g2.pinpow(pin,PBLEN);
+ g1.mul(g2);
+
+ FP4 c=g1.compow(z,r);
+/*
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(z);
+ a.mod(m);
+
+ BIG b=new BIG(z);
+ b.div(m);
+
+
+ FP4 c=g1.trace();
+ g2.copy(g1);
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+*/
+ t=mpin_hash(sha,c,W);
+
+ for (int i=0;i<ECP.AESKEY;i++) CK[i]=t[i];
+
+ return 0;
+ }
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+ public static int SERVER_KEY(int sha,byte[] Z,byte[] SST,byte[] W,byte[] H,byte[] HID,byte[] xID,byte[] xCID,byte[] SK)
+ {
+ byte[] t;
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+ ECP R=ECP.fromBytes(Z);
+ if (R.is_infinity()) return INVALID_POINT;
+ ECP A=ECP.fromBytes(HID);
+ if (A.is_infinity()) return INVALID_POINT;
+
+ ECP U;
+ if (xCID!=null)
+ U=ECP.fromBytes(xCID);
+ else
+ U=ECP.fromBytes(xID);
+ if (U.is_infinity()) return INVALID_POINT;
+
+ BIG w=BIG.fromBytes(W);
+ BIG h=BIG.fromBytes(H);
+ A=PAIR.G1mul(A,h); // new
+ R.add(A); //R.affine();
+
+ U=PAIR.G1mul(U,w);
+ FP12 g=PAIR.ate(sQ,R);
+ g=PAIR.fexp(g);
+
+ FP4 c=g.trace();
+
+ t=mpin_hash(sha,c,U);
+
+ for (int i=0;i<ECP.AESKEY;i++) SK[i]=t[i];
+
+ return 0;
+ }
+
+/* Generate Y = H(epoch, xCID/xID) */
+ public static void GET_Y(int sha,int TimeValue,byte[] xCID,byte[] Y)
+ {
+ byte[] h = hashit(sha,TimeValue,xCID,EFS);
+ BIG y = BIG.fromBytes(h);
+ BIG q=new BIG(ROM.CURVE_Order);
+ y.mod(q);
+ //if (ROM.AES_S>0)
+ //{
+ // y.mod2m(2*ROM.AES_S);
+ //}
+ y.toBytes(Y);
+ }
+
+/* One pass MPIN Client */
+ public static int CLIENT(int sha,int date,byte[] CLIENT_ID,RAND RNG,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT, int TimeValue, byte[] Y)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ rtn = CLIENT_1(sha,date,CLIENT_ID,RNG,X,pin,TOKEN,SEC,xID,xCID,PERMIT);
+ if (rtn != 0)
+ return rtn;
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+
+/* One pass MPIN Server */
+ public static int SERVER(int sha,int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] SEC,byte[] E,byte[] F,byte[] CID, int TimeValue)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ SERVER_1(sha,date,CID,HID,HTID);
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = SERVER_2(date,HID,HTID,Y,SST,xID,xCID,SEC,E,F);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/PAIR.java b/src/main/java/org/apache/milagro/amcl/FP256BN/PAIR.java
new file mode 100644
index 0000000..93dc4ca
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/PAIR.java
@@ -0,0 +1,817 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BN Curve Pairing functions */
+
+package org.apache.milagro.amcl.FP256BN;
+
+public final class PAIR {
+
+ public static final boolean USE_GLV =true;
+ public static final boolean USE_GS_G2 =true;
+ public static final boolean USE_GS_GT =true;
+ public static final boolean GT_STRONG=false;
+
+
+/* Line function */
+ public static FP12 line(ECP2 A,ECP2 B,FP Qx,FP Qy)
+ {
+//System.out.println("Into line");
+ FP4 a,b,c; // Edits here
+// c=new FP4(0);
+ if (A==B)
+ { // Doubling
+ FP2 XX=new FP2(A.getx()); //X
+ FP2 YY=new FP2(A.gety()); //Y
+ FP2 ZZ=new FP2(A.getz()); //Z
+ FP2 YZ=new FP2(YY); //Y
+ YZ.mul(ZZ); //YZ
+ XX.sqr(); //X^2
+ YY.sqr(); //Y^2
+ ZZ.sqr(); //Z^2
+
+ YZ.imul(4);
+ YZ.neg(); YZ.norm(); //-2YZ
+ YZ.pmul(Qy); //-2YZ.Ys
+
+ XX.imul(6); //3X^2
+ XX.pmul(Qx); //3X^2.Xs
+
+ int sb=3*ROM.CURVE_B_I;
+ ZZ.imul(sb);
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ ZZ.div_ip2();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ ZZ.mul_ip();
+ ZZ.add(ZZ);
+ YZ.mul_ip();
+ YZ.norm();
+ }
+
+ ZZ.norm(); // 3b.Z^2
+
+ YY.add(YY);
+ ZZ.sub(YY); ZZ.norm(); // 3b.Z^2-Y^2
+
+ a=new FP4(YZ,ZZ); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(XX); // L(0,1) | L(0,0) | L(1,0)
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(XX); c.times_i();
+ }
+ A.dbl();
+ }
+ else
+ { // Addition - assume B is affine
+
+ FP2 X1=new FP2(A.getx()); // X1
+ FP2 Y1=new FP2(A.gety()); // Y1
+ FP2 T1=new FP2(A.getz()); // Z1
+ FP2 T2=new FP2(A.getz()); // Z1
+
+ T1.mul(B.gety()); // T1=Z1.Y2
+ T2.mul(B.getx()); // T2=Z1.X2
+
+ X1.sub(T2); X1.norm(); // X1=X1-Z1.X2
+ Y1.sub(T1); Y1.norm(); // Y1=Y1-Z1.Y2
+
+ T1.copy(X1); // T1=X1-Z1.X2
+ X1.pmul(Qy); // X1=(X1-Z1.X2).Ys
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X1.mul_ip();
+ X1.norm();
+ }
+
+ T1.mul(B.gety()); // T1=(X1-Z1.X2).Y2
+
+ T2.copy(Y1); // T2=Y1-Z1.Y2
+ T2.mul(B.getx()); // T2=(Y1-Z1.Y2).X2
+ T2.sub(T1); T2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+ Y1.pmul(Qx); Y1.neg(); Y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+ a=new FP4(X1,T2); // (X1-Z1.X2).Ys | (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2 | - (Y1-Z1.Y2).Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(Y1);
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(Y1); c.times_i();
+ }
+ A.add(B);
+ }
+//System.out.println("Out of line");
+ return new FP12(a,b,c);
+ }
+
+/* Optimal R-ate pairing */
+ public static FP12 ate(ECP2 P1,ECP Q1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+
+ ECP2 A=new ECP2();
+ FP12 r=new FP12(1);
+ A.copy(P);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ //r.conj();
+ A.neg();
+ }
+ K.copy(P);
+ K.frob(f);
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+ public static FP12 ate2(ECP2 P1,ECP Q1,ECP2 R1,ECP S1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ ECP2 R=new ECP2(R1);
+ ECP S=new ECP(S1);
+
+ R.affine();
+ S.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+ FP Sx=new FP(S.getx());
+ FP Sy=new FP(S.gety());
+
+ ECP2 A=new ECP2();
+ ECP2 B=new ECP2();
+ FP12 r=new FP12(1);
+
+ A.copy(P);
+ B.copy(R);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+ ECP2 MR=new ECP2();
+ MR.copy(R); MR.neg();
+
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ lv=line(B,B,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ lv=line(B,R,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ //R.neg();
+ lv=line(B,MR,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //R.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ // r.conj();
+ A.neg();
+ B.neg();
+ }
+
+ K.copy(P);
+ K.frob(f);
+
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.copy(R);
+ K.frob(f);
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+ public static FP12 fexp(FP12 m)
+ {
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ FP12 r=new FP12(m);
+
+/* Easy part of final exp */
+ FP12 lv=new FP12(r);
+ lv.inverse();
+ r.conj();
+
+ r.mul(lv);
+ lv.copy(r);
+ r.frob(f);
+ r.frob(f);
+ r.mul(lv);
+/* Hard part of final exp */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ FP12 x0,x1,x2,x3,x4,x5;
+ lv.copy(r);
+ lv.frob(f);
+ x0=new FP12(lv);
+ x0.frob(f);
+ lv.mul(r);
+ x0.mul(lv);
+ x0.frob(f);
+ x1=new FP12(r);
+ x1.conj();
+ x4=r.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x4.conj();
+ }
+
+ x3=new FP12(x4);
+ x3.frob(f);
+
+ x2=x4.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x2.conj();
+ }
+ x5=new FP12(x2); x5.conj();
+ lv=x2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ lv.conj();
+ }
+ x2.frob(f);
+ r.copy(x2); r.conj();
+
+ x4.mul(r);
+ x2.frob(f);
+
+ r.copy(lv);
+ r.frob(f);
+ lv.mul(r);
+
+ lv.usqr();
+ lv.mul(x4);
+ lv.mul(x5);
+ r.copy(x3);
+ r.mul(x5);
+ r.mul(lv);
+ lv.mul(x2);
+ r.usqr();
+ r.mul(lv);
+ r.usqr();
+ lv.copy(r);
+ lv.mul(x1);
+ r.mul(x0);
+ lv.usqr();
+ r.mul(lv);
+ r.reduce();
+ }
+ else
+ {
+
+ FP12 y0,y1,y2,y3;
+// Ghamman & Fouotsa Method
+ y0=new FP12(r); y0.usqr();
+ y1=y0.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y1.conj();
+ }
+ x.fshr(1); y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+
+ x.fshl(1);
+ y3=new FP12(r); y3.conj();
+ y1.mul(y3);
+
+ y1.conj();
+ y1.mul(y2);
+
+ y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y3=y2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y3.conj();
+ }
+ y1.conj();
+ y3.mul(y1);
+
+ y1.conj();
+ y1.frob(f); y1.frob(f); y1.frob(f);
+ y2.frob(f); y2.frob(f);
+ y1.mul(y2);
+
+ y2=y3.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y2.mul(y0);
+ y2.mul(r);
+
+ y1.mul(y2);
+ y2.copy(y3); y2.frob(f);
+ y1.mul(y2);
+ r.copy(y1);
+ r.reduce();
+ }
+
+ return r;
+ }
+
+/* GLV method */
+ public static BIG[] glv(BIG e)
+ {
+ BIG[] u=new BIG[2];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+
+ BIG[] v=new BIG[2];
+ for (i=0;i<2;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_W[i])); // why not just t=new BIG(ROM.CURVE_W[i]);
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<2;i++)
+ for (j=0;j<2;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_SB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ { // -(x^2).P = (Beta.x,y)
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG x2=BIG.smul(x,x);
+ u[0]=new BIG(e);
+ u[0].mod(x2);
+ u[1]=new BIG(e);
+ u[1].div(x2);
+ u[1].rsub(q);
+ }
+ return u;
+ }
+
+/* Galbraith & Scott Method */
+ public static BIG[] gs(BIG e)
+ {
+ BIG[] u=new BIG[4];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] v=new BIG[4];
+ for (i=0;i<4;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_WB[i]));
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<4;i++)
+ for (j=0;j<4;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_BB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ {
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG w=new BIG(e);
+ for (int i=0;i<3;i++)
+ {
+ u[i]=new BIG(w);
+ u[i].mod(x);
+ w.div(x);
+ }
+ u[3]=new BIG(w);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ u[1].copy(BIG.modneg(u[1],q));
+ u[3].copy(BIG.modneg(u[3],q));
+ }
+ }
+ return u;
+ }
+
+/* Multiply P by e in group G1 */
+ public static ECP G1mul(ECP P,BIG e)
+ {
+ ECP R;
+ if (USE_GLV)
+ {
+ //P.affine();
+ R=new ECP();
+ R.copy(P);
+ int i,np,nn;
+ ECP Q=new ECP();
+ Q.copy(P); Q.affine();
+ BIG q=new BIG(ROM.CURVE_Order);
+ FP cru=new FP(new BIG(ROM.CURVE_Cru));
+ BIG t=new BIG(0);
+ BIG[] u=glv(e);
+ Q.getx().mul(cru);
+
+ np=u[0].nbits();
+ t.copy(BIG.modneg(u[0],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[0].copy(t);
+ R.neg();
+ }
+
+ np=u[1].nbits();
+ t.copy(BIG.modneg(u[1],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[1].copy(t);
+ Q.neg();
+ }
+ u[0].norm();
+ u[1].norm();
+ R=R.mul2(u[0],Q,u[1]);
+
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* Multiply P by e in group G2 */
+ public static ECP2 G2mul(ECP2 P,BIG e)
+ {
+ ECP2 R;
+ if (USE_GS_G2)
+ {
+ ECP2[] Q=new ECP2[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] u=gs(e);
+
+ BIG t=new BIG(0);
+ int i,np,nn;
+ //P.affine();
+
+ Q[0]=new ECP2(); Q[0].copy(P);
+ for (i=1;i<4;i++)
+ {
+ Q[i]=new ECP2(); Q[i].copy(Q[i-1]);
+ Q[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ Q[i].neg();
+ }
+ u[i].norm();
+ //Q[i].affine();
+ }
+
+ R=ECP2.mul4(Q,u);
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */
+ public static FP12 GTpow(FP12 d,BIG e)
+ {
+ FP12 r;
+ if (USE_GS_GT)
+ {
+ FP12[] g=new FP12[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG t=new BIG(0);
+ int i,np,nn;
+ BIG[] u=gs(e);
+
+ g[0]=new FP12(d);
+ for (i=1;i<4;i++)
+ {
+ g[i]=new FP12(0); g[i].copy(g[i-1]);
+ g[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ g[i].conj();
+ }
+ u[i].norm();
+ }
+ r=FP12.pow4(g,u);
+ }
+ else
+ {
+ r=d.pow(e);
+ }
+ return r;
+ }
+
+/* test group membership - no longer needed */
+/* with GT-Strong curve, now only check that m!=1, conj(m)*m==1, and m.m^{p^4}=m^{p^2} */
+/*
+ public static boolean GTmember(FP12 m)
+ {
+ if (m.isunity()) return false;
+ FP12 r=new FP12(m);
+ r.conj();
+ r.mul(m);
+ if (!r.isunity()) return false;
+
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ r.copy(m); r.frob(f); r.frob(f);
+ FP12 w=new FP12(r); w.frob(f); w.frob(f);
+ w.mul(m);
+ if (!ROM.GT_STRONG)
+ {
+ if (!w.equals(r)) return false;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ r.copy(m); w=r.pow(x); w=w.pow(x);
+ r.copy(w); r.sqr(); r.mul(w); r.sqr();
+ w.copy(m); w.frob(f);
+ }
+ return w.equals(r);
+ }
+*/
+/*
+ public static void main(String[] args) {
+ ECP Q=new ECP(new BIG(ROM.CURVE_Gx),new BIG(ROM.CURVE_Gy));
+ ECP2 P=new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG xa=new BIG(ROM.CURVE_Pxa);
+
+ System.out.println("P= "+P.toString());
+ System.out.println("Q= "+Q.toString());
+
+ BIG m=new BIG(17);
+
+ FP12 e=ate(P,Q);
+ System.out.println("\ne= "+e.toString());
+
+ e=fexp(e);
+
+ for (int i=1;i<1000;i++)
+ {
+ e=ate(P,Q);
+ e=fexp(e);
+ }
+ // e=GTpow(e,m);
+
+ System.out.println("\ne= "+e.toString());
+
+ BIG [] GLV=glv(r);
+
+ System.out.println("GLV[0]= "+GLV[0].toString());
+ System.out.println("GLV[0]= "+GLV[1].toString());
+
+ ECP G=new ECP(); G.copy(Q);
+ ECP2 R=new ECP2(); R.copy(P);
+
+
+ e=ate(R,Q);
+ e=fexp(e);
+
+ e=GTpow(e,xa);
+ System.out.println("\ne= "+e.toString());
+
+
+ R=G2mul(R,xa);
+ e=ate(R,G);
+ e=fexp(e);
+
+ System.out.println("\ne= "+e.toString());
+
+ G=G1mul(G,xa);
+ e=ate(P,G);
+ e=fexp(e);
+ System.out.println("\ne= "+e.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/FP256BN/ROM.java b/src/main/java/org/apache/milagro/amcl/FP256BN/ROM.java
new file mode 100644
index 0000000..7a8a637
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP256BN/ROM.java
@@ -0,0 +1,55 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.FP256BN;
+
+public class ROM
+{
+// Base Bits= 56
+public static final long[] Modulus={0x292DDBAED33013L,0x65FB12980A82D3L,0x5EEE71A49F0CDCL,0xFFFCF0CD46E5F2L,0xFFFFFFFFL};
+public static final long[] R2modp={0xEDE336303B9F8BL,0x92FFEE9FEC54E8L,0x13C1C063C55F79L,0xA12F2EAC0123FAL,0x8E559B2AL};
+public static final long MConst= 0x6C964E0537E5E5L;
+
+public static final int CURVE_A= 0;
+public static final int CURVE_B_I= 3;
+public static final int CURVE_Cof_I= 1;
+public static final long[] CURVE_B= {0x3L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Order= {0x2D536CD10B500DL,0x65FB1299921AF6L,0x5EEE71A49E0CDCL,0xFFFCF0CD46E5F2L,0xFFFFFFFFL};
+public static final long[] CURVE_Gx= {0x1L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Gy= {0x2L,0x0L,0x0L,0x0L,0x0L};
+
+public static final long[] Fra={0x760328AF943106L,0x71511E3AB28F74L,0x8DDB0867CF39A1L,0xCA786F352D1A6EL,0x3D617662L};
+public static final long[] Frb={0xB32AB2FF3EFF0DL,0xF4A9F45D57F35EL,0xD113693CCFD33AL,0x3584819819CB83L,0xC29E899DL};
+public static final long[] CURVE_Bnx= {0x82F5C030B0A801L,0x68L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Cru= {0x1C0A24A3A1B807L,0xD79DF1932D1EDBL,0x40921018659BCDL,0x13988E1L,0x0L};
+public static final long[] CURVE_Pxa= {0x2616B689C09EFBL,0x539A12BF843CD2L,0x577C28913ACE1CL,0xB4C96C2028560FL,0xFE0C3350L};
+public static final long[] CURVE_Pxb= {0x69ED34A37E6A2BL,0x78E287D03589D2L,0xC637D813B924DDL,0x738AC054DB5AE1L,0x4EA66057L};
+public static final long[] CURVE_Pya= {0x9B481BEDC27FFL,0x24758D615848E9L,0x75124E3E51EFCBL,0xC542A3B376770DL,0x702046E7L};
+public static final long[] CURVE_Pyb= {0x1281114AAD049BL,0xBE80821A98B3E0L,0x49297EB29F8B4CL,0xD388C29042EEA6L,0x554E3BCL};
+public static final long[][] CURVE_W= {{0xF0036E1B054003L,0xFFFFFFFE78663AL,0xFFFFL,0x0L,0x0L},{0x5EB8061615001L,0xD1L,0x0L,0x0L,0x0L}};
+public static final long[][][] CURVE_SB= {{{0xF5EEEE7C669004L,0xFFFFFFFE78670BL,0xFFFFL,0x0L,0x0L},{0x5EB8061615001L,0xD1L,0x0L,0x0L,0x0L}},{{0x5EB8061615001L,0xD1L,0x0L,0x0L,0x0L},{0x3D4FFEB606100AL,0x65FB129B19B4BBL,0x5EEE71A49D0CDCL,0xFFFCF0CD46E5F2L,0xFFFFFFFFL}}};
+public static final long[][] CURVE_WB= {{0x20678F0D30A800L,0x55555554D2CC10L,0x5555L,0x0L,0x0L},{0xD6764C0D7DC805L,0x8FBEA10BC3AD1AL,0x806160104467DEL,0xD105EBL,0x0L},{0xACB6061F173803L,0x47DF5085E1D6C1L,0xC030B0082233EFL,0x6882F5L,0x0L},{0x26530F6E91F801L,0x55555554D2CCE1L,0x5555L,0x0L,0x0L}};
+public static final long[][][] CURVE_BB= {{{0xAA5DACA05AA80DL,0x65FB1299921A8DL,0x5EEE71A49E0CDCL,0xFFFCF0CD46E5F2L,0xFFFFFFFFL},{0xAA5DACA05AA80CL,0x65FB1299921A8DL,0x5EEE71A49E0CDCL,0xFFFCF0CD46E5F2L,0xFFFFFFFFL},{0xAA5DACA05AA80CL,0x65FB1299921A8DL,0x5EEE71A49E0CDCL,0xFFFCF0CD46E5F2L,0xFFFFFFFFL},{0x5EB8061615002L,0xD1L,0x0L,0x0L,0x0L}},{{0x5EB8061615001L,0xD1L,0x0L,0x0L,0x0L},{0xAA5DACA05AA80CL,0x65FB1299921A8DL,0x5EEE71A49E0CDCL,0xFFFCF0CD46E5F2L,0xFFFFFFFFL},{0xAA5DACA05AA80DL,0x65FB1299921A8DL,0x5EEE71A49E0CDCL,0xFFFCF0CD46E5F2L,0xFFFFFFFFL},{0xAA5DACA05AA80CL,0x65FB1299921A8DL,0x5EEE71A49E0CDCL,0xFFFCF0CD46E5F2L,0xFFFFFFFFL}},{{0x5EB8061615002L,0xD1L,0x0L,0x0L,0x0L},{0x5EB8061615001L,0xD1L,0x0L,0x0L,0x0L},{0x5EB8061615001L,0xD1L,0x0L,0x0L,0x0L},{0x5EB8061615001L,0xD1L,0x0L,0x0L,0x0L}},{{0x82F5C030B0A802L,0x68L,0x0L,0x0L,0x0L},{0xBD700C2C2A002L,0x1A2L,0x0L,0x0L,0x0L},{0x2767EC6FAA000AL,0x65FB1299921A25L,0x5EEE71A49E0CDCL,0xFFFCF0CD46E5F2L,0xFFFFFFFFL},{0x82F5C030B0A802L,0x68L,0x0L,0x0L,0x0L}}};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/BIG.java b/src/main/java/org/apache/milagro/amcl/FP512BN/BIG.java
new file mode 100644
index 0000000..a865f22
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.FP512BN;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=64; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=60;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/DBIG.java b/src/main/java/org/apache/milagro/amcl/FP512BN/DBIG.java
new file mode 100644
index 0000000..b563ee9
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.FP512BN;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/ECDH.java b/src/main/java/org/apache/milagro/amcl/FP512BN/ECDH.java
new file mode 100644
index 0000000..227faca
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.FP512BN;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/ECP.java b/src/main/java/org/apache/milagro/amcl/FP512BN/ECP.java
new file mode 100644
index 0000000..557e2c3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.FP512BN;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=BN;
+ public static final int SEXTIC_TWIST=M_TYPE;
+ public static final int SIGN_OF_X=POSITIVEX;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/ECP2.java b/src/main/java/org/apache/milagro/amcl/FP512BN/ECP2.java
new file mode 100644
index 0000000..2213a56
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/ECP2.java
@@ -0,0 +1,796 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Weierstrass elliptic curve functions over FP2 */
+
+package org.apache.milagro.amcl.FP512BN;
+
+public final class ECP2 {
+ private FP2 x;
+ private FP2 y;
+ private FP2 z;
+// private boolean INF;
+
+/* Constructor - set this=O */
+ public ECP2() {
+// INF=true;
+ x=new FP2(0);
+ y=new FP2(1);
+ z=new FP2(0);
+ }
+
+ public ECP2(ECP2 e) {
+ this.x = new FP2(e.x);
+ this.y = new FP2(e.y);
+ this.z = new FP2(e.z);
+ }
+
+/* Test this=O? */
+ public boolean is_infinity() {
+// if (INF) return true; //******
+ return (x.iszilch() && z.iszilch());
+ }
+/* copy this=P */
+ public void copy(ECP2 P)
+ {
+ x.copy(P.x);
+ y.copy(P.y);
+ z.copy(P.z);
+// INF=P.INF;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ y.one();
+ z.zero();
+ }
+
+/* Conditional move of Q to P dependant on d */
+ public void cmove(ECP2 Q,int d)
+ {
+ x.cmove(Q.x,d);
+ y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ }
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(ECP2 W[],int b)
+ {
+ ECP2 MP=new ECP2();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test if P == Q */
+ public boolean equals(ECP2 Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+
+ FP2 a=new FP2(x); // *****
+ FP2 b=new FP2(Q.x);
+ a.mul(Q.z);
+ b.mul(z);
+ if (!a.equals(b)) return false;
+
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+
+ return true;
+ }
+/* set this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ y.norm();
+ y.neg(); y.norm();
+ return;
+ }
+/* set to Affine - (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return;
+ FP2 one=new FP2(1);
+ if (z.equals(one))
+ {
+ x.reduce();
+ y.reduce();
+ return;
+ }
+ z.inverse();
+
+ x.mul(z); x.reduce(); // *****
+ y.mul(z); y.reduce();
+ z.copy(one);
+ }
+/* extract affine x as FP2 */
+ public FP2 getX()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.x;
+ }
+/* extract affine y as FP2 */
+ public FP2 getY()
+ {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ return W.y;
+ }
+/* extract projective x */
+ public FP2 getx()
+ {
+ return x;
+ }
+/* extract projective y */
+ public FP2 gety()
+ {
+ return y;
+ }
+/* extract projective z */
+ public FP2 getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP2 W=new ECP2(this);
+ W.affine();
+ W.x.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i]=t[i];
+ W.x.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+BIG.MODBYTES]=t[i];
+
+ W.y.getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+2*BIG.MODBYTES]=t[i];
+ W.y.getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++)
+ b[i+3*BIG.MODBYTES]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP2 fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG ra;
+ BIG rb;
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 rx=new FP2(ra,rb);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+2*BIG.MODBYTES];
+ ra=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+3*BIG.MODBYTES];
+ rb=BIG.fromBytes(t);
+ FP2 ry=new FP2(ra,rb);
+
+ return new ECP2(rx,ry);
+ }
+/* convert this to hex string */
+ public String toString() {
+ ECP2 W=new ECP2(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ return "("+W.x.toString()+","+W.y.toString()+")";
+ }
+
+/* Calculate RHS of twisted curve equation x^3+B/i */
+ public static FP2 RHS(FP2 x) {
+ x.norm();
+ FP2 r=new FP2(x);
+ r.sqr();
+ FP2 b=new FP2(new BIG(ROM.CURVE_B));
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b.div_ip();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b.norm();
+ b.mul_ip();
+ b.norm();
+ }
+
+
+ r.mul(x);
+ r.add(b);
+
+ r.reduce();
+ return r;
+ }
+
+/* construct this from (x,y) - but set to O if not on curve */
+ public ECP2(FP2 ix,FP2 iy) {
+ x=new FP2(ix);
+ y=new FP2(iy);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ FP2 y2=new FP2(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+// if (y2.equals(rhs)) INF=false;
+// else {x.zero();INF=true;}
+ }
+
+/* construct this from x - but set to O if not on curve */
+ public ECP2(FP2 ix) {
+ x=new FP2(ix);
+ y=new FP2(1);
+ z=new FP2(1);
+ FP2 rhs=RHS(x);
+ if (rhs.sqrt())
+ {
+ y.copy(rhs);
+ //INF=false;
+ }
+ else {/*x.zero();INF=true;*/ inf();}
+ }
+
+/* this+=this */
+ public int dbl() {
+// if (INF) return -1;
+//System.out.println("Into dbl");
+ FP2 iy=new FP2(y);
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ iy.mul_ip(); iy.norm();
+ }
+ FP2 t0=new FP2(y); //***** Change
+ t0.sqr();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip();
+ }
+ FP2 t1=new FP2(iy);
+ t1.mul(z);
+ FP2 t2=new FP2(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z);
+ z.add(z);
+ z.norm();
+
+ t2.imul(3*ROM.CURVE_B_I);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip();
+ t2.norm();
+ }
+
+ FP2 x3=new FP2(t2);
+ x3.mul(z);
+
+ FP2 y3=new FP2(t0);
+
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1); t2.norm();
+ t0.sub(t2); t0.norm(); //y^2-9bz^2
+ y3.mul(t0); y3.add(x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+ t1.copy(x); t1.mul(iy); //
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x); //(y^2-9bz^2)xy2
+
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ return 1;
+ }
+
+/* this+=Q - return 0 for add, 1 for double, -1 for O */
+ public int add(ECP2 Q) {
+// if (INF)
+// {
+// copy(Q);
+// return -1;
+// }
+// if (Q.INF) return -1;
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP2 t0=new FP2(x);
+ t0.mul(Q.x); // x.Q.x
+ FP2 t1=new FP2(y);
+ t1.mul(Q.y); // y.Q.y
+
+ FP2 t2=new FP2(z);
+ t2.mul(Q.z);
+ FP2 t3=new FP2(x);
+ t3.add(y); t3.norm(); //t3=X1+Y1
+ FP2 t4=new FP2(Q.x);
+ t4.add(Q.y); t4.norm(); //t4=X2+Y2
+ t3.mul(t4); //t3=(X1+Y1)(X2+Y2)
+ t4.copy(t0); t4.add(t1); //t4=X1.X2+Y1.Y2
+
+ t3.sub(t4); t3.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t3.mul_ip(); t3.norm(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+ }
+ t4.copy(y);
+ t4.add(z); t4.norm(); //t4=Y1+Z1
+ FP2 x3=new FP2(Q.y);
+ x3.add(Q.z); x3.norm(); //x3=Y2+Z2
+
+ t4.mul(x3); //t4=(Y1+Z1)(Y2+Z2)
+ x3.copy(t1); //
+ x3.add(t2); //X3=Y1.Y2+Z1.Z2
+
+ t4.sub(x3); t4.norm();
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t4.mul_ip(); t4.norm(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+ }
+ x3.copy(x); x3.add(z); x3.norm(); // x3=X1+Z1
+ FP2 y3=new FP2(Q.x);
+ y3.add(Q.z); y3.norm(); // y3=X2+Z2
+ x3.mul(y3); // x3=(X1+Z1)(X2+Z2)
+ y3.copy(t0);
+ y3.add(t2); // y3=X1.X2+Z1+Z2
+ y3.rsub(x3); y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ t0.mul_ip(); t0.norm(); // x.Q.x
+ t1.mul_ip(); t1.norm(); // y.Q.y
+ }
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ t2.mul_ip(); t2.norm();
+ }
+ FP2 z3=new FP2(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ y3.mul_ip();
+ y3.norm();
+ }
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ return 0;
+ }
+
+/* set this-=Q */
+ public int sub(ECP2 Q) {
+ ECP2 NQ=new ECP2(Q);
+ NQ.neg();
+ int D=add(NQ);
+ //Q.neg();
+ //int D=add(Q);
+ //Q.neg();
+ return D;
+ }
+/* set this*=q, where q is Modulus, using Frobenius */
+ public void frob(FP2 X)
+ {
+// if (INF) return;
+ FP2 X2=new FP2(X);
+
+ X2.sqr();
+ x.conj();
+ y.conj();
+ z.conj();
+ z.reduce();
+ x.mul(X2);
+
+ y.mul(X2);
+ y.mul(X);
+ }
+
+/* P*=e */
+ public ECP2 mul(BIG e)
+ {
+/* fixed size windows */
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP2 P=new ECP2();
+ ECP2 Q=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2[] W=new ECP2[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ if (is_infinity()) return new ECP2();
+
+ //affine();
+
+/* precompute table */
+ Q.copy(this);
+ Q.dbl();
+ W[0]=new ECP2();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP2();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+/* make exponent odd - add 2P if even, P if odd */
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+/* convert exponent to signed 4-bit window */
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C);
+ P.affine();
+ return P;
+ }
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ ECP2 W=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] T=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ //Q[i].affine();
+ }
+
+ T[0] = new ECP2(); T[0].copy(Q[0]); // Q[0]
+ T[1] = new ECP2(); T[1].copy(T[0]); T[1].add(Q[1]); // Q[0]+Q[1]
+ T[2] = new ECP2(); T[2].copy(T[0]); T[2].add(Q[2]); // Q[0]+Q[2]
+ T[3] = new ECP2(); T[3].copy(T[1]); T[3].add(Q[2]); // Q[0]+Q[1]+Q[2]
+ T[4] = new ECP2(); T[4].copy(T[0]); T[4].add(Q[3]); // Q[0]+Q[3]
+ T[5] = new ECP2(); T[5].copy(T[1]); T[5].add(Q[3]); // Q[0]+Q[1]+Q[3]
+ T[6] = new ECP2(); T[6].copy(T[2]); T[6].add(Q[3]); // Q[0]+Q[2]+Q[3]
+ T[7] = new ECP2(); T[7].copy(T[3]); T[7].add(Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ P.select(T,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ P.dbl();
+ W.select(T,(int)(2*w[i]+s[i]));
+ P.add(W);
+ }
+
+ // apply correction
+ W.copy(P);
+ W.sub(Q[0]);
+ P.cmove(W,pb);
+ P.affine();
+ return P;
+ }
+
+
+/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+/*
+ public static ECP2 mul4(ECP2[] Q,BIG[] u)
+ {
+ int i,j,nb;
+ int[] a=new int[4];
+ ECP2 T=new ECP2();
+ ECP2 C=new ECP2();
+ ECP2 P=new ECP2();
+ ECP2[] W=new ECP2[8];
+
+ BIG mt=new BIG();
+ BIG[] t=new BIG[4];
+
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ Q[i].affine();
+ }
+
+// precompute table
+
+ W[0]=new ECP2(); W[0].copy(Q[0]); W[0].sub(Q[1]);
+
+ W[1]=new ECP2(); W[1].copy(W[0]);
+ W[2]=new ECP2(); W[2].copy(W[0]);
+ W[3]=new ECP2(); W[3].copy(W[0]);
+ W[4]=new ECP2(); W[4].copy(Q[0]); W[4].add(Q[1]);
+ W[5]=new ECP2(); W[5].copy(W[4]);
+ W[6]=new ECP2(); W[6].copy(W[4]);
+ W[7]=new ECP2(); W[7].copy(W[4]);
+ T.copy(Q[2]); T.sub(Q[3]);
+ W[1].sub(T);
+ W[2].add(T);
+ W[5].sub(T);
+ W[6].add(T);
+ T.copy(Q[2]); T.add(Q[3]);
+ W[0].sub(T);
+ W[3].add(T);
+ W[4].sub(T);
+ W[7].add(T);
+
+// if multiplier is even add 1 to multiplier, and add P to correction
+ mt.zero(); C.inf();
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ C.add(Q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(byte)(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ P.dbl();
+ P.add(T);
+ }
+ P.sub(C); // apply correction
+
+ P.affine();
+ return P;
+ }
+*/
+
+/* needed for SOK */
+ public static ECP2 mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ BIG one=new BIG(1);
+ FP2 X;
+ ECP2 Q;
+ x.mod(q);
+ while (true)
+ {
+ X=new FP2(one,x);
+ Q=new ECP2(X);
+ if (!Q.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+
+ BIG Fra=new BIG(ROM.Fra);
+ BIG Frb=new BIG(ROM.Frb);
+ X=new FP2(Fra,Frb);
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X.inverse();
+ X.norm();
+ }
+
+ x=new BIG(ROM.CURVE_Bnx);
+
+/* Fast Hashing to G2 - Fuentes-Castaneda, Knapp and Rodriguez-Henriquez */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ ECP2 T,K;
+
+ T=new ECP2(); T.copy(Q);
+ T=T.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ T.neg();
+ }
+ K=new ECP2(); K.copy(T);
+ K.dbl(); K.add(T); //K.affine();
+
+ K.frob(X);
+ Q.frob(X); Q.frob(X); Q.frob(X);
+ Q.add(T); Q.add(K);
+ T.frob(X); T.frob(X);
+ Q.add(T);
+
+ }
+
+/* Efficient hash maps to G2 on BLS curves - Budroni, Pintore */
+/* Q -> x2Q -xQ -Q +F(xQ -Q) +F(F(2Q)) */
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BLS)
+ {
+ // ECP2 xQ,x2Q;
+ // xQ=new ECP2();
+ // x2Q=new ECP2();
+
+ ECP2 xQ=Q.mul(x);
+ ECP2 x2Q=xQ.mul(x);
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ xQ.neg();
+ }
+
+ x2Q.sub(xQ);
+ x2Q.sub(Q);
+
+ xQ.sub(Q);
+ xQ.frob(X);
+
+ Q.dbl();
+ Q.frob(X);
+ Q.frob(X);
+
+ Q.add(x2Q);
+ Q.add(xQ);
+ }
+ Q.affine();
+ return Q;
+ }
+
+ public static ECP2 generator()
+ {
+ return new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+ }
+
+/*
+ public static void main(String[] args) {
+ BIG r=new BIG(ROM.Modulus);
+
+ BIG Pxa=new BIG(ROM.CURVE_Pxa);
+ BIG Pxb=new BIG(ROM.CURVE_Pxb);
+ BIG Pya=new BIG(ROM.CURVE_Pya);
+ BIG Pyb=new BIG(ROM.CURVE_Pyb);
+
+ BIG Fra=new BIG(ROM.CURVE_Fra);
+ BIG Frb=new BIG(ROM.CURVE_Frb);
+
+ FP2 f=new FP2(Fra,Frb);
+
+ FP2 Px=new FP2(Pxa,Pxb);
+ FP2 Py=new FP2(Pya,Pyb);
+
+ ECP2 P=new ECP2(Px,Py);
+
+ System.out.println("P= "+P.toString());
+
+ P=P.mul(r);
+ System.out.println("P= "+P.toString());
+
+ ECP2 Q=new ECP2(Px,Py);
+ Q.frob(f);
+ System.out.println("Q= "+Q.toString());
+ } */
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/FP.java b/src/main/java/org/apache/milagro/amcl/FP512BN/FP.java
new file mode 100644
index 0000000..42ee199
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.FP512BN;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=512; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<28); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/FP12.java b/src/main/java/org/apache/milagro/amcl/FP512BN/FP12.java
new file mode 100644
index 0000000..d3dc59f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/FP12.java
@@ -0,0 +1,907 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL Fp^12 functions */
+/* FP12 elements are of the form a+i.b+i^2.c */
+
+package org.apache.milagro.amcl.FP512BN;
+
+public final class FP12 {
+ private final FP4 a;
+ private final FP4 b;
+ private final FP4 c;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ c.reduce();
+ }
+/* normalise all components of this */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ c.norm();
+ }
+/* test x==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch() && c.iszilch());
+ }
+
+ public void cmove(FP12 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ c.cmove(g.c,d);
+ }
+
+
+/* return 1 if b==c, no branching */
+ public static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ public void select(FP12 g[],int b)
+ {
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+
+ cmove(g[0],teq(babs,0)); // conditional move
+ cmove(g[1],teq(babs,1));
+ cmove(g[2],teq(babs,2));
+ cmove(g[3],teq(babs,3));
+ cmove(g[4],teq(babs,4));
+ cmove(g[5],teq(babs,5));
+ cmove(g[6],teq(babs,6));
+ cmove(g[7],teq(babs,7));
+
+ FP12 invf=new FP12(this);
+ invf.conj();
+ cmove(invf,(int)(m&1));
+ }
+
+
+/* test x==1 ? */
+ public boolean isunity() {
+ FP4 one=new FP4(1);
+ return (a.equals(one) && b.iszilch() && c.iszilch());
+ }
+/* return 1 if x==y, else 0 */
+ public boolean equals(FP12 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b) && c.equals(x.c));
+ }
+/* extract a from this */
+ public FP4 geta()
+ {
+ return a;
+ }
+/* extract b */
+ public FP4 getb()
+ {
+ return b;
+ }
+/* extract c */
+ public FP4 getc()
+ {
+ return c;
+ }
+/* copy this=x */
+ public void copy(FP12 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ c.copy(x.c);
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ c.zero();
+ }
+/* this=conj(this) */
+ public void conj()
+ {
+ a.conj();
+ b.nconj();
+ c.conj();
+ }
+/* Constructors */
+ public FP12(FP4 d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(int d)
+ {
+ a=new FP4(d);
+ b=new FP4(0);
+ c=new FP4(0);
+ }
+
+ public FP12(FP4 d,FP4 e,FP4 f)
+ {
+ a=new FP4(d);
+ b=new FP4(e);
+ c=new FP4(f);
+ }
+
+ public FP12(FP12 x)
+ {
+ a=new FP4(x.a);
+ b=new FP4(x.b);
+ c=new FP4(x.c);
+ }
+
+/* Granger-Scott Unitary Squaring */
+ public void usqr()
+ {
+//System.out.println("Into usqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(c);
+ FP4 C=new FP4(b);
+ FP4 D=new FP4(0);
+
+ a.sqr();
+ D.copy(a); D.add(a);
+ a.add(D);
+
+ a.norm();
+ A.nconj();
+
+ A.add(A);
+ a.add(A);
+ B.sqr();
+ B.times_i();
+
+ D.copy(B); D.add(B);
+ B.add(D);
+ B.norm();
+
+ C.sqr();
+ D.copy(C); D.add(C);
+ C.add(D);
+ C.norm();
+
+ b.conj();
+ b.add(b);
+ c.nconj();
+
+ c.add(c);
+ b.add(B);
+ c.add(C);
+//System.out.println("Out of usqr 1");
+ reduce();
+//System.out.println("Out of usqr 2");
+ }
+
+/* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+ public void sqr()
+ {
+//System.out.println("Into sqr");
+ FP4 A=new FP4(a);
+ FP4 B=new FP4(b);
+ FP4 C=new FP4(c);
+ FP4 D=new FP4(a);
+
+ A.sqr();
+ B.mul(c);
+ B.add(B);
+ B.norm();
+ C.sqr();
+ D.mul(b);
+ D.add(D);
+
+ c.add(a);
+ c.add(b);
+ c.norm();
+ c.sqr();
+
+ a.copy(A);
+
+ A.add(B);
+ A.norm();
+ A.add(C);
+ A.add(D);
+ A.norm();
+
+ A.neg();
+ B.times_i();
+ C.times_i();
+
+ a.add(B);
+
+ b.copy(C); b.add(D);
+ c.add(A);
+//System.out.println("Out of sqr");
+ norm();
+ }
+
+/* FP12 full multiplication this=this*y */
+ public void mul(FP12 y)
+ {
+//System.out.println("Into mul");
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(y.a);
+
+ z0.mul(y.a);
+ z2.mul(y.b);
+
+ t0.add(b);
+ t1.add(y.b);
+
+ t0.norm();
+ t1.norm();
+
+ z1.copy(t0); z1.mul(t1);
+ t0.copy(b); t0.add(c);
+
+ t1.copy(y.b); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ z3.copy(t0); z3.mul(t1);
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ z1.add(t0);
+ //z1.norm();
+ b.copy(z1); b.add(t1);
+
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+t0.norm();
+t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c); t0.mul(y.c);
+ t1.copy(t0); t1.neg();
+
+// z2.norm();
+// z3.norm();
+// b.norm();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ norm();
+//System.out.println("Out of mul");
+ }
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+ public void smul(FP12 y,int type)
+ {
+//System.out.println("Into smul");
+
+ if (type==ECP.D_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z2=new FP4(b);
+ FP4 z3=new FP4(b);
+ FP4 t0=new FP4(0);
+ FP4 t1=new FP4(y.a);
+ z0.mul(y.a);
+ z2.pmul(y.b.real());
+ b.add(a);
+ t1.real().add(y.b.real());
+
+ t1.norm();
+ b.norm();
+ b.mul(t1);
+ z3.add(c);
+ z3.norm();
+ z3.pmul(y.b.real());
+
+ t0.copy(z0); t0.neg();
+ t1.copy(z2); t1.neg();
+
+ b.add(t0);
+
+ b.add(t1);
+ z3.add(t1);
+ z2.add(t0);
+
+ t0.copy(a); t0.add(c);
+ t0.norm();
+ z3.norm();
+ t0.mul(y.a);
+ c.copy(z2); c.add(t0);
+
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ if (type==ECP.M_TYPE)
+ {
+ FP4 z0=new FP4(a);
+ FP4 z1=new FP4(0);
+ FP4 z2=new FP4(0);
+ FP4 z3=new FP4(0);
+ FP4 t0=new FP4(a);
+ FP4 t1=new FP4(0);
+
+ z0.mul(y.a);
+ t0.add(b);
+ t0.norm();
+
+ z1.copy(t0); z1.mul(y.a);
+ t0.copy(b); t0.add(c);
+ t0.norm();
+
+ z3.copy(t0); //z3.mul(y.c);
+ z3.pmul(y.c.getb());
+ z3.times_i();
+
+ t0.copy(z0); t0.neg();
+
+ z1.add(t0);
+ b.copy(z1);
+ z2.copy(t0);
+
+ t0.copy(a); t0.add(c);
+ t1.copy(y.a); t1.add(y.c);
+
+ t0.norm();
+ t1.norm();
+
+ t0.mul(t1);
+ z2.add(t0);
+
+ t0.copy(c);
+
+ t0.pmul(y.c.getb());
+ t0.times_i();
+
+ t1.copy(t0); t1.neg();
+
+ c.copy(z2); c.add(t1);
+ z3.add(t1);
+ t0.times_i();
+ b.add(t0);
+ z3.norm();
+ z3.times_i();
+ a.copy(z0); a.add(z3);
+ }
+ norm();
+//System.out.println("Out of smul");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ FP4 f0=new FP4(a);
+ FP4 f1=new FP4(b);
+ FP4 f2=new FP4(a);
+ FP4 f3=new FP4(0);
+
+ norm();
+ f0.sqr();
+ f1.mul(c);
+ f1.times_i();
+ f0.sub(f1);
+ f0.norm();
+
+ f1.copy(c); f1.sqr();
+ f1.times_i();
+ f2.mul(b);
+ f1.sub(f2);
+ f1.norm();
+
+ f2.copy(b); f2.sqr();
+ f3.copy(a); f3.mul(c);
+ f2.sub(f3);
+ f2.norm();
+
+ f3.copy(b); f3.mul(f2);
+ f3.times_i();
+ a.mul(f0);
+ f3.add(a);
+ c.mul(f1);
+ c.times_i();
+
+ f3.add(c);
+ f3.norm();
+ f3.inverse();
+ a.copy(f0); a.mul(f3);
+ b.copy(f1); b.mul(f3);
+ c.copy(f2); c.mul(f3);
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ FP2 f2=new FP2(f);
+ FP2 f3=new FP2(f);
+
+ f2.sqr();
+ f3.mul(f2);
+
+ a.frob(f3);
+ b.frob(f3);
+ c.frob(f3);
+
+ b.pmul(f);
+ c.pmul(f2);
+ }
+
+/* trace function */
+ public FP4 trace()
+ {
+ FP4 t=new FP4(0);
+ t.copy(a);
+ t.imul(3);
+ t.reduce();
+ return t;
+ }
+
+/* convert from byte array to FP12 */
+ public static FP12 fromBytes(byte[] w)
+ {
+ BIG a,b;
+ FP2 c,d;
+ FP4 e,f,g;
+ byte[] t=new byte[BIG.MODBYTES];
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+2*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+3*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ e=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+4*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+5*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+6*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+7*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ f=new FP4(c,d);
+
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+8*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+9*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ c=new FP2(a,b);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+10*BIG.MODBYTES];
+ a=BIG.fromBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=w[i+11*BIG.MODBYTES];
+ b=BIG.fromBytes(t);
+ d=new FP2(a,b);
+
+ g=new FP4(c,d);
+
+ return new FP12(e,f,g);
+ }
+
+/* convert this to byte array */
+ public void toBytes(byte[] w)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ a.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i]=t[i];
+ a.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+BIG.MODBYTES]=t[i];
+ a.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+2*BIG.MODBYTES]=t[i];
+ a.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+3*BIG.MODBYTES]=t[i];
+
+ b.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+4*BIG.MODBYTES]=t[i];
+ b.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+5*BIG.MODBYTES]=t[i];
+ b.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+6*BIG.MODBYTES]=t[i];
+ b.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+7*BIG.MODBYTES]=t[i];
+
+ c.geta().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+8*BIG.MODBYTES]=t[i];
+ c.geta().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+9*BIG.MODBYTES]=t[i];
+ c.getb().getA().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+10*BIG.MODBYTES]=t[i];
+ c.getb().getB().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) w[i+11*BIG.MODBYTES]=t[i];
+ }
+
+/* convert to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+","+c.toString()+"]");
+ }
+
+/* this=this^e */
+/* Note this is simple square and multiply, so not side-channel safe */
+ public FP12 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ BIG e3=new BIG(e);
+ e3.pmul(3);
+ e3.norm();
+
+ FP12 w=new FP12(this);
+
+ int nb=e3.nbits();
+ for (int i=nb-2;i>=1;i--)
+ {
+ w.usqr();
+ int bt=e3.bit(i)-e.bit(i);
+ if (bt==1)
+ w.mul(this);
+ if (bt==-1)
+ {
+ conj(); w.mul(this); conj();
+ }
+ }
+ w.reduce();
+ return w;
+
+
+/*
+ BIG z=new BIG(e);
+ FP12 r=new FP12(1);
+
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.usqr();
+ }
+ r.reduce();
+ return r; */
+ }
+
+/* constant time powering by small integer of max length bts */
+ public void pinpow(int e,int bts)
+ {
+ int i,b;
+ FP12 [] R=new FP12[2];
+ R[0]=new FP12(1);
+ R[1]=new FP12(this);
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ R[1-b].mul(R[b]);
+ R[b].usqr();
+ }
+ this.copy(R[0]);
+ }
+
+ public FP4 compow(BIG e,BIG r)
+ {
+ FP12 g1=new FP12(0);
+ FP12 g2=new FP12(0);
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.Modulus);
+
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(e);
+ a.mod(m);
+
+ BIG b=new BIG(e);
+ b.div(m);
+
+ g1.copy(this);
+ g2.copy(this);
+
+ FP4 c=g1.trace();
+
+ if (b.iszilch())
+ {
+ c=c.xtr_pow(e);
+ return c;
+ }
+
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+
+ return c;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+// Bos & Costello https://eprint.iacr.org/2013/458.pdf
+// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
+// Side channel attack secure
+
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,pb;
+ FP12 [] g=new FP12[8];
+ FP12 r=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+ byte[] s=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ {
+ t[i]=new BIG(u[i]);
+ t[i].norm();
+ }
+ g[0]=new FP12(q[0]); // q[0]
+ g[1]=new FP12(g[0]); g[1].mul(q[1]); // q[0].q[1]
+ g[2]=new FP12(g[0]); g[2].mul(q[2]); // q[0].q[2]
+ g[3]=new FP12(g[1]); g[3].mul(q[2]); // q[0].q[1].q[2]
+ g[4]=new FP12(q[0]); g[4].mul(q[3]); // q[0].q[3]
+ g[5]=new FP12(g[1]); g[5].mul(q[3]); // q[0].q[1].q[3]
+ g[6]=new FP12(g[2]); g[6].mul(q[3]); // q[0].q[2].q[3]
+ g[7]=new FP12(g[3]); g[7].mul(q[3]); // q[0].q[1].q[2].q[3]
+
+ // Make it odd
+ pb=1-t[0].parity();
+ t[0].inc(pb);
+ t[0].norm();
+
+ // Number of bits
+ mt.zero();
+ for (i=0;i<4;i++) {
+ mt.or(t[i]);
+ }
+ nb=1+mt.nbits();
+
+ // Sign pivot
+ s[nb-1]=1;
+ for (i=0;i<nb-1;i++) {
+ t[0].fshr(1);
+ s[i]=(byte)(2*t[0].parity()-1);
+ }
+
+ // Recoded exponent
+ for (i=0; i<nb; i++) {
+ w[i]=0;
+ int k=1;
+ for (j=1; j<4; j++) {
+ byte bt=(byte)(s[i]*t[j].parity());
+ t[j].fshr(1);
+ t[j].dec((int)(bt)>>1);
+ t[j].norm();
+ w[i]+=bt*(byte)k;
+ k*=2;
+ }
+ }
+
+ // Main loop
+ p.select(g,(int)(2*w[nb-1]+1));
+ for (i=nb-2;i>=0;i--) {
+ p.usqr();
+ r.select(g,(int)(2*w[i]+s[i]));
+ p.mul(r);
+ }
+
+ // apply correction
+ r.copy(q[0]); r.conj();
+ r.mul(p);
+ p.cmove(r,pb);
+
+ p.reduce();
+ return p;
+ }
+
+/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+/* Timing attack secure, but not cache attack secure */
+/*
+ public static FP12 pow4(FP12[] q,BIG[] u)
+ {
+ int i,j,nb,m;
+ int[] a=new int[4];
+ FP12 [] g=new FP12[8];
+ FP12 [] s=new FP12[2];
+ FP12 c=new FP12(1);
+ FP12 p=new FP12(0);
+ BIG [] t=new BIG[4];
+ BIG mt=new BIG(0);
+ byte[] w=new byte[BIG.NLEN*BIG.BASEBITS+1];
+
+ for (i=0;i<4;i++)
+ t[i]=new BIG(u[i]);
+
+ s[0]=new FP12(0);
+ s[1]=new FP12(0);
+
+ g[0]=new FP12(q[0]); s[0].copy(q[1]); s[0].conj(); g[0].mul(s[0]);
+ g[1]=new FP12(g[0]);
+ g[2]=new FP12(g[0]);
+ g[3]=new FP12(g[0]);
+ g[4]=new FP12(q[0]); g[4].mul(q[1]);
+ g[5]=new FP12(g[4]);
+ g[6]=new FP12(g[4]);
+ g[7]=new FP12(g[4]);
+
+ s[1].copy(q[2]); s[0].copy(q[3]); s[0].conj(); s[1].mul(s[0]);
+ s[0].copy(s[1]); s[0].conj(); g[1].mul(s[0]);
+ g[2].mul(s[1]);
+ g[5].mul(s[0]);
+ g[6].mul(s[1]);
+ s[1].copy(q[2]); s[1].mul(q[3]);
+ s[0].copy(s[1]); s[0].conj(); g[0].mul(s[0]);
+ g[3].mul(s[1]);
+ g[4].mul(s[0]);
+ g[7].mul(s[1]);
+
+// if power is even add 1 to power, and add q to correction
+
+ for (i=0;i<4;i++)
+ {
+ if (t[i].parity()==0)
+ {
+ t[i].inc(1); t[i].norm();
+ c.mul(q[i]);
+ }
+ mt.add(t[i]); mt.norm();
+ }
+ c.conj();
+ nb=1+mt.nbits();
+
+// convert exponent to signed 1-bit window
+ for (j=0;j<nb;j++)
+ {
+ for (i=0;i<4;i++)
+ {
+ a[i]=(t[i].lastbits(2)-2);
+ t[i].dec(a[i]); t[i].norm();
+ t[i].fshr(1);
+ }
+ w[j]=(byte)(8*a[0]+4*a[1]+2*a[2]+a[3]);
+ }
+ w[nb]=(byte)(8*t[0].lastbits(2)+4*t[1].lastbits(2)+2*t[2].lastbits(2)+t[3].lastbits(2));
+ p.copy(g[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ m=w[i]>>7;
+ j=(w[i]^m)-m; // j=abs(w[i])
+ j=(j-1)/2;
+ s[0].copy(g[j]); s[1].copy(g[j]); s[1].conj();
+ p.usqr();
+ p.mul(s[m&1]);
+ }
+ p.mul(c); // apply correction
+ p.reduce();
+ return p;
+ }
+*/
+/*
+ public static void main(String[] args) {
+ BIG p=new BIG(ROM.Modulus);
+ FP2 w0,w1;
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.zero(); b.zero(); a.inc(1); b.inc(2);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(3); b.inc(4);
+ w1=new FP2(a,b);
+ FP4 t0=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(5); b.inc(6);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(7); b.inc(8);
+ w1=new FP2(a,b);
+ FP4 t1=new FP4(w0,w1);
+
+ a.zero(); b.zero(); a.inc(9); b.inc(10);
+ w0=new FP2(a,b);
+ a.zero(); b.zero(); a.inc(11); b.inc(12);
+ w1=new FP2(a,b);
+ FP4 t2=new FP4(w0,w1);
+
+ FP12 w=new FP12(t0,t1,t2);
+ FP12 t=new FP12(w);
+
+ System.out.println("w= "+w.toString());
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ w.frob(f);
+ System.out.println("w= "+w.toString());
+
+ w=t.pow(p);
+
+ System.out.println("w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/w= "+w.toString());
+
+ w.inverse();
+
+ System.out.println("w= "+w.toString());
+
+ t.copy(w);
+ w.conj();
+ t.inverse();
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)= "+w.toString());
+
+ t.copy(w);
+ w.frob(f);
+ w.frob(f);
+ w.mul(t);
+
+ System.out.println("w^(p^6-1)(p^2+1)= "+w.toString());
+
+ t.copy(w);
+
+ t.inverse();
+ w.conj();
+
+ System.out.println("w= "+w.toString());
+ System.out.println("t= "+t.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/FP2.java b/src/main/java/org/apache/milagro/amcl/FP512BN/FP2.java
new file mode 100644
index 0000000..50a4d6d
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/FP2.java
@@ -0,0 +1,425 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^2 functions */
+
+/* FP2 elements are of the form a+ib, where i is sqrt(-1) */
+
+package org.apache.milagro.amcl.FP512BN;
+
+public final class FP2 {
+ private final FP a;
+ private final FP b;
+
+/* reduce components mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+
+/* normalise components of w */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+
+/* test this=0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP2 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this=1 ? */
+ public boolean isunity() {
+ FP one=new FP(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test this=x */
+ public boolean equals(FP2 x) {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+
+/* Constructors */
+ public FP2(int c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(FP2 x)
+ {
+ a=new FP(x.a);
+ b=new FP(x.b);
+ }
+
+ public FP2(FP c,FP d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(BIG c,BIG d)
+ {
+ a=new FP(c);
+ b=new FP(d);
+ }
+
+ public FP2(FP c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+
+ public FP2(BIG c)
+ {
+ a=new FP(c);
+ b=new FP(0);
+ }
+/*
+ public BIG geta()
+ {
+ return a.tobig();
+ }
+*/
+/* extract a */
+ public BIG getA()
+ {
+ return a.redc();
+ }
+
+/* extract b */
+ public BIG getB()
+ {
+ return b.redc();
+ }
+
+/* copy this=x */
+ public void copy(FP2 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+
+/* negate this mod Modulus */
+ public void neg()
+ {
+ FP m=new FP(a);
+ FP t=new FP(0);
+
+ m.add(b);
+ m.neg();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ }
+
+/* set to a-ib */
+ public void conj()
+ {
+ b.neg();
+ b.norm();
+ }
+
+/* this+=a */
+ public void add(FP2 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+
+/* this-=a */
+ public void sub(FP2 x)
+ {
+ FP2 m=new FP2(x);
+ m.neg();
+ add(m);
+ }
+
+ public void rsub(FP2 x) // *****
+ {
+ neg();
+ add(x);
+ }
+
+/* this*=s, where s is an FP */
+ public void pmul(FP s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this*=i, where i is an int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+
+/* this*=this */
+ public void sqr()
+ {
+ FP w1=new FP(a);
+ FP w3=new FP(a);
+ FP mb=new FP(b);
+
+ w1.add(b);
+ mb.neg();
+
+ w3.add(a);
+ w3.norm();
+ b.mul(w3);
+
+ a.add(mb);
+
+ w1.norm();
+ a.norm();
+
+ a.mul(w1);
+ }
+
+/* this*=y */
+/* Now uses Lazy reduction */
+ public void mul(FP2 y)
+ {
+ if ((long)(a.XES+b.XES)*(y.a.XES+y.b.XES)>(long)FP.FEXCESS)
+ {
+ if (a.XES>1) a.reduce();
+ if (b.XES>1) b.reduce();
+ }
+
+ DBIG pR=new DBIG(0);
+ BIG C=new BIG(a.x);
+ BIG D=new BIG(y.a.x);
+
+ pR.ucopy(new BIG(ROM.Modulus));
+
+ DBIG A=BIG.mul(a.x,y.a.x);
+ DBIG B=BIG.mul(b.x,y.b.x);
+
+ C.add(b.x); C.norm();
+ D.add(y.b.x); D.norm();
+
+ DBIG E=BIG.mul(C,D);
+ DBIG F=new DBIG(A); F.add(B);
+ B.rsub(pR);
+
+ A.add(B); A.norm();
+ E.sub(F); E.norm();
+
+ a.x.copy(FP.mod(A)); a.XES=3;
+ b.x.copy(FP.mod(E)); b.XES=2;
+ }
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP w1=new FP(b);
+ FP w2=new FP(a);
+ w1.sqr(); w2.sqr(); w1.add(w2);
+ if (w1.jacobi()!=1) { zero(); return false; }
+ w1=w1.sqrt();
+ w2.copy(a); w2.add(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1)
+ {
+ w2.copy(a); w2.sub(w1);
+ w2.norm(); w2.div2();
+ if (w2.jacobi()!=1) { zero(); return false; }
+ }
+ w2=w2.sqrt();
+ a.copy(w2);
+ w2.add(w2);
+ w2.inverse();
+ b.mul(w2);
+ return true;
+ }
+
+/* output to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+ norm();
+ FP w1=new FP(a);
+ FP w2=new FP(b);
+
+ w1.sqr();
+ w2.sqr();
+ w1.add(w2);
+ w1.inverse();
+ a.mul(w1);
+ w1.neg();
+ w1.norm();
+ b.mul(w1);
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+/* this*=sqrt(-1) */
+ public void times_i()
+ {
+ FP z=new FP(a);
+ a.copy(b); a.neg();
+ b.copy(z);
+ }
+
+/* w*=(1+sqrt(-1)) */
+/* where X*2-(1+sqrt(-1)) is irreducible for FP4, assumes p=3 mod 8 */
+ public void mul_ip()
+ {
+ FP2 t=new FP2(this);
+ FP z=new FP(a);
+ a.copy(b);
+ a.neg();
+ b.copy(z);
+ add(t);
+ }
+
+ public void div_ip2()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ }
+
+/* w/=(1+sqrt(-1)) */
+ public void div_ip()
+ {
+ FP2 t=new FP2(0);
+ norm();
+ t.a.copy(a); t.a.add(b);
+ t.b.copy(b); t.b.sub(a);
+ copy(t);
+ norm();
+ div2();
+ }
+/*
+ public FP2 pow(BIG e)
+ {
+ int bt;
+ FP2 r=new FP2(1);
+ e.norm();
+ norm();
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(this);
+ if (e.iszilch()) break;
+ sqr();
+ }
+
+ r.reduce();
+ return r;
+ }
+
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(27);
+ BIG pp1=new BIG(m);
+ BIG pm1=new BIG(m);
+ BIG a=new BIG(1);
+ BIG b=new BIG(1);
+ FP2 w=new FP2(a,b);
+ FP2 z=new FP2(w);
+
+ byte[] RAW=new byte[100];
+
+ RAND rng=new RAND();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ // for (int i=0;i<100;i++)
+ // {
+ a.randomnum(rng);
+ b.randomnum(rng);
+
+ w=new FP2(a,b);
+ System.out.println("w="+w.toString());
+
+ z=new FP2(w);
+ z.inverse();
+ System.out.println("z="+z.toString());
+
+ z.inverse();
+ if (!z.equals(w)) System.out.println("Error");
+ // }
+
+// System.out.println("m="+m.toString());
+// w.sqr();
+// w.mul(z);
+
+ System.out.println("w="+w.toString());
+
+
+ pp1.inc(1); pp1.norm();
+ pm1.dec(1); pm1.norm();
+ System.out.println("p+1="+pp1.toString());
+ System.out.println("p-1="+pm1.toString());
+ w=w.pow(pp1);
+ w=w.pow(pm1);
+ System.out.println("w="+w.toString());
+ }
+*/
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/FP4.java b/src/main/java/org/apache/milagro/amcl/FP512BN/FP4.java
new file mode 100644
index 0000000..97fa39f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/FP4.java
@@ -0,0 +1,721 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic Fp^4 functions */
+
+/* FP4 elements are of the form a+ib, where i is sqrt(-1+sqrt(-1)) */
+
+package org.apache.milagro.amcl.FP512BN;
+
+public final class FP4 {
+ private final FP2 a;
+ private final FP2 b;
+/* reduce all components of this mod Modulus */
+ public void reduce()
+ {
+ a.reduce();
+ b.reduce();
+ }
+/* normalise all components of this mod Modulus */
+ public void norm()
+ {
+ a.norm();
+ b.norm();
+ }
+/* test this==0 ? */
+ public boolean iszilch() {
+ //reduce();
+ return (a.iszilch() && b.iszilch());
+ }
+
+ public void cmove(FP4 g,int d)
+ {
+ a.cmove(g.a,d);
+ b.cmove(g.b,d);
+ }
+
+/* test this==1 ? */
+ public boolean isunity() {
+ FP2 one=new FP2(1);
+ return (a.equals(one) && b.iszilch());
+ }
+
+/* test is w real? That is in a+ib test b is zero */
+ public boolean isreal()
+ {
+ return b.iszilch();
+ }
+/* extract real part a */
+ public FP2 real()
+ {
+ return a;
+ }
+
+ public FP2 geta()
+ {
+ return a;
+ }
+/* extract imaginary part b */
+ public FP2 getb()
+ {
+ return b;
+ }
+/* test this=x? */
+ public boolean equals(FP4 x)
+ {
+ return (a.equals(x.a) && b.equals(x.b));
+ }
+/* constructors */
+ public FP4(int c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+
+ public FP4(FP4 x)
+ {
+ a=new FP2(x.a);
+ b=new FP2(x.b);
+ }
+
+ public FP4(FP2 c,FP2 d)
+ {
+ a=new FP2(c);
+ b=new FP2(d);
+ }
+
+ public FP4(FP2 c)
+ {
+ a=new FP2(c);
+ b=new FP2(0);
+ }
+/* copy this=x */
+ public void copy(FP4 x)
+ {
+ a.copy(x.a);
+ b.copy(x.b);
+ }
+/* set this=0 */
+ public void zero()
+ {
+ a.zero();
+ b.zero();
+ }
+/* set this=1 */
+ public void one()
+ {
+ a.one();
+ b.zero();
+ }
+/* set this=-this */
+ public void neg()
+ {
+ norm();
+ FP2 m=new FP2(a);
+ FP2 t=new FP2(0);
+ m.add(b);
+// m.norm();
+ m.neg();
+ // m.norm();
+ t.copy(m); t.add(b);
+ b.copy(m);
+ b.add(a);
+ a.copy(t);
+ norm();
+ }
+/* this=conjugate(this) */
+ public void conj()
+ {
+ b.neg(); norm();
+ }
+/* this=-conjugate(this) */
+ public void nconj()
+ {
+ a.neg(); norm();
+ }
+/* this+=x */
+ public void add(FP4 x)
+ {
+ a.add(x.a);
+ b.add(x.b);
+ }
+/* this-=x */
+ public void sub(FP4 x)
+ {
+ FP4 m=new FP4(x);
+ m.neg();
+ add(m);
+ }
+
+/* this*=s where s is FP2 */
+ public void pmul(FP2 s)
+ {
+ a.mul(s);
+ b.mul(s);
+ }
+
+/* this=x-this */
+ public void rsub(FP4 x)
+ {
+ neg();
+ add(x);
+ }
+
+
+/* this*=c where c is int */
+ public void imul(int c)
+ {
+ a.imul(c);
+ b.imul(c);
+ }
+/* this*=this */
+ public void sqr()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(a);
+
+ t3.mul(b);
+ t1.add(b);
+ t2.mul_ip();
+
+ t2.add(a);
+
+ t1.norm();
+ t2.norm();
+
+ a.copy(t1);
+
+ a.mul(t2);
+
+ t2.copy(t3);
+ t2.mul_ip();
+ t2.add(t3);
+ t2.norm();
+ t2.neg();
+ a.add(t2);
+
+ b.copy(t3);
+ b.add(t3);
+
+ norm();
+ }
+/* this*=y */
+ public void mul(FP4 y)
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+ FP2 t3=new FP2(0);
+ FP2 t4=new FP2(b);
+
+ t1.mul(y.a);
+ t2.mul(y.b);
+ t3.copy(y.b);
+ t3.add(y.a);
+ t4.add(a);
+
+ t3.norm();
+ t4.norm();
+
+ t4.mul(t3);
+
+ t3.copy(t1);
+ t3.neg();
+ t4.add(t3);
+ t4.norm();
+
+ // t4.sub(t1);
+ // t4.norm();
+
+ t3.copy(t2);
+ t3.neg();
+ b.copy(t4);
+ b.add(t3);
+
+ // b.copy(t4);
+ // b.sub(t2);
+
+ t2.mul_ip();
+ a.copy(t2);
+ a.add(t1);
+
+ norm();
+ }
+/* convert this to hex string */
+ public String toString()
+ {
+ return ("["+a.toString()+","+b.toString()+"]");
+ }
+
+ public String toRawString()
+ {
+ return ("["+a.toRawString()+","+b.toRawString()+"]");
+ }
+
+/* this=1/this */
+ public void inverse()
+ {
+// norm();
+
+ FP2 t1=new FP2(a);
+ FP2 t2=new FP2(b);
+
+ t1.sqr();
+ t2.sqr();
+ t2.mul_ip();
+ t2.norm();
+ t1.sub(t2);
+ t1.inverse();
+ a.mul(t1);
+ t1.neg();
+ t1.norm();
+ b.mul(t1);
+ }
+
+
+/* this*=i where i = sqrt(-1+sqrt(-1)) */
+ public void times_i()
+ {
+// norm();
+ FP2 s=new FP2(b);
+ FP2 t=new FP2(b);
+ s.times_i();
+ t.add(s);
+ // t.norm();
+ b.copy(a);
+ a.copy(t);
+ norm();
+ }
+
+/* this=this^p using Frobenius */
+ public void frob(FP2 f)
+ {
+ a.conj();
+ b.conj();
+ b.mul(f);
+ }
+
+/* this=this^e */
+ public FP4 pow(BIG e)
+ {
+ norm();
+ e.norm();
+ FP4 w=new FP4(this);
+ BIG z=new BIG(e);
+ FP4 r=new FP4(1);
+ while (true)
+ {
+ int bt=z.parity();
+ z.fshr(1);
+ if (bt==1) r.mul(w);
+ if (z.iszilch()) break;
+ w.sqr();
+ }
+ r.reduce();
+ return r;
+ }
+/* XTR xtr_a function */
+ public void xtr_A(FP4 w,FP4 y,FP4 z)
+ {
+ FP4 r=new FP4(w);
+ FP4 t=new FP4(w);
+ //y.norm();
+ r.sub(y);
+ r.norm();
+ r.pmul(a);
+ t.add(y);
+ t.norm();
+ t.pmul(b);
+ t.times_i();
+
+ copy(r);
+ add(t);
+ add(z);
+
+ norm();
+ }
+
+/* XTR xtr_d function */
+ public void xtr_D() {
+ FP4 w=new FP4(this);
+ sqr(); w.conj();
+ w.add(w);
+ w.norm();
+ sub(w);
+ reduce();
+ }
+
+/* r=x^n using XTR method on traces of FP12s */
+ public FP4 xtr_pow(BIG n) {
+ FP4 a=new FP4(3);
+ FP4 b=new FP4(this);
+ FP4 c=new FP4(b);
+ c.xtr_D();
+ FP4 t=new FP4(0);
+ FP4 r=new FP4(0);
+
+ n.norm();
+ int par=n.parity();
+ BIG v=new BIG(n); v.fshr(1);
+ if (par==0) {v.dec(1); v.norm();}
+
+ int nb=v.nbits();
+ for (int i=nb-1;i>=0;i--)
+ {
+ if (v.bit(i)!=1)
+ {
+ t.copy(b);
+ conj();
+ c.conj();
+ b.xtr_A(a,this,c);
+ conj();
+ c.copy(t);
+ c.xtr_D();
+ a.xtr_D();
+ }
+ else
+ {
+ t.copy(a); t.conj();
+ a.copy(b);
+ a.xtr_D();
+ b.xtr_A(c,this,t);
+ c.xtr_D();
+ }
+ }
+ if (par==0) r.copy(c);
+ else r.copy(b);
+ r.reduce();
+ return r;
+ }
+
+/* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+ public FP4 xtr_pow2(FP4 ck,FP4 ckml,FP4 ckm2l,BIG a,BIG b)
+ {
+ a.norm(); b.norm();
+ BIG e=new BIG(a);
+ BIG d=new BIG(b);
+ BIG w=new BIG(0);
+
+ FP4 cu=new FP4(ck); // can probably be passed in w/o copying
+ FP4 cv=new FP4(this);
+ FP4 cumv=new FP4(ckml);
+ FP4 cum2v=new FP4(ckm2l);
+ FP4 r=new FP4(0);
+ FP4 t=new FP4(0);
+
+ int f2=0;
+ while (d.parity()==0 && e.parity()==0)
+ {
+ d.fshr(1);
+ e.fshr(1);
+ f2++;
+ }
+
+ while (BIG.comp(d,e)!=0)
+ {
+ if (BIG.comp(d,e)>0)
+ {
+ w.copy(e); w.imul(4); w.norm();
+ if (BIG.comp(d,w)<=0)
+ {
+ w.copy(d); d.copy(e);
+ e.rsub(w); e.norm();
+
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cum2v.conj();
+ cumv.copy(cv);
+ cv.copy(cu);
+ cu.copy(t);
+
+ }
+ else if (d.parity()==0)
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ else if (e.parity()==1)
+ {
+ d.sub(e); d.norm();
+ d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cu.xtr_D();
+ cum2v.copy(cv);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cv.copy(t);
+ }
+ else
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ }
+ if (BIG.comp(d,e)<0)
+ {
+ w.copy(d); w.imul(4); w.norm();
+ if (BIG.comp(e,w)<=0)
+ {
+ e.sub(d); e.norm();
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cum2v.copy(cumv);
+ cumv.copy(cu);
+ cu.copy(t);
+ }
+ else if (e.parity()==0)
+ {
+ w.copy(d);
+ d.copy(e); d.fshr(1);
+ e.copy(w);
+ t.copy(cumv);
+ t.xtr_D();
+ cumv.copy(cum2v); cumv.conj();
+ cum2v.copy(t); cum2v.conj();
+ t.copy(cv);
+ t.xtr_D();
+ cv.copy(cu);
+ cu.copy(t);
+ }
+ else if (d.parity()==1)
+ {
+ w.copy(e);
+ e.copy(d);
+ w.sub(d); w.norm();
+ d.copy(w); d.fshr(1);
+ t.copy(cv);
+ t.xtr_A(cu,cumv,cum2v);
+ cumv.conj();
+ cum2v.copy(cu);
+ cum2v.xtr_D();
+ cum2v.conj();
+ cu.copy(cv);
+ cu.xtr_D();
+ cv.copy(t);
+ }
+ else
+ {
+ d.fshr(1);
+ r.copy(cum2v); r.conj();
+ t.copy(cumv);
+ t.xtr_A(cu,cv,r);
+ cum2v.copy(cumv);
+ cum2v.xtr_D();
+ cumv.copy(t);
+ cu.xtr_D();
+ }
+ }
+ }
+ r.copy(cv);
+ r.xtr_A(cu,cumv,cum2v);
+ for (int i=0;i<f2;i++)
+ r.xtr_D();
+ r=r.xtr_pow(d);
+ return r;
+ }
+
+/* this/=2 */
+ public void div2()
+ {
+ a.div2();
+ b.div2();
+ }
+
+ public void div_i()
+ {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip();
+ a.copy(v);
+ b.copy(u);
+ }
+
+ public void div_2i() {
+ FP2 u=new FP2(a);
+ FP2 v=new FP2(b);
+ u.div_ip2();
+ v.add(v); v.norm();
+ a.copy(v);
+ b.copy(u);
+ }
+
+
+/* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+/* returns true if this is QR */
+ public boolean sqrt()
+ {
+ if (iszilch()) return true;
+ FP2 wa=new FP2(a);
+ FP2 ws=new FP2(b);
+ FP2 wt=new FP2(a);
+
+ if (ws.iszilch())
+ {
+ if (wt.sqrt())
+ {
+ a.copy(wt);
+ b.zero();
+ } else {
+ wt.div_ip();
+ wt.sqrt();
+ b.copy(wt);
+ a.zero();
+ }
+ return true;
+ }
+
+ ws.sqr();
+ wa.sqr();
+ ws.mul_ip();
+ ws.norm();
+ wa.sub(ws);
+
+ ws.copy(wa);
+ if (!ws.sqrt()) {
+ return false;
+ }
+
+ wa.copy(wt); wa.add(ws); wa.norm(); wa.div2();
+
+ if (!wa.sqrt()) {
+ wa.copy(wt); wa.sub(ws); wa.norm(); wa.div2();
+ if (!wa.sqrt()) {
+ return false;
+ }
+ }
+ wt.copy(b);
+ ws.copy(wa); ws.add(wa);
+ ws.inverse();
+
+ wt.mul(ws);
+ a.copy(wa);
+ b.copy(wt);
+
+ return true;
+ }
+
+/* this*=s where s is FP */
+ public void qmul(FP s)
+ {
+ a.pmul(s);
+ b.pmul(s);
+ }
+
+
+
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG e=new BIG(12);
+ BIG a=new BIG(0);
+ BIG b=new BIG(0);
+
+ a.inc(27); b.inc(45);
+
+ FP2 w0=new FP2(a,b);
+
+ a.zero(); b.zero();
+ a.inc(33); b.inc(54);
+
+ FP2 w1=new FP2(a,b);
+
+
+ FP4 w=new FP4(w0,w1);
+ FP4 t=new FP4(w);
+
+ a=new BIG(ROM_ZZZ.CURVE_Fra);
+ b=new BIG(ROM_ZZZ.CURVE_Frb);
+
+ FP2 f=new FP2(a,b);
+
+ System.out.println("w= "+w.toString());
+
+ w=w.pow(m);
+
+ System.out.println("w^p= "+w.toString());
+
+ t.frob(f);
+
+
+ System.out.println("w^p= "+t.toString());
+
+ w=w.pow(m);
+ w=w.pow(m);
+ w=w.pow(m);
+ System.out.println("w^p4= "+w.toString());
+
+
+ System.out.println("Test Inversion");
+
+ w=new FP4(w0,w1);
+
+ w.inverse();
+
+ System.out.println("1/w mod p^4 = "+w.toString());
+
+ w.inverse();
+
+ System.out.println("1/(1/w) mod p^4 = "+w.toString());
+
+ FP4 ww=new FP4(w);
+
+ w=w.xtr_pow(e);
+ System.out.println("w^e= "+w.toString());
+
+
+ a.zero(); b.zero();
+ a.inc(37); b.inc(17);
+ w0=new FP2(a,b);
+ a.zero(); b.zero();
+ a.inc(49); b.inc(31);
+ w1=new FP2(a,b);
+
+ FP4 c1=new FP4(w0,w1);
+ FP4 c2=new FP4(w0,w1);
+ FP4 c3=new FP4(w0,w1);
+
+ BIG e1=new BIG(3331);
+ BIG e2=new BIG(3372);
+
+ FP4 cr=w.xtr_pow2(c1,c2,c3,e1,e2);
+
+ System.out.println("c^e= "+cr.toString());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/MPIN.java b/src/main/java/org/apache/milagro/amcl/FP512BN/MPIN.java
new file mode 100644
index 0000000..6f43fba
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/MPIN.java
@@ -0,0 +1,823 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* MPIN API Functions */
+
+package org.apache.milagro.amcl.FP512BN;
+
+import java.util.Date;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public class MPIN
+{
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int PAS=16;
+ public static final int INVALID_POINT=-14;
+ public static final int BAD_PARAMS=-11;
+ public static final int WRONG_ORDER=-18;
+ public static final int BAD_PIN=-19;
+
+/* Configure your PIN here */
+
+ public static final int MAXPIN=10000; /* PIN less than this */
+ public static final int PBLEN=14; /* Number of bits in PIN */
+ public static final int TS=10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+ public static final int TRAP=200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN - approx 2*sqrt(MAXPIN) */
+
+// public static final int HASH_TYPE=SHA256;
+
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,int n,byte[] B,int len)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (n>0) H.process_num(n);
+
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (n>0) H.process_num(n);
+ H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+ byte[] W=new byte[len];
+
+ if (sha>=len)
+ for (int i=0;i<len;i++) W[i]=R[i];
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+len-sha]=R[i];
+ for (int i=0;i<len-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<len;i++) W[i]=0;
+ }
+ return W;
+ }
+
+ /* return time in slots since epoch */
+ public static int today() {
+ Date date=new Date();
+ return (int) (date.getTime()/(1000*60*1440));
+ }
+
+ public static byte[] HASH_ID(int sha,byte[] ID,int len)
+ {
+ return hashit(sha,0,ID,len);
+ }
+
+/* Hash the M-Pin transcript - new */
+
+ public static byte[] HASH_ALL(int sha,byte[] HID,byte[] xID,byte[] xCID,byte[] SEC,byte[] Y,byte[] R,byte[] W,int len)
+ {
+ int i,ilen,tlen=0;
+
+ ilen=HID.length+SEC.length+Y.length+R.length+W.length;
+ if (xCID!=null) ilen+=xCID.length;
+ else ilen+=xID.length;
+
+ byte[] T = new byte[ilen];
+
+ for (i=0;i<HID.length;i++) T[i]=HID[i];
+ tlen+=HID.length;
+ if (xCID!=null)
+ {
+ for (i=0;i<xCID.length;i++) T[i+tlen]=xCID[i];
+ tlen+=xCID.length;
+ }
+ else
+ {
+ for (i=0;i<xID.length;i++) T[i+tlen]=xID[i];
+ tlen+=xID.length;
+ }
+ for (i=0;i<SEC.length;i++) T[i+tlen]=SEC[i];
+ tlen+=SEC.length;
+ for (i=0;i<Y.length;i++) T[i+tlen]=Y[i];
+ tlen+=Y.length;
+ for (i=0;i<R.length;i++) T[i+tlen]=R[i];
+ tlen+=R.length;
+ for (i=0;i<W.length;i++) T[i+tlen]=W[i];
+ tlen+=W.length;
+
+ return hashit(sha,0,T,len);
+ }
+
+/* return time since epoch */
+ public static int GET_TIME() {
+ Date date=new Date();
+ return (int) (date.getTime()/1000);
+ }
+
+ public static byte[] mpin_hash(int sha,FP4 c,ECP U)
+ {
+ byte[] w=new byte[EFS];
+ byte[] t=new byte[6*EFS];
+ byte[] h=null;
+ c.geta().getA().toBytes(w); for (int i=0;i<EFS;i++) t[i]=w[i];
+ c.geta().getB().toBytes(w); for (int i=EFS;i<2*EFS;i++) t[i]=w[i-EFS];
+ c.getb().getA().toBytes(w); for (int i=2*EFS;i<3*EFS;i++) t[i]=w[i-2*EFS];
+ c.getb().getB().toBytes(w); for (int i=3*EFS;i<4*EFS;i++) t[i]=w[i-3*EFS];
+
+ U.getX().toBytes(w); for (int i=4*EFS;i<5*EFS;i++) t[i]=w[i-4*EFS];
+ U.getY().toBytes(w); for (int i=5*EFS;i<6*EFS;i++) t[i]=w[i-5*EFS];
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(t);
+ h=H.hash();
+ }
+ if (h==null) return null;
+ byte[] R=new byte[ECP.AESKEY];
+ for (int i=0;i<ECP.AESKEY;i++) R[i]=h[i];
+ return R;
+ }
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+ public static ECP map(BIG u,int cb)
+ {
+ ECP P;
+ BIG x=new BIG(u);
+ BIG p=new BIG(ROM.Modulus);
+ x.mod(p);
+ while (true)
+ {
+ P=new ECP(x,cb);
+ if (!P.is_infinity()) break;
+ x.inc(1); x.norm();
+ }
+ return P;
+ }
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+ public static int unmap(BIG u,ECP P)
+ {
+ int s=P.getS();
+ ECP R;
+ int r=0;
+ BIG x=P.getX();
+ u.copy(x);
+ while (true)
+ {
+ u.dec(1); u.norm();
+ r++;
+ R=new ECP(u,s);
+ if (!R.is_infinity()) break;
+ }
+ return r;
+ }
+
+
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+ public static int ENCODING(RAND rng,byte[] E)
+ {
+ int rn,m,su,sv;
+ byte[] T=new byte[EFS];
+
+ for (int i=0;i<EFS;i++) T[i]=E[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=E[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ ECP P=new ECP(u,v);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG p=new BIG(ROM.Modulus);
+ u=BIG.randomnum(p,rng);
+
+ su=rng.getByte(); /*if (su<0) su=-su;*/ su%=2;
+
+ ECP W=map(u,su);
+ P.sub(W); //P.affine();
+ sv=P.getS();
+ rn=unmap(v,P);
+ m=rng.getByte(); /*if (m<0) m=-m;*/ m%=rn;
+ v.inc(m+1);
+ E[0]=(byte)(su+2*sv);
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) E[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+ public static int DECODING(byte[] D)
+ {
+ int su,sv;
+ byte[] T=new byte[EFS];
+
+ if ((D[0]&0x04)!=0) return INVALID_POINT;
+
+ for (int i=0;i<EFS;i++) T[i]=D[i+1];
+ BIG u=BIG.fromBytes(T);
+ for (int i=0;i<EFS;i++) T[i]=D[i+EFS+1];
+ BIG v=BIG.fromBytes(T);
+
+ su=D[0]&1;
+ sv=(D[0]>>1)&1;
+ ECP W=map(u,su);
+ ECP P=map(v,sv);
+ P.add(W); //P.affine();
+ u=P.getX();
+ v=P.getY();
+ D[0]=0x04;
+ u.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+1]=T[i];
+ v.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i+EFS+1]=T[i];
+
+ return 0;
+ }
+
+/* R=R1+R2 in group G1 */
+ public static int RECOMBINE_G1(byte[] R1,byte[] R2,byte[] R)
+ {
+ ECP P=ECP.fromBytes(R1);
+ ECP Q=ECP.fromBytes(R2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(R,false);
+ return 0;
+ }
+
+/* W=W1+W2 in group G2 */
+ public static int RECOMBINE_G2(byte[] W1,byte[] W2,byte[] W)
+ {
+ ECP2 P=ECP2.fromBytes(W1);
+ ECP2 Q=ECP2.fromBytes(W2);
+
+ if (P.is_infinity() || Q.is_infinity()) return INVALID_POINT;
+
+ P.add(Q); //P.affine();
+
+ P.toBytes(W);
+ return 0;
+ }
+
+/* create random secret S */
+ public static int RANDOM_GENERATE(RAND rng,byte[] S)
+ {
+ BIG s;
+ BIG r=new BIG(ROM.CURVE_Order);
+ s=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+ return 0;
+ }
+
+/* Extract PIN from TOKEN for identity CID */
+ public static int EXTRACT_PIN(int sha,byte[] CID,int pin,byte[] TOKEN)
+ {
+ ECP P=ECP.fromBytes(TOKEN);
+ if (P.is_infinity()) return INVALID_POINT;
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R=ECP.mapit(h);
+
+
+ pin%=MAXPIN;
+
+ R=R.pinmul(pin,PBLEN);
+ P.sub(R); //P.affine();
+
+ P.toBytes(TOKEN,false);
+
+ return 0;
+ }
+
+/* Implement step 2 on client side of MPin protocol */
+ public static int CLIENT_2(byte[] X,byte[] Y,byte[] SEC)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ ECP P=ECP.fromBytes(SEC);
+ if (P.is_infinity()) return INVALID_POINT;
+
+ BIG px=BIG.fromBytes(X);
+ BIG py=BIG.fromBytes(Y);
+ px.add(py);
+ px.mod(r);
+ // px.rsub(r);
+
+ P=PAIR.G1mul(P,px);
+ P.neg();
+ P.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Implement step 1 on client side of MPin protocol */
+ public static int CLIENT_1(int sha,int date,byte[] CLIENT_ID,RAND rng,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT)
+ {
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG x;
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P,T,W;
+ BIG px;
+// byte[] t=new byte[EFS];
+
+ byte[] h=hashit(sha,0,CLIENT_ID,EFS);
+ P=ECP.mapit(h);
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ pin%=MAXPIN;
+ W=P.pinmul(pin,PBLEN);
+ T.add(W);
+ if (date!=0)
+ {
+ W=ECP.fromBytes(PERMIT);
+ if (W.is_infinity()) return INVALID_POINT;
+ T.add(W);
+ h=hashit(sha,date,h,EFS);
+ W=ECP.mapit(h);
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ W=PAIR.G1mul(W,x);
+ P.add(W);
+ //P.affine();
+ }
+ else
+ {
+ P.add(W); //P.affine();
+ P=PAIR.G1mul(P,x);
+ }
+ if (xCID!=null) P.toBytes(xCID,false);
+ }
+ else
+ {
+ if (xID!=null)
+ {
+ P=PAIR.G1mul(P,x);
+ P.toBytes(xID,false);
+ }
+ }
+
+ //T.affine();
+ T.toBytes(SEC,false);
+ return 0;
+ }
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+ public static int GET_SERVER_SECRET(byte[] S,byte[] SST)
+ {
+ ECP2 Q=ECP2.generator();
+ BIG s=BIG.fromBytes(S);
+ Q=PAIR.G2mul(Q,s);
+ Q.toBytes(SST);
+ return 0;
+ }
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+ public static int GET_G1_MULTIPLE(RAND rng, int type,byte[] X,byte[] G,byte[] W)
+ {
+ BIG x;
+ BIG r=new BIG(ROM.CURVE_Order);
+ if (rng!=null)
+ {
+ x=BIG.randomnum(r,rng);
+ //if (ROM.AES_S>0)
+ //{
+ // x.mod2m(2*ROM.AES_S);
+ //}
+ x.toBytes(X);
+ }
+ else
+ {
+ x=BIG.fromBytes(X);
+ }
+ ECP P;
+ if (type==0)
+ {
+ P=ECP.fromBytes(G);
+ if (P.is_infinity()) return INVALID_POINT;
+ }
+ else
+ P=ECP.mapit(G);
+
+ PAIR.G1mul(P,x).toBytes(W,false);
+ return 0;
+ }
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+ public static int GET_CLIENT_SECRET(byte[] S,byte[] CID,byte[] CST)
+ {
+ return GET_G1_MULTIPLE(null,1,S,CID,CST);
+ }
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+ public static int GET_CLIENT_PERMIT(int sha,int date,byte[] S,byte[] CID,byte[] CTT)
+ {
+ byte[] h=hashit(sha,date,CID,EFS);
+ ECP P=ECP.mapit(h);
+
+ BIG s=BIG.fromBytes(S);
+ ECP OP=PAIR.G1mul(P,s);
+
+ OP.toBytes(CTT,false);
+ return 0;
+ }
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+ public static void SERVER_1(int sha,int date,byte[] CID,byte[] HID,byte[] HTID)
+ {
+ byte[] h=hashit(sha,0,CID,EFS);
+ ECP R,P=ECP.mapit(h);
+
+ P.toBytes(HID,false); // new
+ if (date!=0)
+ {
+ // if (HID!=null) P.toBytes(HID);
+ h=hashit(sha,date,h,EFS);
+ R=ECP.mapit(h);
+ P.add(R); //P.affine();
+ P.toBytes(HTID,false);
+ }
+ // else P.toBytes(HID,false);
+ }
+
+/* Implement step 2 of MPin protocol on server side */
+ public static int SERVER_2(int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] mSEC,byte[] E,byte[] F)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ ECP2 Q=ECP2.generator();
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+
+ ECP R;
+ if (date!=0)
+ R=ECP.fromBytes(xCID);
+ else
+ {
+ if (xID==null) return BAD_PARAMS;
+ R=ECP.fromBytes(xID);
+ }
+ if (R.is_infinity()) return INVALID_POINT;
+
+ BIG y=BIG.fromBytes(Y);
+ ECP P;
+ if (date!=0) P=ECP.fromBytes(HTID);
+ else
+ {
+ if (HID==null) return BAD_PARAMS;
+ P=ECP.fromBytes(HID);
+ }
+
+ if (P.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ R=ECP.fromBytes(mSEC);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ FP12 g;
+
+ g=PAIR.ate2(Q,R,sQ,P);
+ g=PAIR.fexp(g);
+
+ if (!g.isunity())
+ {
+ if (HID!=null && xID!=null && E!=null && F!=null)
+ {
+ g.toBytes(E);
+ if (date!=0)
+ {
+ P=ECP.fromBytes(HID);
+ if (P.is_infinity()) return INVALID_POINT;
+ R=ECP.fromBytes(xID);
+ if (R.is_infinity()) return INVALID_POINT;
+
+ P=PAIR.G1mul(P,y);
+ P.add(R); //P.affine();
+ }
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(F);
+ }
+ return BAD_PIN;
+ }
+
+ return 0;
+ }
+
+/* Pollards kangaroos used to return PIN error */
+ public static int KANGAROO(byte[] E,byte[] F)
+ {
+ FP12 ge=FP12.fromBytes(E);
+ FP12 gf=FP12.fromBytes(F);
+ int[] distance = new int[TS];
+ FP12 t=new FP12(gf);
+ FP12[] table=new FP12[TS];
+ int i,j,m,s,dn,dm,res,steps;
+
+ s=1;
+ for (m=0;m<TS;m++)
+ {
+ distance[m]=s;
+ table[m]=new FP12(t);
+ s*=2;
+ t.usqr();
+ }
+ t.one();
+ dn=0;
+ for (j=0;j<TRAP;j++)
+ {
+ i=t.geta().geta().getA().lastbits(20)%TS;
+ t.mul(table[i]);
+ dn+=distance[i];
+ }
+ gf.copy(t); gf.conj();
+ steps=0; dm=0;
+ res=0;
+ while (dm-dn<MAXPIN)
+ {
+ steps++;
+ if (steps>4*TRAP) break;
+ i=ge.geta().geta().getA().lastbits(20)%TS;
+ ge.mul(table[i]);
+ dm+=distance[i];
+ if (ge.equals(t))
+ {
+ res=dm-dn;
+ break;
+ }
+ if (ge.equals(gf))
+ {
+ res=dn-dm;
+ break;
+ }
+
+ }
+ if (steps>4*TRAP || dm-dn>=MAXPIN) {res=0; } // Trap Failed - probable invalid token
+ return res;
+ }
+
+/* Functions to support M-Pin Full */
+
+ public static int PRECOMPUTE(byte[] TOKEN,byte[] CID,byte[] G1,byte[] G2)
+ {
+ ECP P,T;
+ FP12 g;
+
+ T=ECP.fromBytes(TOKEN);
+ if (T.is_infinity()) return INVALID_POINT;
+
+ P=ECP.mapit(CID);
+
+ ECP2 Q=ECP2.generator();
+
+ g=PAIR.ate(Q,T);
+ g=PAIR.fexp(g);
+ g.toBytes(G1);
+
+ g=PAIR.ate(Q,P);
+ g=PAIR.fexp(g);
+ g.toBytes(G2);
+
+ return 0;
+ }
+
+
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+ public static int CLIENT_KEY(int sha,byte[] G1,byte[] G2,int pin,byte[] R,byte[] X,byte[] H,byte[] wCID,byte[] CK)
+ {
+ byte[] t;
+
+ FP12 g1=FP12.fromBytes(G1);
+ FP12 g2=FP12.fromBytes(G2);
+ BIG z=BIG.fromBytes(R);
+ BIG x=BIG.fromBytes(X);
+ BIG h=BIG.fromBytes(H);
+
+ ECP W=ECP.fromBytes(wCID);
+ if (W.is_infinity()) return INVALID_POINT;
+
+ W=PAIR.G1mul(W,x);
+
+// FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG r=new BIG(ROM.CURVE_Order);
+// BIG q=new BIG(ROM.Modulus);
+
+ z.add(h); //new
+ z.mod(r);
+
+ g2.pinpow(pin,PBLEN);
+ g1.mul(g2);
+
+ FP4 c=g1.compow(z,r);
+/*
+ BIG m=new BIG(q);
+ m.mod(r);
+
+ BIG a=new BIG(z);
+ a.mod(m);
+
+ BIG b=new BIG(z);
+ b.div(m);
+
+
+ FP4 c=g1.trace();
+ g2.copy(g1);
+ g2.frob(f);
+ FP4 cp=g2.trace();
+ g1.conj();
+ g2.mul(g1);
+ FP4 cpm1=g2.trace();
+ g2.mul(g1);
+ FP4 cpm2=g2.trace();
+
+ c=c.xtr_pow2(cp,cpm1,cpm2,a,b);
+*/
+ t=mpin_hash(sha,c,W);
+
+ for (int i=0;i<ECP.AESKEY;i++) CK[i]=t[i];
+
+ return 0;
+ }
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+ public static int SERVER_KEY(int sha,byte[] Z,byte[] SST,byte[] W,byte[] H,byte[] HID,byte[] xID,byte[] xCID,byte[] SK)
+ {
+ byte[] t;
+
+ ECP2 sQ=ECP2.fromBytes(SST);
+ if (sQ.is_infinity()) return INVALID_POINT;
+ ECP R=ECP.fromBytes(Z);
+ if (R.is_infinity()) return INVALID_POINT;
+ ECP A=ECP.fromBytes(HID);
+ if (A.is_infinity()) return INVALID_POINT;
+
+ ECP U;
+ if (xCID!=null)
+ U=ECP.fromBytes(xCID);
+ else
+ U=ECP.fromBytes(xID);
+ if (U.is_infinity()) return INVALID_POINT;
+
+ BIG w=BIG.fromBytes(W);
+ BIG h=BIG.fromBytes(H);
+ A=PAIR.G1mul(A,h); // new
+ R.add(A); //R.affine();
+
+ U=PAIR.G1mul(U,w);
+ FP12 g=PAIR.ate(sQ,R);
+ g=PAIR.fexp(g);
+
+ FP4 c=g.trace();
+
+ t=mpin_hash(sha,c,U);
+
+ for (int i=0;i<ECP.AESKEY;i++) SK[i]=t[i];
+
+ return 0;
+ }
+
+/* Generate Y = H(epoch, xCID/xID) */
+ public static void GET_Y(int sha,int TimeValue,byte[] xCID,byte[] Y)
+ {
+ byte[] h = hashit(sha,TimeValue,xCID,EFS);
+ BIG y = BIG.fromBytes(h);
+ BIG q=new BIG(ROM.CURVE_Order);
+ y.mod(q);
+ //if (ROM.AES_S>0)
+ //{
+ // y.mod2m(2*ROM.AES_S);
+ //}
+ y.toBytes(Y);
+ }
+
+/* One pass MPIN Client */
+ public static int CLIENT(int sha,int date,byte[] CLIENT_ID,RAND RNG,byte[] X,int pin,byte[] TOKEN,byte[] SEC,byte[] xID,byte[] xCID,byte[] PERMIT, int TimeValue, byte[] Y)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ rtn = CLIENT_1(sha,date,CLIENT_ID,RNG,X,pin,TOKEN,SEC,xID,xCID,PERMIT);
+ if (rtn != 0)
+ return rtn;
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+
+/* One pass MPIN Server */
+ public static int SERVER(int sha,int date,byte[] HID,byte[] HTID,byte[] Y,byte[] SST,byte[] xID,byte[] xCID,byte[] SEC,byte[] E,byte[] F,byte[] CID, int TimeValue)
+ {
+ int rtn=0;
+
+ byte[] pID;
+ if (date == 0)
+ pID = xID;
+ else
+ pID = xCID;
+
+ SERVER_1(sha,date,CID,HID,HTID);
+
+ GET_Y(sha,TimeValue,pID,Y);
+
+ rtn = SERVER_2(date,HID,HTID,Y,SST,xID,xCID,SEC,E,F);
+ if (rtn != 0)
+ return rtn;
+
+ return 0;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/PAIR.java b/src/main/java/org/apache/milagro/amcl/FP512BN/PAIR.java
new file mode 100644
index 0000000..fecf81d
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/PAIR.java
@@ -0,0 +1,817 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BN Curve Pairing functions */
+
+package org.apache.milagro.amcl.FP512BN;
+
+public final class PAIR {
+
+ public static final boolean USE_GLV =true;
+ public static final boolean USE_GS_G2 =true;
+ public static final boolean USE_GS_GT =true;
+ public static final boolean GT_STRONG=false;
+
+
+/* Line function */
+ public static FP12 line(ECP2 A,ECP2 B,FP Qx,FP Qy)
+ {
+//System.out.println("Into line");
+ FP4 a,b,c; // Edits here
+// c=new FP4(0);
+ if (A==B)
+ { // Doubling
+ FP2 XX=new FP2(A.getx()); //X
+ FP2 YY=new FP2(A.gety()); //Y
+ FP2 ZZ=new FP2(A.getz()); //Z
+ FP2 YZ=new FP2(YY); //Y
+ YZ.mul(ZZ); //YZ
+ XX.sqr(); //X^2
+ YY.sqr(); //Y^2
+ ZZ.sqr(); //Z^2
+
+ YZ.imul(4);
+ YZ.neg(); YZ.norm(); //-2YZ
+ YZ.pmul(Qy); //-2YZ.Ys
+
+ XX.imul(6); //3X^2
+ XX.pmul(Qx); //3X^2.Xs
+
+ int sb=3*ROM.CURVE_B_I;
+ ZZ.imul(sb);
+
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ ZZ.div_ip2();
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ ZZ.mul_ip();
+ ZZ.add(ZZ);
+ YZ.mul_ip();
+ YZ.norm();
+ }
+
+ ZZ.norm(); // 3b.Z^2
+
+ YY.add(YY);
+ ZZ.sub(YY); ZZ.norm(); // 3b.Z^2-Y^2
+
+ a=new FP4(YZ,ZZ); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(XX); // L(0,1) | L(0,0) | L(1,0)
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(XX); c.times_i();
+ }
+ A.dbl();
+ }
+ else
+ { // Addition - assume B is affine
+
+ FP2 X1=new FP2(A.getx()); // X1
+ FP2 Y1=new FP2(A.gety()); // Y1
+ FP2 T1=new FP2(A.getz()); // Z1
+ FP2 T2=new FP2(A.getz()); // Z1
+
+ T1.mul(B.gety()); // T1=Z1.Y2
+ T2.mul(B.getx()); // T2=Z1.X2
+
+ X1.sub(T2); X1.norm(); // X1=X1-Z1.X2
+ Y1.sub(T1); Y1.norm(); // Y1=Y1-Z1.Y2
+
+ T1.copy(X1); // T1=X1-Z1.X2
+ X1.pmul(Qy); // X1=(X1-Z1.X2).Ys
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ X1.mul_ip();
+ X1.norm();
+ }
+
+ T1.mul(B.gety()); // T1=(X1-Z1.X2).Y2
+
+ T2.copy(Y1); // T2=Y1-Z1.Y2
+ T2.mul(B.getx()); // T2=(Y1-Z1.Y2).X2
+ T2.sub(T1); T2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+ Y1.pmul(Qx); Y1.neg(); Y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+ a=new FP4(X1,T2); // (X1-Z1.X2).Ys | (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2 | - (Y1-Z1.Y2).Xs
+ if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
+ {
+ b=new FP4(Y1);
+ c=new FP4(0);
+ }
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ b=new FP4(0);
+ c=new FP4(Y1); c.times_i();
+ }
+ A.add(B);
+ }
+//System.out.println("Out of line");
+ return new FP12(a,b,c);
+ }
+
+/* Optimal R-ate pairing */
+ public static FP12 ate(ECP2 P1,ECP Q1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+
+ ECP2 A=new ECP2();
+ FP12 r=new FP12(1);
+ A.copy(P);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ //r.conj();
+ A.neg();
+ }
+ K.copy(P);
+ K.frob(f);
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+ public static FP12 ate2(ECP2 P1,ECP Q1,ECP2 R1,ECP S1)
+ {
+ FP2 f;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG n=new BIG(x);
+ ECP2 K=new ECP2();
+ FP12 lv;
+ int bt;
+
+ ECP2 P=new ECP2(P1);
+ ECP Q=new ECP(Q1);
+
+ P.affine();
+ Q.affine();
+
+ ECP2 R=new ECP2(R1);
+ ECP S=new ECP(S1);
+
+ R.affine();
+ S.affine();
+
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+ n.pmul(6);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ n.inc(2);
+ } else {
+ n.dec(2);
+ }
+ }
+ else
+ n.copy(x);
+ n.norm();
+
+ BIG n3=new BIG(n);
+ n3.pmul(3);
+ n3.norm();
+
+ FP Qx=new FP(Q.getx());
+ FP Qy=new FP(Q.gety());
+ FP Sx=new FP(S.getx());
+ FP Sy=new FP(S.gety());
+
+ ECP2 A=new ECP2();
+ ECP2 B=new ECP2();
+ FP12 r=new FP12(1);
+
+ A.copy(P);
+ B.copy(R);
+
+ ECP2 MP=new ECP2();
+ MP.copy(P); MP.neg();
+ ECP2 MR=new ECP2();
+ MR.copy(R); MR.neg();
+
+
+ int nb=n3.nbits();
+
+ for (int i=nb-2;i>=1;i--)
+ {
+ r.sqr();
+ lv=line(A,A,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ lv=line(B,B,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+
+ bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+ if (bt==1)
+ {
+ lv=line(A,P,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ lv=line(B,R,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ if (bt==-1)
+ {
+ //P.neg();
+ lv=line(A,MP,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //P.neg();
+ //R.neg();
+ lv=line(B,MR,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ //R.neg();
+ }
+ }
+
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ r.conj();
+ }
+
+/* R-ate fixup required for BN curves */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ // r.conj();
+ A.neg();
+ B.neg();
+ }
+
+ K.copy(P);
+ K.frob(f);
+
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(A,K,Qx,Qy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.copy(R);
+ K.frob(f);
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ K.frob(f);
+ K.neg();
+ lv=line(B,K,Sx,Sy);
+ r.smul(lv,ECP.SEXTIC_TWIST);
+ }
+ return r;
+ }
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+ public static FP12 fexp(FP12 m)
+ {
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ FP12 r=new FP12(m);
+
+/* Easy part of final exp */
+ FP12 lv=new FP12(r);
+ lv.inverse();
+ r.conj();
+
+ r.mul(lv);
+ lv.copy(r);
+ r.frob(f);
+ r.frob(f);
+ r.mul(lv);
+/* Hard part of final exp */
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ FP12 x0,x1,x2,x3,x4,x5;
+ lv.copy(r);
+ lv.frob(f);
+ x0=new FP12(lv);
+ x0.frob(f);
+ lv.mul(r);
+ x0.mul(lv);
+ x0.frob(f);
+ x1=new FP12(r);
+ x1.conj();
+ x4=r.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x4.conj();
+ }
+
+ x3=new FP12(x4);
+ x3.frob(f);
+
+ x2=x4.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ x2.conj();
+ }
+ x5=new FP12(x2); x5.conj();
+ lv=x2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.POSITIVEX)
+ {
+ lv.conj();
+ }
+ x2.frob(f);
+ r.copy(x2); r.conj();
+
+ x4.mul(r);
+ x2.frob(f);
+
+ r.copy(lv);
+ r.frob(f);
+ lv.mul(r);
+
+ lv.usqr();
+ lv.mul(x4);
+ lv.mul(x5);
+ r.copy(x3);
+ r.mul(x5);
+ r.mul(lv);
+ lv.mul(x2);
+ r.usqr();
+ r.mul(lv);
+ r.usqr();
+ lv.copy(r);
+ lv.mul(x1);
+ r.mul(x0);
+ lv.usqr();
+ r.mul(lv);
+ r.reduce();
+ }
+ else
+ {
+
+ FP12 y0,y1,y2,y3;
+// Ghamman & Fouotsa Method
+ y0=new FP12(r); y0.usqr();
+ y1=y0.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y1.conj();
+ }
+ x.fshr(1); y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+
+ x.fshl(1);
+ y3=new FP12(r); y3.conj();
+ y1.mul(y3);
+
+ y1.conj();
+ y1.mul(y2);
+
+ y2=y1.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y3=y2.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y3.conj();
+ }
+ y1.conj();
+ y3.mul(y1);
+
+ y1.conj();
+ y1.frob(f); y1.frob(f); y1.frob(f);
+ y2.frob(f); y2.frob(f);
+ y1.mul(y2);
+
+ y2=y3.pow(x);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ y2.conj();
+ }
+ y2.mul(y0);
+ y2.mul(r);
+
+ y1.mul(y2);
+ y2.copy(y3); y2.frob(f);
+ y1.mul(y2);
+ r.copy(y1);
+ r.reduce();
+ }
+
+ return r;
+ }
+
+/* GLV method */
+ public static BIG[] glv(BIG e)
+ {
+ BIG[] u=new BIG[2];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+
+ BIG[] v=new BIG[2];
+ for (i=0;i<2;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_W[i])); // why not just t=new BIG(ROM.CURVE_W[i]);
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<2;i++)
+ for (j=0;j<2;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_SB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ { // -(x^2).P = (Beta.x,y)
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG x2=BIG.smul(x,x);
+ u[0]=new BIG(e);
+ u[0].mod(x2);
+ u[1]=new BIG(e);
+ u[1].div(x2);
+ u[1].rsub(q);
+ }
+ return u;
+ }
+
+/* Galbraith & Scott Method */
+ public static BIG[] gs(BIG e)
+ {
+ BIG[] u=new BIG[4];
+ if (ECP.CURVE_PAIRING_TYPE==ECP.BN)
+ {
+ int i,j;
+ BIG t=new BIG(0);
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] v=new BIG[4];
+ for (i=0;i<4;i++)
+ {
+ t.copy(new BIG(ROM.CURVE_WB[i]));
+ DBIG d=BIG.mul(t,e);
+ v[i]=new BIG(d.div(q));
+ u[i]=new BIG(0);
+ }
+ u[0].copy(e);
+ for (i=0;i<4;i++)
+ for (j=0;j<4;j++)
+ {
+ t.copy(new BIG(ROM.CURVE_BB[j][i]));
+ t.copy(BIG.modmul(v[j],t,q));
+ u[i].add(q);
+ u[i].sub(t);
+ u[i].mod(q);
+ }
+ }
+ else
+ {
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ BIG w=new BIG(e);
+ for (int i=0;i<3;i++)
+ {
+ u[i]=new BIG(w);
+ u[i].mod(x);
+ w.div(x);
+ }
+ u[3]=new BIG(w);
+ if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
+ {
+ u[1].copy(BIG.modneg(u[1],q));
+ u[3].copy(BIG.modneg(u[3],q));
+ }
+ }
+ return u;
+ }
+
+/* Multiply P by e in group G1 */
+ public static ECP G1mul(ECP P,BIG e)
+ {
+ ECP R;
+ if (USE_GLV)
+ {
+ //P.affine();
+ R=new ECP();
+ R.copy(P);
+ int i,np,nn;
+ ECP Q=new ECP();
+ Q.copy(P); Q.affine();
+ BIG q=new BIG(ROM.CURVE_Order);
+ FP cru=new FP(new BIG(ROM.CURVE_Cru));
+ BIG t=new BIG(0);
+ BIG[] u=glv(e);
+ Q.getx().mul(cru);
+
+ np=u[0].nbits();
+ t.copy(BIG.modneg(u[0],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[0].copy(t);
+ R.neg();
+ }
+
+ np=u[1].nbits();
+ t.copy(BIG.modneg(u[1],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[1].copy(t);
+ Q.neg();
+ }
+ u[0].norm();
+ u[1].norm();
+ R=R.mul2(u[0],Q,u[1]);
+
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* Multiply P by e in group G2 */
+ public static ECP2 G2mul(ECP2 P,BIG e)
+ {
+ ECP2 R;
+ if (USE_GS_G2)
+ {
+ ECP2[] Q=new ECP2[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
+ {
+ f.inverse();
+ f.norm();
+ }
+
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG[] u=gs(e);
+
+ BIG t=new BIG(0);
+ int i,np,nn;
+ //P.affine();
+
+ Q[0]=new ECP2(); Q[0].copy(P);
+ for (i=1;i<4;i++)
+ {
+ Q[i]=new ECP2(); Q[i].copy(Q[i-1]);
+ Q[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ Q[i].neg();
+ }
+ u[i].norm();
+ //Q[i].affine();
+ }
+
+ R=ECP2.mul4(Q,u);
+ }
+ else
+ {
+ R=P.mul(e);
+ }
+ return R;
+ }
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */
+ public static FP12 GTpow(FP12 d,BIG e)
+ {
+ FP12 r;
+ if (USE_GS_GT)
+ {
+ FP12[] g=new FP12[4];
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+ BIG q=new BIG(ROM.CURVE_Order);
+ BIG t=new BIG(0);
+ int i,np,nn;
+ BIG[] u=gs(e);
+
+ g[0]=new FP12(d);
+ for (i=1;i<4;i++)
+ {
+ g[i]=new FP12(0); g[i].copy(g[i-1]);
+ g[i].frob(f);
+ }
+ for (i=0;i<4;i++)
+ {
+ np=u[i].nbits();
+ t.copy(BIG.modneg(u[i],q));
+ nn=t.nbits();
+ if (nn<np)
+ {
+ u[i].copy(t);
+ g[i].conj();
+ }
+ u[i].norm();
+ }
+ r=FP12.pow4(g,u);
+ }
+ else
+ {
+ r=d.pow(e);
+ }
+ return r;
+ }
+
+/* test group membership - no longer needed */
+/* with GT-Strong curve, now only check that m!=1, conj(m)*m==1, and m.m^{p^4}=m^{p^2} */
+/*
+ public static boolean GTmember(FP12 m)
+ {
+ if (m.isunity()) return false;
+ FP12 r=new FP12(m);
+ r.conj();
+ r.mul(m);
+ if (!r.isunity()) return false;
+
+ FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+
+ r.copy(m); r.frob(f); r.frob(f);
+ FP12 w=new FP12(r); w.frob(f); w.frob(f);
+ w.mul(m);
+ if (!ROM.GT_STRONG)
+ {
+ if (!w.equals(r)) return false;
+ BIG x=new BIG(ROM.CURVE_Bnx);
+ r.copy(m); w=r.pow(x); w=w.pow(x);
+ r.copy(w); r.sqr(); r.mul(w); r.sqr();
+ w.copy(m); w.frob(f);
+ }
+ return w.equals(r);
+ }
+*/
+/*
+ public static void main(String[] args) {
+ ECP Q=new ECP(new BIG(ROM.CURVE_Gx),new BIG(ROM.CURVE_Gy));
+ ECP2 P=new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
+
+ BIG r=new BIG(ROM.CURVE_Order);
+ BIG xa=new BIG(ROM.CURVE_Pxa);
+
+ System.out.println("P= "+P.toString());
+ System.out.println("Q= "+Q.toString());
+
+ BIG m=new BIG(17);
+
+ FP12 e=ate(P,Q);
+ System.out.println("\ne= "+e.toString());
+
+ e=fexp(e);
+
+ for (int i=1;i<1000;i++)
+ {
+ e=ate(P,Q);
+ e=fexp(e);
+ }
+ // e=GTpow(e,m);
+
+ System.out.println("\ne= "+e.toString());
+
+ BIG [] GLV=glv(r);
+
+ System.out.println("GLV[0]= "+GLV[0].toString());
+ System.out.println("GLV[0]= "+GLV[1].toString());
+
+ ECP G=new ECP(); G.copy(Q);
+ ECP2 R=new ECP2(); R.copy(P);
+
+
+ e=ate(R,Q);
+ e=fexp(e);
+
+ e=GTpow(e,xa);
+ System.out.println("\ne= "+e.toString());
+
+
+ R=G2mul(R,xa);
+ e=ate(R,G);
+ e=fexp(e);
+
+ System.out.println("\ne= "+e.toString());
+
+ G=G1mul(G,xa);
+ e=ate(P,G);
+ e=fexp(e);
+ System.out.println("\ne= "+e.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/FP512BN/ROM.java b/src/main/java/org/apache/milagro/amcl/FP512BN/ROM.java
new file mode 100644
index 0000000..0d2db45
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/FP512BN/ROM.java
@@ -0,0 +1,56 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.FP512BN;
+
+public class ROM
+{
+
+// Base Bits= 60
+public static final long[] Modulus= {0x4EB280922ADEF33L,0x6A55CE5F4C6467BL,0xC65DEAB236FE191L,0xCF1EACBE98B8E48L,0x3C111B0EF455146L,0xA1D8CB5307C0BBEL,0xFFFF9EC7F01C60BL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL};
+public static final long[] R2modp= {0x1FA6DCEF99812E9L,0xAB3452895A0B74EL,0xC53EA988C079E1EL,0x1E90E033BA630B9L,0xF1EA41C0714D8B0L,0xE72785387509E28L,0xD86794F834DAB00L,0x9757C2ACCD342A1L,0x44ECB079L};
+public static final long MConst= 0x692A189FCCC5C05L;
+
+public static final int CURVE_A= 0;
+public static final int CURVE_B_I= 3;
+public static final int CURVE_Cof_I= 1;
+public static final long[] CURVE_B= {0x3L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Order= {0x6A64A5F519A09EDL,0x10313E04F9A2B40L,0xC65DEAB2679A34AL,0xCF1EACBE98B8E48L,0x3C111B0EF445146L,0xA1D8CB5307C0BBEL,0xFFFF9EC7F01C60BL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL};
+public static final long[] CURVE_Gx= {0x1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Gy= {0x2L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+
+public static final long[] Fra= {0x49617B1F4B73AB2L,0x71514F6202AED1FL,0xF6080D3BD8681E1L,0xF8AA9E852CBBB59L,0xC8CF2E2068398E9L,0x8A5296F791AB26BL,0x196A8C7C68B4EA1L,0xCF5BBF9095A1B79L,0x1EF71AA9L};
+public static final long[] Frb= {0x5510572DF6B481L,0xF9047EFD49B595CL,0xD055DD765E95FAFL,0xD6740E396BFD2EEL,0x7341ECEE8C1B85CL,0x1786345B7615952L,0xE695124B876776AL,0x30A4406F6A5E486L,0xE108E556L};
+public static final long[] CURVE_Bnx= {0xB306BB5E1BD80FL,0x82F5C030B0F7F01L,0x68L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Cru= {0xB0716209C79298AL,0xCEE6799B8B17C14L,0x78966BE526092AEL,0x20089C27507ACD8L,0xF8EF7611FA3074BL,0x6146B86B378EA2CL,0xFFFF9EC7DC83D2AL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL};
+public static final long[] CURVE_Pxa= {0xF07A96E0DB646B5L,0x18F87319072FFE8L,0x7BE21BCBBC78F22L,0x537863514DC6DC5L,0xDA57CC78CD0B024L,0xD29B358F0DB9B57L,0x7412F3CEA1E4BBBL,0xE138648958801BAL,0x3B165339L};
+public static final long[] CURVE_Pxb= {0xDB5CBEFDA8AE0E9L,0xCA411CD88911B3L,0xD6E1383D5ADCE4L,0x227285526E0D5E5L,0xB02566B94D9781EL,0x56DC6C6EF2476A8L,0x680ABE8B4825EA6L,0xF85067E6C89B4C4L,0x481C13CBL};
+public static final long[] CURVE_Pya= {0x2480312ADDE67A1L,0xDA17AD615EFB85EL,0x312542808B7BC5CL,0x18BDEC153E8EDD2L,0xE5C158699D4B6CDL,0xB1DF660AFCDD03EL,0xB0CBA374F277085L,0xC827C7B8292EF5AL,0x6F01EC84L};
+public static final long[] CURVE_Pyb= {0x58B7186C84F8E8BL,0xF05C2224BF76168L,0x10AD7EE279C08DFL,0x7FC3E2E50714A43L,0x3D04961941DA289L,0x38C118867B0C9B6L,0xC315F75D91F0214L,0x8B04E7831AC3640L,0x51A3BCECL};
+public static final long[][] CURVE_W= {{0x110F89749834583L,0x65FB911D16A173FL,0xFFFFFFFFCF63FE9L,0xFFFFFFFFFFFFFFFL,0xFFFFL,0x0L,0x0L,0x0L,0x0L},{0x1660D76BC37B01FL,0x5EB806161EFE02L,0xD1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}};
+public static final long[][][] CURVE_SB= {{{0xFAAEB208D4B9564L,0x601010BBB4B193CL,0xFFFFFFFFCF63F18L,0xFFFFFFFFFFFFFFFL,0xFFFFL,0x0L,0x0L,0x0L,0x0L},{0x5403CE8956259CEL,0xA45BDA397B2D3EL,0xC65DEAB2679A279L,0xCF1EACBE98B8E48L,0x3C111B0EF445146L,0xA1D8CB5307C0BBEL,0xFFFF9EC7F01C60BL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL}},{{0x1660D76BC37B01FL,0x5EB806161EFE02L,0xD1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x110F89749834583L,0x65FB911D16A173FL,0xFFFFFFFFCF63FE9L,0xFFFFFFFFFFFFFFFL,0xFFFFL,0x0L,0x0L,0x0L,0x0L}}};
+public static final long[][] CURVE_WB= {{0x6DAB36AB55A29F0L,0xFC42C60583D30C1L,0x5555555545215FBL,0x555555555555555L,0x5555L,0x0L,0x0L,0x0L,0x0L},{0xEEB012BA2355D4BL,0xF20FC1FD7F84F17L,0x892FA9DE2BB5E5CL,0x74B96064DAD40F5L,0xD76BC3535163152L,0x806161EFE021660L,0xD105EBL,0x0L,0x0L},{0x7CF03F380289AADL,0xBA82C117183E70CL,0xC497D4EF15DAF62L,0x3A5CB0326D6A07AL,0x6BB5E1A9A8B18A9L,0xC030B0F7F010B30L,0x6882F5L,0x0L,0x0L},{0x574A5F3F92279D1L,0xF65745A421E32BFL,0x55555555452152AL,0x555555555555555L,0x5555L,0x0L,0x0L,0x0L,0x0L}};
+public static final long[][][] CURVE_BB= {{{0xB306BB5E1BD810L,0x82F5C030B0F7F01L,0x68L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0xB306BB5E1BD80FL,0x82F5C030B0F7F01L,0x68L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0xB306BB5E1BD80FL,0x82F5C030B0F7F01L,0x68L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x5403CE8956259CFL,0xA45BDA397B2D3EL,0xC65DEAB2679A279L,0xCF1EACBE98B8E48L,0x3C111B0EF445146L,0xA1D8CB5307C0BBEL,0xFFFF9EC7F01C60BL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL}},{{0x1660D76BC37B01FL,0x5EB806161EFE02L,0xD1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x5F343A3F37E31DEL,0x8D3B7DD448AAC3FL,0xC65DEAB2679A2E1L,0xCF1EACBE98B8E48L,0x3C111B0EF445146L,0xA1D8CB5307C0BBEL,0xFFFF9EC7F01C60BL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL},{0x5F343A3F37E31DDL,0x8D3B7DD448AAC3FL,0xC65DEAB2679A2E1L,0xCF1EACBE98B8E48L,0x3C111B0EF445146L,0xA1D8CB5307C0BBEL,0xFFFF9EC7F01C60BL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL},{0x5F343A3F37E31DEL,0x8D3B7DD448AAC3FL,0xC65DEAB2679A2E1L,0xCF1EACBE98B8E48L,0x3C111B0EF445146L,0xA1D8CB5307C0BBEL,0xFFFF9EC7F01C60BL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL}},{{0x1660D76BC37B01EL,0x5EB806161EFE02L,0xD1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x1660D76BC37B01FL,0x5EB806161EFE02L,0xD1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x1660D76BC37B01FL,0x5EB806161EFE02L,0xD1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x1660D76BC37B01FL,0x5EB806161EFE02L,0xD1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L}},{{0x5F343A3F37E31DFL,0x8D3B7DD448AAC3FL,0xC65DEAB2679A2E1L,0xCF1EACBE98B8E48L,0x3C111B0EF445146L,0xA1D8CB5307C0BBEL,0xFFFF9EC7F01C60BL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL},{0x3DA2F71D92AA9AFL,0x45A3D4235C2F3CL,0xC65DEAB2679A1A8L,0xCF1EACBE98B8E48L,0x3C111B0EF445146L,0xA1D8CB5307C0BBEL,0xFFFF9EC7F01C60BL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL},{0x1660D76BC37B01DL,0x5EB806161EFE02L,0xD1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L},{0x5F343A3F37E31DFL,0x8D3B7DD448AAC3FL,0xC65DEAB2679A2E1L,0xCF1EACBE98B8E48L,0x3C111B0EF445146L,0xA1D8CB5307C0BBEL,0xFFFF9EC7F01C60BL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL}}};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/GCM.java b/src/main/java/org/apache/milagro/amcl/GCM.java
new file mode 100644
index 0000000..3cafa82
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/GCM.java
@@ -0,0 +1,376 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+
+/*
+ * Implementation of the AES-GCM Encryption/Authentication
+ *
+ * Some restrictions..
+ * 1. Only for use with AES
+ * 2. Returned tag is always 128-bits. Truncate at your own risk.
+ * 3. The order of function calls must follow some rules
+ *
+ * Typical sequence of calls..
+ * 1. call GCM_init
+ * 2. call GCM_add_header any number of times, as long as length of header is multiple of 16 bytes (block size)
+ * 3. call GCM_add_header one last time with any length of header
+ * 4. call GCM_add_cipher any number of times, as long as length of cipher/plaintext is multiple of 16 bytes
+ * 5. call GCM_add_cipher one last time with any length of cipher/plaintext
+ * 6. call GCM_finish to extract the tag.
+ *
+ * See http://www.mindspring.com/~dmcgrew/gcm-nist-6.pdf
+ */
+
+package org.apache.milagro.amcl;
+
+public class GCM {
+ public static final int NB=4;
+ public static final int GCM_ACCEPTING_HEADER=0;
+ public static final int GCM_ACCEPTING_CIPHER=1;
+ public static final int GCM_NOT_ACCEPTING_MORE=2;
+ public static final int GCM_FINISHED=3;
+ public static final int GCM_ENCRYPTING=0;
+ public static final int GCM_DECRYPTING=1;
+
+ private int[][] table=new int[128][4]; /* 2k bytes */
+ private byte[] stateX=new byte[16];
+ private byte[]Y_0=new byte[16];
+ private int counter;
+ private int[] lenA=new int[2];
+ private int[] lenC=new int[2];
+ private int status;
+ private AES a=new AES();
+
+ private static int pack(byte[] b)
+ { /* pack bytes into a 32-bit Word */
+ return ((((int)b[0])&0xff)<<24)|(((int)b[1]&0xff)<<16)|(((int)b[2]&0xff)<<8)|((int)b[3]&0xff);
+ }
+
+ private static byte[] unpack(int a)
+ { /* unpack bytes from a word */
+ byte [] b=new byte[4];
+ b[3]=(byte)(a);
+ b[2]=(byte)(a>>>8);
+ b[1]=(byte)(a>>>16);
+ b[0]=(byte)(a>>>24);
+ return b;
+ }
+
+ private void precompute(byte[] H)
+ {
+ int i,j,c;
+ byte[] b=new byte[4];
+
+ for (i=j=0;i<NB;i++,j+=4)
+ {
+ b[0]=H[j]; b[1]=H[j+1]; b[2]=H[j+2]; b[3]=H[j+3];
+ table[0][i]=pack(b);
+ }
+ for (i=1;i<128;i++)
+ {
+ c=0;
+ for (j=0;j<NB;j++) {table[i][j]=c|(table[i-1][j])>>>1; c=table[i-1][j]<<31;}
+ if (c!=0) table[i][0]^=0xE1000000; /* irreducible polynomial */
+ }
+ }
+
+ private void gf2mul()
+ { /* gf2m mul - Z=H*X mod 2^128 */
+ int i,j,m,k;
+ int[] P=new int[4];
+ int c;
+ byte[] b;//=new byte[4];
+
+ P[0]=P[1]=P[2]=P[3]=0;
+ j=8; m=0;
+ for (i=0;i<128;i++)
+ {
+ c=(stateX[m]>>>(--j))&1; c=~c+1;
+ for (k=0;k<NB;k++) P[k]^=(table[i][k]&c);
+ if (j==0)
+ {
+ j=8; m++;
+ if (m==16) break;
+ }
+ }
+ for (i=j=0;i<NB;i++,j+=4)
+ {
+ b=unpack(P[i]);
+ stateX[j]=b[0]; stateX[j+1]=b[1]; stateX[j+2]=b[2]; stateX[j+3]=b[3];
+ }
+ }
+
+ private void wrap()
+ { /* Finish off GHASH */
+ int i,j;
+ int[] F=new int[4];
+ byte[] L=new byte[16];
+ byte[] b;//=new byte[4];
+
+/* convert lengths from bytes to bits */
+ F[0]=(lenA[0]<<3)|(lenA[1]&0xE0000000)>>>29;
+ F[1]=lenA[1]<<3;
+ F[2]=(lenC[0]<<3)|(lenC[1]&0xE0000000)>>>29;
+ F[3]=lenC[1]<<3;
+ for (i=j=0;i<NB;i++,j+=4)
+ {
+ b=unpack(F[i]);
+ L[j]=b[0]; L[j+1]=b[1]; L[j+2]=b[2]; L[j+3]=b[3];
+ }
+ for (i=0;i<16;i++) stateX[i]^=L[i];
+ gf2mul();
+ }
+
+/* Initialize GCM mode */
+ public void init(int nk,byte[] key,int niv,byte[] iv)
+ { /* iv size niv is usually 12 bytes (96 bits). AES key size nk can be 16,24 or 32 bytes */
+ int i;
+ byte[] H=new byte[16];
+ byte[] b;//=new byte[4];
+
+ for (i=0;i<16;i++) {H[i]=0; stateX[i]=0;}
+
+ a.init(AES.ECB,nk,key,iv);
+ a.ecb_encrypt(H); /* E(K,0) */
+ precompute(H);
+
+ lenA[0]=lenC[0]=lenA[1]=lenC[1]=0;
+ if (niv==12)
+ {
+ for (i=0;i<12;i++) a.f[i]=iv[i];
+ b=unpack((int)1);
+ a.f[12]=b[0]; a.f[13]=b[1]; a.f[14]=b[2]; a.f[15]=b[3]; /* initialise IV */
+ for (i=0;i<16;i++) Y_0[i]=a.f[i];
+ }
+ else
+ {
+ status=GCM_ACCEPTING_CIPHER;
+ ghash(iv,niv); /* GHASH(H,0,IV) */
+ wrap();
+ for (i=0;i<16;i++) {a.f[i]=stateX[i];Y_0[i]=a.f[i];stateX[i]=0;}
+ lenA[0]=lenC[0]=lenA[1]=lenC[1]=0;
+ }
+ status=GCM_ACCEPTING_HEADER;
+ }
+
+/* Add Header data - included but not encrypted */
+ public boolean add_header(byte[] header,int len)
+ { /* Add some header. Won't be encrypted, but will be authenticated. len is length of header */
+ int i,j=0;
+ if (status!=GCM_ACCEPTING_HEADER) return false;
+
+ while (j<len)
+ {
+ for (i=0;i<16 && j<len;i++)
+ {
+ stateX[i]^=header[j++];
+ lenA[1]++; if (lenA[1]==0) lenA[0]++;
+ }
+ gf2mul();
+ }
+ if (len%16!=0) status=GCM_ACCEPTING_CIPHER;
+ return true;
+ }
+
+ private boolean ghash(byte[] plain,int len)
+ {
+ int i,j=0;
+ int counter;
+ // byte[] B=new byte[16];
+ // byte[] b=new byte[4];
+
+ if (status==GCM_ACCEPTING_HEADER) status=GCM_ACCEPTING_CIPHER;
+ if (status!=GCM_ACCEPTING_CIPHER) return false;
+
+ while (j<len)
+ {
+ for (i=0;i<16 && j<len;i++)
+ {
+ stateX[i]^=plain[j++];
+ lenC[1]++; if (lenC[1]==0) lenC[0]++;
+ }
+ gf2mul();
+ }
+ if (len%16!=0) status=GCM_NOT_ACCEPTING_MORE;
+ return true;
+ }
+
+/* Add Plaintext - included and encrypted */
+ public byte[] add_plain(byte[] plain,int len)
+ {
+ int i,j=0;
+ int counter;
+ byte[] B=new byte[16];
+ byte[] b=new byte[4];
+ byte[] cipher=new byte[len];
+
+ if (status==GCM_ACCEPTING_HEADER) status=GCM_ACCEPTING_CIPHER;
+ if (status!=GCM_ACCEPTING_CIPHER) return new byte[0];
+
+ while (j<len)
+ {
+
+ b[0]=a.f[12]; b[1]=a.f[13]; b[2]=a.f[14]; b[3]=a.f[15];
+ counter=pack(b);
+ counter++;
+ b=unpack(counter);
+ a.f[12]=b[0]; a.f[13]=b[1]; a.f[14]=b[2]; a.f[15]=b[3]; /* increment counter */
+ for (i=0;i<16;i++) B[i]=a.f[i];
+ a.ecb_encrypt(B); /* encrypt it */
+
+ for (i=0;i<16 && j<len;i++)
+ {
+ cipher[j]=(byte)(plain[j]^B[i]);
+ stateX[i]^=cipher[j++];
+ lenC[1]++; if (lenC[1]==0) lenC[0]++;
+ }
+ gf2mul();
+ }
+ if (len%16!=0) status=GCM_NOT_ACCEPTING_MORE;
+ return cipher;
+ }
+
+/* Add Ciphertext - decrypts to plaintext */
+ public byte[] add_cipher(byte[] cipher,int len)
+ {
+ int i,j=0;
+ int counter;
+ byte[] B=new byte[16];
+ byte[] b=new byte[4];
+ byte[] plain=new byte[len];
+
+ if (status==GCM_ACCEPTING_HEADER) status=GCM_ACCEPTING_CIPHER;
+ if (status!=GCM_ACCEPTING_CIPHER) return new byte[0];
+
+ while (j<len)
+ {
+
+ b[0]=a.f[12]; b[1]=a.f[13]; b[2]=a.f[14]; b[3]=a.f[15];
+ counter=pack(b);
+ counter++;
+ b=unpack(counter);
+ a.f[12]=b[0]; a.f[13]=b[1]; a.f[14]=b[2]; a.f[15]=b[3]; /* increment counter */
+ for (i=0;i<16;i++) B[i]=a.f[i];
+ a.ecb_encrypt(B); /* encrypt it */
+ for (i=0;i<16 && j<len;i++)
+ {
+ byte oc=cipher[j];
+ plain[j]=(byte)(cipher[j]^B[i]);
+ stateX[i]^=oc; j++;
+ lenC[1]++; if (lenC[1]==0) lenC[0]++;
+ }
+ gf2mul();
+ }
+ if (len%16!=0) status=GCM_NOT_ACCEPTING_MORE;
+ return plain;
+ }
+
+/* Finish and extract Tag */
+ public byte[] finish(boolean extract)
+ { /* Finish off GHASH and extract tag (MAC) */
+ int i;
+ byte[] tag=new byte[16];
+
+ wrap();
+/* extract tag */
+ if (extract)
+ {
+ a.ecb_encrypt(Y_0); /* E(K,Y0) */
+ for (i=0;i<16;i++) Y_0[i]^=stateX[i];
+ for (i=0;i<16;i++) {tag[i]=Y_0[i];Y_0[i]=stateX[i]=0;}
+ }
+ status=GCM_FINISHED;
+ a.end();
+ return tag;
+ }
+
+ public static byte[] hex2bytes(String s) {
+ int len = s.length();
+ byte[] data = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ + Character.digit(s.charAt(i+1), 16));
+ }
+ return data;
+ }
+/*
+ public static void main(String[] args) {
+ int i;
+
+ String KT="feffe9928665731c6d6a8f9467308308";
+ String MT="d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39";
+ String HT="feedfacedeadbeeffeedfacedeadbeefabaddad2";
+// char* NT="cafebabefacedbaddecaf888";
+// Tag should be 5bc94fbc3221a5db94fae95ae7121a47
+ String NT="9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b";
+// Tag should be 619cc5aefffe0bfa462af43c1699d050
+
+
+ byte[] T=new byte[16]; // Tag
+ byte[] K=new byte[16]; // AES Key
+ byte[] H=new byte[64]; // Header - to be included in Authentication, but not encrypted
+ byte[] N=new byte[100]; // IV - Initialisation vector
+ byte[] M=new byte[100]; // Plaintext to be encrypted/authenticated
+ byte[] C=new byte[100]; // Ciphertext
+ byte[] P=new byte[100]; // Recovered Plaintext
+
+ GCM g=new GCM();
+
+ M=hex2bytes(MT);
+ H=hex2bytes(HT);
+ N=hex2bytes(NT);
+ K=hex2bytes(KT);
+
+ int len=M.length;
+ int lenH=H.length;
+ int lenK=K.length;
+ int lenIV=N.length;
+
+ System.out.format("Plaintext=\n");
+ for (i=0;i<len;i++) System.out.format("%02x",M[i]);
+ System.out.format("\n");
+
+ g.init(16,K,lenIV,N);
+ g.add_header(H,lenH);
+ C=g.add_plain(M,len);
+ T=g.finish(true);
+
+ System.out.format("Ciphertext=\n");
+ for (i=0;i<len;i++) System.out.format("%02x",C[i]);
+ System.out.format("\n");
+
+ System.out.format("Tag=\n");
+ for (i=0;i<16;i++) System.out.format("%02x",T[i]);
+ System.out.format("\n");
+
+ g.init(16,K,lenIV,N);
+ g.add_header(H,lenH);
+ P=g.add_cipher(C,len);
+ T=g.finish(true);
+
+ System.out.format("Plaintext=\n");
+ for (i=0;i<len;i++) System.out.format("%02x",P[i]);
+ System.out.format("\n");
+
+ System.out.format("Tag=\n");
+ for (i=0;i<16;i++) System.out.format("%02x",T[i]);
+ System.out.format("\n");
+ }
+*/
+}
diff --git a/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/BIG.java b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/BIG.java
new file mode 100644
index 0000000..4ffc2d0
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.GOLDILOCKS;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=56; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=58;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/DBIG.java b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/DBIG.java
new file mode 100644
index 0000000..e6d8fa3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.GOLDILOCKS;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/ECDH.java b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/ECDH.java
new file mode 100644
index 0000000..1f41b34
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.GOLDILOCKS;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/ECP.java b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/ECP.java
new file mode 100644
index 0000000..e2f80b2
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.GOLDILOCKS;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=EDWARDS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=64;
+ public static final int AESKEY=32;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/FP.java b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/FP.java
new file mode 100644
index 0000000..58a7bb4
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.GOLDILOCKS;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=448; /* Number of bits in Modulus */
+ public static final int MOD8=7; /* Modulus mod 8 */
+ public static final int MODTYPE=GENERALISED_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<16); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/ROM.java b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/ROM.java
new file mode 100644
index 0000000..b402de7
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/GOLDILOCKS/ROM.java
@@ -0,0 +1,44 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.GOLDILOCKS;
+
+public class ROM
+{
+
+// Base Bits= 58
+public static final long[] Modulus= {0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FBFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFL};
+public static final long[] R2modp= {0x200000000L,0x0L,0x0L,0x0L,0x3000000L,0x0L,0x0L,0x0L};
+public static final long MConst= 0x1L;
+
+
+public static final int CURVE_Cof_I= 4;
+public static final long[] CURVE_Cof= {0x4L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+public static final int CURVE_A= 1;
+public static final int CURVE_B_I= -39081;
+public static final long[] CURVE_B= {0x3FFFFFFFFFF6756L,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FBFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFL};
+public static final long[] CURVE_Order= {0x378C292AB5844F3L,0x3309CA37163D548L,0x1B49AED63690216L,0x3FDF3288FA7113BL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0xFFFFFFFFFFL};
+public static final long[] CURVE_Gx= {0x155555555555555L,0x155555555555555L,0x155555555555555L,0x2A5555555555555L,0x2AAAAAAAAAAAAAAL,0x2AAAAAAAAAAAAAAL,0x2AAAAAAAAAAAAAAL,0x2AAAAAAAAAAL};
+public static final long[] CURVE_Gy= {0x2EAFBCDEA9386EDL,0x32CAFB473681AF6L,0x25833A2A3098BBBL,0x1CA2B6312E03595L,0x35884DD7B7E36DL,0x21B0AC00DBB5E8L,0x17048DB359D6205L,0x2B817A58D2BL};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/HASH256.java b/src/main/java/org/apache/milagro/amcl/HASH256.java
new file mode 100644
index 0000000..915642e
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/HASH256.java
@@ -0,0 +1,218 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/*
+ * Implementation of the Secure Hashing Algorithm (SHA-256)
+ *
+ * Generates a 256 bit message digest. It should be impossible to come
+ * come up with two messages that hash to the same value ("collision free").
+ *
+ * For use with byte-oriented messages only.
+ */
+
+package org.apache.milagro.amcl;
+
+public class HASH256 {
+ private int[] length=new int[2];
+ private int[] h=new int[8];
+ private int[] w=new int[80];
+
+ public static final int H0=0x6A09E667;
+ public static final int H1=0xBB67AE85;
+ public static final int H2=0x3C6EF372;
+ public static final int H3=0xA54FF53A;
+ public static final int H4=0x510E527F;
+ public static final int H5=0x9B05688C;
+ public static final int H6=0x1F83D9AB;
+ public static final int H7=0x5BE0CD19;
+
+ public static final int len=32;
+
+ public static final int[] K={
+ 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
+ 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
+ 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
+ 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
+ 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
+ 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
+ 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
+ 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2};
+
+
+/* functions */
+ private static int S(int n,int x)
+ {
+ return (((x)>>>n) | ((x)<<(32-n)));
+ }
+
+ private static int R(int n,int x)
+ {
+ return ((x)>>>n);
+ }
+
+ private static int Ch(int x,int y,int z)
+ {
+ return ((x&y)^(~(x)&z));
+ }
+
+ private static int Maj(int x,int y,int z)
+ {
+ return ((x&y)^(x&z)^(y&z));
+ }
+
+ private static int Sig0(int x)
+ {
+ return (S(2,x)^S(13,x)^S(22,x));
+ }
+
+ private static int Sig1(int x)
+ {
+ return (S(6,x)^S(11,x)^S(25,x));
+ }
+
+ private static int theta0(int x)
+ {
+ return (S(7,x)^S(18,x)^R(3,x));
+ }
+
+ private static int theta1(int x)
+ {
+ return (S(17,x)^S(19,x)^R(10,x));
+ }
+
+
+ private void transform()
+ { /* basic transformation step */
+ int a,b,c,d,e,f,g,hh,t1,t2;
+ int j;
+ for (j=16;j<64;j++)
+ w[j]=theta1(w[j-2])+w[j-7]+theta0(w[j-15])+w[j-16];
+ a=h[0]; b=h[1]; c=h[2]; d=h[3];
+ e=h[4]; f=h[5]; g=h[6]; hh=h[7];
+
+ for (j=0;j<64;j++)
+ { /* 64 times - mush it up */
+ t1=hh+Sig1(e)+Ch(e,f,g)+K[j]+w[j];
+ t2=Sig0(a)+Maj(a,b,c);
+ hh=g; g=f; f=e;
+ e=d+t1;
+ d=c;
+ c=b;
+ b=a;
+ a=t1+t2;
+
+ }
+ h[0]+=a; h[1]+=b; h[2]+=c; h[3]+=d;
+ h[4]+=e; h[5]+=f; h[6]+=g; h[7]+=hh;
+ }
+
+/* Initialise Hash function */
+ public void init()
+ { /* initialise */
+ int i;
+ for (i=0;i<64;i++) w[i]=0;
+ length[0]=length[1]=0;
+ h[0]=H0;
+ h[1]=H1;
+ h[2]=H2;
+ h[3]=H3;
+ h[4]=H4;
+ h[5]=H5;
+ h[6]=H6;
+ h[7]=H7;
+ }
+
+/* Constructor */
+ public HASH256()
+ {
+ init();
+ }
+
+/* process a single byte */
+ public void process(int byt)
+ { /* process the next message byte */
+ int cnt;
+ cnt=(length[0]/32)%16;
+
+ w[cnt]<<=8;
+ w[cnt]|=(byt&0xFF);
+ length[0]+=8;
+ if (length[0]==0) { length[1]++; length[0]=0; }
+ if ((length[0]%512)==0) transform();
+ }
+
+/* process an array of bytes */
+ public void process_array(byte[] b)
+ {
+ for (int i=0;i<b.length;i++) process((int)b[i]);
+ }
+
+/* process a 32-bit integer */
+ public void process_num(int n)
+ {
+ process((n>>24)&0xff);
+ process((n>>16)&0xff);
+ process((n>>8)&0xff);
+ process(n&0xff);
+ }
+
+/* Generate 32-byte Hash */
+ public byte[] hash()
+ { /* pad message and finish - supply digest */
+ int i;
+ byte[] digest=new byte[32];
+ int len0,len1;
+ len0=length[0];
+ len1=length[1];
+ process(0x80);
+ while ((length[0]%512)!=448) process(0);
+ w[14]=len1;
+ w[15]=len0;
+ transform();
+ for (i=0;i<len;i++)
+ { /* convert to bytes */
+ digest[i]=(byte)((h[i/4]>>(8*(3-i%4))) & 0xff);
+ }
+ init();
+ return digest;
+ }
+
+/* test program: should produce digest */
+
+//248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1
+
+ public static void main(String[] args) {
+ byte[] test="abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".getBytes();
+ byte[] digest;
+ int i;
+ HASH256 sh=new HASH256();
+
+ for (i=0;i<test.length;i++)
+ sh.process(test[i]);
+
+ digest=sh.hash();
+ for (i=0;i<32;i++) System.out.format("%02x",digest[i]);
+
+ // for (i=0;i<32;i++) System.out.format("%d ",digest[i]);
+
+ System.out.println("");
+ }
+}
+
+
diff --git a/src/main/java/org/apache/milagro/amcl/HASH384.java b/src/main/java/org/apache/milagro/amcl/HASH384.java
new file mode 100644
index 0000000..6fc50fc
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/HASH384.java
@@ -0,0 +1,229 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/*
+ * Implementation of the Secure Hashing Algorithm (SHA-512)
+ *
+ * Generates a 512 bit message digest. It should be impossible to come
+ * come up with two messages that hash to the same value ("collision free").
+ *
+ * For use with byte-oriented messages only.
+ */
+
+package org.apache.milagro.amcl;
+
+public class HASH384 {
+ private long[] length=new long[2];
+ private long[] h=new long[8];
+ private long[] w=new long[80];
+
+ public static final long H0=0xcbbb9d5dc1059ed8L;
+ public static final long H1=0x629a292a367cd507L;
+ public static final long H2=0x9159015a3070dd17L;
+ public static final long H3=0x152fecd8f70e5939L;
+ public static final long H4=0x67332667ffc00b31L;
+ public static final long H5=0x8eb44a8768581511L;
+ public static final long H6=0xdb0c2e0d64f98fa7L;
+ public static final long H7=0x47b5481dbefa4fa4L;
+
+ public static final int len=48;
+
+ public static final long[] K=
+ {0x428a2f98d728ae22L,0x7137449123ef65cdL,0xb5c0fbcfec4d3b2fL,0xe9b5dba58189dbbcL,
+ 0x3956c25bf348b538L,0x59f111f1b605d019L,0x923f82a4af194f9bL,0xab1c5ed5da6d8118L,
+ 0xd807aa98a3030242L,0x12835b0145706fbeL,0x243185be4ee4b28cL,0x550c7dc3d5ffb4e2L,
+ 0x72be5d74f27b896fL,0x80deb1fe3b1696b1L,0x9bdc06a725c71235L,0xc19bf174cf692694L,
+ 0xe49b69c19ef14ad2L,0xefbe4786384f25e3L,0x0fc19dc68b8cd5b5L,0x240ca1cc77ac9c65L,
+ 0x2de92c6f592b0275L,0x4a7484aa6ea6e483L,0x5cb0a9dcbd41fbd4L,0x76f988da831153b5L,
+ 0x983e5152ee66dfabL,0xa831c66d2db43210L,0xb00327c898fb213fL,0xbf597fc7beef0ee4L,
+ 0xc6e00bf33da88fc2L,0xd5a79147930aa725L,0x06ca6351e003826fL,0x142929670a0e6e70L,
+ 0x27b70a8546d22ffcL,0x2e1b21385c26c926L,0x4d2c6dfc5ac42aedL,0x53380d139d95b3dfL,
+ 0x650a73548baf63deL,0x766a0abb3c77b2a8L,0x81c2c92e47edaee6L,0x92722c851482353bL,
+ 0xa2bfe8a14cf10364L,0xa81a664bbc423001L,0xc24b8b70d0f89791L,0xc76c51a30654be30L,
+ 0xd192e819d6ef5218L,0xd69906245565a910L,0xf40e35855771202aL,0x106aa07032bbd1b8L,
+ 0x19a4c116b8d2d0c8L,0x1e376c085141ab53L,0x2748774cdf8eeb99L,0x34b0bcb5e19b48a8L,
+ 0x391c0cb3c5c95a63L,0x4ed8aa4ae3418acbL,0x5b9cca4f7763e373L,0x682e6ff3d6b2b8a3L,
+ 0x748f82ee5defb2fcL,0x78a5636f43172f60L,0x84c87814a1f0ab72L,0x8cc702081a6439ecL,
+ 0x90befffa23631e28L,0xa4506cebde82bde9L,0xbef9a3f7b2c67915L,0xc67178f2e372532bL,
+ 0xca273eceea26619cL,0xd186b8c721c0c207L,0xeada7dd6cde0eb1eL,0xf57d4f7fee6ed178L,
+ 0x06f067aa72176fbaL,0x0a637dc5a2c898a6L,0x113f9804bef90daeL,0x1b710b35131c471bL,
+ 0x28db77f523047d84L,0x32caab7b40c72493L,0x3c9ebe0a15c9bebcL,0x431d67c49c100d4cL,
+ 0x4cc5d4becb3e42b6L,0x597f299cfc657e2aL,0x5fcb6fab3ad6faecL,0x6c44198c4a475817L};
+
+/* functions */
+ private static long S(int n,long x)
+ {
+ return (((x)>>>n) | ((x)<<(64-n)));
+ }
+
+ private static long R(int n,long x)
+ {
+ return ((x)>>>n);
+ }
+
+ private static long Ch(long x,long y,long z)
+ {
+ return ((x&y)^(~(x)&z));
+ }
+
+ private static long Maj(long x,long y,long z)
+ {
+ return ((x&y)^(x&z)^(y&z));
+ }
+
+ private static long Sig0(long x)
+ {
+ return (S(28,x)^S(34,x)^S(39,x));
+ }
+
+ private static long Sig1(long x)
+ {
+ return (S(14,x)^S(18,x)^S(41,x));
+ }
+
+ private static long theta0(long x)
+ {
+ return (S(1,x)^S(8,x)^R(7,x));
+ }
+
+ private static long theta1(long x)
+ {
+ return (S(19,x)^S(61,x)^R(6,x));
+ }
+
+ private void transform()
+ { /* basic transformation step */
+ long a,b,c,d,e,f,g,hh,t1,t2;
+ int j;
+ for (j=16;j<80;j++)
+ w[j]=theta1(w[j-2])+w[j-7]+theta0(w[j-15])+w[j-16];
+ a=h[0]; b=h[1]; c=h[2]; d=h[3];
+ e=h[4]; f=h[5]; g=h[6]; hh=h[7];
+
+ for (j=0;j<80;j++)
+ { /* 80 times - mush it up */
+ t1=hh+Sig1(e)+Ch(e,f,g)+K[j]+w[j];
+ t2=Sig0(a)+Maj(a,b,c);
+ hh=g; g=f; f=e;
+ e=d+t1;
+ d=c;
+ c=b;
+ b=a;
+ a=t1+t2;
+
+ }
+ h[0]+=a; h[1]+=b; h[2]+=c; h[3]+=d;
+ h[4]+=e; h[5]+=f; h[6]+=g; h[7]+=hh;
+ }
+
+/* Initialise Hash function */
+ public void init()
+ { /* initialise */
+ int i;
+ for (i=0;i<80;i++) w[i]=0L;
+ length[0]=length[1]=0L;
+ h[0]=H0;
+ h[1]=H1;
+ h[2]=H2;
+ h[3]=H3;
+ h[4]=H4;
+ h[5]=H5;
+ h[6]=H6;
+ h[7]=H7;
+ }
+
+/* Constructor */
+ public HASH384()
+ {
+ init();
+ }
+
+/* process a single byte */
+ public void process(int byt)
+ { /* process the next message byte */
+ int cnt;
+ cnt=(int)(length[0]/64)%16;
+
+ w[cnt]<<=8;
+ w[cnt]|=(byt&0xFF);
+ length[0]+=8;
+ if (length[0]==0L) { length[1]++; length[0]=0L; }
+ if ((length[0]%1024)==0) transform();
+ }
+
+/* process an array of bytes */
+ public void process_array(byte[] b)
+ {
+ for (int i=0;i<b.length;i++) process((int)b[i]);
+ }
+
+/* process a 32-bit integer */
+ public void process_num(int n)
+ {
+ process((n>>24)&0xff);
+ process((n>>16)&0xff);
+ process((n>>8)&0xff);
+ process(n&0xff);
+ }
+
+/* Generate 48-byte Hash */
+ public byte[] hash()
+ { /* pad message and finish - supply digest */
+ int i;
+ byte[] digest=new byte[48];
+ long len0,len1;
+ len0=length[0];
+ len1=length[1];
+ process(0x80);
+ while ((length[0]%1024)!=896) process(0);
+ w[14]=len1;
+ w[15]=len0;
+ transform();
+ for (i=0;i<len;i++)
+ { /* convert to bytes */
+ digest[i]=(byte)((h[i/8]>>(8*(7-i%8))) & 0xffL);
+ }
+ init();
+ return digest;
+ }
+
+/* test program: should produce digest */
+
+//09330c33f71147e8 3d192fc782cd1b47 53111b173b3b05d2 2fa08086e3b0f712 fcc7c71a557e2db9 66c3e9fa91746039
+
+ public static void main(String[] args) {
+
+ byte[] test="abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu".getBytes();
+ byte[] digest;
+ int i;
+ HASH384 sh=new HASH384();
+
+ for (i=0;i<test.length;i++)
+ sh.process(test[i]);
+
+ digest=sh.hash();
+ for (i=0;i<48;i++) System.out.format("%02x",digest[i]);
+
+ // for (i=0;i<32;i++) System.out.format("%d ",digest[i]);
+
+ System.out.println("");
+
+ }
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/HASH512.java b/src/main/java/org/apache/milagro/amcl/HASH512.java
new file mode 100644
index 0000000..686ab22
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/HASH512.java
@@ -0,0 +1,232 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/*
+ * Implementation of the Secure Hashing Algorithm (SHA-512)
+ *
+ * Generates a 512 bit message digest. It should be impossible to come
+ * come up with two messages that hash to the same value ("collision free").
+ *
+ * For use with byte-oriented messages only.
+ */
+
+
+package org.apache.milagro.amcl;
+
+public class HASH512 {
+
+ private long[] length=new long[2];
+ private long[] h=new long[8];
+ private long[] w=new long[80];
+
+ public static final long H0=0x6a09e667f3bcc908L;
+ public static final long H1=0xbb67ae8584caa73bL;
+ public static final long H2=0x3c6ef372fe94f82bL;
+ public static final long H3=0xa54ff53a5f1d36f1L;
+ public static final long H4=0x510e527fade682d1L;
+ public static final long H5=0x9b05688c2b3e6c1fL;
+ public static final long H6=0x1f83d9abfb41bd6bL;
+ public static final long H7=0x5be0cd19137e2179L;
+
+ public static final int len=64;
+
+ public static final long[] K=
+ {0x428a2f98d728ae22L,0x7137449123ef65cdL,0xb5c0fbcfec4d3b2fL,0xe9b5dba58189dbbcL,
+ 0x3956c25bf348b538L,0x59f111f1b605d019L,0x923f82a4af194f9bL,0xab1c5ed5da6d8118L,
+ 0xd807aa98a3030242L,0x12835b0145706fbeL,0x243185be4ee4b28cL,0x550c7dc3d5ffb4e2L,
+ 0x72be5d74f27b896fL,0x80deb1fe3b1696b1L,0x9bdc06a725c71235L,0xc19bf174cf692694L,
+ 0xe49b69c19ef14ad2L,0xefbe4786384f25e3L,0x0fc19dc68b8cd5b5L,0x240ca1cc77ac9c65L,
+ 0x2de92c6f592b0275L,0x4a7484aa6ea6e483L,0x5cb0a9dcbd41fbd4L,0x76f988da831153b5L,
+ 0x983e5152ee66dfabL,0xa831c66d2db43210L,0xb00327c898fb213fL,0xbf597fc7beef0ee4L,
+ 0xc6e00bf33da88fc2L,0xd5a79147930aa725L,0x06ca6351e003826fL,0x142929670a0e6e70L,
+ 0x27b70a8546d22ffcL,0x2e1b21385c26c926L,0x4d2c6dfc5ac42aedL,0x53380d139d95b3dfL,
+ 0x650a73548baf63deL,0x766a0abb3c77b2a8L,0x81c2c92e47edaee6L,0x92722c851482353bL,
+ 0xa2bfe8a14cf10364L,0xa81a664bbc423001L,0xc24b8b70d0f89791L,0xc76c51a30654be30L,
+ 0xd192e819d6ef5218L,0xd69906245565a910L,0xf40e35855771202aL,0x106aa07032bbd1b8L,
+ 0x19a4c116b8d2d0c8L,0x1e376c085141ab53L,0x2748774cdf8eeb99L,0x34b0bcb5e19b48a8L,
+ 0x391c0cb3c5c95a63L,0x4ed8aa4ae3418acbL,0x5b9cca4f7763e373L,0x682e6ff3d6b2b8a3L,
+ 0x748f82ee5defb2fcL,0x78a5636f43172f60L,0x84c87814a1f0ab72L,0x8cc702081a6439ecL,
+ 0x90befffa23631e28L,0xa4506cebde82bde9L,0xbef9a3f7b2c67915L,0xc67178f2e372532bL,
+ 0xca273eceea26619cL,0xd186b8c721c0c207L,0xeada7dd6cde0eb1eL,0xf57d4f7fee6ed178L,
+ 0x06f067aa72176fbaL,0x0a637dc5a2c898a6L,0x113f9804bef90daeL,0x1b710b35131c471bL,
+ 0x28db77f523047d84L,0x32caab7b40c72493L,0x3c9ebe0a15c9bebcL,0x431d67c49c100d4cL,
+ 0x4cc5d4becb3e42b6L,0x597f299cfc657e2aL,0x5fcb6fab3ad6faecL,0x6c44198c4a475817L};
+
+/* functions */
+ private static long S(int n,long x)
+ {
+ return (((x)>>>n) | ((x)<<(64-n)));
+ }
+
+ private static long R(int n,long x)
+ {
+ return ((x)>>>n);
+ }
+
+ private static long Ch(long x,long y,long z)
+ {
+ return ((x&y)^(~(x)&z));
+ }
+
+ private static long Maj(long x,long y,long z)
+ {
+ return ((x&y)^(x&z)^(y&z));
+ }
+
+ private static long Sig0(long x)
+ {
+ return (S(28,x)^S(34,x)^S(39,x));
+ }
+
+ private static long Sig1(long x)
+ {
+ return (S(14,x)^S(18,x)^S(41,x));
+ }
+
+ private static long theta0(long x)
+ {
+ return (S(1,x)^S(8,x)^R(7,x));
+ }
+
+ private static long theta1(long x)
+ {
+ return (S(19,x)^S(61,x)^R(6,x));
+ }
+
+
+ private void transform()
+ { /* basic transformation step */
+ long a,b,c,d,e,f,g,hh,t1,t2;
+ int j;
+ for (j=16;j<80;j++)
+ w[j]=theta1(w[j-2])+w[j-7]+theta0(w[j-15])+w[j-16];
+ a=h[0]; b=h[1]; c=h[2]; d=h[3];
+ e=h[4]; f=h[5]; g=h[6]; hh=h[7];
+
+ for (j=0;j<80;j++)
+ { /* 80 times - mush it up */
+ t1=hh+Sig1(e)+Ch(e,f,g)+K[j]+w[j];
+ t2=Sig0(a)+Maj(a,b,c);
+ hh=g; g=f; f=e;
+ e=d+t1;
+ d=c;
+ c=b;
+ b=a;
+ a=t1+t2;
+
+ }
+ h[0]+=a; h[1]+=b; h[2]+=c; h[3]+=d;
+ h[4]+=e; h[5]+=f; h[6]+=g; h[7]+=hh;
+ }
+
+/* Initialise Hash function */
+ public void init()
+ { /* initialise */
+ int i;
+ for (i=0;i<80;i++) w[i]=0L;
+ length[0]=length[1]=0L;
+ h[0]=H0;
+ h[1]=H1;
+ h[2]=H2;
+ h[3]=H3;
+ h[4]=H4;
+ h[5]=H5;
+ h[6]=H6;
+ h[7]=H7;
+ }
+
+/* Constructor */
+ public HASH512()
+ {
+ init();
+ }
+
+/* process a single byte */
+ public void process(int byt)
+ { /* process the next message byte */
+ int cnt;
+ cnt=(int)(length[0]/64)%16;
+
+ w[cnt]<<=8;
+ w[cnt]|=(byt&0xFF);
+ length[0]+=8;
+ if (length[0]==0L) { length[1]++; length[0]=0L; }
+ if ((length[0]%1024)==0) transform();
+ }
+
+/* process an array of bytes */
+ public void process_array(byte[] b)
+ {
+ for (int i=0;i<b.length;i++) process((int)b[i]);
+ }
+
+/* process a 32-bit integer */
+ public void process_num(int n)
+ {
+ process((n>>24)&0xff);
+ process((n>>16)&0xff);
+ process((n>>8)&0xff);
+ process(n&0xff);
+ }
+
+/* Generate 64-byte Hash */
+ public byte[] hash()
+ { /* pad message and finish - supply digest */
+ int i;
+ byte[] digest=new byte[64];
+ long len0,len1;
+ len0=length[0];
+ len1=length[1];
+ process(0x80);
+ while ((length[0]%1024)!=896) process(0);
+ w[14]=len1;
+ w[15]=len0;
+ transform();
+ for (i=0;i<len;i++)
+ { /* convert to bytes */
+ digest[i]=(byte)((h[i/8]>>(8*(7-i%8))) & 0xffL);
+ }
+ init();
+ return digest;
+ }
+
+/* test program: should produce digest */
+
+//8e959b75dae313da 8cf4f72814fc143f 8f7779c6eb9f7fa1 7299aeadb6889018 501d289e4900f7e4 331b99dec4b5433a c7d329eeb6dd2654 5e96e55b874be909
+
+ public static void main(String[] args) {
+
+ byte[] test="abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu".getBytes();
+ byte[] digest;
+ int i;
+ HASH512 sh=new HASH512();
+
+ for (i=0;i<test.length;i++)
+ sh.process(test[i]);
+
+ digest=sh.hash();
+ for (i=0;i<64;i++) System.out.format("%02x",digest[i]);
+
+ // for (i=0;i<32;i++) System.out.format("%d ",digest[i]);
+
+ System.out.println("");
+
+ }
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/HIFIVE/BIG.java b/src/main/java/org/apache/milagro/amcl/HIFIVE/BIG.java
new file mode 100644
index 0000000..ed52f3e
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/HIFIVE/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.HIFIVE;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=42; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=60;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/HIFIVE/DBIG.java b/src/main/java/org/apache/milagro/amcl/HIFIVE/DBIG.java
new file mode 100644
index 0000000..446cff6
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/HIFIVE/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.HIFIVE;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/HIFIVE/ECDH.java b/src/main/java/org/apache/milagro/amcl/HIFIVE/ECDH.java
new file mode 100644
index 0000000..4acdf2e
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/HIFIVE/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.HIFIVE;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/HIFIVE/ECP.java b/src/main/java/org/apache/milagro/amcl/HIFIVE/ECP.java
new file mode 100644
index 0000000..a39665f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/HIFIVE/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.HIFIVE;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=EDWARDS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=48;
+ public static final int AESKEY=24;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/HIFIVE/FP.java b/src/main/java/org/apache/milagro/amcl/HIFIVE/FP.java
new file mode 100644
index 0000000..c6017f1
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/HIFIVE/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.HIFIVE;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=336; /* Number of bits in Modulus */
+ public static final int MOD8=5; /* Modulus mod 8 */
+ public static final int MODTYPE=PSEUDO_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<24); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/HIFIVE/ROM.java b/src/main/java/org/apache/milagro/amcl/HIFIVE/ROM.java
new file mode 100644
index 0000000..bed83e6
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/HIFIVE/ROM.java
@@ -0,0 +1,43 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.HIFIVE;
+
+public class ROM
+{
+
+// Base Bits= 60
+ public static final long[] Modulus= {0xFFFFFFFFFFFFFFDL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFL};
+ public static final long[] R2modp= {0x9000000000000L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long MConst= 0x3L;
+
+ public static final int CURVE_Cof_I= 8;
+ public static final long[] CURVE_Cof= {0x8L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= 1;
+ public static final int CURVE_B_I= 11111;
+ public static final long[] CURVE_B= {0x2B67L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Order= {0xB2F95973E9FA805L,0xC0BD6B87F93BAA7L,0x71415FA9850L,0x0L,0x0L,0x200000000L};
+ public static final long[] CURVE_Gx= {0xCL,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Gy= {0x2BEC68505FE8632L,0x5D5650CA0365DB1L,0x3811C7EF435B6DBL,0x7853D1B14B46CL,0x56502E18E1C161DL,0xC0DC616BL};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NHS.java b/src/main/java/org/apache/milagro/amcl/NHS.java
new file mode 100644
index 0000000..66b764e
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NHS.java
@@ -0,0 +1,577 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* NewHope API high-level functions */
+
+//import amcl.SHA3;
+//import amcl.RAND;
+
+package org.apache.milagro.amcl;
+
+public final class NHS {
+
+ public static final int RLWE_PRIME=0x3001; // q in Hex
+ public final static int RLWE_LGN=10; // Degree n=2^LGN
+ public final static long RLWE_ND=0xF7002FFFL; // 1/(R-q) mod R
+ public final static int RLWE_ONE=0x2AC8; // R mod q
+ public final static int RLWE_R2MODP=0x1620; // R^2 mod q
+
+ public final static int DEGREE=(1<<RLWE_LGN);
+ public final static int WL=32;
+
+ public final static int[] roots ={0x2ac8,0x2baf,0x299b,0x685,0x2f04,0x158d,0x2d49,0x24b5,0x1edc,0xab3,0x2a95,0x24d,0x3cb,0x6a8,0x12f9,0x15ba,0x1861,0x2a89,0x1c5c,0xbe6,0xc1e,0x2024,0x207,0x19ce,0x2710,0x1744,0x18bc,0x2cd7,0x396,0x18d5,0x1c45,0xc4,0x21a6,0xe03,0x2b3c,0x2d91,0xc5d,0x432,0x1fbc,0xcae,0x2512,0x2979,0x3b2,0x714,0xb2e,0x1a97,0x1a03,0x1bcd,0x2216,0x2701,0xa,0x263c,0x1179,0x200c,0x2d08,0x1c34,0x291,0x2c99,0x2a5a,0x723,0xb1d,0x1ccc,0x1fb6,0x2f58,0x2bfe,0x1cda,0x2a0,0x5f1,0x2de,0x1fc7,0x1ea8,0x1719,0x2fa7,0x27ec,0x20ff,0x12c0,0x1ac1,0x2232,0x2f9b,0xd3e,0x2aed,0x15f0,0x11e8,0xed0,0x26a,0x1de5,0xa3f,0xf43,0xebf,0x204e,0xac7,0x2d9c,0x5ea,0x25d1,0xb6,0x49c,0x995,0x2555,0x26e2,0x100,0x1878,0x5aa,0x2e10,0x271c,0xcb,0x1b4c,0x2fb8,0x25b7,0x1543,0x2c7b,0x241a,0x2223,0x20ca,0x24ed,0x137,0x1b65,0x1dc2,0x7c7,0x2ec3,0xd0c,0x1169,0x1c7a,0x1ea1,0xf89,0x2199,0x291d,0x1088,0x2046,0x256d,0x2bc7,0x2e9b,0x41f,0x1b55,0x2b38,0xd0,0x2e6a,0x1755,0x6bc,0x2724,0x3ba,0x222e,0x2c5c,0x2da5,0x213c,0x10fe,0x169a,0x1552,0x5d3,0x300,0x1b5d,0x1342,0x2004,0x256f,0x2039,0x667,0x23b5,0x1123,0xdb,0x2da0,0xe1e,0x2f54,0x2767,0x154a,0x40a,0x11d3,0x2821,0xc09,0x974,0x694,0xfbf,0x27ba,0x132,0x83f,0x2d06,0x10e,0x183f,0x29ae,0x28c3,0x2dc9,0x1144,0x2c70,0x2a4a,0xf3c,0x1e32,0x1171,0x1e43,0xdd4,0x2ddf,0x28d2,0xfac,0x3c4,0x2f19,0x10a6,0x2f7,0xe1d,0x828,0x138f,0x1332,0xfab,0xcf6,0x13f8,0x24a0,0x112d,0x2717,0x6e7,0x1044,0x36e,0xfe8,0x6a,0xba7,0x1d69,0x29ec,0x23b2,0xaee,0x16df,0x1068,0x1a7e,0x253f,0x24c,0xb33,0x2683,0x15ce,0x1ad3,0x1a36,0xc96,0xaea,0x260a,0xce,0x28b1,0xe4f,0x2b11,0x5f8,0x1fc4,0xe77,0x2366,0x11f9,0x153c,0x24eb,0x20cd,0x1398,0x22,0x2b97,0x249b,0x8eb,0x12b2,0x2fe3,0x29c1,0x1b00,0x2663,0xeaa,0x2e06,0xe0,0x1569,0x10f5,0x284e,0xa38,0x201d,0x1c53,0x1681,0x1f6f,0x2f95,0x2fe8,0xacb,0x1680,0x17fd,0x2c39,0x165a,0x10bb,0x29d8,0x2622,0x1196,0x884,0x2a79,0x140e,0x2d80,0x6fa,0x11b2,0x26c4,0x355,0x1054,0x29e9,0x23ed,0xbe3,0x24fa,0x1fb3,0x10ac,0x2919,0x2584,0x10a4,0xe85,0x650,0x1893,0x1dc1,0xd8e,0x12dc,0x2d42,0x284d,0xfff,0x250f,0xacd,0x13c3,0x6cc,0x1a79,0x1221,0x2614,0x270a,0x1ea,0x155,0x2818,0x222c,0x2e5b,0x25d8,0x1dbf,0x191c,0xb0f,0xdac,0x1082,0x12ef,0x11b6,0xfa8,0x2b72,0x159d,0x209e,0x31b,0x2c7c,0x14f7,0xe09,0x1bb2,0x1ec7,0x2404,0x20ae,0x6ad,0xed6,0x2b70,0x1c7b,0x18d1,0x2732,0x12da,0xd56,0x5c1,0x1648,0x18b7,0x1605,0x1bc4,0x280,0x2ece,0xc,0x1aae,0x1c4,0x1cdb,0x22d6,0x21d8,0x257c,0x51f,0x211b,0xff,0x2ee0,0x2585,0xe1,0x2c35,0x26db,0x2971,0x2208,0x17e1,0x21be,0x135e,0x28d6,0x2891,0x1689,0x2138,0xb86,0x2e3a,0x1204,0x2d10,0x2324,0xf3f,0x2508,0x33d,0xcb2,0x292a,0xe27,0x2e64,0x29f8,0x2d46,0x9b7,0x20eb,0x1b7c,0x9eb,0x2b2a,0x58c,0x27d0,0x121b,0x272e,0x29f6,0x2dbd,0x2697,0x2aac,0xd6f,0x1c67,0x2c5b,0x108d,0x363,0x249d,0x2d5e,0x2fd,0x2cb2,0x1f8f,0x20a4,0xa19,0x2ac9,0x19b1,0x1581,0x17a2,0x29eb,0x1b72,0x13b0,0xee4,0xa8f,0x2315,0x5e6,0x951,0x2e29,0xdad,0x1f2b,0x224e,0x37f,0x1a72,0xa91,0x1407,0x2df9,0x3ad,0x23f7,0x1a24,0x1d2a,0x234b,0x1df3,0x1143,0x7ff,0x1a6d,0x2774,0x2690,0x2ab5,0x586,0x2781,0x2009,0x2fdd,0x2881,0x399,0x2fb6,0x144,0x137f,0xfa0,0x2e4c,0x1c7f,0x2fac,0xb09,0x1264,0x127b,0x198c,0x2b40,0x230,0x1cf4,0x180b,0xb58,0x144a,0x2aec,0xfb,0x2602,0x14ee,0x783,0x1098,0x23d8,0x203,0xe9,0x108a,0x14b8,0xeec,0xc58,0x1248,0x243c,0x28aa,0x6bf,0x27c4,0x276e,0x19b8,0x1d11,0x2e16,0x472,0x1464,0x24b9,0x662,0x1097,0x2067,0x20d6,0x171c,0x4,0x682,0x17bb,0x1186,0x4f2,0x3ff,0x2a43,0x1dc7,0x1ae5,0x8cc,0x2e7c,0x2ef8,0x2ae0,0x2904,0xed4,0x6c5,0x14ae,0xb72,0x11c3,0x337,0x2da3,0x2916,0x6d8,0x1cf9,0x10ee,0x1800,0x1ae4,0xa0d,0x101b,0x1a8d,0x2e98,0x24cd,0x813,0x1aa4,0x9b9,0x680,0x2349,0x24d1,0x20f8,0xe31,0x249f,0x216b,0x12d9,0x1d21,0x19db,0x191a,0x1dd0,0x5df,0x55c,0x2b86,0x213,0xe9e,0x1ef1,0x268a,0x1d5e,0x1e20,0x28c1,0x1379,0x249,0x19de,0x18b,0x1e41,0x2a1e,0x2612,0x297,0x2e96,0x2102,0x46,0x1b9f,0x1a4d,0x2050,0x1b32,0x568,0x11f7,0x1829,0x870,0x1f4,0x1dca,0x990,0x1df6,0x2b62,0x13ec,0x9f2,0x1260,0x2997,0x1412,0x1e6d,0x1694,0x11ac,0x2d8b,0x276f,0x26f5,0x233e,0x2b44,0x2f5a,0x2d37,0x2cb1,0xc75,0x98d,0x1d56,0x7ae,0x10e6,0x113f,0x17b8,0xad3,0x737,0x221e,0x1b70,0x1f3e,0x2966,0x18b2,0x4fa,0x2044,0x1312,0x154e,0x2029,0x700,0x1b45,0x27a6,0x226a,0x21bf,0x58d,0x2f11,0x2e02,0x17fc,0x4d2,0x1757,0xcb1,0x2ef1,0x2582,0x1276,0x881,0x2fc0,0x104a,0x670,0x274f,0x2b53,0x19dd,0x752,0x1663,0xcbd,0x2b2b,0x2fc6,0x13b6,0x21e6,0x15f6,0x126b,0x2637,0x1cd9,0x2f50,0xe82,0x5b0,0x24e0,0x1350,0x2f24,0x21f7,0x1a16,0x2f3e,0x167e,0x1f7d,0x28a0,0x16f0,0xe33,0x53b,0x28c5,0x1500,0x2f88,0x26cc,0x2018,0x1604,0x218b,0x2cd1,0x9ee,0x17f3,0x5fd,0x1f5a,0x2d0,0x2b46,0x23cc,0x503,0x1c46,0x1cc3,0x28e2,0x243e,0x122b,0x2e0c,0xe37,0x2611,0x85e,0x9b8,0x1b24,0x762,0x19b6,0x3bc,0x2d50,0x2079,0x18da,0x170a,0x800,0xaa2,0x135a,0x1a15,0x13d1,0xca,0x2113,0x2db9,0xdb2,0x1a5c,0x29a9,0x1488,0x14c1,0x2c9,0x917,0x28e7,0x265c,0xdab,0x2ab9,0x2bc6,0x105b,0x1839,0x219c,0x50,0x11da,0x1802,0xf56,0x2e6,0x2190,0xddb,0x56e,0x9d9,0x1c81,0x1016,0x12d6,0x296f,0x14b4,0x1014,0x1e64,0x1d90,0x89f,0x2bc2,0x2777,0x2819,0x1c65,0x1a41,0x5a2,0x2cd2,0x427,0xd71,0x29c8,0x1e58,0x53f,0x7c5,0x1dcd,0x4a1,0x1268,0x2597,0x2926,0xee,0x111b,0x1038,0xe6c,0x22dc,0x2f2f,0x441,0x2cfd,0x1cb0,0x6a4,0x2224,0x620,0x5dc,0x16b1,0x2a1d,0x1787,0x20c7,0x641,0xd84,0x1c05,0x2d0d,0x2f52,0x1b8c,0xd7d,0x17e8,0x1589,0xc73,0x151b,0x4e2,0x1ae9,0x1b18,0xb9b,0x949,0x2c60,0x1e7a,0xd5,0x1bdc,0x1f57,0x1753,0x124a,0x559,0xb76,0x2334,0x12d1,0x1de1,0x14b2,0x2faa,0x1697,0x147a,0x5a1,0x2c30,0x1c02,0x1043,0x2ee1,0x2402,0x1cc8,0x2a16,0xff7,0x1364,0x1b9a,0x2a53,0x2f94,0x294c,0x1ee5,0x1a87,0x2141,0xd66,0x953,0x28a3,0x2f30,0x2477,0x18e3,0x1035,0x1fc1,0x1d68,0x2fb3,0x138c,0x2487,0x1bf8,0xd96,0x1018,0x748,0x244e,0x15bd,0x175e,0x2be,0x23d,0x1da,0x176d,0xc17,0x24be,0x2ebb,0x7d8,0x100a,0x759,0x1db4,0x2259,0x23f4,0x2d59,0x2847,0xbf5,0x1cfe,0xa20,0x258,0x1180,0x279c,0x54,0x2abf,0xc5c,0x9f9,0x3d5,0x2ce4,0x165f,0x23d9,0x27b9,0x6f9,0x281a,0x169e,0x627,0x156d,0x1ff8,0x211,0x2e34,0x1724,0x2c2e,0x2790,0x2dd5,0x2bf2,0xdbc,0x2884,0x20a9,0x2390,0x1e1a,0x1b6a,0x5f7,0xab7,0x1333,0x16ab,0x28dd,0x20,0x30f,0x24b6,0x5c2,0x1ce4,0x1400,0x2669,0x60,0x156c,0xe20,0x26d4,0x26ab,0x1ebb,0x223d,0x5b4,0x2025,0x1e1c,0xaae,0x2e08,0x6cd,0x1677,0x13d9,0x17b5,0x1046,0x1d8c,0x14eb,0x18d8,0x1ce5,0x2478,0x16ae,0xb79,0x23d4,0x684,0x156b,0x567,0x1a,0x29ce,0x83a,0x19e8,0x58e,0x294a,0x1136,0x2319,0x2fba,0x1a29,0x1d,0x1879,0x291b,0x19f6,0x2c2f,0x21c9,0x19bb,0xbbc,0x26f9,0xc22,0x708,0x11a1,0x18d3,0x7f8,0x28f8,0x2427,0x1deb,0xaed,0x26aa,0x2482,0x203b,0x2f05,0x2b82,0x192f,0x2df4,0x8dc,0x2877,0xd5e,0x240e,0x775,0x2dae,0x1d3e,0x20ba,0x215b,0x22d1,0xeba,0xf50,0xaa8,0x184a,0x1f67,0x2e04,0xc6e,0x6dd,0x1a09,0x27f,0x494,0x1426,0xae3,0xe15,0x65f,0x13c4,0x105,0x872,0x2667,0x1ff6,0xd9f,0x2ca1,0x2f39,0x2657,0x23fd,0x2405,0xb73,0x2294,0x1f1e,0x2eba,0x110a,0x2cae,0x141f,0x22cd,0x25d6,0x11c1,0x1c,0x2d8e,0x161a,0x1aa8,0x229e,0x1bf9,0x7cf,0x106d,0x2c40,0xd93,0x255e,0x28c2,0xc1a,0x2f17,0x7ca,0x2f63,0xbf};
+ public final static int[] iroots= {0x2ac8,0x452,0x297c,0x666,0xb4c,0x2b8,0x1a74,0xfd,0x1a47,0x1d08,0x2959,0x2c36,0x2db4,0x56c,0x254e,0x1125,0x2f3d,0x13bc,0x172c,0x2c6b,0x32a,0x1745,0x18bd,0x8f1,0x1633,0x2dfa,0xfdd,0x23e3,0x241b,0x13a5,0x578,0x17a0,0xa9,0x104b,0x1335,0x24e4,0x28de,0x5a7,0x368,0x2d70,0x13cd,0x2f9,0xff5,0x1e88,0x9c5,0x2ff7,0x900,0xdeb,0x1434,0x15fe,0x156a,0x24d3,0x28ed,0x2c4f,0x688,0xaef,0x2353,0x1045,0x2bcf,0x23a4,0x270,0x4c5,0x21fe,0xe5b,0xfbb,0x1f79,0x6e4,0xe68,0x2078,0x1160,0x1387,0x1e98,0x22f5,0x13e,0x283a,0x123f,0x149c,0x2eca,0xb14,0xf37,0xdde,0xbe7,0x386,0x1abe,0xa4a,0x49,0x14b5,0x2f36,0x8e5,0x1f1,0x2a57,0x1789,0x2f01,0x91f,0xaac,0x266c,0x2b65,0x2f4b,0xa30,0x2a17,0x265,0x253a,0xfb3,0x2142,0x20be,0x25c2,0x121c,0x2d97,0x2131,0x1e19,0x1a11,0x514,0x22c3,0x66,0xdcf,0x1540,0x1d41,0xf02,0x815,0x5a,0x18e8,0x1159,0x103a,0x2d23,0x2a10,0x2d61,0x1327,0x403,0x25c9,0x7b3,0x1f0c,0x1a98,0x2f21,0x1fb,0x2157,0x99e,0x1501,0x640,0x1e,0x1d4f,0x2716,0xb66,0x46a,0x2fdf,0x1c69,0xf34,0xb16,0x1ac5,0x1e08,0xc9b,0x218a,0x103d,0x2a09,0x4f0,0x21b2,0x750,0x2f33,0x9f7,0x2517,0x236b,0x15cb,0x152e,0x1a33,0x97e,0x24ce,0x2db5,0xac2,0x1583,0x1f99,0x1922,0x2513,0xc4f,0x615,0x1298,0x245a,0x2f97,0x2019,0x2c93,0x1fbd,0x291a,0x8ea,0x1ed4,0xb61,0x1c09,0x230b,0x2056,0x1ccf,0x1c72,0x27d9,0x21e4,0x2d0a,0x1f5b,0xe8,0x2c3d,0x2055,0x72f,0x222,0x222d,0x11be,0x1e90,0x11cf,0x20c5,0x5b7,0x391,0x1ebd,0x238,0x73e,0x653,0x17c2,0x2ef3,0x2fb,0x27c2,0x2ecf,0x847,0x2042,0x296d,0x268d,0x23f8,0x7e0,0x1e2e,0x2bf7,0x1ab7,0x89a,0xad,0x21e3,0x261,0x2f26,0x1ede,0xc4c,0x299a,0xfc8,0xa92,0xffd,0x1cbf,0x14a4,0x2d01,0x2a2e,0x1aaf,0x1967,0x1f03,0xec5,0x25c,0x3a5,0xdd3,0x2c47,0x8dd,0x2945,0x18ac,0x197,0x2f31,0x4c9,0x14ac,0x2be2,0x166,0x43a,0xa94,0x1b53,0x293c,0x212d,0x6fd,0x521,0x109,0x185,0x2735,0x151c,0x123a,0x5be,0x2c02,0x2b0f,0x1e7b,0x1846,0x297f,0x2ffd,0x18e5,0xf2b,0xf9a,0x1f6a,0x299f,0xb48,0x1b9d,0x2b8f,0x1eb,0x12f0,0x1649,0x893,0x83d,0x2942,0x757,0xbc5,0x1db9,0x23a9,0x2115,0x1b49,0x1f77,0x2f18,0x2dfe,0xc29,0x1f69,0x287e,0x1b13,0x9ff,0x2f06,0x515,0x1bb7,0x24a9,0x17f6,0x130d,0x2dd1,0x4c1,0x1675,0x1d86,0x1d9d,0x24f8,0x55,0x1382,0x1b5,0x2061,0x1c82,0x2ebd,0x4b,0x2c68,0x780,0x24,0xff8,0x880,0x2a7b,0x54c,0x971,0x88d,0x1594,0x2802,0x1ebe,0x120e,0xcb6,0x12d7,0x15dd,0xc0a,0x2c54,0x208,0x1bfa,0x2570,0x158f,0x2c82,0xdb3,0x10d6,0x2254,0x1d8,0x26b0,0x2a1b,0xcec,0x2572,0x211d,0x1c51,0x148f,0x616,0x185f,0x1a80,0x1650,0x538,0x25e8,0xf5d,0x1072,0x34f,0x2d04,0x2a3,0xb64,0x2c9e,0x1f74,0x3a6,0x139a,0x2292,0x555,0x96a,0x244,0x60b,0x8d3,0x1de6,0x831,0x2a75,0x4d7,0x2616,0x1485,0xf16,0x264a,0x2bb,0x609,0x19d,0x21da,0x6d7,0x234f,0x2cc4,0xaf9,0x20c2,0xcdd,0x2f1,0x1dfd,0x1c7,0x247b,0xec9,0x1978,0x770,0x72b,0x1ca3,0xe43,0x1820,0xdf9,0x690,0x926,0x3cc,0x2f20,0xa7c,0x121,0x2f02,0xee6,0x2ae2,0xa85,0xe29,0xd2b,0x1326,0x2e3d,0x1553,0x2ff5,0x133,0x2d81,0x143d,0x19fc,0x174a,0x19b9,0x2a40,0x22ab,0x1d27,0x8cf,0x1730,0x1386,0x491,0x212b,0x2954,0xf53,0xbfd,0x113a,0x144f,0x21f8,0x1b0a,0x385,0x2ce6,0xf63,0x1a64,0x48f,0x2059,0x1e4b,0x1d12,0x1f7f,0x2255,0x24f2,0x16e5,0x1242,0xa29,0x1a6,0xdd5,0x7e9,0x2eac,0x2e17,0x8f7,0x9ed,0x1de0,0x1588,0x2935,0x1c3e,0x2534,0xaf2,0x2002,0x7b4,0x2bf,0x1d25,0x2273,0x1240,0x176e,0x29b1,0x217c,0x1f5d,0xa7d,0x6e8,0x1f55,0x104e,0xb07,0x241e,0xc14,0x618,0x1fad,0x2cac,0x93d,0x1e4f,0x2907,0x281,0x1bf3,0x588,0x277d,0x1e6b,0x9df,0x629,0x1f46,0x19a7,0x3c8,0x1804,0x1981,0x2536,0x19,0x6c,0x1092,0x1980,0x13ae,0xfe4,0x2f42,0x9e,0x2837,0xea,0x23e7,0x73f,0xaa3,0x226e,0x3c1,0x1f94,0x2832,0x1408,0xd63,0x1559,0x19e7,0x273,0x2fe5,0x1e40,0xa2b,0xd34,0x1be2,0x353,0x1ef7,0x147,0x10e3,0xd6d,0x248e,0xbfc,0xc04,0x9aa,0xc8,0x360,0x2262,0x100b,0x99a,0x278f,0x2efc,0x1c3d,0x29a2,0x21ec,0x251e,0x1bdb,0x2b6d,0x2d82,0x15f8,0x2924,0x2393,0x1fd,0x109a,0x17b7,0x2559,0x20b1,0x2147,0xd30,0xea6,0xf47,0x12c3,0x253,0x288c,0xbf3,0x22a3,0x78a,0x2725,0x20d,0x16d2,0x47f,0xfc,0xfc6,0xb7f,0x957,0x2514,0x1216,0xbda,0x709,0x2809,0x172e,0x1e60,0x28f9,0x23df,0x908,0x2445,0x1646,0xe38,0x3d2,0x160b,0x6e6,0x1788,0x2fe4,0x15d8,0x47,0xce8,0x1ecb,0x6b7,0x2a73,0x1619,0x27c7,0x633,0x2fe7,0x2a9a,0x1a96,0x297d,0xc2d,0x2488,0x1953,0xb89,0x131c,0x1729,0x1b16,0x1275,0x1fbb,0x184c,0x1c28,0x198a,0x2934,0x1f9,0x2553,0x11e5,0xfdc,0x2a4d,0xdc4,0x1146,0x956,0x92d,0x21e1,0x1a95,0x2fa1,0x998,0x1c01,0x131d,0x2a3f,0xb4b,0x2cf2,0x2fe1,0x724,0x1956,0x1cce,0x254a,0x2a0a,0x1497,0x11e7,0xc71,0xf58,0x77d,0x2245,0x40f,0x22c,0x871,0x3d3,0x18dd,0x1cd,0x2df0,0x1009,0x1a94,0x29da,0x1963,0x7e7,0x2908,0x848,0xc28,0x19a2,0x31d,0x2c2c,0x2608,0x23a5,0x542,0x2fad,0x865,0x1e81,0x2da9,0x25e1,0x1303,0x240c,0x7ba,0x2a8,0xc0d,0xda8,0x124d,0x28a8,0x1ff7,0x2829,0x146,0xb43,0x23ea,0x1894,0x2e27,0x2dc4,0x2d43,0x18a3,0x1a44,0xbb3,0x28b9,0x1fe9,0x226b,0x1409,0xb7a,0x1c75,0x4e,0x1299,0x1040,0x1fcc,0x171e,0xb8a,0xd1,0x75e,0x26ae,0x229b,0xec0,0x157a,0x111c,0x6b5,0x6d,0x5ae,0x1467,0x1c9d,0x200a,0x5eb,0x1339,0xbff,0x120,0x1fbe,0x13ff,0x3d1,0x2a60,0x1b87,0x196a,0x57,0x1b4f,0x1220,0x1d30,0xccd,0x248b,0x2aa8,0x1db7,0x18ae,0x10aa,0x1425,0x2f2c,0x1187,0x3a1,0x26b8,0x2466,0x14e9,0x1518,0x2b1f,0x1ae6,0x238e,0x1a78,0x1819,0x2284,0x1475,0xaf,0x2f4,0x13fc,0x227d,0x29c0,0xf3a,0x187a,0x5e4,0x1950,0x2a25,0x29e1,0xddd,0x295d,0x1351,0x304,0x2bc0,0xd2,0xd25,0x2195,0x1fc9,0x1ee6,0x2f13,0x6db,0xa6a,0x1d99,0x2b60,0x1234,0x283c,0x2ac2,0x11a9,0x639,0x2290,0x2bda,0x32f,0x2a5f,0x15c0,0x139c,0x7e8,0x88a,0x43f,0x2762,0x1271,0x119d,0x1fed,0x1b4d,0x692,0x1d2b,0x1feb,0x1380,0x2628,0x2a93,0x2226,0xe71,0x2d1b,0x20ab,0x17ff,0x1e27,0x2fb1,0xe65,0x17c8,0x1fa6,0x43b,0x548,0x2256,0x9a5,0x71a,0x26ea,0x2d38,0x1b40,0x1b79,0x658,0x15a5,0x224f,0x248,0xeee,0x2f37,0x1c30,0x15ec,0x1ca7,0x255f,0x2801,0x18f7,0x1727,0xf88,0x2b1,0x2c45,0x164b,0x289f,0x14dd,0x2649,0x27a3,0x9f0,0x21ca,0x1f5,0x1dd6,0xbc3,0x71f,0x133e,0x13bb,0x2afe,0xc35,0x4bb,0x2d31,0x10a7,0x2a04,0x180e,0x2613,0x330,0xe76,0x19fd,0xfe9,0x935,0x79,0x1b01,0x73c,0x2ac6,0x21ce,0x1911,0x761,0x1084,0x1983,0xc3,0x15eb,0xe0a,0xdd,0x1cb1,0xb21,0x2a51,0x217f,0xb1,0x1328,0x9ca,0x1d96,0x1a0b,0xe1b,0x1c4b,0x3b,0x4d6,0x2344,0x199e,0x28af,0x1624,0x4ae,0x8b2,0x2991,0x1fb7,0x41,0x2780,0x1d8b,0xa7f,0x110,0x2350,0x18aa,0x2b2f,0x1805,0x1ff,0xf0,0x2a74,0xe42,0xd97,0x85b,0x14bc,0x2901,0xfd8,0x1ab3,0x1cef,0xfbd,0x2b07,0x174f,0x69b,0x10c3,0x1491,0xde3,0x28ca,0x252e,0x1849,0x1ec2,0x1f1b,0x2853,0x12ab,0x2674,0x238c,0x350,0x2ca,0xa7,0x4bd,0xcc3,0x90c,0x892,0x276,0x1e55,0x196d,0x1194,0x1bef,0x66a,0x1da1,0x260f,0x1c15,0x49f,0x120b,0x2671,0x1237,0x2e0d,0x2791,0x17d8,0x1e0a,0x2a99,0x14cf,0xfb1,0x15b4,0x1462,0x2fbb,0xeff,0x16b,0x2d6a,0x9ef,0x5e3,0x11c0,0x2e76,0x1623,0x2db8,0x1c88,0x740,0x11e1,0x12a3,0x977,0x1110,0x2163,0x2dee,0x47b,0x2aa5,0x2a22,0x1231,0x16e7,0x1626,0x12e0,0x1d28,0xe96,0xb62,0x21d0,0xf09,0xb30,0xcb8,0x2981,0x2648,0x155d,0x27ee,0xb34,0x169,0x1574,0x1fe6,0x25f4,0x151d,0x1801,0x1f13,0x1308,0x2929,0x6eb,0x25e,0x2cca,0x1e3e,0x248f};
+ public final static int inv= 0xeab;
+ public final static int invpr= 0x2c2a;
+
+ static int round(int a,int b)
+ {
+ return (a+b/2)/b;
+ }
+
+/* constant time absolute vaue */
+ static int nabs(int x)
+ {
+ int mask=(x>>31);
+ return (x+mask)^mask;
+ }
+
+/* Montgomery stuff */
+
+ static int redc(long T)
+ {
+ long m=(T*RLWE_ND)&0xffffffffL;
+ return (int)((m*RLWE_PRIME+T)>>>WL);
+ }
+
+ static int nres(int x)
+ {
+ return redc((long)x*RLWE_R2MODP);
+ }
+
+ static int modmul(int a,int b)
+ {
+ return redc((long)a*b);
+ }
+
+/* NTT code */
+/* Cooley-Tukey NTT */
+
+ static void ntt(int[] x)
+ {
+ int m,i,j,k,t=DEGREE/2;
+ int S,U,V,W,q=RLWE_PRIME;
+
+/* Convert to Montgomery form */
+ for (j=0;j<DEGREE;j++)
+ x[j]=nres(x[j]);
+
+ m=1;
+ while (m<DEGREE)
+ {
+ k=0;
+ for (i=0;i<m;i++)
+ {
+ S=roots[m+i];
+ for (j=k;j<k+t;j++)
+ {
+ U=x[j];
+ V=modmul(x[j+t],S);
+ x[j]=U+V;
+ x[j+t]=U+2*q-V;
+ }
+ k+=2*t;
+ }
+ t/=2;
+ m*=2;
+ }
+ }
+
+/* Gentleman-Sande INTT */
+
+ static void intt(int[] x)
+ {
+ int m,i,j,k,t=1;
+ int S,U,V,W,q=RLWE_PRIME;
+
+ m=DEGREE/2;
+ while (m>1)
+ {
+ k=0;
+ for (i=0;i<m;i++)
+ {
+ S=iroots[m+i];
+ for (j=k;j<k+t;j++)
+ {
+ U=x[j];
+ V=x[j+t];
+ x[j]=U+V;
+ W=U+DEGREE*q-V;
+ x[j+t]=modmul(W,S);
+ }
+ k+=2*t;
+ }
+ t*=2;
+ m/=2;
+ }
+
+/* Last iteration merged with n^-1 */
+
+ t=DEGREE/2;
+ for (j=0;j<t;j++)
+ {
+ U=x[j];
+ V=x[j+t];
+ W=U+DEGREE*q-V;
+ x[j+t]=modmul(W,invpr);
+ x[j]=modmul(U+V,inv);
+ }
+/* convert back from Montgomery to "normal" form */
+ for (j=0;j<DEGREE;j++)
+ {
+ x[j]=redc(x[j]);
+ x[j]-=q;
+ x[j]+=(x[j]>>(WL-1))&q;
+ }
+ }
+
+/* See https://eprint.iacr.org/2016/1157.pdf */
+
+ static void Encode(byte[] key,int[] poly)
+ {
+ int i,j,b,k,kj,q2;
+
+ q2=RLWE_PRIME/2;
+ for (i=j=0;i<256;)
+ {
+ kj=key[j++];
+ for (k=0;k<8;k++)
+ {
+ b=kj&1;
+ poly[i]=b*q2;
+ poly[i+256]=b*q2;
+ poly[i+512]=b*q2;
+ poly[i+768]=b*q2;
+ kj>>=1;
+ i++;
+ }
+ }
+ }
+
+ static void Decode(int[] poly,byte[] key)
+ {
+ int i,j,k;
+ int b,t,q2;
+ q2=RLWE_PRIME/2;
+ for (i=0;i<32;i++)
+ key[i]=0;
+
+ for (i=j=0;i<256;)
+ {
+ for (k=0;k<8;k++)
+ {
+ t=nabs(poly[i]-q2)+nabs(poly[i+256]-q2)+nabs(poly[i+512]-q2)+nabs(poly[i+768]-q2);
+
+ b=t-RLWE_PRIME;
+ b=(b>>31)&1;
+ key[j]=(byte)((((int)key[j]&0xff)>>1) + (b<<7));
+ i++;
+ }
+ j++;
+ }
+ }
+
+/* convert 32-byte seed to random polynomial */
+
+ static void Parse(byte[] seed,int[] poly)
+ {
+ int i,j;
+ int n;
+ byte[] hash=new byte[4*DEGREE];
+ SHA3 sh=new SHA3(SHA3.SHAKE128);
+
+ for (i=0;i<32;i++)
+ sh.process(seed[i]);
+ sh.shake(hash,4*DEGREE);
+
+ for (i=j=0;i<DEGREE;i++)
+ {
+ n=(int)hash[j]&0x7f; n<<=8;
+ n+=(int)hash[j+1]&0xff; n<<=8;
+ n+=(int)hash[j+2]&0xff; n<<=8;
+ n+=(int)hash[j+3]&0xff; j+=4;
+ poly[i]=nres(n);
+ //poly[i]=modmul(n,RLWE_ONE); // reduce 31-bit random number mod q
+ }
+ }
+
+/* Compress 14 bits polynomial coefficients into byte array */
+/* 7 bytes is 3x14 */
+ static void pack(int[] poly,byte[] array)
+ {
+ int i,j,k;
+ int a,b,c,d;
+
+ for (i=j=0;i<DEGREE; )
+ {
+ a=poly[i++]; b=poly[i++]; c=poly[i++]; d=poly[i++];
+ array[j++]=(byte)(a&0xff);
+ array[j++]=(byte)(((a>>8)|(b<<6))&0xff);
+ array[j++]=(byte)((b>>2)&0xff);
+ array[j++]=(byte)(((b>>10)|(c<<4))&0xff);
+ array[j++]=(byte)((c>>4)&0xff);
+ array[j++]=(byte)(((c>>12)|(d<<2))&0xff);
+ array[j++]=(byte)(d>>6);
+ }
+ }
+
+ static void unpack(byte[] array,int[] poly)
+ {
+ int i,j,k;
+ int a,b,c,d,e,f,g;
+
+ for (i=j=0;i<DEGREE; )
+ {
+ a=((int)array[j++])&0xff; b=((int)array[j++])&0xff; c=((int)array[j++])&0xff; d=((int)array[j++])&0xff; e=((int)array[j++])&0xff; f=((int)array[j++])&0xff; g=((int)array[j++])&0xff;
+ poly[i++]=a|((b&0x3f)<<8);
+ poly[i++]=(b>>6)|(c<<2)|((d&0xf)<<10);
+ poly[i++]=(d>>4)|(e<<4)|((f&3)<<12);
+ poly[i++]=(f>>2)|(g<<6);
+ }
+ }
+
+
+/* See https://eprint.iacr.org/2016/1157.pdf */
+
+ static void Compress(int[] poly,byte[] array)
+ {
+ int i,j,k,b;
+ int col=0;
+
+ for (i=j=0;i<DEGREE;)
+ {
+ for (k=0;k<8;k++)
+ {
+ b=round((poly[i]*8),RLWE_PRIME)&7;
+ col=(col<<3)+b;
+ i++;
+ }
+ array[j]=(byte)(col&0xff);
+ array[j+1]=(byte)((col>>>8)&0xff);
+ array[j+2]=(byte)((col>>>16)&0xff);
+ j+=3; col=0;
+ }
+ }
+
+ static void Decompress(byte[] array,int[] poly)
+ {
+ int i,j,k,b;
+ int col=0;
+
+ for (i=j=0;i<DEGREE;)
+ {
+ col=(int)array[j+2]&0xff;
+ col=(col<<8)+((int)array[j+1]&0xff);
+ col=(col<<8)+((int)array[j]&0xff);
+ j+=3;
+ for (k=0;k<8;k++)
+ {
+ b=(col&0xe00000)>>>21; col<<=3;
+ poly[i]=round((b*RLWE_PRIME),8);
+ i++;
+ }
+ }
+ }
+
+/* generate centered binomial distribution */
+
+ static void Error(RAND RNG,int[] poly)
+ {
+ int i,j;
+ int n1,n2,r;
+ for (i=0;i<DEGREE;i++)
+ {
+ n1=RNG.getByte()+(RNG.getByte()<<8);
+ n2=RNG.getByte()+(RNG.getByte()<<8);
+ r=0;
+ for (j=0;j<16;j++)
+ {
+ r+=(n1&1)-(n2&1);
+ n1>>=1; n2>>=1;
+ }
+ poly[i]=(r+RLWE_PRIME);
+ }
+ }
+
+ static void redc_it(int[] p)
+ {
+ int i;
+ for (i=0;i<DEGREE;i++)
+ p[i]=redc(p[i]);
+ }
+
+ static void nres_it(int[] p)
+ {
+ int i;
+ for (i=0;i<DEGREE;i++)
+ p[i]=nres(p[i]);
+ }
+
+ static void poly_mul(int[] p1,int[] p2,int[] p3)
+ {
+ int i;
+ for (i=0;i<DEGREE;i++)
+ p1[i]=modmul(p2[i],p3[i]);
+ }
+
+ static void poly_add(int[] p1,int[] p2,int[] p3)
+ {
+ int i;
+ for (i=0;i<DEGREE;i++)
+ p1[i]=(p2[i]+p3[i]);
+ }
+
+ static void poly_sub(int[] p1,int[] p2,int[] p3)
+ {
+ int i;
+ for (i=0;i<DEGREE;i++)
+ p1[i]=(p2[i]+RLWE_PRIME-p3[i]);
+ }
+
+/* reduces inputs < 2q */
+ static void poly_soft_reduce(int[] poly)
+ {
+ int i;
+ int e;
+ for (i=0;i<DEGREE;i++)
+ {
+ e=poly[i]-RLWE_PRIME;
+ poly[i]=e+((e>>(WL-1))&RLWE_PRIME);
+ }
+ }
+
+/* fully reduces modulo q */
+ static void poly_hard_reduce(int[] poly)
+ {
+ int i;
+ int e;
+ for (i=0;i<DEGREE;i++)
+ {
+ e=modmul(poly[i],RLWE_ONE);
+ e=e-RLWE_PRIME;
+ poly[i]=e+((e>>(WL-1))&RLWE_PRIME);
+ }
+ }
+
+/* API files */
+
+ public static void SERVER_1(RAND RNG,byte[] SB,byte[] S)
+ {
+ int i;
+ byte[] seed=new byte[32];
+ byte[] array=new byte[1792];
+
+ int[] s=new int[DEGREE];
+ int[] e=new int[DEGREE];
+ int[] b=new int[DEGREE];
+
+ for (i=0;i<32;i++)
+ seed[i]=(byte)RNG.getByte();
+
+ Parse(seed,b);
+
+ Error(RNG,e);
+ Error(RNG,s);
+
+ ntt(s);
+ ntt(e);
+ poly_mul(b,b,s);
+ poly_add(b,b,e);
+ poly_hard_reduce(b);
+
+ redc_it(b);
+ pack(b,array);
+
+ for (i=0;i<32;i++)
+ SB[i]=seed[i];
+ for (i=0;i<1792;i++)
+ SB[i+32]=array[i];
+
+ poly_hard_reduce(s);
+
+ pack(s,array);
+
+ for (i=0;i<1792;i++)
+ S[i]=array[i];
+
+ }
+
+ public static void CLIENT(RAND RNG,byte[] SB,byte[] UC,byte[] KEY)
+ {
+ int i;
+ SHA3 sh=new SHA3(SHA3.HASH256);
+
+ byte[] seed=new byte[32];
+ byte[] array=new byte[1792];
+ byte[] key=new byte[32];
+ byte[] cc=new byte[384];
+
+ int[] sd=new int[DEGREE];
+ int[] ed=new int[DEGREE];
+ int[] u=new int[DEGREE];
+ int[] k=new int[DEGREE];
+ int[] c=new int[DEGREE];
+
+ Error(RNG,sd);
+ Error(RNG,ed);
+
+ ntt(sd);
+ ntt(ed);
+
+ for (i=0;i<32;i++)
+ seed[i]=SB[i];
+
+ for (i=0;i<1792;i++)
+ array[i]=SB[i+32];
+
+ Parse(seed,u);
+
+ poly_mul(u,u,sd);
+ poly_add(u,u,ed);
+ poly_hard_reduce(u);
+
+ for (i=0;i<32;i++)
+ key[i]=(byte)RNG.getByte();
+
+ for (i=0;i<32;i++)
+ sh.process(key[i]);
+ sh.hash(key);
+
+ Encode(key,k);
+
+ unpack(array,c);
+ nres_it(c);
+
+ poly_mul(c,c,sd);
+ intt(c);
+ Error(RNG,ed);
+ poly_add(c,c,ed);
+ poly_add(c,c,k);
+
+ Compress(c,cc);
+
+ sh.init(SHA3.HASH256);
+ for (i=0;i<32;i++)
+ sh.process(key[i]);
+ sh.hash(key);
+
+ for (i=0;i<32;i++)
+ KEY[i]=key[i];
+
+ redc_it(u);
+ pack(u,array);
+
+ for (i=0;i<1792;i++)
+ UC[i]=array[i];
+ for (i=0;i<384;i++)
+ UC[i+1792]=cc[i];
+
+ }
+
+ public static void SERVER_2(byte[] S,byte[] UC,byte[] KEY)
+ {
+ int i;
+ SHA3 sh=new SHA3(SHA3.HASH256);
+
+ int[] c=new int[DEGREE];
+ int[] s=new int[DEGREE];
+ int[] k=new int[DEGREE];
+
+ byte[] array=new byte[1792];
+ byte[] key=new byte[32];
+ byte[] cc=new byte[384];
+
+ for (i=0;i<1792;i++)
+ array[i]=UC[i];
+
+ unpack(array,k);
+ nres_it(k);
+
+ for (i=0;i<384;i++)
+ cc[i]=UC[i+1792];
+
+ Decompress(cc,c);
+
+ for (i=0;i<1792;i++)
+ array[i]=S[i];
+
+ unpack(array,s);
+
+ poly_mul(k,k,s);
+ intt(k);
+ poly_sub(k,c,k);
+ poly_soft_reduce(k);
+
+ Decode(k,key);
+
+ for (i=0;i<32;i++)
+ sh.process(key[i]);
+ sh.hash(key);
+
+ for (i=0;i<32;i++)
+ KEY[i]=key[i];
+ }
+/*
+ public static void main(String[] args) {
+ int i;
+ byte[] RAW=new byte[100];
+ byte[] S=new byte[1792];
+ byte[] SB=new byte[1824];
+ byte[] UC=new byte[2176];
+ byte[] KEYA=new byte[32];
+ byte[] KEYB=new byte[32];
+
+ RAND SRNG=new RAND();
+ RAND CRNG=new RAND();
+ SRNG.clean(); CRNG.clean();
+
+ for (i=0;i<100;i++) RAW[i]=(byte)(i+1);
+ SRNG.seed(100,RAW);
+
+ for (i=0;i<100;i++) RAW[i]=(byte)(i+2);
+ CRNG.seed(100,RAW);
+
+// NewHope Simple key exchange
+
+ SERVER_1(SRNG,SB,S);
+ CLIENT(CRNG,SB,UC,KEYB);
+ SERVER_2(S,UC,KEYA);
+
+ System.out.printf("Alice key= 0x");
+ for (i=0;i<KEYA.length;i++)
+ System.out.printf("%02x", KEYA[i]);
+ System.out.println();
+
+
+ System.out.printf("Bob's key= 0x");
+ for (i=0;i<KEYA.length;i++)
+ System.out.printf("%02x", KEYB[i]);
+ System.out.println();
+ } */
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/NIST256/BIG.java b/src/main/java/org/apache/milagro/amcl/NIST256/BIG.java
new file mode 100644
index 0000000..0e69f4f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST256/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.NIST256;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=32; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST256/DBIG.java b/src/main/java/org/apache/milagro/amcl/NIST256/DBIG.java
new file mode 100644
index 0000000..cdaaf63
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST256/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.NIST256;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST256/ECDH.java b/src/main/java/org/apache/milagro/amcl/NIST256/ECDH.java
new file mode 100644
index 0000000..1bf50e6
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST256/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.NIST256;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST256/ECP.java b/src/main/java/org/apache/milagro/amcl/NIST256/ECP.java
new file mode 100644
index 0000000..da0510a
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST256/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.NIST256;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NIST256/FP.java b/src/main/java/org/apache/milagro/amcl/NIST256/FP.java
new file mode 100644
index 0000000..88e7a49
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST256/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.NIST256;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=256; /* Number of bits in Modulus */
+ public static final int MOD8=7; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<24); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST256/ROM.java b/src/main/java/org/apache/milagro/amcl/NIST256/ROM.java
new file mode 100644
index 0000000..6cbdddf
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST256/ROM.java
@@ -0,0 +1,43 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.NIST256;
+
+public class ROM
+{
+
+// Base Bits= 56
+ public static final long[] Modulus= {0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFL,0x0L,0x1000000L,0xFFFFFFFFL};
+ public static final long[] R2modp= {0x3000000050000L,0x0L,0xFFFFFBFFFFFFFAL,0xFFFAFFFFFFFEFFL,0x2FFFFL};
+ public static final long MConst= 0x1L;
+
+ public static final int CURVE_Cof_I= 1;
+ public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= -3;
+ public static final int CURVE_B_I= 0;
+ public static final long[] CURVE_B= {0xCE3C3E27D2604BL,0x6B0CC53B0F63BL,0x55769886BC651DL,0xAA3A93E7B3EBBDL,0x5AC635D8L};
+ public static final long[] CURVE_Order= {0xB9CAC2FC632551L,0xFAADA7179E84F3L,0xFFFFFFFFFFBCE6L,0xFFFFFFL,0xFFFFFFFFL};
+ public static final long[] CURVE_Gx= {0xA13945D898C296L,0x7D812DEB33A0F4L,0xE563A440F27703L,0xE12C4247F8BCE6L,0x6B17D1F2L};
+ public static final long[] CURVE_Gy= {0xB6406837BF51F5L,0x33576B315ECECBL,0x4A7C0F9E162BCEL,0xFE1A7F9B8EE7EBL,0x4FE342E2L};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NIST384/BIG.java b/src/main/java/org/apache/milagro/amcl/NIST384/BIG.java
new file mode 100644
index 0000000..dab1864
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST384/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.NIST384;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=48; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST384/DBIG.java b/src/main/java/org/apache/milagro/amcl/NIST384/DBIG.java
new file mode 100644
index 0000000..29a403d
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST384/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.NIST384;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST384/ECDH.java b/src/main/java/org/apache/milagro/amcl/NIST384/ECDH.java
new file mode 100644
index 0000000..93ef59c
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST384/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.NIST384;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST384/ECP.java b/src/main/java/org/apache/milagro/amcl/NIST384/ECP.java
new file mode 100644
index 0000000..c3bcfbd
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST384/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.NIST384;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=48;
+ public static final int AESKEY=24;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NIST384/FP.java b/src/main/java/org/apache/milagro/amcl/NIST384/FP.java
new file mode 100644
index 0000000..cd56e35
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST384/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.NIST384;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=384; /* Number of bits in Modulus */
+ public static final int MOD8=7; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<8); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST384/ROM.java b/src/main/java/org/apache/milagro/amcl/NIST384/ROM.java
new file mode 100644
index 0000000..94ca0e7
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST384/ROM.java
@@ -0,0 +1,44 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.NIST384;
+
+public class ROM
+{
+
+// Base Bits= 56
+ public static final long[] Modulus= {0xFFFFFFFFL,0xFFFF0000000000L,0xFFFFFFFFFEFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFL};
+ public static final long[] R2modp= {0xFE000000010000L,0xFFFFFFL,0x2L,0xFFFFFFFE00L,0x1000000020000L,0x0L,0x0L};
+ public static final long MConst= 0x100000001L;
+
+ public static final int CURVE_Cof_I= 1;
+ public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= -3;
+ public static final int CURVE_B_I= 0;
+ public static final long[] CURVE_B= {0x85C8EDD3EC2AEFL,0x398D8A2ED19D2AL,0x8F5013875AC656L,0xFE814112031408L,0xF82D19181D9C6EL,0xE7E4988E056BE3L,0xB3312FA7E23EL};
+ public static final long[] CURVE_Order= {0xEC196ACCC52973L,0xDB248B0A77AECL,0x81F4372DDF581AL,0xFFFFFFFFC7634DL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFL};
+ public static final long[] CURVE_Gx= {0x545E3872760AB7L,0xF25DBF55296C3AL,0xE082542A385502L,0x8BA79B9859F741L,0x20AD746E1D3B62L,0x5378EB1C71EF3L,0xAA87CA22BE8BL};
+ public static final long[] CURVE_Gy= {0x431D7C90EA0E5FL,0xB1CE1D7E819D7AL,0x13B5F0B8C00A60L,0x289A147CE9DA31L,0x92DC29F8F41DBDL,0x2C6F5D9E98BF92L,0x3617DE4A9626L};
+
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NIST521/BIG.java b/src/main/java/org/apache/milagro/amcl/NIST521/BIG.java
new file mode 100644
index 0000000..1626fd9
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST521/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.NIST521;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=66; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=60;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST521/DBIG.java b/src/main/java/org/apache/milagro/amcl/NIST521/DBIG.java
new file mode 100644
index 0000000..08599a4
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST521/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.NIST521;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST521/ECDH.java b/src/main/java/org/apache/milagro/amcl/NIST521/ECDH.java
new file mode 100644
index 0000000..8f58017
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST521/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.NIST521;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST521/ECP.java b/src/main/java/org/apache/milagro/amcl/NIST521/ECP.java
new file mode 100644
index 0000000..80f3e05
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST521/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.NIST521;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=64;
+ public static final int AESKEY=32;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NIST521/FP.java b/src/main/java/org/apache/milagro/amcl/NIST521/FP.java
new file mode 100644
index 0000000..fa36640
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST521/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.NIST521;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=521; /* Number of bits in Modulus */
+ public static final int MOD8=7; /* Modulus mod 8 */
+ public static final int MODTYPE=PSEUDO_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<19); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NIST521/ROM.java b/src/main/java/org/apache/milagro/amcl/NIST521/ROM.java
new file mode 100644
index 0000000..a7b4e42
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NIST521/ROM.java
@@ -0,0 +1,44 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.NIST521;
+
+public class ROM
+{
+
+// Base Bits= 60
+ public static final long[] Modulus= {0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0x1FFFFFFFFFFL};
+ public static final long[] R2modp= {0x4000000000L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long MConst= 0x1L;
+
+
+ public static final int CURVE_Cof_I= 1;
+ public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= -3;
+ public static final int CURVE_B_I= 0;
+ public static final long[] CURVE_B= {0xF451FD46B503F00L,0x73DF883D2C34F1EL,0x2C0BD3BB1BF0735L,0x3951EC7E937B165L,0x9918EF109E15619L,0x5B99B315F3B8B48L,0xB68540EEA2DA72L,0x8E1C9A1F929A21AL,0x51953EB961L};
+ public static final long[] CURVE_Order= {0xB6FB71E91386409L,0xB5C9B8899C47AEBL,0xC0148F709A5D03BL,0x8783BF2F966B7FCL,0xFFFFFFFFFFA5186L,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0x1FFFFFFFFFFL};
+ public static final long[] CURVE_Gx= {0x97E7E31C2E5BD66L,0x48B3C1856A429BFL,0xDC127A2FFA8DE33L,0x5E77EFE75928FE1L,0xF606B4D3DBAA14BL,0x39053FB521F828AL,0x62395B4429C6481L,0x404E9CD9E3ECB6L,0xC6858E06B7L};
+ public static final long[] CURVE_Gy= {0x8BE94769FD16650L,0x3C7086A272C2408L,0xB9013FAD076135L,0x72995EF42640C55L,0xD17273E662C97EEL,0x49579B446817AFBL,0x42C7D1BD998F544L,0x9A3BC0045C8A5FBL,0x11839296A78L};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256E/BIG.java b/src/main/java/org/apache/milagro/amcl/NUMS256E/BIG.java
new file mode 100644
index 0000000..19f3da3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256E/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.NUMS256E;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=32; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256E/DBIG.java b/src/main/java/org/apache/milagro/amcl/NUMS256E/DBIG.java
new file mode 100644
index 0000000..0d7bec3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256E/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.NUMS256E;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256E/ECDH.java b/src/main/java/org/apache/milagro/amcl/NUMS256E/ECDH.java
new file mode 100644
index 0000000..32d8fa0
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256E/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.NUMS256E;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256E/ECP.java b/src/main/java/org/apache/milagro/amcl/NUMS256E/ECP.java
new file mode 100644
index 0000000..be85c68
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256E/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.NUMS256E;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=EDWARDS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256E/FP.java b/src/main/java/org/apache/milagro/amcl/NUMS256E/FP.java
new file mode 100644
index 0000000..1d61675
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256E/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.NUMS256E;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=256; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=PSEUDO_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<24); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256E/ROM.java b/src/main/java/org/apache/milagro/amcl/NUMS256E/ROM.java
new file mode 100644
index 0000000..dada8b2
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256E/ROM.java
@@ -0,0 +1,42 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.NUMS256E;
+
+public class ROM
+{
+ public static final long[] Modulus= {0xFFFFFFFFFFFF43L,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFL};
+ public static final long[] R2modp= {0x89000000000000L,0x8BL,0x0L,0x0L,0x0L};
+ public static final long MConst= 0xBDL;
+
+ public static final int CURVE_Cof_I= 4;
+ public static final long[] CURVE_Cof= {0x4L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= 1;
+ public static final int CURVE_B_I= -15342;
+ public static final long[] CURVE_B= {0xFFFFFFFFFFC355L,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFL};
+ public static final long[] CURVE_Order= {0x47B190EEDD4AF5L,0x5AA52F59439B1AL,0x4195L,0x0L,0x40000000L};
+ public static final long[] CURVE_Gx= {0xDEC0902EED13DAL,0x8A0EE3083586A0L,0x5F69209BD60C39L,0x6AEA237DCD1E3DL,0x8A7514FBL};
+ public static final long[] CURVE_Gy= {0xA616E7798A89E6L,0x61D810856ED32FL,0xD9A64B8010715FL,0xD9D925C7CE9665L,0x44D53E9FL};
+
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256W/BIG.java b/src/main/java/org/apache/milagro/amcl/NUMS256W/BIG.java
new file mode 100644
index 0000000..fd66263
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256W/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.NUMS256W;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=32; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256W/DBIG.java b/src/main/java/org/apache/milagro/amcl/NUMS256W/DBIG.java
new file mode 100644
index 0000000..96e3e03
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256W/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.NUMS256W;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256W/ECDH.java b/src/main/java/org/apache/milagro/amcl/NUMS256W/ECDH.java
new file mode 100644
index 0000000..19c3104
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256W/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.NUMS256W;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256W/ECP.java b/src/main/java/org/apache/milagro/amcl/NUMS256W/ECP.java
new file mode 100644
index 0000000..20dd2f2
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256W/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.NUMS256W;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256W/FP.java b/src/main/java/org/apache/milagro/amcl/NUMS256W/FP.java
new file mode 100644
index 0000000..25d10bc
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256W/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.NUMS256W;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=256; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=PSEUDO_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<24); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS256W/ROM.java b/src/main/java/org/apache/milagro/amcl/NUMS256W/ROM.java
new file mode 100644
index 0000000..1fa94d6
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS256W/ROM.java
@@ -0,0 +1,45 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.NUMS256W;
+
+public class ROM
+{
+
+// Modulus
+ public static final long[] Modulus= {0xFFFFFFFFFFFF43L,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFL};
+ public static final long[] R2modp= {0x89000000000000L,0x8BL,0x0L,0x0L,0x0L};
+ public static final long MConst= 0xBDL;
+
+// Curve
+
+ public static final int CURVE_Cof_I= 1;
+ public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= -3;
+ public static final int CURVE_B_I= 152961;
+ public static final long[] CURVE_B= {0x25581L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Order= {0xAB20294751A825L,0x8275EA265C6020L,0xFFFFFFFFFFE43CL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFL};
+ public static final long[] CURVE_Gx= {0x52EE1EB21AACB1L,0x9B0903D4C73ABCL,0xA04F42CB098357L,0x5AAADB61297A95L,0xBC9ED6B6L};
+ public static final long[] CURVE_Gy= {0xB5B9CB2184DE9FL,0xC3D115310FBB80L,0xF77E04E035C955L,0x3399B6A673448BL,0xD08FC0F1L};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384E/BIG.java b/src/main/java/org/apache/milagro/amcl/NUMS384E/BIG.java
new file mode 100644
index 0000000..d8dfbb3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384E/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.NUMS384E;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=48; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384E/DBIG.java b/src/main/java/org/apache/milagro/amcl/NUMS384E/DBIG.java
new file mode 100644
index 0000000..88896bd
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384E/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.NUMS384E;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384E/ECDH.java b/src/main/java/org/apache/milagro/amcl/NUMS384E/ECDH.java
new file mode 100644
index 0000000..23a4236
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384E/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.NUMS384E;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384E/ECP.java b/src/main/java/org/apache/milagro/amcl/NUMS384E/ECP.java
new file mode 100644
index 0000000..f37a4c5
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384E/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.NUMS384E;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=EDWARDS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=48;
+ public static final int AESKEY=24;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384E/FP.java b/src/main/java/org/apache/milagro/amcl/NUMS384E/FP.java
new file mode 100644
index 0000000..45db06f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384E/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.NUMS384E;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=384; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=PSEUDO_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<8); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384E/ROM.java b/src/main/java/org/apache/milagro/amcl/NUMS384E/ROM.java
new file mode 100644
index 0000000..54f142d
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384E/ROM.java
@@ -0,0 +1,40 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.NUMS384E;
+
+public class ROM
+{
+// Base Bits= 56
+ public static final long[] Modulus= {0xFFFFFFFFFFFEC3L,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFL};
+ public static final long[] R2modp= {0x188890000L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long MConst= 0x13DL;
+
+ public static final int CURVE_Cof_I= 4;
+ public static final long[] CURVE_Cof= {0x4L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= 1;
+ public static final int CURVE_B_I= -11556;
+ public static final long[] CURVE_B= {0xFFFFFFFFFFD19FL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFL};
+ public static final long[] CURVE_Order= {0xB9DCC4E6A3897DL,0x555AAB35C87920L,0x1CB46BE1CF61E4L,0xFFFFFFFFE2471AL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0x3FFFFFFFFFFFL};
+ public static final long[] CURVE_Gx= {0xD540E46C206BDEL,0x92B16545941350L,0xA8F33163406FF2L,0xE5BE4C005763FFL,0xE55DB5B30BF446L,0x266CC0B6A2129AL,0x61B111FB45A9L};
+ public static final long[] CURVE_Gy= {0x8D03E1F0729392L,0xB0F946EC48DC9DL,0xF7F645964B0072L,0xF1425F56830F98L,0xB10DD716AD8274L,0xEEB08738B1A423L,0x82983E67B9A6L};
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384W/BIG.java b/src/main/java/org/apache/milagro/amcl/NUMS384W/BIG.java
new file mode 100644
index 0000000..0f87234
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384W/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.NUMS384W;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=48; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=58;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384W/DBIG.java b/src/main/java/org/apache/milagro/amcl/NUMS384W/DBIG.java
new file mode 100644
index 0000000..fcc04fe
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384W/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.NUMS384W;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384W/ECDH.java b/src/main/java/org/apache/milagro/amcl/NUMS384W/ECDH.java
new file mode 100644
index 0000000..49b9ed1
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384W/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.NUMS384W;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384W/ECP.java b/src/main/java/org/apache/milagro/amcl/NUMS384W/ECP.java
new file mode 100644
index 0000000..d9bce92
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384W/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.NUMS384W;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=48;
+ public static final int AESKEY=24;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384W/FP.java b/src/main/java/org/apache/milagro/amcl/NUMS384W/FP.java
new file mode 100644
index 0000000..5fbb4c3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384W/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.NUMS384W;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=384; /* Number of bits in Modulus */
+ public static final int MOD8=3; /* Modulus mod 8 */
+ public static final int MODTYPE=PSEUDO_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<22); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS384W/ROM.java b/src/main/java/org/apache/milagro/amcl/NUMS384W/ROM.java
new file mode 100644
index 0000000..314efd0
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS384W/ROM.java
@@ -0,0 +1,55 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.NUMS384W;
+
+public class ROM
+{
+/*
+// Base Bits= 56
+ public static final long[] Modulus= {0xFFFFFFFFFFFEC3L,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFL};
+ public static final long[] R2modp= {0x188890000L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long MConst= 0x13DL;
+
+ public static final int CURVE_A= -3;
+ public static final int CURVE_B_I= -34568;
+ public static final long[] CURVE_B= {0xFFFFFFFFFF77BBL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFL};
+ public static final long[] CURVE_Order= {0x4D81F67B0E61B9L,0x9D3D4C37E27A60L,0x1EEB5D6881BEDAL,0xFFFFFFFFD61EAFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFL};
+ public static final long[] CURVE_Gx= {0x9CBA042098152AL,0xED100F61C47BEBL,0x1B2A6CC742522EL,0xFB81F9F4F3BD29L,0x5F1A60225C1CDL,0x181C4880CA2241L,0x757956F0B16FL};
+ public static final long[] CURVE_Gy= {0x74B8EC66180716L,0xB4DBBFF4AD265CL,0x7D121A837EBCD6L,0xF87F739CB92083L,0x84CF7EB0046977L,0x8E38D7E33D3005L,0xACDEE368E19BL};
+*/
+// Base Bits= 58
+ public static final long[] Modulus= {0x3FFFFFFFFFFFEC3L,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0xFFFFFFFFFL};
+ public static final long[] R2modp= {0x88900000000000L,0x6L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long MConst= 0x13DL;
+
+ public static final int CURVE_Cof_I= 1;
+ public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= -3;
+ public static final int CURVE_B_I= -34568;
+ public static final long[] CURVE_B= {0x3FFFFFFFFFF77BBL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0xFFFFFFFFFL};
+ public static final long[] CURVE_Order= {0x4D81F67B0E61B9L,0x2A74F530DF89E98L,0x2F1EEB5D6881BEDL,0x3FFFFFFFFFF587AL,0x3FFFFFFFFFFFFFFL,0x3FFFFFFFFFFFFFFL,0xFFFFFFFFFL};
+ public static final long[] CURVE_Gx= {0x39CBA042098152AL,0x3BB4403D8711EFAL,0x291B2A6CC742522L,0x337EE07E7D3CEF4L,0x24105F1A60225C1L,0x5BC60712203288L,0x757956F0BL};
+ public static final long[] CURVE_Gy= {0x74B8EC66180716L,0x1AD36EFFD2B4997L,0x37D121A837EBCDL,0x1DFE1FDCE72E482L,0x584CF7EB00469L,0x66E38E35F8CF4CL,0xACDEE368EL};
+
+
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512E/BIG.java b/src/main/java/org/apache/milagro/amcl/NUMS512E/BIG.java
new file mode 100644
index 0000000..fc4e029
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512E/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.NUMS512E;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=64; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512E/DBIG.java b/src/main/java/org/apache/milagro/amcl/NUMS512E/DBIG.java
new file mode 100644
index 0000000..4a4547c
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512E/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.NUMS512E;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512E/ECDH.java b/src/main/java/org/apache/milagro/amcl/NUMS512E/ECDH.java
new file mode 100644
index 0000000..7f59cc8
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512E/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.NUMS512E;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512E/ECP.java b/src/main/java/org/apache/milagro/amcl/NUMS512E/ECP.java
new file mode 100644
index 0000000..73a5515
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512E/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.NUMS512E;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=EDWARDS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=64;
+ public static final int AESKEY=32;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512E/FP.java b/src/main/java/org/apache/milagro/amcl/NUMS512E/FP.java
new file mode 100644
index 0000000..1914efe
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512E/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.NUMS512E;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=512; /* Number of bits in Modulus */
+ public static final int MOD8=7; /* Modulus mod 8 */
+ public static final int MODTYPE=PSEUDO_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<30); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512E/ROM.java b/src/main/java/org/apache/milagro/amcl/NUMS512E/ROM.java
new file mode 100644
index 0000000..32f63a3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512E/ROM.java
@@ -0,0 +1,40 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.NUMS512E;
+
+public class ROM
+{
+// Base Bits= 56
+ public static final long[] Modulus= {0xFFFFFFFFFFFDC7L,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFL};
+ public static final long[] R2modp= {0x0L,0xF0B10000000000L,0x4L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long MConst= 0x239L;
+
+ public static final int CURVE_Cof_I= 4;
+ public static final long[] CURVE_Cof= {0x4L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= 1;
+ public static final int CURVE_B_I= -78296;
+ public static final long[] CURVE_B= {0xFFFFFFFFFECBEFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFL};
+ public static final long[] CURVE_Order= {0x468CF51BEED46DL,0x5786DEFECFF67L,0xC970B686F52A46L,0x2FCF91BA9E3FD8L,0xFFFFFFB4F0636DL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0x3FL};
+ public static final long[] CURVE_Gx= {0xB9AB2999EC57FEL,0x25427CC4F015C5L,0x92568904AD0FE5L,0xEE46730F78BDC9L,0x3B81474621C14EL,0xA38227A17EBE27L,0x332FD1E79F4DC4L,0x7A18CB7888D3C5L,0x8E316D128DB69CL,0xDFL};
+ public static final long[] CURVE_Gy= {0x6DDEC0C1E2F5E1L,0xD38A9BF1D01F32L,0x862AECC1FD0266L,0xE9963562601A06L,0x9E834120CA53F2L,0x9D22A92B6B9590L,0x6EE476F726D825L,0x98B0F577A82A25L,0x9BFF39D49CA71L,0x6DL};
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512W/BIG.java b/src/main/java/org/apache/milagro/amcl/NUMS512W/BIG.java
new file mode 100644
index 0000000..1ab5814
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512W/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.NUMS512W;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=64; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=60;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512W/DBIG.java b/src/main/java/org/apache/milagro/amcl/NUMS512W/DBIG.java
new file mode 100644
index 0000000..c515656
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512W/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.NUMS512W;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512W/ECDH.java b/src/main/java/org/apache/milagro/amcl/NUMS512W/ECDH.java
new file mode 100644
index 0000000..8d59730
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512W/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.NUMS512W;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512W/ECP.java b/src/main/java/org/apache/milagro/amcl/NUMS512W/ECP.java
new file mode 100644
index 0000000..7cbac93
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512W/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.NUMS512W;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=64;
+ public static final int AESKEY=32;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512W/FP.java b/src/main/java/org/apache/milagro/amcl/NUMS512W/FP.java
new file mode 100644
index 0000000..053b739
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512W/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.NUMS512W;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=512; /* Number of bits in Modulus */
+ public static final int MOD8=7; /* Modulus mod 8 */
+ public static final int MODTYPE=PSEUDO_MERSENNE;
+
+ public static final int FEXCESS =((int)1<<28); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/NUMS512W/ROM.java b/src/main/java/org/apache/milagro/amcl/NUMS512W/ROM.java
new file mode 100644
index 0000000..ef267dc
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/NUMS512W/ROM.java
@@ -0,0 +1,41 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.NUMS512W;
+
+public class ROM
+{
+
+// Base Bits= 60
+ public static final long[] Modulus= {0xFFFFFFFFFFFFDC7L,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL};
+ public static final long[] R2modp= {0x100000000000000L,0x4F0BL,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long MConst= 0x239L;
+
+ public static final int CURVE_Cof_I= 1;
+ public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final int CURVE_A= -3;
+ public static final int CURVE_B_I= 121243;
+ public static final long[] CURVE_B= {0x1D99BL,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L,0x0L};
+ public static final long[] CURVE_Order= {0xE153F390433555DL,0x568B36607CD243CL,0x258ED97D0BDC63BL,0xA4FB94E7831B4FCL,0xFFFFFFFFFFF5B3CL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFFL,0xFFFFFFFFL};
+ public static final long[] CURVE_Gx= {0xC8287958CABAE57L,0x5D60137D6F5DE2DL,0x94286255615831DL,0xA151076B359E937L,0xC25306D9F95021L,0x3BB501F6854506EL,0x2A03D3B5298CAD8L,0x141D0A93DA2B700L,0x3AC03447L};
+ public static final long[] CURVE_Gy= {0x3A08760383527A6L,0x2B5C1E4CFD0FE92L,0x1A840B25A5602CFL,0x15DA8B0EEDE9C12L,0x60C7BD14F14A284L,0xDEABBCBB8C8F4B2L,0xC63EBB1004B97DBL,0x29AD56B3CE0EEEDL,0x943A54CAL};
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RAND.java b/src/main/java/org/apache/milagro/amcl/RAND.java
new file mode 100644
index 0000000..7ba7064
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RAND.java
@@ -0,0 +1,163 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/*
+ * Cryptographic strong random number generator
+ *
+ * Unguessable seed -> SHA -> PRNG internal state -> SHA -> random numbers
+ * Slow - but secure
+ *
+ * See ftp://ftp.rsasecurity.com/pub/pdfs/bull-1.pdf for a justification
+ */
+
+/* Marsaglia & Zaman Random number generator constants */
+
+
+package org.apache.milagro.amcl;
+
+public class RAND {
+/* Cryptographically strong pseudo-random number generator */
+
+ private static final int NK=21;
+ private static final int NJ=6;
+ private static final int NV=8;
+ private int[] ira=new int[NK]; /* random number... */
+ private int rndptr; /* ...array & pointer */
+ private int borrow;
+ private int pool_ptr;
+ private byte[] pool=new byte[32]; /* random pool */
+
+ public RAND()
+ {
+ clean();
+ }
+
+ private int sbrand()
+ { /* Marsaglia & Zaman random number generator */
+ int i,k;
+ long pdiff,t;
+
+ rndptr++;
+ if (rndptr<NK) return ira[rndptr];
+ rndptr=0;
+ for (i=0,k=NK-NJ;i<NK;i++,k++)
+ { /* calculate next NK values */
+ if (k==NK) k=0;
+ t=((long)ira[k])&0xffffffffL;
+ pdiff=(t - (((long)ira[i])&0xffffffffL) - (long)borrow)&0xffffffffL;
+ if (pdiff<t) borrow=0;
+ if (pdiff>t) borrow=1;
+ ira[i]=(int)(pdiff&0xffffffffL);
+ }
+
+ return ira[0];
+ }
+
+ public void sirand(int seed)
+ {
+ int i,in;
+ int t,m=1;
+ borrow=0;
+ rndptr=0;
+ ira[0]^=seed;
+ for (i=1;i<NK;i++)
+ { /* fill initialisation vector */
+ in=(NV*i)%NK;
+ ira[in]^=m; /* note XOR */
+ t=m;
+ m=seed-m;
+ seed=t;
+ }
+ for (i=0;i<10000;i++) sbrand(); /* "warm-up" & stir the generator */
+ }
+
+ private void fill_pool()
+ {
+ HASH256 sh=new HASH256();
+ for (int i=0;i<128;i++) sh.process(sbrand());
+ pool=sh.hash();
+ pool_ptr=0;
+ }
+
+ private static int pack(byte[] b)
+ { /* pack 4 bytes into a 32-bit Word */
+ return ((((int)b[3])&0xff)<<24)|(((int)b[2]&0xff)<<16)|(((int)b[1]&0xff)<<8)|((int)b[0]&0xff);
+ }
+
+/* Initialize RNG with some real entropy from some external source */
+ public void seed(int rawlen,byte[] raw)
+ { /* initialise from at least 128 byte string of raw random entropy */
+ int i;
+ byte [] digest;
+ byte [] b=new byte[4];
+ HASH256 sh=new HASH256();
+ pool_ptr=0;
+ for (i=0;i<NK;i++) ira[i]=0;
+ if (rawlen>0)
+ {
+ for (i=0;i<rawlen;i++)
+ sh.process(raw[i]);
+ digest=sh.hash();
+
+/* initialise PRNG from distilled randomness */
+
+ for (i=0;i<8;i++)
+ {
+ b[0]=digest[4*i]; b[1]=digest[4*i+1]; b[2]=digest[4*i+2]; b[3]=digest[4*i+3];
+ sirand(pack(b));
+ }
+ }
+ fill_pool();
+ }
+
+/* Terminate and clean up */
+ public void clean()
+ { /* kill internal state */
+ int i;
+ pool_ptr=rndptr=0;
+ for (i=0;i<32;i++) pool[i]=0;
+ for (i=0;i<NK;i++) ira[i]=0;
+ borrow=0;
+ }
+
+/* get random byte */
+ public int getByte()
+ {
+ int r;
+ r=pool[pool_ptr++];
+ if (pool_ptr>=32) fill_pool();
+ return (r&0xff);
+ }
+
+/* test main program */
+/*
+ public static void main(String[] args) {
+ int i;
+ byte[] raw=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (i=0;i<100;i++) raw[i]=(byte)i;
+
+ rng.seed(100,raw);
+
+ for (i=0;i<1000;i++)
+ System.out.format("%03d ",rng.getByte());
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA2048/BIG.java b/src/main/java/org/apache/milagro/amcl/RSA2048/BIG.java
new file mode 100644
index 0000000..fad9376
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA2048/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.RSA2048;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=128; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=58;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA2048/DBIG.java b/src/main/java/org/apache/milagro/amcl/RSA2048/DBIG.java
new file mode 100644
index 0000000..f12d682
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA2048/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.RSA2048;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA2048/FF.java b/src/main/java/org/apache/milagro/amcl/RSA2048/FF.java
new file mode 100644
index 0000000..9dd820c
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA2048/FF.java
@@ -0,0 +1,1028 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Large Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.RSA2048;
+import org.apache.milagro.amcl.RAND;
+
+public final class FF {
+
+/* RSA/DH modulus length as multiple of BIGBITS */
+ public static final int FFLEN=2;
+
+/* Don't Modify from here... */
+
+
+/* Finite field support - for RSA, DH etc. */
+ public static final int FF_BITS=(BIG.BIGBITS*FFLEN); /* Finite Field Size in bits - must be 256.2^n */
+ public static final int HFLEN=(FFLEN/2); /* Useful for half-size RSA private key operations */
+
+ public static final int P_MBITS=BIG.MODBYTES*8;
+ public static final int P_TBITS=(P_MBITS%BIG.BASEBITS);
+
+ private final BIG[] v;
+ private final int length;
+
+/**************** 64-bit specific ************************/
+
+ public static final long P_OMASK=((long)(-1)<<(P_MBITS%BIG.BASEBITS));
+ public static final long P_FEXCESS=((long)1<<(BIG.BASEBITS*BIG.NLEN-P_MBITS-1));
+
+ public static long EXCESS(BIG a)
+ {
+ return ((a.get(BIG.NLEN-1)&P_OMASK)>>(P_TBITS))+1;
+ }
+
+/* Check if product causes excess */
+ public static boolean pexceed(BIG a,BIG b)
+ {
+ long ea,eb;
+ ea=EXCESS(a);
+ eb=EXCESS(b);
+ if ((ea+1)>P_FEXCESS/(eb+1)) return true;
+ return false;
+ }
+
+/* Check if square causes excess */
+ public static boolean sexceed(BIG a)
+ {
+ long ea;
+ ea=EXCESS(a);
+ if ((ea+1)>P_FEXCESS/(ea+1)) return true;
+ return false;
+ }
+
+/******************************************************/
+
+/* Constructors */
+ public FF(int n)
+ {
+ v=new BIG[n];
+ for (int i=0;i<n;i++)
+ v[i]=new BIG(0);
+ length=n;
+ }
+
+ public int getlen()
+ {
+ return length;
+ }
+
+/* set to integer */
+ public void set(int m)
+ {
+ zero();
+ v[0].set(0,(m&BIG.BMASK));
+ v[0].set(1,(m>>BIG.BASEBITS));
+ }
+
+/* copy from FF b */
+ public void copy(FF b)
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].copy(b.v[i]);
+ }
+ }
+
+/* x=y<<n */
+ public void dsucopy(FF b)
+ {
+ for (int i=0;i<b.length;i++)
+ {
+ v[b.length+i].copy(b.v[i]);
+ v[i].zero();
+ }
+ }
+
+/* x=y */
+ public void dscopy(FF b)
+ {
+ for (int i=0;i<b.length;i++)
+ {
+ v[i].copy(b.v[i]);
+ v[b.length+i].zero();
+ }
+ }
+
+/* x=y>>n */
+ public void sducopy(FF b)
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].copy(b.v[length+i]);
+ }
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].zero();
+ }
+ }
+
+ public void one()
+ {
+ v[0].one();
+ for (int i=1;i<length;i++)
+ {
+ v[i].zero();
+ }
+ }
+
+/* test equals 0 */
+ public boolean iszilch()
+ {
+ for (int i=0;i<length;i++)
+ {
+ if (!v[i].iszilch()) return false;
+ }
+ return true;
+ }
+
+/* shift right by BIGBITS-bit words */
+ public void shrw(int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[i].copy(v[i+n]);
+ v[i+n].zero();
+ }
+ }
+
+/* shift left by BIGBITS-bit words */
+ public void shlw(int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[n+i].copy(v[i]);
+ v[i].zero();
+ }
+ }
+
+/* extract last bit */
+ public int parity()
+ {
+ return v[0].parity();
+ }
+
+ public int lastbits(int m)
+ {
+ return v[0].lastbits(m);
+ }
+
+/* compare x and y - must be normalised, and of same length */
+ public static int comp(FF a,FF b)
+ {
+ int i,j;
+ for (i=a.length-1;i>=0;i--)
+ {
+ j=BIG.comp(a.v[i],b.v[i]);
+ if (j!=0) return j;
+ }
+ return 0;
+ }
+
+/* recursive add */
+ public void radd(int vp,FF x,int xp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].copy(x.v[xp+i]);
+ v[vp+i].add(y.v[yp+i]);
+ }
+ }
+
+/* recursive inc */
+ public void rinc(int vp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].add(y.v[yp+i]);
+ }
+ }
+
+/* recursive sub */
+ public void rsub(int vp,FF x,int xp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].copy(x.v[xp+i]);
+ v[vp+i].sub(y.v[yp+i]);
+ }
+ }
+
+/* recursive dec */
+ public void rdec(int vp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].sub(y.v[yp+i]);
+ }
+ }
+
+/* simple add */
+ public void add(FF b)
+ {
+ for (int i=0;i<length;i++)
+ v[i].add(b.v[i]);
+ }
+
+/* simple sub */
+ public void sub(FF b)
+ {
+ for (int i=0;i<length;i++)
+ v[i].sub(b.v[i]);
+ }
+
+/* reverse sub */
+ public void revsub(FF b)
+ {
+ for (int i=0;i<length;i++)
+ v[i].rsub(b.v[i]);
+ }
+
+/* increment/decrement by a small integer */
+ public void inc(int m)
+ {
+ v[0].inc(m);
+ norm();
+ }
+
+ public void dec(int m)
+ {
+ v[0].dec(m);
+ norm();
+ }
+
+ /* normalise - but hold any overflow in top part unless n<0 */
+ private void rnorm(int vp,int n)
+ {
+ boolean trunc=false;
+ int i;
+ long carry;
+ if (n<0)
+ { /* -v n signals to do truncation */
+ n=-n;
+ trunc=true;
+ }
+ for (i=0;i<n-1;i++)
+ {
+ carry=v[vp+i].norm();
+ v[vp+i].xortop(carry<<P_TBITS);
+ v[vp+i+1].incl(carry);
+ }
+ carry=v[vp+n-1].norm();
+ if (trunc)
+ v[vp+n-1].xortop(carry<<P_TBITS);
+ }
+
+ public void norm()
+ {
+ rnorm(0,length);
+ }
+
+/* shift left by one bit */
+ public void shl()
+ {
+ int i,carry,delay_carry=0;
+ for (i=0;i<length-1;i++)
+ {
+ carry=v[i].fshl(1);
+ v[i].inc(delay_carry);
+ v[i].xortop((long)carry<<P_TBITS);
+ delay_carry=carry;
+ }
+ v[length-1].fshl(1);
+ v[length-1].inc(delay_carry);
+ }
+
+/* shift right by one bit */
+
+ public void shr()
+ {
+ int carry;
+ for (int i=length-1;i>0;i--)
+ {
+ carry=v[i].fshr(1);
+ v[i-1].xortop((long)carry<<P_TBITS);
+ }
+ v[0].fshr(1);
+ }
+
+/* Convert to Hex String */
+ public String toString()
+ {
+ norm();
+ String s="";
+ for (int i=length-1;i>=0;i--)
+ {
+ s+=v[i].toString(); //s+=" ";
+ }
+ return s;
+ }
+
+/*
+ public String toRawString(int len)
+ {
+ // norm(len);
+ String s="";
+ for (int i=len-1;i>=0;i--)
+ {
+ s+=v[i].toRawString(); s+=" ";
+ }
+ return s;
+ }
+*/
+/* Convert FFs to/from byte arrays */
+ public void toBytes(byte[] b)
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].tobytearray(b,(length-i-1)*BIG.MODBYTES);
+ }
+ }
+
+ public static void fromBytes(FF x,byte[] b)
+ {
+ for (int i=0;i<x.length;i++)
+ {
+ x.v[i]=BIG.frombytearray(b,(x.length-i-1)*BIG.MODBYTES);
+ }
+ }
+
+/* in-place swapping using xor - side channel resistant - lengths must be the same */
+ private static void cswap(FF a,FF b,int d)
+ {
+ for (int i=0;i<a.length;i++)
+ {
+ // BIG.cswap(a.v[i],b.v[i],d);
+ a.v[i].cswap(b.v[i],d);
+ }
+ }
+
+/* z=x*y, t is workspace */
+ private void karmul(int vp,FF x,int xp,FF y,int yp,FF t,int tp,int n)
+ {
+ int nd2;
+ if (n==1)
+ {
+ x.v[xp].norm();
+ y.v[yp].norm();
+ DBIG d=BIG.mul(x.v[xp],y.v[yp]);
+ v[vp+1]=d.split(8*BIG.MODBYTES);
+ v[vp].copy(d);
+ return;
+ }
+ nd2=n/2;
+ radd(vp,x,xp,x,xp+nd2,nd2);
+ rnorm(vp,nd2); /* Important - required for 32-bit build */
+ radd(vp+nd2,y,yp,y,yp+nd2,nd2);
+ rnorm(vp+nd2,nd2); /* Important - required for 32-bit build */
+
+ t.karmul(tp,this,vp,this,vp+nd2,t,tp+n,nd2);
+ karmul(vp,x,xp,y,yp,t,tp+n,nd2);
+ karmul(vp+n,x,xp+nd2,y,yp+nd2,t,tp+n,nd2);
+ t.rdec(tp,this,vp,n);
+ t.rdec(tp,this,vp+n,n);
+ rinc(vp+nd2,t,tp,n);
+ rnorm(vp,2*n);
+ }
+
+ private void karsqr(int vp,FF x,int xp,FF t,int tp,int n)
+ {
+ int nd2;
+ if (n==1)
+ {
+ x.v[xp].norm();
+ DBIG d=BIG.sqr(x.v[xp]);
+ v[vp+1].copy(d.split(8*BIG.MODBYTES));
+ v[vp].copy(d);
+ return;
+ }
+
+ nd2=n/2;
+ karsqr(vp,x,xp,t,tp+n,nd2);
+ karsqr(vp+n,x,xp+nd2,t,tp+n,nd2);
+ t.karmul(tp,x,xp,x,xp+nd2,t,tp+n,nd2);
+ rinc(vp+nd2,t,tp,n);
+ rinc(vp+nd2,t,tp,n);
+ rnorm(vp+nd2,n);
+ }
+
+
+ private void karmul_lower(int vp,FF x,int xp,FF y,int yp,FF t,int tp,int n)
+ { /* Calculates Least Significant bottom half of x*y */
+ int nd2;
+ if (n==1)
+ { /* only calculate bottom half of product */
+ v[vp].copy(BIG.smul(x.v[xp],y.v[yp]));
+ return;
+ }
+ nd2=n/2;
+ karmul(vp,x,xp,y,yp,t,tp+n,nd2);
+ t.karmul_lower(tp,x,xp+nd2,y,yp,t,tp+n,nd2);
+ rinc(vp+nd2,t,tp,nd2);
+ t.karmul_lower(tp,x,xp,y,yp+nd2,t,tp+n,nd2);
+
+ rinc(vp+nd2,t,tp,nd2);
+ rnorm(vp+nd2,-nd2); /* truncate it */
+ }
+
+ private void karmul_upper(FF x,FF y,FF t,int n)
+ { /* Calculates Most Significant upper half of x*y, given lower part */
+ int nd2;
+
+ nd2=n/2;
+ radd(n,x,0,x,nd2,nd2);
+ radd(n+nd2,y,0,y,nd2,nd2);
+ rnorm(n,nd2);
+ rnorm(n+nd2,nd2);
+
+ t.karmul(0,this,n+nd2,this,n,t,n,nd2); /* t = (a0+a1)(b0+b1) */
+ karmul(n,x,nd2,y,nd2,t,n,nd2); /* z[n]= a1*b1 */
+ /* z[0-nd2]=l(a0b0) z[nd2-n]= h(a0b0)+l(t)-l(a0b0)-l(a1b1) */
+ t.rdec(0,this,n,n); /* t=t-a1b1 */
+ rinc(nd2,this,0,nd2); /* z[nd2-n]+=l(a0b0) = h(a0b0)+l(t)-l(a1b1) */
+ rdec(nd2,t,0,nd2); /* z[nd2-n]=h(a0b0)+l(t)-l(a1b1)-l(t-a1b1)=h(a0b0) */
+ rnorm(0,-n); /* a0b0 now in z - truncate it */
+ t.rdec(0,this,0,n); /* (a0+a1)(b0+b1) - a0b0 */
+ rinc(nd2,t,0,n);
+
+ rnorm(nd2,n);
+ }
+
+ /* z=x*y. Assumes x and y are of same length. */
+ public static FF mul(FF x,FF y)
+ {
+ int n=x.length;
+ FF z=new FF(2*n);
+ FF t=new FF(2*n);
+// x.norm(); y.norm();
+ z.karmul(0,x,0,y,0,t,0,n);
+ return z;
+ }
+
+ /* z=x^2 */
+ public static FF sqr(FF x)
+ {
+ int n=x.length;
+ FF z=new FF(2*n);
+ FF t=new FF(2*n);
+// x.norm();
+ z.karsqr(0,x,0,t,0,n);
+ return z;
+ }
+
+/* return low part of product this*y */
+ public void lmul(FF y)
+ {
+ int n=length;
+ FF t=new FF(2*n);
+ FF x=new FF(n); x.copy(this);
+// x.norm(); y.norm();
+ karmul_lower(0,x,0,y,0,t,0,n);
+ }
+
+/* Set b=b mod c */
+ public void mod(FF c)
+ {
+ int k=0;
+
+ norm();
+ if (comp(this,c)<0)
+ return;
+ do
+ {
+ c.shl();
+ k++;
+ } while (comp(this,c)>=0);
+
+ while (k>0)
+ {
+ c.shr();
+ if (comp(this,c)>=0)
+ {
+ sub(c);
+ norm();
+ }
+ k--;
+ }
+ }
+
+/* return This mod modulus, N is modulus, ND is Montgomery Constant */
+ public FF reduce(FF N,FF ND)
+ { /* fast karatsuba Montgomery reduction */
+ int n=N.length;
+ FF t=new FF(2*n);
+ FF r=new FF(n);
+ FF m=new FF(n);
+
+ r.sducopy(this);
+ m.karmul_lower(0,this,0,ND,0,t,0,n);
+ karmul_upper(N,m,t,n);
+ m.sducopy(this);
+
+ r.add(N);
+ r.sub(m);
+ r.norm();
+
+ return r;
+ }
+
+/* Set r=this mod b */
+/* this is of length - 2*n */
+/* r,b is of length - n */
+ public FF dmod(FF b)
+ {
+ int k,n=b.length;
+ FF m=new FF(2*n);
+ FF x=new FF(2*n);
+ FF r=new FF(n);
+
+ x.copy(this);
+ x.norm();
+ m.dsucopy(b); k=BIG.BIGBITS*n;
+
+ while (comp(x,m)>=0)
+ {
+ x.sub(m);
+ x.norm();
+ }
+
+ while (k>0)
+ {
+ m.shr();
+
+ if (comp(x,m)>=0)
+ {
+ x.sub(m);
+ x.norm();
+ }
+ k--;
+ }
+
+ r.copy(x);
+ r.mod(b);
+ return r;
+ }
+
+/* Set return=1/this mod p. Binary method - a<p on entry */
+
+ public void invmodp(FF p)
+ {
+ int n=p.length;
+
+ FF u=new FF(n);
+ FF v=new FF(n);
+ FF x1=new FF(n);
+ FF x2=new FF(n);
+ FF t=new FF(n);
+ FF one=new FF(n);
+
+ one.one();
+ u.copy(this);
+ v.copy(p);
+ x1.copy(one);
+ x2.zero();
+
+ // reduce n in here as well!
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.shr();
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.shr();
+ }
+ while (v.parity()==0)
+ {
+ v.shr();
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.shr();
+ }
+ if (comp(u,v)>=0)
+ {
+
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0)
+ copy(x1);
+ else
+ copy(x2);
+ }
+
+/* nresidue mod m */
+ public void nres(FF m)
+ {
+ int n=m.length;
+ if (n==1)
+ {
+ DBIG d=new DBIG(this.v[0]);
+ d.shl(BIG.NLEN*BIG.BASEBITS);
+ this.v[0].copy(d.mod(m.v[0]));
+ }
+ else
+ {
+ FF d=new FF(2*n);
+ d.dsucopy(this);
+ copy(d.dmod(m));
+ }
+ }
+
+ public void redc(FF m,FF ND)
+ {
+ int n=m.length;
+ if (n==1)
+ {
+ DBIG d=new DBIG(this.v[0]);
+ this.v[0].copy(BIG.monty(m.v[0],(BIG.cast_to_chunk(1)<<BIG.BASEBITS)-ND.v[0].w[0],d));
+ }
+ else
+ {
+ FF d=new FF(2*n);
+ mod(m);
+ d.dscopy(this);
+ copy(d.reduce(m,ND));
+ mod(m);
+ }
+ }
+
+ private void mod2m(int m)
+ {
+ for (int i=m;i<length;i++)
+ v[i].zero();
+ }
+
+ /* U=1/a mod 2^m - Arazi & Qi */
+ private FF invmod2m()
+ {
+ int i,n=length;
+
+ FF b=new FF(n);
+ FF c=new FF(n);
+ FF U=new FF(n);
+ FF t;
+
+ U.zero();
+ U.v[0].copy(v[0]);
+ U.v[0].invmod2m();
+
+ for (i=1;i<n;i<<=1)
+ {
+ b.copy(this); b.mod2m(i);
+ t=mul(U,b);
+
+ t.shrw(i); b.copy(t);
+ c.copy(this); c.shrw(i); c.mod2m(i);
+ c.lmul(U); c.mod2m(i);
+
+ b.add(c); b.norm();
+ b.lmul(U); b.mod2m(i);
+
+ c.one(); c.shlw(i); b.revsub(c); b.norm();
+ b.shlw(i);
+ U.add(b);
+ }
+ U.norm();
+ return U;
+ }
+
+ public void random(RAND rng)
+ {
+ int n=length;
+ for (int i=0;i<n;i++)
+ {
+ v[i].copy(BIG.random(rng));
+ }
+ /* make sure top bit is 1 */
+ while (v[n-1].nbits()<BIG.MODBYTES*8) v[n-1].copy(BIG.random(rng));
+ }
+
+ /* generate random x */
+ public void randomnum(FF p,RAND rng)
+ {
+ int n=length;
+ FF d=new FF(2*n);
+
+ for (int i=0;i<2*n;i++)
+ {
+ d.v[i].copy(BIG.random(rng));
+ }
+ copy(d.dmod(p));
+ }
+
+ /* this*=y mod p */
+ public void modmul(FF y,FF p,FF nd)
+ {
+ if (pexceed(v[length-1],y.v[y.length-1])) mod(p);
+ int n=p.length;
+ if (n==1)
+ {
+ DBIG d=BIG.mul(this.v[0],y.v[0]);
+ this.v[0].copy(BIG.monty(p.v[0],(BIG.cast_to_chunk(1)<<BIG.BASEBITS)-nd.v[0].w[0],d));
+ }
+ else
+ {
+ FF d=mul(this,y);
+ copy(d.reduce(p,nd));
+ }
+ }
+
+ /* this*=y mod p */
+ public void modsqr(FF p,FF nd)
+ {
+ if (sexceed(v[length-1])) mod(p);
+ int n=p.length;
+ if (n==1)
+ {
+ DBIG d=BIG.sqr(this.v[0]);
+ this.v[0].copy(BIG.monty(p.v[0],(BIG.cast_to_chunk(1)<<BIG.BASEBITS)-nd.v[0].w[0],d));
+
+ }
+ else
+ {
+ FF d=sqr(this);
+ copy(d.reduce(p,nd));
+ }
+ }
+
+ /* this=this^e mod p using side-channel resistant Montgomery Ladder, for large e */
+ public void skpow(FF e,FF p)
+ {
+ int i,b,n=p.length;
+ FF R0=new FF(n);
+ FF R1=new FF(n);
+ FF ND=p.invmod2m();
+
+ mod(p);
+ R0.one();
+ R1.copy(this);
+ R0.nres(p);
+ R1.nres(p);
+
+ for (i=8*BIG.MODBYTES*n-1;i>=0;i--)
+ {
+ b=e.v[i/BIG.BIGBITS].bit(i%BIG.BIGBITS);
+ copy(R0);
+ modmul(R1,p,ND);
+
+ cswap(R0,R1,b);
+ R0.modsqr(p,ND);
+
+ R1.copy(this);
+ cswap(R0,R1,b);
+ }
+ copy(R0);
+ redc(p,ND);
+ }
+
+ /* this =this^e mod p using side-channel resistant Montgomery Ladder, for short e */
+ public void skpow(BIG e,FF p)
+ {
+ int i,b,n=p.length;
+ FF R0=new FF(n);
+ FF R1=new FF(n);
+ FF ND=p.invmod2m();
+
+ mod(p);
+ R0.one();
+ R1.copy(this);
+ R0.nres(p);
+ R1.nres(p);
+
+ for (i=8*BIG.MODBYTES-1;i>=0;i--)
+ {
+ b=e.bit(i);
+ copy(R0);
+ modmul(R1,p,ND);
+
+ cswap(R0,R1,b);
+ R0.modsqr(p,ND);
+
+ R1.copy(this);
+ cswap(R0,R1,b);
+ }
+ copy(R0);
+ redc(p,ND);
+ }
+
+ /* raise to an integer power - right-to-left method */
+ public void power(int e,FF p)
+ {
+ int n=p.length;
+ FF w=new FF(n);
+ FF ND=p.invmod2m();
+ boolean f=true;
+
+ w.copy(this);
+ w.nres(p);
+
+ if (e==2)
+ {
+ copy(w);
+ modsqr(p,ND);
+ }
+ else for (; ; )
+ {
+ if (e%2==1)
+ {
+ if (f) copy(w);
+ else modmul(w,p,ND);
+ f=false;
+ }
+ e>>=1;
+ if (e==0) break;
+ w.modsqr(p,ND);
+ }
+ redc(p,ND);
+ }
+
+ /* this=this^e mod p, faster but not side channel resistant */
+ public void pow(FF e,FF p)
+ {
+ int i,b,n=p.length;
+ FF w=new FF(n);
+ FF ND=p.invmod2m();
+
+ w.copy(this);
+ one();
+ nres(p);
+ w.nres(p);
+ for (i=8*BIG.MODBYTES*n-1;i>=0;i--)
+ {
+ modsqr(p,ND);
+ b=e.v[i/BIG.BIGBITS].bit(i%BIG.BIGBITS);
+ if (b==1) modmul(w,p,ND);
+ }
+ redc(p,ND);
+ }
+
+ /* double exponentiation r=x^e.y^f mod p */
+ public void pow2(BIG e,FF y,BIG f,FF p)
+ {
+ int i,eb,fb,n=p.length;
+ FF xn=new FF(n);
+ FF yn=new FF(n);
+ FF xy=new FF(n);
+ FF ND=p.invmod2m();
+
+ xn.copy(this);
+ yn.copy(y);
+ xn.nres(p);
+ yn.nres(p);
+ xy.copy(xn); xy.modmul(yn,p,ND);
+ one();
+ nres(p);
+
+ for (i=8*BIG.MODBYTES-1;i>=0;i--)
+ {
+ eb=e.bit(i);
+ fb=f.bit(i);
+ modsqr(p,ND);
+ if (eb==1)
+ {
+ if (fb==1) modmul(xy,p,ND);
+ else modmul(xn,p,ND);
+ }
+ else
+ {
+ if (fb==1) modmul(yn,p,ND);
+ }
+ }
+ redc(p,ND);
+ }
+
+ private static int igcd(int x,int y)
+ { /* integer GCD, returns GCD of x and y */
+ int r;
+ if (y==0) return x;
+ while ((r=x%y)!=0)
+ {x=y;y=r;}
+ return y;
+ }
+
+ /* quick and dirty check for common factor with n */
+ public boolean cfactor(int s)
+ {
+ int r,n=length;
+ int g;
+
+ FF x=new FF(n);
+ FF y=new FF(n);
+
+ y.set(s);
+ x.copy(this);
+ x.norm();
+
+ do
+ {
+ x.sub(y);
+ x.norm();
+ while (!x.iszilch() && x.parity()==0) x.shr();
+ }
+ while (comp(x,y)>0);
+
+ g=(int)x.v[0].get(0);
+ r=igcd(s,g);
+ if (r>1) return true;
+ return false;
+ }
+
+ /* Miller-Rabin test for primality. Slow. */
+ public static boolean prime(FF p,RAND rng)
+ {
+ int i,j,s=0,n=p.length;
+ boolean loop;
+ FF d=new FF(n);
+ FF x=new FF(n);
+ FF unity=new FF(n);
+ FF nm1=new FF(n);
+
+ int sf=4849845; /* 3*5*.. *19 */
+ p.norm();
+
+ if (p.cfactor(sf)) return false;
+ unity.one();
+ nm1.copy(p);
+ nm1.sub(unity);
+ nm1.norm();
+ d.copy(nm1);
+
+ while (d.parity()==0)
+ {
+ d.shr();
+ s++;
+ }
+ if (s==0) return false;
+ for (i=0;i<10;i++)
+ {
+ x.randomnum(p,rng);
+ x.pow(d,p);
+
+ if (comp(x,unity)==0 || comp(x,nm1)==0) continue;
+ loop=false;
+ for (j=1;j<s;j++)
+ {
+ x.power(2,p);
+ if (comp(x,unity)==0) return false;
+ if (comp(x,nm1)==0) {loop=true; break;}
+ }
+ if (loop) continue;
+ return false;
+ }
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/RSA2048/RSA.java b/src/main/java/org/apache/milagro/amcl/RSA2048/RSA.java
new file mode 100644
index 0000000..73609d0
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA2048/RSA.java
@@ -0,0 +1,369 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* RSA API high-level functions */
+
+package org.apache.milagro.amcl.RSA2048;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+
+public final class RSA {
+
+ public static final int RFS=BIG.MODBYTES*FF.FFLEN;
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=SHA256;
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,byte[] A,int n)
+ {
+ byte[] R=null;
+
+ if (sha==SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (A!=null) H.process_array(A);
+ if (n>=0) H.process_num(n);
+ R=H.hash();
+ }
+ if (sha==SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (A!=null) H.process_array(A);
+ if (n>=0) H.process_num(n);
+ R=H.hash();
+ }
+ if (sha==SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (A!=null) H.process_array(A);
+ if (n>=0) H.process_num(n);
+ R=H.hash();
+ }
+ return R;
+ }
+
+/* generate an RSA key pair */
+
+ public static void KEY_PAIR(RAND rng,int e,private_key PRIV,public_key PUB)
+ { /* IEEE1363 A16.11/A16.12 more or less */
+
+ int n=PUB.n.getlen()/2;
+ FF t = new FF(n);
+ FF p1=new FF(n);
+ FF q1=new FF(n);
+
+ for (;;)
+ {
+ PRIV.p.random(rng);
+ while (PRIV.p.lastbits(2)!=3) PRIV.p.inc(1);
+ while (!FF.prime(PRIV.p,rng)) PRIV.p.inc(4);
+
+ p1.copy(PRIV.p);
+ p1.dec(1);
+
+ if (p1.cfactor(e)) continue;
+ break;
+ }
+
+ for (;;)
+ {
+ PRIV.q.random(rng);
+ while (PRIV.q.lastbits(2)!=3) PRIV.q.inc(1);
+ while (!FF.prime(PRIV.q,rng)) PRIV.q.inc(4);
+
+ q1.copy(PRIV.q);
+ q1.dec(1);
+
+ if (q1.cfactor(e)) continue;
+
+ break;
+ }
+
+ PUB.n=FF.mul(PRIV.p,PRIV.q);
+ PUB.e=e;
+
+ t.copy(p1);
+ t.shr();
+ PRIV.dp.set(e);
+ PRIV.dp.invmodp(t);
+ if (PRIV.dp.parity()==0) PRIV.dp.add(t);
+ PRIV.dp.norm();
+
+ t.copy(q1);
+ t.shr();
+ PRIV.dq.set(e);
+ PRIV.dq.invmodp(t);
+ if (PRIV.dq.parity()==0) PRIV.dq.add(t);
+ PRIV.dq.norm();
+
+ PRIV.c.copy(PRIV.p);
+ PRIV.c.invmodp(PRIV.q);
+
+ return;
+ }
+
+/* Mask Generation Function */
+
+ public static void MGF1(int sha,byte[] Z,int olen,byte[] K)
+ {
+ int hlen=sha;
+ byte[] B;
+
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ }
+
+ public static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+
+/* SHARSA2048 identifier strings */
+ private static final byte[] SHA256ID={0x30,0x31,0x30,0x0d,0x06,0x09,0x60,(byte)0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20};
+ private static final byte[] SHA384ID={0x30,0x41,0x30,0x0d,0x06,0x09,0x60,(byte)0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,0x30};
+ private static final byte[] SHA512ID={0x30,0x51,0x30,0x0d,0x06,0x09,0x60,(byte)0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40};
+
+/* PKCS 1.5 padding of a message to be signed */
+
+ public static boolean PKCS15(int sha,byte[] m,byte[] w)
+ {
+ int olen=FF.FF_BITS/8;
+ int i,hlen=sha;
+ int idlen=19;
+
+ if (olen<idlen+hlen+10) return false;
+ byte[] H=hashit(sha,m,-1);
+
+ for (i=0;i<w.length;i++) w[i]=0;
+ i=0;
+ w[i++]=0;
+ w[i++]=1;
+ for (int j=0;j<olen-idlen-hlen-3;j++)
+ w[i++]=(byte)0xff;
+ w[i++]=0;
+
+
+ if (hlen==SHA256) for (int j=0;j<idlen;j++) w[i++]=SHA256ID[j];
+ if (hlen==SHA384) for (int j=0;j<idlen;j++) w[i++]=SHA384ID[j];
+ if (hlen==SHA512) for (int j=0;j<idlen;j++) w[i++]=SHA512ID[j];
+
+ for (int j=0;j<hlen;j++)
+ w[i++]=H[j];
+
+ return true;
+ }
+
+
+ /* OAEP Message Encoding for Encryption */
+ public static byte[] OAEP_ENCODE(int sha,byte[] m,RAND rng,byte[] p)
+ {
+ int i,slen,olen=RFS-1;
+ int mlen=m.length;
+ int hlen,seedlen;
+ byte[] f=new byte[RFS];
+
+ hlen=sha;
+ byte[] SEED=new byte[hlen];
+ seedlen=hlen;
+
+ if (mlen>olen-hlen-seedlen-1) return new byte[0];
+
+ byte[] DBMASK=new byte[olen-seedlen];
+
+ byte[] h=hashit(sha,p,-1);
+
+ for (i=0;i<hlen;i++) f[i]=h[i];
+
+ slen=olen-mlen-hlen-seedlen-1;
+
+ for (i=0;i<slen;i++) f[hlen+i]=0;
+ f[hlen+slen]=1;
+ for (i=0;i<mlen;i++) f[hlen+slen+1+i]=m[i];
+
+ for (i=0;i<seedlen;i++) SEED[i]=(byte)rng.getByte();
+
+ MGF1(sha,SEED,olen-seedlen,DBMASK);
+
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]^=f[i];
+
+ MGF1(sha,DBMASK,seedlen,f);
+
+ for (i=0;i<seedlen;i++) f[i]^=SEED[i];
+
+ for (i=0;i<olen-seedlen;i++) f[i+seedlen]=DBMASK[i];
+
+ /* pad to length RFS */
+ int d=1;
+ for (i=RFS-1;i>=d;i--)
+ f[i]=f[i-d];
+ for (i=d-1;i>=0;i--)
+ f[i]=0;
+
+ return f;
+ }
+
+ /* OAEP Message Decoding for Decryption */
+ public static byte[] OAEP_DECODE(int sha,byte[] p,byte[] f)
+ {
+ int x,t;
+ boolean comp;
+ int i,k,olen=RFS-1;
+ int hlen,seedlen;
+
+ hlen=sha;
+ byte[] SEED=new byte[hlen];
+ seedlen=hlen;
+ byte[] CHASH=new byte[hlen];
+
+ if (olen<seedlen+hlen+1) return new byte[0];
+ byte[] DBMASK=new byte[olen-seedlen];
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]=0;
+
+ if (f.length<RFS)
+ {
+ int d=RFS-f.length;
+ for (i=RFS-1;i>=d;i--)
+ f[i]=f[i-d];
+ for (i=d-1;i>=0;i--)
+ f[i]=0;
+
+ }
+
+ byte[] h=hashit(sha,p,-1);
+
+ for (i=0;i<hlen;i++) CHASH[i]=h[i];
+
+ x=f[0];
+
+ for (i=seedlen;i<olen;i++)
+ DBMASK[i-seedlen]=f[i+1];
+
+ MGF1(sha,DBMASK,seedlen,SEED);
+ for (i=0;i<seedlen;i++) SEED[i]^=f[i+1];
+ MGF1(sha,SEED,olen-seedlen,f);
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]^=f[i];
+
+ comp=true;
+ for (i=0;i<hlen;i++)
+ {
+ if (CHASH[i]!=DBMASK[i]) comp=false;
+ }
+
+ for (i=0;i<olen-seedlen-hlen;i++)
+ DBMASK[i]=DBMASK[i+hlen];
+
+ for (i=0;i<hlen;i++)
+ SEED[i]=CHASH[i]=0;
+
+ for (k=0;;k++)
+ {
+ if (k>=olen-seedlen-hlen) return new byte[0];
+ if (DBMASK[k]!=0) break;
+ }
+
+ t=DBMASK[k];
+ if (!comp || x!=0 || t!=0x01)
+ {
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]=0;
+ return new byte[0];
+ }
+
+ byte[] r=new byte[olen-seedlen-hlen-k-1];
+
+ for (i=0;i<olen-seedlen-hlen-k-1;i++)
+ r[i]=DBMASK[i+k+1];
+
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]=0;
+
+ return r;
+ }
+
+ /* destroy the Private Key structure */
+ public static void PRIVATE_KEY_KILL(private_key PRIV)
+ {
+ PRIV.p.zero();
+ PRIV.q.zero();
+ PRIV.dp.zero();
+ PRIV.dq.zero();
+ PRIV.c.zero();
+ }
+
+ /* RSA encryption with the public key */
+ public static void ENCRYPT(public_key PUB,byte[] F,byte[] G)
+ {
+ int n=PUB.n.getlen();
+ FF f=new FF(n);
+ FF.fromBytes(f,F);
+ f.power(PUB.e,PUB.n);
+ f.toBytes(G);
+ }
+
+ /* RSA decryption with the private key */
+ public static void DECRYPT(private_key PRIV,byte[] G,byte[] F)
+ {
+ int n=PRIV.p.getlen();
+ FF g=new FF(2*n);
+
+ FF.fromBytes(g,G);
+ FF jp=g.dmod(PRIV.p);
+ FF jq=g.dmod(PRIV.q);
+
+ jp.skpow(PRIV.dp,PRIV.p);
+ jq.skpow(PRIV.dq,PRIV.q);
+
+ g.zero();
+ g.dscopy(jp);
+ jp.mod(PRIV.q);
+ if (FF.comp(jp,jq)>0) jq.add(PRIV.q);
+ jq.sub(jp);
+ jq.norm();
+
+ FF t=FF.mul(PRIV.c,jq);
+ jq=t.dmod(PRIV.q);
+
+ t=FF.mul(jq,PRIV.p);
+ g.add(t);
+ g.norm();
+
+ g.toBytes(F);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA2048/private_key.java b/src/main/java/org/apache/milagro/amcl/RSA2048/private_key.java
new file mode 100644
index 0000000..359bf81
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA2048/private_key.java
@@ -0,0 +1,16 @@
+
+package org.apache.milagro.amcl.RSA2048;
+
+public final class private_key
+{
+ public FF p,q,dp,dq,c;
+
+ public private_key(int n)
+ {
+ p=new FF(n);
+ q=new FF(n);
+ dp=new FF(n);
+ dq=new FF(n);
+ c=new FF(n);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/RSA2048/public_key.java b/src/main/java/org/apache/milagro/amcl/RSA2048/public_key.java
new file mode 100644
index 0000000..a08d884
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA2048/public_key.java
@@ -0,0 +1,14 @@
+
+package org.apache.milagro.amcl.RSA2048;
+
+public final class public_key
+{
+ public int e;
+ public FF n;
+
+ public public_key(int m)
+ {
+ e=0;
+ n=new FF(m);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA3072/BIG.java b/src/main/java/org/apache/milagro/amcl/RSA3072/BIG.java
new file mode 100644
index 0000000..7796c59
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA3072/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.RSA3072;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=48; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA3072/DBIG.java b/src/main/java/org/apache/milagro/amcl/RSA3072/DBIG.java
new file mode 100644
index 0000000..564f403
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA3072/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.RSA3072;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA3072/FF.java b/src/main/java/org/apache/milagro/amcl/RSA3072/FF.java
new file mode 100644
index 0000000..48d5e2f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA3072/FF.java
@@ -0,0 +1,1028 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Large Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.RSA3072;
+import org.apache.milagro.amcl.RAND;
+
+public final class FF {
+
+/* RSA/DH modulus length as multiple of BIGBITS */
+ public static final int FFLEN=8;
+
+/* Don't Modify from here... */
+
+
+/* Finite field support - for RSA, DH etc. */
+ public static final int FF_BITS=(BIG.BIGBITS*FFLEN); /* Finite Field Size in bits - must be 256.2^n */
+ public static final int HFLEN=(FFLEN/2); /* Useful for half-size RSA private key operations */
+
+ public static final int P_MBITS=BIG.MODBYTES*8;
+ public static final int P_TBITS=(P_MBITS%BIG.BASEBITS);
+
+ private final BIG[] v;
+ private final int length;
+
+/**************** 64-bit specific ************************/
+
+ public static final long P_OMASK=((long)(-1)<<(P_MBITS%BIG.BASEBITS));
+ public static final long P_FEXCESS=((long)1<<(BIG.BASEBITS*BIG.NLEN-P_MBITS-1));
+
+ public static long EXCESS(BIG a)
+ {
+ return ((a.get(BIG.NLEN-1)&P_OMASK)>>(P_TBITS))+1;
+ }
+
+/* Check if product causes excess */
+ public static boolean pexceed(BIG a,BIG b)
+ {
+ long ea,eb;
+ ea=EXCESS(a);
+ eb=EXCESS(b);
+ if ((ea+1)>P_FEXCESS/(eb+1)) return true;
+ return false;
+ }
+
+/* Check if square causes excess */
+ public static boolean sexceed(BIG a)
+ {
+ long ea;
+ ea=EXCESS(a);
+ if ((ea+1)>P_FEXCESS/(ea+1)) return true;
+ return false;
+ }
+
+/******************************************************/
+
+/* Constructors */
+ public FF(int n)
+ {
+ v=new BIG[n];
+ for (int i=0;i<n;i++)
+ v[i]=new BIG(0);
+ length=n;
+ }
+
+ public int getlen()
+ {
+ return length;
+ }
+
+/* set to integer */
+ public void set(int m)
+ {
+ zero();
+ v[0].set(0,(m&BIG.BMASK));
+ v[0].set(1,(m>>BIG.BASEBITS));
+ }
+
+/* copy from FF b */
+ public void copy(FF b)
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].copy(b.v[i]);
+ }
+ }
+
+/* x=y<<n */
+ public void dsucopy(FF b)
+ {
+ for (int i=0;i<b.length;i++)
+ {
+ v[b.length+i].copy(b.v[i]);
+ v[i].zero();
+ }
+ }
+
+/* x=y */
+ public void dscopy(FF b)
+ {
+ for (int i=0;i<b.length;i++)
+ {
+ v[i].copy(b.v[i]);
+ v[b.length+i].zero();
+ }
+ }
+
+/* x=y>>n */
+ public void sducopy(FF b)
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].copy(b.v[length+i]);
+ }
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].zero();
+ }
+ }
+
+ public void one()
+ {
+ v[0].one();
+ for (int i=1;i<length;i++)
+ {
+ v[i].zero();
+ }
+ }
+
+/* test equals 0 */
+ public boolean iszilch()
+ {
+ for (int i=0;i<length;i++)
+ {
+ if (!v[i].iszilch()) return false;
+ }
+ return true;
+ }
+
+/* shift right by BIGBITS-bit words */
+ public void shrw(int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[i].copy(v[i+n]);
+ v[i+n].zero();
+ }
+ }
+
+/* shift left by BIGBITS-bit words */
+ public void shlw(int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[n+i].copy(v[i]);
+ v[i].zero();
+ }
+ }
+
+/* extract last bit */
+ public int parity()
+ {
+ return v[0].parity();
+ }
+
+ public int lastbits(int m)
+ {
+ return v[0].lastbits(m);
+ }
+
+/* compare x and y - must be normalised, and of same length */
+ public static int comp(FF a,FF b)
+ {
+ int i,j;
+ for (i=a.length-1;i>=0;i--)
+ {
+ j=BIG.comp(a.v[i],b.v[i]);
+ if (j!=0) return j;
+ }
+ return 0;
+ }
+
+/* recursive add */
+ public void radd(int vp,FF x,int xp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].copy(x.v[xp+i]);
+ v[vp+i].add(y.v[yp+i]);
+ }
+ }
+
+/* recursive inc */
+ public void rinc(int vp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].add(y.v[yp+i]);
+ }
+ }
+
+/* recursive sub */
+ public void rsub(int vp,FF x,int xp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].copy(x.v[xp+i]);
+ v[vp+i].sub(y.v[yp+i]);
+ }
+ }
+
+/* recursive dec */
+ public void rdec(int vp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].sub(y.v[yp+i]);
+ }
+ }
+
+/* simple add */
+ public void add(FF b)
+ {
+ for (int i=0;i<length;i++)
+ v[i].add(b.v[i]);
+ }
+
+/* simple sub */
+ public void sub(FF b)
+ {
+ for (int i=0;i<length;i++)
+ v[i].sub(b.v[i]);
+ }
+
+/* reverse sub */
+ public void revsub(FF b)
+ {
+ for (int i=0;i<length;i++)
+ v[i].rsub(b.v[i]);
+ }
+
+/* increment/decrement by a small integer */
+ public void inc(int m)
+ {
+ v[0].inc(m);
+ norm();
+ }
+
+ public void dec(int m)
+ {
+ v[0].dec(m);
+ norm();
+ }
+
+ /* normalise - but hold any overflow in top part unless n<0 */
+ private void rnorm(int vp,int n)
+ {
+ boolean trunc=false;
+ int i;
+ long carry;
+ if (n<0)
+ { /* -v n signals to do truncation */
+ n=-n;
+ trunc=true;
+ }
+ for (i=0;i<n-1;i++)
+ {
+ carry=v[vp+i].norm();
+ v[vp+i].xortop(carry<<P_TBITS);
+ v[vp+i+1].incl(carry);
+ }
+ carry=v[vp+n-1].norm();
+ if (trunc)
+ v[vp+n-1].xortop(carry<<P_TBITS);
+ }
+
+ public void norm()
+ {
+ rnorm(0,length);
+ }
+
+/* shift left by one bit */
+ public void shl()
+ {
+ int i,carry,delay_carry=0;
+ for (i=0;i<length-1;i++)
+ {
+ carry=v[i].fshl(1);
+ v[i].inc(delay_carry);
+ v[i].xortop((long)carry<<P_TBITS);
+ delay_carry=carry;
+ }
+ v[length-1].fshl(1);
+ v[length-1].inc(delay_carry);
+ }
+
+/* shift right by one bit */
+
+ public void shr()
+ {
+ int carry;
+ for (int i=length-1;i>0;i--)
+ {
+ carry=v[i].fshr(1);
+ v[i-1].xortop((long)carry<<P_TBITS);
+ }
+ v[0].fshr(1);
+ }
+
+/* Convert to Hex String */
+ public String toString()
+ {
+ norm();
+ String s="";
+ for (int i=length-1;i>=0;i--)
+ {
+ s+=v[i].toString(); //s+=" ";
+ }
+ return s;
+ }
+
+/*
+ public String toRawString(int len)
+ {
+ // norm(len);
+ String s="";
+ for (int i=len-1;i>=0;i--)
+ {
+ s+=v[i].toRawString(); s+=" ";
+ }
+ return s;
+ }
+*/
+/* Convert FFs to/from byte arrays */
+ public void toBytes(byte[] b)
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].tobytearray(b,(length-i-1)*BIG.MODBYTES);
+ }
+ }
+
+ public static void fromBytes(FF x,byte[] b)
+ {
+ for (int i=0;i<x.length;i++)
+ {
+ x.v[i]=BIG.frombytearray(b,(x.length-i-1)*BIG.MODBYTES);
+ }
+ }
+
+/* in-place swapping using xor - side channel resistant - lengths must be the same */
+ private static void cswap(FF a,FF b,int d)
+ {
+ for (int i=0;i<a.length;i++)
+ {
+ // BIG.cswap(a.v[i],b.v[i],d);
+ a.v[i].cswap(b.v[i],d);
+ }
+ }
+
+/* z=x*y, t is workspace */
+ private void karmul(int vp,FF x,int xp,FF y,int yp,FF t,int tp,int n)
+ {
+ int nd2;
+ if (n==1)
+ {
+ x.v[xp].norm();
+ y.v[yp].norm();
+ DBIG d=BIG.mul(x.v[xp],y.v[yp]);
+ v[vp+1]=d.split(8*BIG.MODBYTES);
+ v[vp].copy(d);
+ return;
+ }
+ nd2=n/2;
+ radd(vp,x,xp,x,xp+nd2,nd2);
+ rnorm(vp,nd2); /* Important - required for 32-bit build */
+ radd(vp+nd2,y,yp,y,yp+nd2,nd2);
+ rnorm(vp+nd2,nd2); /* Important - required for 32-bit build */
+
+ t.karmul(tp,this,vp,this,vp+nd2,t,tp+n,nd2);
+ karmul(vp,x,xp,y,yp,t,tp+n,nd2);
+ karmul(vp+n,x,xp+nd2,y,yp+nd2,t,tp+n,nd2);
+ t.rdec(tp,this,vp,n);
+ t.rdec(tp,this,vp+n,n);
+ rinc(vp+nd2,t,tp,n);
+ rnorm(vp,2*n);
+ }
+
+ private void karsqr(int vp,FF x,int xp,FF t,int tp,int n)
+ {
+ int nd2;
+ if (n==1)
+ {
+ x.v[xp].norm();
+ DBIG d=BIG.sqr(x.v[xp]);
+ v[vp+1].copy(d.split(8*BIG.MODBYTES));
+ v[vp].copy(d);
+ return;
+ }
+
+ nd2=n/2;
+ karsqr(vp,x,xp,t,tp+n,nd2);
+ karsqr(vp+n,x,xp+nd2,t,tp+n,nd2);
+ t.karmul(tp,x,xp,x,xp+nd2,t,tp+n,nd2);
+ rinc(vp+nd2,t,tp,n);
+ rinc(vp+nd2,t,tp,n);
+ rnorm(vp+nd2,n);
+ }
+
+
+ private void karmul_lower(int vp,FF x,int xp,FF y,int yp,FF t,int tp,int n)
+ { /* Calculates Least Significant bottom half of x*y */
+ int nd2;
+ if (n==1)
+ { /* only calculate bottom half of product */
+ v[vp].copy(BIG.smul(x.v[xp],y.v[yp]));
+ return;
+ }
+ nd2=n/2;
+ karmul(vp,x,xp,y,yp,t,tp+n,nd2);
+ t.karmul_lower(tp,x,xp+nd2,y,yp,t,tp+n,nd2);
+ rinc(vp+nd2,t,tp,nd2);
+ t.karmul_lower(tp,x,xp,y,yp+nd2,t,tp+n,nd2);
+
+ rinc(vp+nd2,t,tp,nd2);
+ rnorm(vp+nd2,-nd2); /* truncate it */
+ }
+
+ private void karmul_upper(FF x,FF y,FF t,int n)
+ { /* Calculates Most Significant upper half of x*y, given lower part */
+ int nd2;
+
+ nd2=n/2;
+ radd(n,x,0,x,nd2,nd2);
+ radd(n+nd2,y,0,y,nd2,nd2);
+ rnorm(n,nd2);
+ rnorm(n+nd2,nd2);
+
+ t.karmul(0,this,n+nd2,this,n,t,n,nd2); /* t = (a0+a1)(b0+b1) */
+ karmul(n,x,nd2,y,nd2,t,n,nd2); /* z[n]= a1*b1 */
+ /* z[0-nd2]=l(a0b0) z[nd2-n]= h(a0b0)+l(t)-l(a0b0)-l(a1b1) */
+ t.rdec(0,this,n,n); /* t=t-a1b1 */
+ rinc(nd2,this,0,nd2); /* z[nd2-n]+=l(a0b0) = h(a0b0)+l(t)-l(a1b1) */
+ rdec(nd2,t,0,nd2); /* z[nd2-n]=h(a0b0)+l(t)-l(a1b1)-l(t-a1b1)=h(a0b0) */
+ rnorm(0,-n); /* a0b0 now in z - truncate it */
+ t.rdec(0,this,0,n); /* (a0+a1)(b0+b1) - a0b0 */
+ rinc(nd2,t,0,n);
+
+ rnorm(nd2,n);
+ }
+
+ /* z=x*y. Assumes x and y are of same length. */
+ public static FF mul(FF x,FF y)
+ {
+ int n=x.length;
+ FF z=new FF(2*n);
+ FF t=new FF(2*n);
+// x.norm(); y.norm();
+ z.karmul(0,x,0,y,0,t,0,n);
+ return z;
+ }
+
+ /* z=x^2 */
+ public static FF sqr(FF x)
+ {
+ int n=x.length;
+ FF z=new FF(2*n);
+ FF t=new FF(2*n);
+// x.norm();
+ z.karsqr(0,x,0,t,0,n);
+ return z;
+ }
+
+/* return low part of product this*y */
+ public void lmul(FF y)
+ {
+ int n=length;
+ FF t=new FF(2*n);
+ FF x=new FF(n); x.copy(this);
+// x.norm(); y.norm();
+ karmul_lower(0,x,0,y,0,t,0,n);
+ }
+
+/* Set b=b mod c */
+ public void mod(FF c)
+ {
+ int k=0;
+
+ norm();
+ if (comp(this,c)<0)
+ return;
+ do
+ {
+ c.shl();
+ k++;
+ } while (comp(this,c)>=0);
+
+ while (k>0)
+ {
+ c.shr();
+ if (comp(this,c)>=0)
+ {
+ sub(c);
+ norm();
+ }
+ k--;
+ }
+ }
+
+/* return This mod modulus, N is modulus, ND is Montgomery Constant */
+ public FF reduce(FF N,FF ND)
+ { /* fast karatsuba Montgomery reduction */
+ int n=N.length;
+ FF t=new FF(2*n);
+ FF r=new FF(n);
+ FF m=new FF(n);
+
+ r.sducopy(this);
+ m.karmul_lower(0,this,0,ND,0,t,0,n);
+ karmul_upper(N,m,t,n);
+ m.sducopy(this);
+
+ r.add(N);
+ r.sub(m);
+ r.norm();
+
+ return r;
+ }
+
+/* Set r=this mod b */
+/* this is of length - 2*n */
+/* r,b is of length - n */
+ public FF dmod(FF b)
+ {
+ int k,n=b.length;
+ FF m=new FF(2*n);
+ FF x=new FF(2*n);
+ FF r=new FF(n);
+
+ x.copy(this);
+ x.norm();
+ m.dsucopy(b); k=BIG.BIGBITS*n;
+
+ while (comp(x,m)>=0)
+ {
+ x.sub(m);
+ x.norm();
+ }
+
+ while (k>0)
+ {
+ m.shr();
+
+ if (comp(x,m)>=0)
+ {
+ x.sub(m);
+ x.norm();
+ }
+ k--;
+ }
+
+ r.copy(x);
+ r.mod(b);
+ return r;
+ }
+
+/* Set return=1/this mod p. Binary method - a<p on entry */
+
+ public void invmodp(FF p)
+ {
+ int n=p.length;
+
+ FF u=new FF(n);
+ FF v=new FF(n);
+ FF x1=new FF(n);
+ FF x2=new FF(n);
+ FF t=new FF(n);
+ FF one=new FF(n);
+
+ one.one();
+ u.copy(this);
+ v.copy(p);
+ x1.copy(one);
+ x2.zero();
+
+ // reduce n in here as well!
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.shr();
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.shr();
+ }
+ while (v.parity()==0)
+ {
+ v.shr();
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.shr();
+ }
+ if (comp(u,v)>=0)
+ {
+
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0)
+ copy(x1);
+ else
+ copy(x2);
+ }
+
+/* nresidue mod m */
+ public void nres(FF m)
+ {
+ int n=m.length;
+ if (n==1)
+ {
+ DBIG d=new DBIG(this.v[0]);
+ d.shl(BIG.NLEN*BIG.BASEBITS);
+ this.v[0].copy(d.mod(m.v[0]));
+ }
+ else
+ {
+ FF d=new FF(2*n);
+ d.dsucopy(this);
+ copy(d.dmod(m));
+ }
+ }
+
+ public void redc(FF m,FF ND)
+ {
+ int n=m.length;
+ if (n==1)
+ {
+ DBIG d=new DBIG(this.v[0]);
+ this.v[0].copy(BIG.monty(m.v[0],(BIG.cast_to_chunk(1)<<BIG.BASEBITS)-ND.v[0].w[0],d));
+ }
+ else
+ {
+ FF d=new FF(2*n);
+ mod(m);
+ d.dscopy(this);
+ copy(d.reduce(m,ND));
+ mod(m);
+ }
+ }
+
+ private void mod2m(int m)
+ {
+ for (int i=m;i<length;i++)
+ v[i].zero();
+ }
+
+ /* U=1/a mod 2^m - Arazi & Qi */
+ private FF invmod2m()
+ {
+ int i,n=length;
+
+ FF b=new FF(n);
+ FF c=new FF(n);
+ FF U=new FF(n);
+ FF t;
+
+ U.zero();
+ U.v[0].copy(v[0]);
+ U.v[0].invmod2m();
+
+ for (i=1;i<n;i<<=1)
+ {
+ b.copy(this); b.mod2m(i);
+ t=mul(U,b);
+
+ t.shrw(i); b.copy(t);
+ c.copy(this); c.shrw(i); c.mod2m(i);
+ c.lmul(U); c.mod2m(i);
+
+ b.add(c); b.norm();
+ b.lmul(U); b.mod2m(i);
+
+ c.one(); c.shlw(i); b.revsub(c); b.norm();
+ b.shlw(i);
+ U.add(b);
+ }
+ U.norm();
+ return U;
+ }
+
+ public void random(RAND rng)
+ {
+ int n=length;
+ for (int i=0;i<n;i++)
+ {
+ v[i].copy(BIG.random(rng));
+ }
+ /* make sure top bit is 1 */
+ while (v[n-1].nbits()<BIG.MODBYTES*8) v[n-1].copy(BIG.random(rng));
+ }
+
+ /* generate random x */
+ public void randomnum(FF p,RAND rng)
+ {
+ int n=length;
+ FF d=new FF(2*n);
+
+ for (int i=0;i<2*n;i++)
+ {
+ d.v[i].copy(BIG.random(rng));
+ }
+ copy(d.dmod(p));
+ }
+
+ /* this*=y mod p */
+ public void modmul(FF y,FF p,FF nd)
+ {
+ if (pexceed(v[length-1],y.v[y.length-1])) mod(p);
+ int n=p.length;
+ if (n==1)
+ {
+ DBIG d=BIG.mul(this.v[0],y.v[0]);
+ this.v[0].copy(BIG.monty(p.v[0],(BIG.cast_to_chunk(1)<<BIG.BASEBITS)-nd.v[0].w[0],d));
+ }
+ else
+ {
+ FF d=mul(this,y);
+ copy(d.reduce(p,nd));
+ }
+ }
+
+ /* this*=y mod p */
+ public void modsqr(FF p,FF nd)
+ {
+ if (sexceed(v[length-1])) mod(p);
+ int n=p.length;
+ if (n==1)
+ {
+ DBIG d=BIG.sqr(this.v[0]);
+ this.v[0].copy(BIG.monty(p.v[0],(BIG.cast_to_chunk(1)<<BIG.BASEBITS)-nd.v[0].w[0],d));
+
+ }
+ else
+ {
+ FF d=sqr(this);
+ copy(d.reduce(p,nd));
+ }
+ }
+
+ /* this=this^e mod p using side-channel resistant Montgomery Ladder, for large e */
+ public void skpow(FF e,FF p)
+ {
+ int i,b,n=p.length;
+ FF R0=new FF(n);
+ FF R1=new FF(n);
+ FF ND=p.invmod2m();
+
+ mod(p);
+ R0.one();
+ R1.copy(this);
+ R0.nres(p);
+ R1.nres(p);
+
+ for (i=8*BIG.MODBYTES*n-1;i>=0;i--)
+ {
+ b=e.v[i/BIG.BIGBITS].bit(i%BIG.BIGBITS);
+ copy(R0);
+ modmul(R1,p,ND);
+
+ cswap(R0,R1,b);
+ R0.modsqr(p,ND);
+
+ R1.copy(this);
+ cswap(R0,R1,b);
+ }
+ copy(R0);
+ redc(p,ND);
+ }
+
+ /* this =this^e mod p using side-channel resistant Montgomery Ladder, for short e */
+ public void skpow(BIG e,FF p)
+ {
+ int i,b,n=p.length;
+ FF R0=new FF(n);
+ FF R1=new FF(n);
+ FF ND=p.invmod2m();
+
+ mod(p);
+ R0.one();
+ R1.copy(this);
+ R0.nres(p);
+ R1.nres(p);
+
+ for (i=8*BIG.MODBYTES-1;i>=0;i--)
+ {
+ b=e.bit(i);
+ copy(R0);
+ modmul(R1,p,ND);
+
+ cswap(R0,R1,b);
+ R0.modsqr(p,ND);
+
+ R1.copy(this);
+ cswap(R0,R1,b);
+ }
+ copy(R0);
+ redc(p,ND);
+ }
+
+ /* raise to an integer power - right-to-left method */
+ public void power(int e,FF p)
+ {
+ int n=p.length;
+ FF w=new FF(n);
+ FF ND=p.invmod2m();
+ boolean f=true;
+
+ w.copy(this);
+ w.nres(p);
+
+ if (e==2)
+ {
+ copy(w);
+ modsqr(p,ND);
+ }
+ else for (; ; )
+ {
+ if (e%2==1)
+ {
+ if (f) copy(w);
+ else modmul(w,p,ND);
+ f=false;
+ }
+ e>>=1;
+ if (e==0) break;
+ w.modsqr(p,ND);
+ }
+ redc(p,ND);
+ }
+
+ /* this=this^e mod p, faster but not side channel resistant */
+ public void pow(FF e,FF p)
+ {
+ int i,b,n=p.length;
+ FF w=new FF(n);
+ FF ND=p.invmod2m();
+
+ w.copy(this);
+ one();
+ nres(p);
+ w.nres(p);
+ for (i=8*BIG.MODBYTES*n-1;i>=0;i--)
+ {
+ modsqr(p,ND);
+ b=e.v[i/BIG.BIGBITS].bit(i%BIG.BIGBITS);
+ if (b==1) modmul(w,p,ND);
+ }
+ redc(p,ND);
+ }
+
+ /* double exponentiation r=x^e.y^f mod p */
+ public void pow2(BIG e,FF y,BIG f,FF p)
+ {
+ int i,eb,fb,n=p.length;
+ FF xn=new FF(n);
+ FF yn=new FF(n);
+ FF xy=new FF(n);
+ FF ND=p.invmod2m();
+
+ xn.copy(this);
+ yn.copy(y);
+ xn.nres(p);
+ yn.nres(p);
+ xy.copy(xn); xy.modmul(yn,p,ND);
+ one();
+ nres(p);
+
+ for (i=8*BIG.MODBYTES-1;i>=0;i--)
+ {
+ eb=e.bit(i);
+ fb=f.bit(i);
+ modsqr(p,ND);
+ if (eb==1)
+ {
+ if (fb==1) modmul(xy,p,ND);
+ else modmul(xn,p,ND);
+ }
+ else
+ {
+ if (fb==1) modmul(yn,p,ND);
+ }
+ }
+ redc(p,ND);
+ }
+
+ private static int igcd(int x,int y)
+ { /* integer GCD, returns GCD of x and y */
+ int r;
+ if (y==0) return x;
+ while ((r=x%y)!=0)
+ {x=y;y=r;}
+ return y;
+ }
+
+ /* quick and dirty check for common factor with n */
+ public boolean cfactor(int s)
+ {
+ int r,n=length;
+ int g;
+
+ FF x=new FF(n);
+ FF y=new FF(n);
+
+ y.set(s);
+ x.copy(this);
+ x.norm();
+
+ do
+ {
+ x.sub(y);
+ x.norm();
+ while (!x.iszilch() && x.parity()==0) x.shr();
+ }
+ while (comp(x,y)>0);
+
+ g=(int)x.v[0].get(0);
+ r=igcd(s,g);
+ if (r>1) return true;
+ return false;
+ }
+
+ /* Miller-Rabin test for primality. Slow. */
+ public static boolean prime(FF p,RAND rng)
+ {
+ int i,j,s=0,n=p.length;
+ boolean loop;
+ FF d=new FF(n);
+ FF x=new FF(n);
+ FF unity=new FF(n);
+ FF nm1=new FF(n);
+
+ int sf=4849845; /* 3*5*.. *19 */
+ p.norm();
+
+ if (p.cfactor(sf)) return false;
+ unity.one();
+ nm1.copy(p);
+ nm1.sub(unity);
+ nm1.norm();
+ d.copy(nm1);
+
+ while (d.parity()==0)
+ {
+ d.shr();
+ s++;
+ }
+ if (s==0) return false;
+ for (i=0;i<10;i++)
+ {
+ x.randomnum(p,rng);
+ x.pow(d,p);
+
+ if (comp(x,unity)==0 || comp(x,nm1)==0) continue;
+ loop=false;
+ for (j=1;j<s;j++)
+ {
+ x.power(2,p);
+ if (comp(x,unity)==0) return false;
+ if (comp(x,nm1)==0) {loop=true; break;}
+ }
+ if (loop) continue;
+ return false;
+ }
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/RSA3072/RSA.java b/src/main/java/org/apache/milagro/amcl/RSA3072/RSA.java
new file mode 100644
index 0000000..1936bcb
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA3072/RSA.java
@@ -0,0 +1,369 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* RSA API high-level functions */
+
+package org.apache.milagro.amcl.RSA3072;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+
+public final class RSA {
+
+ public static final int RFS=BIG.MODBYTES*FF.FFLEN;
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=SHA256;
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,byte[] A,int n)
+ {
+ byte[] R=null;
+
+ if (sha==SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (A!=null) H.process_array(A);
+ if (n>=0) H.process_num(n);
+ R=H.hash();
+ }
+ if (sha==SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (A!=null) H.process_array(A);
+ if (n>=0) H.process_num(n);
+ R=H.hash();
+ }
+ if (sha==SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (A!=null) H.process_array(A);
+ if (n>=0) H.process_num(n);
+ R=H.hash();
+ }
+ return R;
+ }
+
+/* generate an RSA key pair */
+
+ public static void KEY_PAIR(RAND rng,int e,private_key PRIV,public_key PUB)
+ { /* IEEE1363 A16.11/A16.12 more or less */
+
+ int n=PUB.n.getlen()/2;
+ FF t = new FF(n);
+ FF p1=new FF(n);
+ FF q1=new FF(n);
+
+ for (;;)
+ {
+ PRIV.p.random(rng);
+ while (PRIV.p.lastbits(2)!=3) PRIV.p.inc(1);
+ while (!FF.prime(PRIV.p,rng)) PRIV.p.inc(4);
+
+ p1.copy(PRIV.p);
+ p1.dec(1);
+
+ if (p1.cfactor(e)) continue;
+ break;
+ }
+
+ for (;;)
+ {
+ PRIV.q.random(rng);
+ while (PRIV.q.lastbits(2)!=3) PRIV.q.inc(1);
+ while (!FF.prime(PRIV.q,rng)) PRIV.q.inc(4);
+
+ q1.copy(PRIV.q);
+ q1.dec(1);
+
+ if (q1.cfactor(e)) continue;
+
+ break;
+ }
+
+ PUB.n=FF.mul(PRIV.p,PRIV.q);
+ PUB.e=e;
+
+ t.copy(p1);
+ t.shr();
+ PRIV.dp.set(e);
+ PRIV.dp.invmodp(t);
+ if (PRIV.dp.parity()==0) PRIV.dp.add(t);
+ PRIV.dp.norm();
+
+ t.copy(q1);
+ t.shr();
+ PRIV.dq.set(e);
+ PRIV.dq.invmodp(t);
+ if (PRIV.dq.parity()==0) PRIV.dq.add(t);
+ PRIV.dq.norm();
+
+ PRIV.c.copy(PRIV.p);
+ PRIV.c.invmodp(PRIV.q);
+
+ return;
+ }
+
+/* Mask Generation Function */
+
+ public static void MGF1(int sha,byte[] Z,int olen,byte[] K)
+ {
+ int hlen=sha;
+ byte[] B;
+
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ }
+
+ public static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+
+/* SHARSA3072 identifier strings */
+ private static final byte[] SHA256ID={0x30,0x31,0x30,0x0d,0x06,0x09,0x60,(byte)0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20};
+ private static final byte[] SHA384ID={0x30,0x41,0x30,0x0d,0x06,0x09,0x60,(byte)0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,0x30};
+ private static final byte[] SHA512ID={0x30,0x51,0x30,0x0d,0x06,0x09,0x60,(byte)0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40};
+
+/* PKCS 1.5 padding of a message to be signed */
+
+ public static boolean PKCS15(int sha,byte[] m,byte[] w)
+ {
+ int olen=FF.FF_BITS/8;
+ int i,hlen=sha;
+ int idlen=19;
+
+ if (olen<idlen+hlen+10) return false;
+ byte[] H=hashit(sha,m,-1);
+
+ for (i=0;i<w.length;i++) w[i]=0;
+ i=0;
+ w[i++]=0;
+ w[i++]=1;
+ for (int j=0;j<olen-idlen-hlen-3;j++)
+ w[i++]=(byte)0xff;
+ w[i++]=0;
+
+
+ if (hlen==SHA256) for (int j=0;j<idlen;j++) w[i++]=SHA256ID[j];
+ if (hlen==SHA384) for (int j=0;j<idlen;j++) w[i++]=SHA384ID[j];
+ if (hlen==SHA512) for (int j=0;j<idlen;j++) w[i++]=SHA512ID[j];
+
+ for (int j=0;j<hlen;j++)
+ w[i++]=H[j];
+
+ return true;
+ }
+
+
+ /* OAEP Message Encoding for Encryption */
+ public static byte[] OAEP_ENCODE(int sha,byte[] m,RAND rng,byte[] p)
+ {
+ int i,slen,olen=RFS-1;
+ int mlen=m.length;
+ int hlen,seedlen;
+ byte[] f=new byte[RFS];
+
+ hlen=sha;
+ byte[] SEED=new byte[hlen];
+ seedlen=hlen;
+
+ if (mlen>olen-hlen-seedlen-1) return new byte[0];
+
+ byte[] DBMASK=new byte[olen-seedlen];
+
+ byte[] h=hashit(sha,p,-1);
+
+ for (i=0;i<hlen;i++) f[i]=h[i];
+
+ slen=olen-mlen-hlen-seedlen-1;
+
+ for (i=0;i<slen;i++) f[hlen+i]=0;
+ f[hlen+slen]=1;
+ for (i=0;i<mlen;i++) f[hlen+slen+1+i]=m[i];
+
+ for (i=0;i<seedlen;i++) SEED[i]=(byte)rng.getByte();
+
+ MGF1(sha,SEED,olen-seedlen,DBMASK);
+
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]^=f[i];
+
+ MGF1(sha,DBMASK,seedlen,f);
+
+ for (i=0;i<seedlen;i++) f[i]^=SEED[i];
+
+ for (i=0;i<olen-seedlen;i++) f[i+seedlen]=DBMASK[i];
+
+ /* pad to length RFS */
+ int d=1;
+ for (i=RFS-1;i>=d;i--)
+ f[i]=f[i-d];
+ for (i=d-1;i>=0;i--)
+ f[i]=0;
+
+ return f;
+ }
+
+ /* OAEP Message Decoding for Decryption */
+ public static byte[] OAEP_DECODE(int sha,byte[] p,byte[] f)
+ {
+ int x,t;
+ boolean comp;
+ int i,k,olen=RFS-1;
+ int hlen,seedlen;
+
+ hlen=sha;
+ byte[] SEED=new byte[hlen];
+ seedlen=hlen;
+ byte[] CHASH=new byte[hlen];
+
+ if (olen<seedlen+hlen+1) return new byte[0];
+ byte[] DBMASK=new byte[olen-seedlen];
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]=0;
+
+ if (f.length<RFS)
+ {
+ int d=RFS-f.length;
+ for (i=RFS-1;i>=d;i--)
+ f[i]=f[i-d];
+ for (i=d-1;i>=0;i--)
+ f[i]=0;
+
+ }
+
+ byte[] h=hashit(sha,p,-1);
+
+ for (i=0;i<hlen;i++) CHASH[i]=h[i];
+
+ x=f[0];
+
+ for (i=seedlen;i<olen;i++)
+ DBMASK[i-seedlen]=f[i+1];
+
+ MGF1(sha,DBMASK,seedlen,SEED);
+ for (i=0;i<seedlen;i++) SEED[i]^=f[i+1];
+ MGF1(sha,SEED,olen-seedlen,f);
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]^=f[i];
+
+ comp=true;
+ for (i=0;i<hlen;i++)
+ {
+ if (CHASH[i]!=DBMASK[i]) comp=false;
+ }
+
+ for (i=0;i<olen-seedlen-hlen;i++)
+ DBMASK[i]=DBMASK[i+hlen];
+
+ for (i=0;i<hlen;i++)
+ SEED[i]=CHASH[i]=0;
+
+ for (k=0;;k++)
+ {
+ if (k>=olen-seedlen-hlen) return new byte[0];
+ if (DBMASK[k]!=0) break;
+ }
+
+ t=DBMASK[k];
+ if (!comp || x!=0 || t!=0x01)
+ {
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]=0;
+ return new byte[0];
+ }
+
+ byte[] r=new byte[olen-seedlen-hlen-k-1];
+
+ for (i=0;i<olen-seedlen-hlen-k-1;i++)
+ r[i]=DBMASK[i+k+1];
+
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]=0;
+
+ return r;
+ }
+
+ /* destroy the Private Key structure */
+ public static void PRIVATE_KEY_KILL(private_key PRIV)
+ {
+ PRIV.p.zero();
+ PRIV.q.zero();
+ PRIV.dp.zero();
+ PRIV.dq.zero();
+ PRIV.c.zero();
+ }
+
+ /* RSA encryption with the public key */
+ public static void ENCRYPT(public_key PUB,byte[] F,byte[] G)
+ {
+ int n=PUB.n.getlen();
+ FF f=new FF(n);
+ FF.fromBytes(f,F);
+ f.power(PUB.e,PUB.n);
+ f.toBytes(G);
+ }
+
+ /* RSA decryption with the private key */
+ public static void DECRYPT(private_key PRIV,byte[] G,byte[] F)
+ {
+ int n=PRIV.p.getlen();
+ FF g=new FF(2*n);
+
+ FF.fromBytes(g,G);
+ FF jp=g.dmod(PRIV.p);
+ FF jq=g.dmod(PRIV.q);
+
+ jp.skpow(PRIV.dp,PRIV.p);
+ jq.skpow(PRIV.dq,PRIV.q);
+
+ g.zero();
+ g.dscopy(jp);
+ jp.mod(PRIV.q);
+ if (FF.comp(jp,jq)>0) jq.add(PRIV.q);
+ jq.sub(jp);
+ jq.norm();
+
+ FF t=FF.mul(PRIV.c,jq);
+ jq=t.dmod(PRIV.q);
+
+ t=FF.mul(jq,PRIV.p);
+ g.add(t);
+ g.norm();
+
+ g.toBytes(F);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA3072/private_key.java b/src/main/java/org/apache/milagro/amcl/RSA3072/private_key.java
new file mode 100644
index 0000000..f46b833
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA3072/private_key.java
@@ -0,0 +1,16 @@
+
+package org.apache.milagro.amcl.RSA3072;
+
+public final class private_key
+{
+ public FF p,q,dp,dq,c;
+
+ public private_key(int n)
+ {
+ p=new FF(n);
+ q=new FF(n);
+ dp=new FF(n);
+ dq=new FF(n);
+ c=new FF(n);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/RSA3072/public_key.java b/src/main/java/org/apache/milagro/amcl/RSA3072/public_key.java
new file mode 100644
index 0000000..564c41f
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA3072/public_key.java
@@ -0,0 +1,14 @@
+
+package org.apache.milagro.amcl.RSA3072;
+
+public final class public_key
+{
+ public int e;
+ public FF n;
+
+ public public_key(int m)
+ {
+ e=0;
+ n=new FF(m);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA4096/BIG.java b/src/main/java/org/apache/milagro/amcl/RSA4096/BIG.java
new file mode 100644
index 0000000..e79eb83
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA4096/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.RSA4096;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=64; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=60;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA4096/DBIG.java b/src/main/java/org/apache/milagro/amcl/RSA4096/DBIG.java
new file mode 100644
index 0000000..0a7b1a5
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA4096/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.RSA4096;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA4096/FF.java b/src/main/java/org/apache/milagro/amcl/RSA4096/FF.java
new file mode 100644
index 0000000..7244bab
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA4096/FF.java
@@ -0,0 +1,1028 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Large Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.RSA4096;
+import org.apache.milagro.amcl.RAND;
+
+public final class FF {
+
+/* RSA/DH modulus length as multiple of BIGBITS */
+ public static final int FFLEN=8;
+
+/* Don't Modify from here... */
+
+
+/* Finite field support - for RSA, DH etc. */
+ public static final int FF_BITS=(BIG.BIGBITS*FFLEN); /* Finite Field Size in bits - must be 256.2^n */
+ public static final int HFLEN=(FFLEN/2); /* Useful for half-size RSA private key operations */
+
+ public static final int P_MBITS=BIG.MODBYTES*8;
+ public static final int P_TBITS=(P_MBITS%BIG.BASEBITS);
+
+ private final BIG[] v;
+ private final int length;
+
+/**************** 64-bit specific ************************/
+
+ public static final long P_OMASK=((long)(-1)<<(P_MBITS%BIG.BASEBITS));
+ public static final long P_FEXCESS=((long)1<<(BIG.BASEBITS*BIG.NLEN-P_MBITS-1));
+
+ public static long EXCESS(BIG a)
+ {
+ return ((a.get(BIG.NLEN-1)&P_OMASK)>>(P_TBITS))+1;
+ }
+
+/* Check if product causes excess */
+ public static boolean pexceed(BIG a,BIG b)
+ {
+ long ea,eb;
+ ea=EXCESS(a);
+ eb=EXCESS(b);
+ if ((ea+1)>P_FEXCESS/(eb+1)) return true;
+ return false;
+ }
+
+/* Check if square causes excess */
+ public static boolean sexceed(BIG a)
+ {
+ long ea;
+ ea=EXCESS(a);
+ if ((ea+1)>P_FEXCESS/(ea+1)) return true;
+ return false;
+ }
+
+/******************************************************/
+
+/* Constructors */
+ public FF(int n)
+ {
+ v=new BIG[n];
+ for (int i=0;i<n;i++)
+ v[i]=new BIG(0);
+ length=n;
+ }
+
+ public int getlen()
+ {
+ return length;
+ }
+
+/* set to integer */
+ public void set(int m)
+ {
+ zero();
+ v[0].set(0,(m&BIG.BMASK));
+ v[0].set(1,(m>>BIG.BASEBITS));
+ }
+
+/* copy from FF b */
+ public void copy(FF b)
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].copy(b.v[i]);
+ }
+ }
+
+/* x=y<<n */
+ public void dsucopy(FF b)
+ {
+ for (int i=0;i<b.length;i++)
+ {
+ v[b.length+i].copy(b.v[i]);
+ v[i].zero();
+ }
+ }
+
+/* x=y */
+ public void dscopy(FF b)
+ {
+ for (int i=0;i<b.length;i++)
+ {
+ v[i].copy(b.v[i]);
+ v[b.length+i].zero();
+ }
+ }
+
+/* x=y>>n */
+ public void sducopy(FF b)
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].copy(b.v[length+i]);
+ }
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].zero();
+ }
+ }
+
+ public void one()
+ {
+ v[0].one();
+ for (int i=1;i<length;i++)
+ {
+ v[i].zero();
+ }
+ }
+
+/* test equals 0 */
+ public boolean iszilch()
+ {
+ for (int i=0;i<length;i++)
+ {
+ if (!v[i].iszilch()) return false;
+ }
+ return true;
+ }
+
+/* shift right by BIGBITS-bit words */
+ public void shrw(int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[i].copy(v[i+n]);
+ v[i+n].zero();
+ }
+ }
+
+/* shift left by BIGBITS-bit words */
+ public void shlw(int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[n+i].copy(v[i]);
+ v[i].zero();
+ }
+ }
+
+/* extract last bit */
+ public int parity()
+ {
+ return v[0].parity();
+ }
+
+ public int lastbits(int m)
+ {
+ return v[0].lastbits(m);
+ }
+
+/* compare x and y - must be normalised, and of same length */
+ public static int comp(FF a,FF b)
+ {
+ int i,j;
+ for (i=a.length-1;i>=0;i--)
+ {
+ j=BIG.comp(a.v[i],b.v[i]);
+ if (j!=0) return j;
+ }
+ return 0;
+ }
+
+/* recursive add */
+ public void radd(int vp,FF x,int xp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].copy(x.v[xp+i]);
+ v[vp+i].add(y.v[yp+i]);
+ }
+ }
+
+/* recursive inc */
+ public void rinc(int vp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].add(y.v[yp+i]);
+ }
+ }
+
+/* recursive sub */
+ public void rsub(int vp,FF x,int xp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].copy(x.v[xp+i]);
+ v[vp+i].sub(y.v[yp+i]);
+ }
+ }
+
+/* recursive dec */
+ public void rdec(int vp,FF y,int yp,int n)
+ {
+ for (int i=0;i<n;i++)
+ {
+ v[vp+i].sub(y.v[yp+i]);
+ }
+ }
+
+/* simple add */
+ public void add(FF b)
+ {
+ for (int i=0;i<length;i++)
+ v[i].add(b.v[i]);
+ }
+
+/* simple sub */
+ public void sub(FF b)
+ {
+ for (int i=0;i<length;i++)
+ v[i].sub(b.v[i]);
+ }
+
+/* reverse sub */
+ public void revsub(FF b)
+ {
+ for (int i=0;i<length;i++)
+ v[i].rsub(b.v[i]);
+ }
+
+/* increment/decrement by a small integer */
+ public void inc(int m)
+ {
+ v[0].inc(m);
+ norm();
+ }
+
+ public void dec(int m)
+ {
+ v[0].dec(m);
+ norm();
+ }
+
+ /* normalise - but hold any overflow in top part unless n<0 */
+ private void rnorm(int vp,int n)
+ {
+ boolean trunc=false;
+ int i;
+ long carry;
+ if (n<0)
+ { /* -v n signals to do truncation */
+ n=-n;
+ trunc=true;
+ }
+ for (i=0;i<n-1;i++)
+ {
+ carry=v[vp+i].norm();
+ v[vp+i].xortop(carry<<P_TBITS);
+ v[vp+i+1].incl(carry);
+ }
+ carry=v[vp+n-1].norm();
+ if (trunc)
+ v[vp+n-1].xortop(carry<<P_TBITS);
+ }
+
+ public void norm()
+ {
+ rnorm(0,length);
+ }
+
+/* shift left by one bit */
+ public void shl()
+ {
+ int i,carry,delay_carry=0;
+ for (i=0;i<length-1;i++)
+ {
+ carry=v[i].fshl(1);
+ v[i].inc(delay_carry);
+ v[i].xortop((long)carry<<P_TBITS);
+ delay_carry=carry;
+ }
+ v[length-1].fshl(1);
+ v[length-1].inc(delay_carry);
+ }
+
+/* shift right by one bit */
+
+ public void shr()
+ {
+ int carry;
+ for (int i=length-1;i>0;i--)
+ {
+ carry=v[i].fshr(1);
+ v[i-1].xortop((long)carry<<P_TBITS);
+ }
+ v[0].fshr(1);
+ }
+
+/* Convert to Hex String */
+ public String toString()
+ {
+ norm();
+ String s="";
+ for (int i=length-1;i>=0;i--)
+ {
+ s+=v[i].toString(); //s+=" ";
+ }
+ return s;
+ }
+
+/*
+ public String toRawString(int len)
+ {
+ // norm(len);
+ String s="";
+ for (int i=len-1;i>=0;i--)
+ {
+ s+=v[i].toRawString(); s+=" ";
+ }
+ return s;
+ }
+*/
+/* Convert FFs to/from byte arrays */
+ public void toBytes(byte[] b)
+ {
+ for (int i=0;i<length;i++)
+ {
+ v[i].tobytearray(b,(length-i-1)*BIG.MODBYTES);
+ }
+ }
+
+ public static void fromBytes(FF x,byte[] b)
+ {
+ for (int i=0;i<x.length;i++)
+ {
+ x.v[i]=BIG.frombytearray(b,(x.length-i-1)*BIG.MODBYTES);
+ }
+ }
+
+/* in-place swapping using xor - side channel resistant - lengths must be the same */
+ private static void cswap(FF a,FF b,int d)
+ {
+ for (int i=0;i<a.length;i++)
+ {
+ // BIG.cswap(a.v[i],b.v[i],d);
+ a.v[i].cswap(b.v[i],d);
+ }
+ }
+
+/* z=x*y, t is workspace */
+ private void karmul(int vp,FF x,int xp,FF y,int yp,FF t,int tp,int n)
+ {
+ int nd2;
+ if (n==1)
+ {
+ x.v[xp].norm();
+ y.v[yp].norm();
+ DBIG d=BIG.mul(x.v[xp],y.v[yp]);
+ v[vp+1]=d.split(8*BIG.MODBYTES);
+ v[vp].copy(d);
+ return;
+ }
+ nd2=n/2;
+ radd(vp,x,xp,x,xp+nd2,nd2);
+ rnorm(vp,nd2); /* Important - required for 32-bit build */
+ radd(vp+nd2,y,yp,y,yp+nd2,nd2);
+ rnorm(vp+nd2,nd2); /* Important - required for 32-bit build */
+
+ t.karmul(tp,this,vp,this,vp+nd2,t,tp+n,nd2);
+ karmul(vp,x,xp,y,yp,t,tp+n,nd2);
+ karmul(vp+n,x,xp+nd2,y,yp+nd2,t,tp+n,nd2);
+ t.rdec(tp,this,vp,n);
+ t.rdec(tp,this,vp+n,n);
+ rinc(vp+nd2,t,tp,n);
+ rnorm(vp,2*n);
+ }
+
+ private void karsqr(int vp,FF x,int xp,FF t,int tp,int n)
+ {
+ int nd2;
+ if (n==1)
+ {
+ x.v[xp].norm();
+ DBIG d=BIG.sqr(x.v[xp]);
+ v[vp+1].copy(d.split(8*BIG.MODBYTES));
+ v[vp].copy(d);
+ return;
+ }
+
+ nd2=n/2;
+ karsqr(vp,x,xp,t,tp+n,nd2);
+ karsqr(vp+n,x,xp+nd2,t,tp+n,nd2);
+ t.karmul(tp,x,xp,x,xp+nd2,t,tp+n,nd2);
+ rinc(vp+nd2,t,tp,n);
+ rinc(vp+nd2,t,tp,n);
+ rnorm(vp+nd2,n);
+ }
+
+
+ private void karmul_lower(int vp,FF x,int xp,FF y,int yp,FF t,int tp,int n)
+ { /* Calculates Least Significant bottom half of x*y */
+ int nd2;
+ if (n==1)
+ { /* only calculate bottom half of product */
+ v[vp].copy(BIG.smul(x.v[xp],y.v[yp]));
+ return;
+ }
+ nd2=n/2;
+ karmul(vp,x,xp,y,yp,t,tp+n,nd2);
+ t.karmul_lower(tp,x,xp+nd2,y,yp,t,tp+n,nd2);
+ rinc(vp+nd2,t,tp,nd2);
+ t.karmul_lower(tp,x,xp,y,yp+nd2,t,tp+n,nd2);
+
+ rinc(vp+nd2,t,tp,nd2);
+ rnorm(vp+nd2,-nd2); /* truncate it */
+ }
+
+ private void karmul_upper(FF x,FF y,FF t,int n)
+ { /* Calculates Most Significant upper half of x*y, given lower part */
+ int nd2;
+
+ nd2=n/2;
+ radd(n,x,0,x,nd2,nd2);
+ radd(n+nd2,y,0,y,nd2,nd2);
+ rnorm(n,nd2);
+ rnorm(n+nd2,nd2);
+
+ t.karmul(0,this,n+nd2,this,n,t,n,nd2); /* t = (a0+a1)(b0+b1) */
+ karmul(n,x,nd2,y,nd2,t,n,nd2); /* z[n]= a1*b1 */
+ /* z[0-nd2]=l(a0b0) z[nd2-n]= h(a0b0)+l(t)-l(a0b0)-l(a1b1) */
+ t.rdec(0,this,n,n); /* t=t-a1b1 */
+ rinc(nd2,this,0,nd2); /* z[nd2-n]+=l(a0b0) = h(a0b0)+l(t)-l(a1b1) */
+ rdec(nd2,t,0,nd2); /* z[nd2-n]=h(a0b0)+l(t)-l(a1b1)-l(t-a1b1)=h(a0b0) */
+ rnorm(0,-n); /* a0b0 now in z - truncate it */
+ t.rdec(0,this,0,n); /* (a0+a1)(b0+b1) - a0b0 */
+ rinc(nd2,t,0,n);
+
+ rnorm(nd2,n);
+ }
+
+ /* z=x*y. Assumes x and y are of same length. */
+ public static FF mul(FF x,FF y)
+ {
+ int n=x.length;
+ FF z=new FF(2*n);
+ FF t=new FF(2*n);
+// x.norm(); y.norm();
+ z.karmul(0,x,0,y,0,t,0,n);
+ return z;
+ }
+
+ /* z=x^2 */
+ public static FF sqr(FF x)
+ {
+ int n=x.length;
+ FF z=new FF(2*n);
+ FF t=new FF(2*n);
+// x.norm();
+ z.karsqr(0,x,0,t,0,n);
+ return z;
+ }
+
+/* return low part of product this*y */
+ public void lmul(FF y)
+ {
+ int n=length;
+ FF t=new FF(2*n);
+ FF x=new FF(n); x.copy(this);
+// x.norm(); y.norm();
+ karmul_lower(0,x,0,y,0,t,0,n);
+ }
+
+/* Set b=b mod c */
+ public void mod(FF c)
+ {
+ int k=0;
+
+ norm();
+ if (comp(this,c)<0)
+ return;
+ do
+ {
+ c.shl();
+ k++;
+ } while (comp(this,c)>=0);
+
+ while (k>0)
+ {
+ c.shr();
+ if (comp(this,c)>=0)
+ {
+ sub(c);
+ norm();
+ }
+ k--;
+ }
+ }
+
+/* return This mod modulus, N is modulus, ND is Montgomery Constant */
+ public FF reduce(FF N,FF ND)
+ { /* fast karatsuba Montgomery reduction */
+ int n=N.length;
+ FF t=new FF(2*n);
+ FF r=new FF(n);
+ FF m=new FF(n);
+
+ r.sducopy(this);
+ m.karmul_lower(0,this,0,ND,0,t,0,n);
+ karmul_upper(N,m,t,n);
+ m.sducopy(this);
+
+ r.add(N);
+ r.sub(m);
+ r.norm();
+
+ return r;
+ }
+
+/* Set r=this mod b */
+/* this is of length - 2*n */
+/* r,b is of length - n */
+ public FF dmod(FF b)
+ {
+ int k,n=b.length;
+ FF m=new FF(2*n);
+ FF x=new FF(2*n);
+ FF r=new FF(n);
+
+ x.copy(this);
+ x.norm();
+ m.dsucopy(b); k=BIG.BIGBITS*n;
+
+ while (comp(x,m)>=0)
+ {
+ x.sub(m);
+ x.norm();
+ }
+
+ while (k>0)
+ {
+ m.shr();
+
+ if (comp(x,m)>=0)
+ {
+ x.sub(m);
+ x.norm();
+ }
+ k--;
+ }
+
+ r.copy(x);
+ r.mod(b);
+ return r;
+ }
+
+/* Set return=1/this mod p. Binary method - a<p on entry */
+
+ public void invmodp(FF p)
+ {
+ int n=p.length;
+
+ FF u=new FF(n);
+ FF v=new FF(n);
+ FF x1=new FF(n);
+ FF x2=new FF(n);
+ FF t=new FF(n);
+ FF one=new FF(n);
+
+ one.one();
+ u.copy(this);
+ v.copy(p);
+ x1.copy(one);
+ x2.zero();
+
+ // reduce n in here as well!
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.shr();
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.shr();
+ }
+ while (v.parity()==0)
+ {
+ v.shr();
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.shr();
+ }
+ if (comp(u,v)>=0)
+ {
+
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0)
+ copy(x1);
+ else
+ copy(x2);
+ }
+
+/* nresidue mod m */
+ public void nres(FF m)
+ {
+ int n=m.length;
+ if (n==1)
+ {
+ DBIG d=new DBIG(this.v[0]);
+ d.shl(BIG.NLEN*BIG.BASEBITS);
+ this.v[0].copy(d.mod(m.v[0]));
+ }
+ else
+ {
+ FF d=new FF(2*n);
+ d.dsucopy(this);
+ copy(d.dmod(m));
+ }
+ }
+
+ public void redc(FF m,FF ND)
+ {
+ int n=m.length;
+ if (n==1)
+ {
+ DBIG d=new DBIG(this.v[0]);
+ this.v[0].copy(BIG.monty(m.v[0],(BIG.cast_to_chunk(1)<<BIG.BASEBITS)-ND.v[0].w[0],d));
+ }
+ else
+ {
+ FF d=new FF(2*n);
+ mod(m);
+ d.dscopy(this);
+ copy(d.reduce(m,ND));
+ mod(m);
+ }
+ }
+
+ private void mod2m(int m)
+ {
+ for (int i=m;i<length;i++)
+ v[i].zero();
+ }
+
+ /* U=1/a mod 2^m - Arazi & Qi */
+ private FF invmod2m()
+ {
+ int i,n=length;
+
+ FF b=new FF(n);
+ FF c=new FF(n);
+ FF U=new FF(n);
+ FF t;
+
+ U.zero();
+ U.v[0].copy(v[0]);
+ U.v[0].invmod2m();
+
+ for (i=1;i<n;i<<=1)
+ {
+ b.copy(this); b.mod2m(i);
+ t=mul(U,b);
+
+ t.shrw(i); b.copy(t);
+ c.copy(this); c.shrw(i); c.mod2m(i);
+ c.lmul(U); c.mod2m(i);
+
+ b.add(c); b.norm();
+ b.lmul(U); b.mod2m(i);
+
+ c.one(); c.shlw(i); b.revsub(c); b.norm();
+ b.shlw(i);
+ U.add(b);
+ }
+ U.norm();
+ return U;
+ }
+
+ public void random(RAND rng)
+ {
+ int n=length;
+ for (int i=0;i<n;i++)
+ {
+ v[i].copy(BIG.random(rng));
+ }
+ /* make sure top bit is 1 */
+ while (v[n-1].nbits()<BIG.MODBYTES*8) v[n-1].copy(BIG.random(rng));
+ }
+
+ /* generate random x */
+ public void randomnum(FF p,RAND rng)
+ {
+ int n=length;
+ FF d=new FF(2*n);
+
+ for (int i=0;i<2*n;i++)
+ {
+ d.v[i].copy(BIG.random(rng));
+ }
+ copy(d.dmod(p));
+ }
+
+ /* this*=y mod p */
+ public void modmul(FF y,FF p,FF nd)
+ {
+ if (pexceed(v[length-1],y.v[y.length-1])) mod(p);
+ int n=p.length;
+ if (n==1)
+ {
+ DBIG d=BIG.mul(this.v[0],y.v[0]);
+ this.v[0].copy(BIG.monty(p.v[0],(BIG.cast_to_chunk(1)<<BIG.BASEBITS)-nd.v[0].w[0],d));
+ }
+ else
+ {
+ FF d=mul(this,y);
+ copy(d.reduce(p,nd));
+ }
+ }
+
+ /* this*=y mod p */
+ public void modsqr(FF p,FF nd)
+ {
+ if (sexceed(v[length-1])) mod(p);
+ int n=p.length;
+ if (n==1)
+ {
+ DBIG d=BIG.sqr(this.v[0]);
+ this.v[0].copy(BIG.monty(p.v[0],(BIG.cast_to_chunk(1)<<BIG.BASEBITS)-nd.v[0].w[0],d));
+
+ }
+ else
+ {
+ FF d=sqr(this);
+ copy(d.reduce(p,nd));
+ }
+ }
+
+ /* this=this^e mod p using side-channel resistant Montgomery Ladder, for large e */
+ public void skpow(FF e,FF p)
+ {
+ int i,b,n=p.length;
+ FF R0=new FF(n);
+ FF R1=new FF(n);
+ FF ND=p.invmod2m();
+
+ mod(p);
+ R0.one();
+ R1.copy(this);
+ R0.nres(p);
+ R1.nres(p);
+
+ for (i=8*BIG.MODBYTES*n-1;i>=0;i--)
+ {
+ b=e.v[i/BIG.BIGBITS].bit(i%BIG.BIGBITS);
+ copy(R0);
+ modmul(R1,p,ND);
+
+ cswap(R0,R1,b);
+ R0.modsqr(p,ND);
+
+ R1.copy(this);
+ cswap(R0,R1,b);
+ }
+ copy(R0);
+ redc(p,ND);
+ }
+
+ /* this =this^e mod p using side-channel resistant Montgomery Ladder, for short e */
+ public void skpow(BIG e,FF p)
+ {
+ int i,b,n=p.length;
+ FF R0=new FF(n);
+ FF R1=new FF(n);
+ FF ND=p.invmod2m();
+
+ mod(p);
+ R0.one();
+ R1.copy(this);
+ R0.nres(p);
+ R1.nres(p);
+
+ for (i=8*BIG.MODBYTES-1;i>=0;i--)
+ {
+ b=e.bit(i);
+ copy(R0);
+ modmul(R1,p,ND);
+
+ cswap(R0,R1,b);
+ R0.modsqr(p,ND);
+
+ R1.copy(this);
+ cswap(R0,R1,b);
+ }
+ copy(R0);
+ redc(p,ND);
+ }
+
+ /* raise to an integer power - right-to-left method */
+ public void power(int e,FF p)
+ {
+ int n=p.length;
+ FF w=new FF(n);
+ FF ND=p.invmod2m();
+ boolean f=true;
+
+ w.copy(this);
+ w.nres(p);
+
+ if (e==2)
+ {
+ copy(w);
+ modsqr(p,ND);
+ }
+ else for (; ; )
+ {
+ if (e%2==1)
+ {
+ if (f) copy(w);
+ else modmul(w,p,ND);
+ f=false;
+ }
+ e>>=1;
+ if (e==0) break;
+ w.modsqr(p,ND);
+ }
+ redc(p,ND);
+ }
+
+ /* this=this^e mod p, faster but not side channel resistant */
+ public void pow(FF e,FF p)
+ {
+ int i,b,n=p.length;
+ FF w=new FF(n);
+ FF ND=p.invmod2m();
+
+ w.copy(this);
+ one();
+ nres(p);
+ w.nres(p);
+ for (i=8*BIG.MODBYTES*n-1;i>=0;i--)
+ {
+ modsqr(p,ND);
+ b=e.v[i/BIG.BIGBITS].bit(i%BIG.BIGBITS);
+ if (b==1) modmul(w,p,ND);
+ }
+ redc(p,ND);
+ }
+
+ /* double exponentiation r=x^e.y^f mod p */
+ public void pow2(BIG e,FF y,BIG f,FF p)
+ {
+ int i,eb,fb,n=p.length;
+ FF xn=new FF(n);
+ FF yn=new FF(n);
+ FF xy=new FF(n);
+ FF ND=p.invmod2m();
+
+ xn.copy(this);
+ yn.copy(y);
+ xn.nres(p);
+ yn.nres(p);
+ xy.copy(xn); xy.modmul(yn,p,ND);
+ one();
+ nres(p);
+
+ for (i=8*BIG.MODBYTES-1;i>=0;i--)
+ {
+ eb=e.bit(i);
+ fb=f.bit(i);
+ modsqr(p,ND);
+ if (eb==1)
+ {
+ if (fb==1) modmul(xy,p,ND);
+ else modmul(xn,p,ND);
+ }
+ else
+ {
+ if (fb==1) modmul(yn,p,ND);
+ }
+ }
+ redc(p,ND);
+ }
+
+ private static int igcd(int x,int y)
+ { /* integer GCD, returns GCD of x and y */
+ int r;
+ if (y==0) return x;
+ while ((r=x%y)!=0)
+ {x=y;y=r;}
+ return y;
+ }
+
+ /* quick and dirty check for common factor with n */
+ public boolean cfactor(int s)
+ {
+ int r,n=length;
+ int g;
+
+ FF x=new FF(n);
+ FF y=new FF(n);
+
+ y.set(s);
+ x.copy(this);
+ x.norm();
+
+ do
+ {
+ x.sub(y);
+ x.norm();
+ while (!x.iszilch() && x.parity()==0) x.shr();
+ }
+ while (comp(x,y)>0);
+
+ g=(int)x.v[0].get(0);
+ r=igcd(s,g);
+ if (r>1) return true;
+ return false;
+ }
+
+ /* Miller-Rabin test for primality. Slow. */
+ public static boolean prime(FF p,RAND rng)
+ {
+ int i,j,s=0,n=p.length;
+ boolean loop;
+ FF d=new FF(n);
+ FF x=new FF(n);
+ FF unity=new FF(n);
+ FF nm1=new FF(n);
+
+ int sf=4849845; /* 3*5*.. *19 */
+ p.norm();
+
+ if (p.cfactor(sf)) return false;
+ unity.one();
+ nm1.copy(p);
+ nm1.sub(unity);
+ nm1.norm();
+ d.copy(nm1);
+
+ while (d.parity()==0)
+ {
+ d.shr();
+ s++;
+ }
+ if (s==0) return false;
+ for (i=0;i<10;i++)
+ {
+ x.randomnum(p,rng);
+ x.pow(d,p);
+
+ if (comp(x,unity)==0 || comp(x,nm1)==0) continue;
+ loop=false;
+ for (j=1;j<s;j++)
+ {
+ x.power(2,p);
+ if (comp(x,unity)==0) return false;
+ if (comp(x,nm1)==0) {loop=true; break;}
+ }
+ if (loop) continue;
+ return false;
+ }
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/RSA4096/RSA.java b/src/main/java/org/apache/milagro/amcl/RSA4096/RSA.java
new file mode 100644
index 0000000..8f6ba8d
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA4096/RSA.java
@@ -0,0 +1,369 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* RSA API high-level functions */
+
+package org.apache.milagro.amcl.RSA4096;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+
+public final class RSA {
+
+ public static final int RFS=BIG.MODBYTES*FF.FFLEN;
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=SHA256;
+
+/* Hash number (optional) and string to array size of Bigs */
+
+ public static byte[] hashit(int sha,byte[] A,int n)
+ {
+ byte[] R=null;
+
+ if (sha==SHA256)
+ {
+ HASH256 H=new HASH256();
+ if (A!=null) H.process_array(A);
+ if (n>=0) H.process_num(n);
+ R=H.hash();
+ }
+ if (sha==SHA384)
+ {
+ HASH384 H=new HASH384();
+ if (A!=null) H.process_array(A);
+ if (n>=0) H.process_num(n);
+ R=H.hash();
+ }
+ if (sha==SHA512)
+ {
+ HASH512 H=new HASH512();
+ if (A!=null) H.process_array(A);
+ if (n>=0) H.process_num(n);
+ R=H.hash();
+ }
+ return R;
+ }
+
+/* generate an RSA key pair */
+
+ public static void KEY_PAIR(RAND rng,int e,private_key PRIV,public_key PUB)
+ { /* IEEE1363 A16.11/A16.12 more or less */
+
+ int n=PUB.n.getlen()/2;
+ FF t = new FF(n);
+ FF p1=new FF(n);
+ FF q1=new FF(n);
+
+ for (;;)
+ {
+ PRIV.p.random(rng);
+ while (PRIV.p.lastbits(2)!=3) PRIV.p.inc(1);
+ while (!FF.prime(PRIV.p,rng)) PRIV.p.inc(4);
+
+ p1.copy(PRIV.p);
+ p1.dec(1);
+
+ if (p1.cfactor(e)) continue;
+ break;
+ }
+
+ for (;;)
+ {
+ PRIV.q.random(rng);
+ while (PRIV.q.lastbits(2)!=3) PRIV.q.inc(1);
+ while (!FF.prime(PRIV.q,rng)) PRIV.q.inc(4);
+
+ q1.copy(PRIV.q);
+ q1.dec(1);
+
+ if (q1.cfactor(e)) continue;
+
+ break;
+ }
+
+ PUB.n=FF.mul(PRIV.p,PRIV.q);
+ PUB.e=e;
+
+ t.copy(p1);
+ t.shr();
+ PRIV.dp.set(e);
+ PRIV.dp.invmodp(t);
+ if (PRIV.dp.parity()==0) PRIV.dp.add(t);
+ PRIV.dp.norm();
+
+ t.copy(q1);
+ t.shr();
+ PRIV.dq.set(e);
+ PRIV.dq.invmodp(t);
+ if (PRIV.dq.parity()==0) PRIV.dq.add(t);
+ PRIV.dq.norm();
+
+ PRIV.c.copy(PRIV.p);
+ PRIV.c.invmodp(PRIV.q);
+
+ return;
+ }
+
+/* Mask Generation Function */
+
+ public static void MGF1(int sha,byte[] Z,int olen,byte[] K)
+ {
+ int hlen=sha;
+ byte[] B;
+
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ }
+
+ public static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+
+/* SHARSA4096 identifier strings */
+ private static final byte[] SHA256ID={0x30,0x31,0x30,0x0d,0x06,0x09,0x60,(byte)0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20};
+ private static final byte[] SHA384ID={0x30,0x41,0x30,0x0d,0x06,0x09,0x60,(byte)0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,0x30};
+ private static final byte[] SHA512ID={0x30,0x51,0x30,0x0d,0x06,0x09,0x60,(byte)0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40};
+
+/* PKCS 1.5 padding of a message to be signed */
+
+ public static boolean PKCS15(int sha,byte[] m,byte[] w)
+ {
+ int olen=FF.FF_BITS/8;
+ int i,hlen=sha;
+ int idlen=19;
+
+ if (olen<idlen+hlen+10) return false;
+ byte[] H=hashit(sha,m,-1);
+
+ for (i=0;i<w.length;i++) w[i]=0;
+ i=0;
+ w[i++]=0;
+ w[i++]=1;
+ for (int j=0;j<olen-idlen-hlen-3;j++)
+ w[i++]=(byte)0xff;
+ w[i++]=0;
+
+
+ if (hlen==SHA256) for (int j=0;j<idlen;j++) w[i++]=SHA256ID[j];
+ if (hlen==SHA384) for (int j=0;j<idlen;j++) w[i++]=SHA384ID[j];
+ if (hlen==SHA512) for (int j=0;j<idlen;j++) w[i++]=SHA512ID[j];
+
+ for (int j=0;j<hlen;j++)
+ w[i++]=H[j];
+
+ return true;
+ }
+
+
+ /* OAEP Message Encoding for Encryption */
+ public static byte[] OAEP_ENCODE(int sha,byte[] m,RAND rng,byte[] p)
+ {
+ int i,slen,olen=RFS-1;
+ int mlen=m.length;
+ int hlen,seedlen;
+ byte[] f=new byte[RFS];
+
+ hlen=sha;
+ byte[] SEED=new byte[hlen];
+ seedlen=hlen;
+
+ if (mlen>olen-hlen-seedlen-1) return new byte[0];
+
+ byte[] DBMASK=new byte[olen-seedlen];
+
+ byte[] h=hashit(sha,p,-1);
+
+ for (i=0;i<hlen;i++) f[i]=h[i];
+
+ slen=olen-mlen-hlen-seedlen-1;
+
+ for (i=0;i<slen;i++) f[hlen+i]=0;
+ f[hlen+slen]=1;
+ for (i=0;i<mlen;i++) f[hlen+slen+1+i]=m[i];
+
+ for (i=0;i<seedlen;i++) SEED[i]=(byte)rng.getByte();
+
+ MGF1(sha,SEED,olen-seedlen,DBMASK);
+
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]^=f[i];
+
+ MGF1(sha,DBMASK,seedlen,f);
+
+ for (i=0;i<seedlen;i++) f[i]^=SEED[i];
+
+ for (i=0;i<olen-seedlen;i++) f[i+seedlen]=DBMASK[i];
+
+ /* pad to length RFS */
+ int d=1;
+ for (i=RFS-1;i>=d;i--)
+ f[i]=f[i-d];
+ for (i=d-1;i>=0;i--)
+ f[i]=0;
+
+ return f;
+ }
+
+ /* OAEP Message Decoding for Decryption */
+ public static byte[] OAEP_DECODE(int sha,byte[] p,byte[] f)
+ {
+ int x,t;
+ boolean comp;
+ int i,k,olen=RFS-1;
+ int hlen,seedlen;
+
+ hlen=sha;
+ byte[] SEED=new byte[hlen];
+ seedlen=hlen;
+ byte[] CHASH=new byte[hlen];
+
+ if (olen<seedlen+hlen+1) return new byte[0];
+ byte[] DBMASK=new byte[olen-seedlen];
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]=0;
+
+ if (f.length<RFS)
+ {
+ int d=RFS-f.length;
+ for (i=RFS-1;i>=d;i--)
+ f[i]=f[i-d];
+ for (i=d-1;i>=0;i--)
+ f[i]=0;
+
+ }
+
+ byte[] h=hashit(sha,p,-1);
+
+ for (i=0;i<hlen;i++) CHASH[i]=h[i];
+
+ x=f[0];
+
+ for (i=seedlen;i<olen;i++)
+ DBMASK[i-seedlen]=f[i+1];
+
+ MGF1(sha,DBMASK,seedlen,SEED);
+ for (i=0;i<seedlen;i++) SEED[i]^=f[i+1];
+ MGF1(sha,SEED,olen-seedlen,f);
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]^=f[i];
+
+ comp=true;
+ for (i=0;i<hlen;i++)
+ {
+ if (CHASH[i]!=DBMASK[i]) comp=false;
+ }
+
+ for (i=0;i<olen-seedlen-hlen;i++)
+ DBMASK[i]=DBMASK[i+hlen];
+
+ for (i=0;i<hlen;i++)
+ SEED[i]=CHASH[i]=0;
+
+ for (k=0;;k++)
+ {
+ if (k>=olen-seedlen-hlen) return new byte[0];
+ if (DBMASK[k]!=0) break;
+ }
+
+ t=DBMASK[k];
+ if (!comp || x!=0 || t!=0x01)
+ {
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]=0;
+ return new byte[0];
+ }
+
+ byte[] r=new byte[olen-seedlen-hlen-k-1];
+
+ for (i=0;i<olen-seedlen-hlen-k-1;i++)
+ r[i]=DBMASK[i+k+1];
+
+ for (i=0;i<olen-seedlen;i++) DBMASK[i]=0;
+
+ return r;
+ }
+
+ /* destroy the Private Key structure */
+ public static void PRIVATE_KEY_KILL(private_key PRIV)
+ {
+ PRIV.p.zero();
+ PRIV.q.zero();
+ PRIV.dp.zero();
+ PRIV.dq.zero();
+ PRIV.c.zero();
+ }
+
+ /* RSA encryption with the public key */
+ public static void ENCRYPT(public_key PUB,byte[] F,byte[] G)
+ {
+ int n=PUB.n.getlen();
+ FF f=new FF(n);
+ FF.fromBytes(f,F);
+ f.power(PUB.e,PUB.n);
+ f.toBytes(G);
+ }
+
+ /* RSA decryption with the private key */
+ public static void DECRYPT(private_key PRIV,byte[] G,byte[] F)
+ {
+ int n=PRIV.p.getlen();
+ FF g=new FF(2*n);
+
+ FF.fromBytes(g,G);
+ FF jp=g.dmod(PRIV.p);
+ FF jq=g.dmod(PRIV.q);
+
+ jp.skpow(PRIV.dp,PRIV.p);
+ jq.skpow(PRIV.dq,PRIV.q);
+
+ g.zero();
+ g.dscopy(jp);
+ jp.mod(PRIV.q);
+ if (FF.comp(jp,jq)>0) jq.add(PRIV.q);
+ jq.sub(jp);
+ jq.norm();
+
+ FF t=FF.mul(PRIV.c,jq);
+ jq=t.dmod(PRIV.q);
+
+ t=FF.mul(jq,PRIV.p);
+ g.add(t);
+ g.norm();
+
+ g.toBytes(F);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/RSA4096/private_key.java b/src/main/java/org/apache/milagro/amcl/RSA4096/private_key.java
new file mode 100644
index 0000000..1fbc69e
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA4096/private_key.java
@@ -0,0 +1,16 @@
+
+package org.apache.milagro.amcl.RSA4096;
+
+public final class private_key
+{
+ public FF p,q,dp,dq,c;
+
+ public private_key(int n)
+ {
+ p=new FF(n);
+ q=new FF(n);
+ dp=new FF(n);
+ dq=new FF(n);
+ c=new FF(n);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/milagro/amcl/RSA4096/public_key.java b/src/main/java/org/apache/milagro/amcl/RSA4096/public_key.java
new file mode 100644
index 0000000..ffa6cb3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/RSA4096/public_key.java
@@ -0,0 +1,14 @@
+
+package org.apache.milagro.amcl.RSA4096;
+
+public final class public_key
+{
+ public int e;
+ public FF n;
+
+ public public_key(int m)
+ {
+ e=0;
+ n=new FF(m);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/SECP256K1/BIG.java b/src/main/java/org/apache/milagro/amcl/SECP256K1/BIG.java
new file mode 100644
index 0000000..bab6041
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/SECP256K1/BIG.java
@@ -0,0 +1,917 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL BIG number class */
+
+package org.apache.milagro.amcl.SECP256K1;
+import org.apache.milagro.amcl.RAND;
+
+public class BIG {
+
+ public static final int CHUNK=64; /* Set word size */
+
+ public static final int MODBYTES=32; //(1+(MODBITS-1)/8);
+ public static final int BASEBITS=56;
+
+ public static final int NLEN=(1+((8*MODBYTES-1)/BASEBITS));
+ public static final int DNLEN=2*NLEN;
+ public static final long BMASK=(((long)1<<BASEBITS)-1);
+
+ public static final int HBITS=BASEBITS/2;
+ public static final long HMASK=(((long)1<<HBITS)-1);
+ public static final int NEXCESS = ((int)1<<(CHUNK-BASEBITS-1));
+ public static final int BIGBITS=(MODBYTES*8);
+
+
+
+ protected long[] w=new long[NLEN];
+/* Constructors */
+ public BIG()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+ public BIG(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public BIG(long[] x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x[i];
+ }
+
+ public long get(int i)
+ {
+ return w[i];
+ }
+
+ public void set(int i,long x)
+ {
+ w[i]=x;
+ }
+
+
+/* Conditional swap of two bigs depending on d using XOR - no branches */
+ public void cswap(BIG b,int d)
+ {
+ int i;
+ long t,c=(long)d;
+ c=~(c-1);
+
+ for (i=0;i<NLEN;i++)
+ {
+ t=c&(w[i]^b.w[i]);
+ w[i]^=t;
+ b.w[i]^=t;
+ }
+ }
+
+ public void cmove(BIG g,int d)
+ {
+ int i;
+ long t,b=-d;
+
+ for (i=0;i<NLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&b;
+ }
+ }
+
+ public static long cast_to_chunk(int x)
+ {
+ return (long)x;
+ }
+
+/* normalise BIG - force all digits < 2^BASEBITS */
+ public long norm() {
+ long d,carry=0;
+ for (int i=0;i<NLEN-1;i++)
+ {
+ d=w[i]+carry;
+ w[i]=d&BMASK;
+ carry=(d>>BASEBITS);
+ }
+ w[NLEN-1]=(w[NLEN-1]+carry);
+ return (long)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS));
+ }
+
+/* return number of bits */
+ public int nbits() {
+ BIG t=new BIG(this);
+ int bts,k=NLEN-1;
+ long c;
+ t.norm();
+ while (k>=0 && t.w[k]==0) k--;
+ if (k<0) return 0;
+ bts=BASEBITS*k;
+ c=t.w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+ public String toRawString()
+ {
+ BIG b=new BIG(this);
+ String s="(";
+ for (int i=0;i<NLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[NLEN-1]); s+=")";
+ return s;
+ }
+
+/* Convert to Hex String */
+ public String toString() {
+ BIG b;
+ String s="";
+ int len=nbits();
+
+ if (len%4==0) len/=4;
+ else {len/=4; len++;}
+ if (len<MODBYTES*2) len=MODBYTES*2;
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new BIG(this);
+ b.shr(i*4);
+ s+=Long.toHexString(b.w[0]&15);
+ }
+ return s;
+ }
+
+/* set this[i]+=x*y+c, and return high part */
+
+ public static long[] muladd(long a,long b,long c,long r)
+ {
+ long x0,x1,y0,y1;
+ long[] tb=new long[2];
+ x0=a&HMASK;
+ x1=(a>>HBITS);
+ y0=b&HMASK;
+ y1=(b>>HBITS);
+ long bot=x0*y0;
+ long top=x1*y1;
+ long mid=x0*y1+x1*y0;
+ x0=mid&HMASK;
+ x1=(mid>>HBITS);
+ bot+=x0<<HBITS; bot+=c; bot+=r;
+ top+=x1;
+ long carry=bot>>BASEBITS;
+ bot&=BMASK;
+ top+=carry;
+ tb[0]=top;
+ tb[1]=bot;
+ return tb;
+ }
+
+/* this*=x, where x is >NEXCESS */
+ public long pmul(int c)
+ {
+ long ak,carry=0;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ ak=w[i];
+ w[i]=0;
+
+ cr=muladd(ak,(long)c,carry,w[i]);
+ carry=cr[0];
+ w[i]=cr[1];
+
+ }
+ return carry;
+ }
+
+/* return this*c and catch overflow in DBIG */
+ public DBIG pxmul(int c)
+ {
+ DBIG m=new DBIG(0);
+ long[] cr=new long[2];
+ long carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(w[j],(long)c,carry,m.w[j]);
+ carry=cr[0];
+ m.w[j]=cr[1];
+ }
+ m.w[NLEN]=carry;
+ return m;
+ }
+
+/* divide by 3 */
+ public int div3()
+ {
+ long ak,base,carry=0;
+ norm();
+ base=((long)1<<BASEBITS);
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ ak=(carry*base+w[i]);
+ w[i]=ak/3;
+ carry=ak%3;
+ }
+ return (int)carry;
+ }
+
+/* return a*b where result fits in a BIG */
+ public static BIG smul(BIG a,BIG b)
+ {
+ long carry;
+ long[] cr=new long[2];
+ BIG c=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ if (i+j<NLEN)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ }
+ return c;
+ }
+
+/* return a*b as DBIG */
+/* Inputs must be normed */
+ public static DBIG mul(BIG a,BIG b)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(a.w[i],b.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ return c;
+ }
+
+/* return a^2 as DBIG */
+/* Input must be normed */
+ public static DBIG sqr(BIG a)
+ {
+ DBIG c=new DBIG(0);
+ long carry;
+ long[] cr=new long[2];
+
+ for (int i=0;i<NLEN;i++)
+ {
+ carry=0;
+ for (int j=i+1;j<NLEN;j++)
+ {
+ cr=muladd(2*a.w[i],a.w[j],carry,c.w[i+j]);
+ carry=cr[0];
+ c.w[i+j]=cr[1];
+ }
+ c.w[NLEN+i]=carry;
+ }
+
+ for (int i=0;i<NLEN;i++)
+ {
+ cr=muladd(a.w[i],a.w[i],0,c.w[2*i]);
+ c.w[2*i+1]+=cr[0];
+ c.w[2*i]=cr[1];
+ }
+ c.norm();
+ return c;
+ }
+
+ static BIG monty(BIG md,long MC,DBIG d)
+ {
+ BIG b;
+ long m,carry;
+ long[] cr=new long[2];
+ for (int i=0;i<NLEN;i++)
+ {
+ if (MC==-1) m=(-d.w[i])&BMASK;
+ else
+ {
+ if (MC==1) m=d.w[i];
+ else m=(MC*d.w[i])&BMASK;
+ }
+
+ carry=0;
+ for (int j=0;j<NLEN;j++)
+ {
+ cr=muladd(m,md.w[j],carry,d.w[i+j]);
+ carry=cr[0];
+ d.w[i+j]=cr[1];
+ }
+ d.w[NLEN+i]+=carry;
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<NLEN;i++ )
+ b.w[i]=d.w[NLEN+i];
+ b.norm();
+ return b;
+ }
+
+
+
+/****************************************************************************/
+
+ public void xortop(long x)
+ {
+ w[NLEN-1]^=x;
+ }
+
+/* set x = x mod 2^m */
+ public void mod2m(int m)
+ {
+ int i,wd,bt;
+ wd=m/BASEBITS;
+ bt=m%BASEBITS;
+ w[wd]&=((cast_to_chunk(1)<<bt)-1);
+ for (i=wd+1;i<NLEN;i++) w[i]=0;
+ }
+
+/* return n-th bit */
+ public int bit(int n)
+ {
+ if ((w[n/BASEBITS]&(cast_to_chunk(1)<<(n%BASEBITS)))>0) return 1;
+ else return 0;
+ }
+
+/* Shift right by less than a word */
+ public int fshr(int k) {
+ int r=(int)(w[0]&((cast_to_chunk(1)<<k)-1)); /* shifted out part */
+ for (int i=0;i<NLEN-1;i++)
+ w[i]=(w[i]>>k)|((w[i+1]<<(BASEBITS-k))&BMASK);
+ w[NLEN-1]=w[NLEN-1]>>k;
+ return r;
+ }
+
+/* Shift right by less than a word */
+ public int fshl(int k) {
+ w[NLEN-1]=((w[NLEN-1]<<k))|(w[NLEN-2]>>(BASEBITS-k));
+ for (int i=NLEN-2;i>0;i--)
+ w[i]=((w[i]<<k)&BMASK)|(w[i-1]>>(BASEBITS-k));
+ w[0]=(w[0]<<k)&BMASK;
+ return (int)(w[NLEN-1]>>((8*MODBYTES)%BASEBITS)); /* return excess - only used in FF.java */
+ }
+
+/* test for zero */
+ public boolean iszilch() {
+ for (int i=0;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* set to zero */
+ public void zero()
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* set to one */
+ public void one()
+ {
+ w[0]=1;
+ for (int i=1;i<NLEN;i++)
+ w[i]=0;
+ }
+
+/* Test for equal to one */
+ public boolean isunity()
+ {
+ for (int i=1;i<NLEN;i++)
+ if (w[i]!=0) return false;
+ if (w[0]!=1) return false;
+ return true;
+ }
+
+/* Copy from another BIG */
+ public void copy(BIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* general shift right */
+ public void shr(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+ for (int i=0;i<NLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BASEBITS-n))&BMASK);
+ if (NLEN>m) w[NLEN-m-1]=w[NLEN-1]>>n;
+ for (int i=NLEN-m;i<NLEN;i++) w[i]=0;
+ }
+
+/* general shift left */
+ public void shl(int k) {
+ int n=k%BASEBITS;
+ int m=k/BASEBITS;
+
+ w[NLEN-1]=((w[NLEN-1-m]<<n));
+ if (NLEN>=m+2) w[NLEN-1]|=(w[NLEN-m-2]>>(BASEBITS-n));
+
+ for (int i=NLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BMASK)|(w[i-m-1]>>(BASEBITS-n));
+ w[m]=(w[0]<<n)&BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* return this+x */
+ public BIG plus(BIG x) {
+ BIG s=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ s.w[i]=w[i]+x.w[i];
+ return s;
+ }
+
+/* this+=x */
+ public void add(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this|=x */
+ public void or(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]|=x.w[i];
+ }
+
+
+/* this+=x, where x is int */
+ public void inc(int x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* this+=x, where x is long */
+ public void incl(long x) {
+ norm();
+ w[0]+=x;
+ }
+
+/* return this.x */
+ public BIG minus(BIG x) {
+ BIG d=new BIG(0);
+ for (int i=0;i<NLEN;i++)
+ d.w[i]=w[i]-x.w[i];
+ return d;
+ }
+
+/* this-=x */
+ public void sub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+/* reverse subtract this=x-this */
+ public void rsub(BIG x) {
+ for (int i=0;i<NLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* this-=x where x is int */
+ public void dec(int x) {
+ norm();
+ w[0]-=x;
+ }
+
+/* this*=x, where x is small int<NEXCESS */
+ public void imul(int c)
+ {
+ for (int i=0;i<NLEN;i++) w[i]*=c;
+ }
+
+/* convert this BIG to byte array */
+ public void tobytearray(byte[] b,int n)
+ {
+
+ BIG c=new BIG(this);
+ c.norm();
+
+ for (int i=MODBYTES-1;i>=0;i--)
+ {
+ b[i+n]=(byte)c.w[0];
+ c.fshr(8);
+ }
+ }
+
+/* convert from byte array to BIG */
+ public static BIG frombytearray(byte[] b,int n)
+ {
+ BIG m=new BIG(0);
+
+ for (int i=0;i<MODBYTES;i++)
+ {
+ m.fshl(8); m.w[0]+=(int)b[i+n]&0xff;
+ //m.inc((int)b[i]&0xff);
+ }
+ return m;
+ }
+
+ public void toBytes(byte[] b)
+ {
+ tobytearray(b,0);
+ }
+
+ public static BIG fromBytes(byte[] b)
+ {
+ return frombytearray(b,0);
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(BIG a,BIG b)
+ {
+ for (int i=NLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* Arazi and Qi inversion mod 256 */
+ public static int invmod256(int a)
+ {
+ int U,t1,t2,b,c;
+ t1=0;
+ c=(a>>1)&1;
+ t1+=c;
+ t1&=1;
+ t1=2-t1;
+ t1<<=1;
+ U=t1+1;
+
+// i=2
+ b=a&3;
+ t1=U*b; t1>>=2;
+ c=(a>>2)&3;
+ t2=(U*c)&3;
+ t1+=t2;
+ t1*=U; t1&=3;
+ t1=4-t1;
+ t1<<=2;
+ U+=t1;
+
+// i=4
+ b=a&15;
+ t1=U*b; t1>>=4;
+ c=(a>>4)&15;
+ t2=(U*c)&15;
+ t1+=t2;
+ t1*=U; t1&=15;
+ t1=16-t1;
+ t1<<=4;
+ U+=t1;
+
+ return U;
+ }
+
+/* a=1/a mod 2^256. This is very fast! */
+ public void invmod2m()
+ {
+ int i;
+ BIG U=new BIG(0);
+ BIG b=new BIG(0);
+ BIG c=new BIG(0);
+
+ U.inc(invmod256(lastbits(8)));
+
+ for (i=8;i<BIGBITS;i<<=1)
+ {
+ U.norm();
+ b.copy(this); b.mod2m(i);
+ BIG t1=BIG.smul(U,b);
+ t1.shr(i);
+
+ c.copy(this); c.shr(i); c.mod2m(i);
+ BIG t2=BIG.smul(U,c); t2.mod2m(i);
+
+ t1.add(t2);
+ t1.norm();
+ b=BIG.smul(t1,U); t1.copy(b);
+ t1.mod2m(i);
+
+ t2.one(); t2.shl(i); t1.rsub(t2); t1.norm();
+
+ t1.shl(i);
+ U.add(t1);
+ }
+ U.mod2m(BIGBITS);
+ copy(U);
+ norm();
+ }
+
+/* reduce this mod m */
+ public void mod(BIG m1)
+ {
+ int k=0;
+ BIG r=new BIG(0);
+ BIG m=new BIG(m1);
+
+ norm();
+ if (comp(this,m)<0) return;
+ do
+ {
+ m.fshl(1);
+ k++;
+ } while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.fshr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1)));
+ k--;
+ }
+ }
+
+/* divide this by m */
+ public void div(BIG m1)
+ {
+ int d,k=0;
+ norm();
+ BIG e=new BIG(1);
+ BIG m=new BIG(m1);
+ BIG b=new BIG(this);
+ BIG r=new BIG(0);
+ zero();
+
+ while (comp(b,m)>=0)
+ {
+ e.fshl(1);
+ m.fshl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.fshr(1);
+ e.fshr(1);
+
+ r.copy(b);
+ r.sub(m);
+ r.norm();
+ d=(int)(1-((r.w[NLEN-1]>>(CHUNK-1))&1));
+ b.cmove(r,d);
+ r.copy(this);
+ r.add(e);
+ r.norm();
+ cmove(r,d);
+ k--;
+ }
+ }
+
+/* return parity */
+ public int parity()
+ {
+ return (int)(w[0]%2);
+ }
+
+/* return n last bits */
+ public int lastbits(int n)
+ {
+ int msk=(1<<n)-1;
+ norm();
+ return ((int)w[0])&msk;
+ }
+
+/* get 8*MODBYTES size random number */
+ public static BIG random(RAND rng)
+ {
+ BIG m=new BIG(0);
+ int i,b,j=0,r=0;
+
+/* generate random BIG */
+ for (i=0;i<8*MODBYTES;i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ m.shl(1); m.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ return m;
+ }
+
+/* Create random BIG in portable way, one bit at a time */
+ public static BIG randomnum(BIG q,RAND rng)
+ {
+ DBIG d=new DBIG(0);
+ int i,b,j=0,r=0;
+ for (i=0;i<2*q.nbits();i++)
+ {
+ if (j==0) r=rng.getByte();
+ else r>>=1;
+
+ b=r&1;
+ d.shl(1); d.w[0]+=b;// m.inc(b);
+ j++; j&=7;
+ }
+ BIG m=d.mod(q);
+ return m;
+ }
+
+/* return a*b mod m */
+ public static BIG modmul(BIG a1,BIG b1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ BIG b=new BIG(b1);
+ a.mod(m);
+ b.mod(m);
+ DBIG d=mul(a,b);
+ return d.mod(m);
+ }
+
+/* return a^2 mod m */
+ public static BIG modsqr(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ DBIG d=sqr(a);
+ return d.mod(m);
+ }
+
+/* return -a mod m */
+ public static BIG modneg(BIG a1,BIG m)
+ {
+ BIG a=new BIG(a1);
+ a.mod(m);
+ return m.minus(a);
+ }
+
+/* return this^e mod m */
+ public BIG powmod(BIG e1,BIG m)
+ {
+ BIG e=new BIG(e1);
+ int bt;
+ norm();
+ e.norm();
+ BIG a=new BIG(1);
+ BIG z=new BIG(e);
+ BIG s=new BIG(this);
+ while (true)
+ {
+ bt=z.parity();
+ z.fshr(1);
+ if (bt==1) a=modmul(a,s,m);
+ if (z.iszilch()) break;
+ s=modsqr(s,m);
+ }
+ return a;
+ }
+
+/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+ public int jacobi(BIG p)
+ {
+ int n8,k,m=0;
+ BIG t=new BIG(0);
+ BIG x=new BIG(0);
+ BIG n=new BIG(0);
+ BIG zilch=new BIG(0);
+ BIG one=new BIG(1);
+ if (p.parity()==0 || comp(this,zilch)==0 || comp(p,one)<=0) return 0;
+ norm();
+ x.copy(this);
+ n.copy(p);
+ x.mod(p);
+
+ while (comp(n,one)>0)
+ {
+ if (comp(x,zilch)==0) return 0;
+ n8=n.lastbits(3);
+ k=0;
+ while (x.parity()==0)
+ {
+ k++;
+ x.shr(1);
+ }
+ if (k%2==1) m+=(n8*n8-1)/8;
+ m+=(n8-1)*(x.lastbits(2)-1)/4;
+ t.copy(n);
+ t.mod(x);
+ n.copy(x);
+ x.copy(t);
+ m%=2;
+
+ }
+ if (m==0) return 1;
+ else return -1;
+ }
+
+/* this=1/this mod p. Binary method */
+ public void invmodp(BIG p)
+ {
+ mod(p);
+ BIG u=new BIG(this);
+ BIG v=new BIG(p);
+ BIG x1=new BIG(1);
+ BIG x2=new BIG(0);
+ BIG t=new BIG(0);
+ BIG one=new BIG(1);
+
+ while (comp(u,one)!=0 && comp(v,one)!=0)
+ {
+ while (u.parity()==0)
+ {
+ u.fshr(1);
+ if (x1.parity()!=0)
+ {
+ x1.add(p);
+ x1.norm();
+ }
+ x1.fshr(1);
+ }
+ while (v.parity()==0)
+ {
+ v.fshr(1);
+ if (x2.parity()!=0)
+ {
+ x2.add(p);
+ x2.norm();
+ }
+ x2.fshr(1);
+ }
+ if (comp(u,v)>=0)
+ {
+ u.sub(v);
+ u.norm();
+ if (comp(x1,x2)>=0) x1.sub(x2);
+ else
+ {
+ t.copy(p);
+ t.sub(x2);
+ x1.add(t);
+ }
+ x1.norm();
+ }
+ else
+ {
+ v.sub(u);
+ v.norm();
+ if (comp(x2,x1)>=0) x2.sub(x1);
+ else
+ {
+ t.copy(p);
+ t.sub(x1);
+ x2.add(t);
+ }
+ x2.norm();
+ }
+ }
+ if (comp(u,one)==0) copy(x1);
+ else copy(x2);
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/SECP256K1/DBIG.java b/src/main/java/org/apache/milagro/amcl/SECP256K1/DBIG.java
new file mode 100644
index 0000000..a66adff
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/SECP256K1/DBIG.java
@@ -0,0 +1,279 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* AMCL double length DBIG number class */
+
+package org.apache.milagro.amcl.SECP256K1;
+
+public class DBIG {
+ protected long[] w=new long[BIG.DNLEN];
+
+/* normalise this */
+ public void norm() {
+ long d,carry=0;
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ d=w[i]+carry;
+ carry=d>>BIG.BASEBITS;
+ w[i]=d&BIG.BMASK;
+ }
+ w[BIG.DNLEN-1]=(w[BIG.DNLEN-1]+carry);
+ }
+
+
+/*
+ public String toRawString()
+ {
+ DBIG b=new DBIG(this);
+ String s="(";
+ for (int i=0;i<BIG.DNLEN-1;i++)
+ {
+ s+=Long.toHexString(b.w[i]); s+=",";
+ }
+ s+=Long.toHexString(b.w[BIG.DNLEN-1]); s+=")";
+ return s;
+ }
+*/
+
+/* split DBIG at position n, return higher half, keep lower half */
+ public BIG split(int n)
+ {
+ BIG t=new BIG(0);
+ int m=n%BIG.BASEBITS;
+ long nw,carry=w[BIG.DNLEN-1]<<(BIG.BASEBITS-m);
+
+ for (int i=BIG.DNLEN-2;i>=BIG.NLEN-1;i--)
+ {
+ nw=(w[i]>>m)|carry;
+ carry=(w[i]<<(BIG.BASEBITS-m))&BIG.BMASK;
+ t.w[i-BIG.NLEN+1]=nw;
+ //t.set(i-BIG.NLEN+1,nw);
+ }
+ w[BIG.NLEN-1]&=(((long)1<<m)-1);
+ return t;
+ }
+
+/****************************************************************************/
+
+/* return number of bits in this */
+ public int nbits() {
+ int bts,k=BIG.DNLEN-1;
+ long c;
+ norm();
+ while (w[k]==0 && k>=0) k--;
+ if (k<0) return 0;
+ bts=BIG.BASEBITS*k;
+ c=w[k];
+ while (c!=0) {c/=2; bts++;}
+ return bts;
+ }
+
+/* convert this to string */
+ public String toString() {
+ DBIG b;
+ String s="";
+ int len=nbits();
+ if (len%4==0) len>>=2; //len/=4;
+ else {len>>=2; len++;}
+
+ for (int i=len-1;i>=0;i--)
+ {
+ b=new DBIG(this);
+ b.shr(i*4);
+ s+=Integer.toHexString((int)(b.w[0]&15));
+ }
+ return s;
+ }
+
+ public void cmove(DBIG g,int d)
+ {
+ int i;
+ for (i=0;i<BIG.DNLEN;i++)
+ {
+ w[i]^=(w[i]^g.w[i])&BIG.cast_to_chunk(-d);
+ }
+ }
+
+/* Constructors */
+ public DBIG(int x)
+ {
+ w[0]=x;
+ for (int i=1;i<BIG.DNLEN;i++)
+ w[i]=0;
+ }
+
+ public DBIG(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+ public DBIG(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN-1;i++)
+ w[i]=x.w[i]; //get(i);
+
+ w[BIG.NLEN-1]=x.w[(BIG.NLEN-1)]&BIG.BMASK; /* top word normalized */
+ w[BIG.NLEN]=(x.w[(BIG.NLEN-1)]>>BIG.BASEBITS);
+
+ for (int i=BIG.NLEN+1;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* Copy from another DBIG */
+ public void copy(DBIG x)
+ {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i];
+ }
+
+/* Copy into upper part */
+ public void ucopy(BIG x)
+ {
+ for (int i=0;i<BIG.NLEN;i++)
+ w[i]=0;
+ for (int i=BIG.NLEN;i<BIG.DNLEN;i++)
+ w[i]=x.w[i-BIG.NLEN];
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ for (int i=0;i<BIG.DNLEN;i++)
+ if (w[i]!=0) return false;
+ return true;
+ }
+
+/* shift this right by k bits */
+ public void shr(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+ for (int i=0;i<BIG.DNLEN-m-1;i++)
+ w[i]=(w[m+i]>>n)|((w[m+i+1]<<(BIG.BASEBITS-n))&BIG.BMASK);
+ w[BIG.DNLEN-m-1]=w[BIG.DNLEN-1]>>n;
+ for (int i=BIG.DNLEN-m;i<BIG.DNLEN;i++) w[i]=0;
+ }
+
+/* shift this left by k bits */
+ public void shl(int k) {
+ int n=k%BIG.BASEBITS;
+ int m=k/BIG.BASEBITS;
+
+ w[BIG.DNLEN-1]=((w[BIG.DNLEN-1-m]<<n))|(w[BIG.DNLEN-m-2]>>(BIG.BASEBITS-n));
+ for (int i=BIG.DNLEN-2;i>m;i--)
+ w[i]=((w[i-m]<<n)&BIG.BMASK)|(w[i-m-1]>>(BIG.BASEBITS-n));
+ w[m]=(w[0]<<n)&BIG.BMASK;
+ for (int i=0;i<m;i++) w[i]=0;
+ }
+
+/* this+=x */
+ public void add(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]+=x.w[i];
+ }
+
+/* this-=x */
+ public void sub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]-=x.w[i];
+ }
+
+ public void rsub(DBIG x) {
+ for (int i=0;i<BIG.DNLEN;i++)
+ w[i]=x.w[i]-w[i];
+ }
+
+/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+ public static int comp(DBIG a,DBIG b)
+ {
+ for (int i=BIG.DNLEN-1;i>=0;i--)
+ {
+ if (a.w[i]==b.w[i]) continue;
+ if (a.w[i]>b.w[i]) return 1;
+ else return -1;
+ }
+ return 0;
+ }
+
+/* reduces this DBIG mod a BIG, and returns the BIG */
+ public BIG mod(BIG c)
+ {
+ int k=0;
+ norm();
+ DBIG m=new DBIG(c);
+ DBIG r=new DBIG(0);
+
+ if (comp(this,m)<0) return new BIG(this);
+
+ do
+ {
+ m.shl(1);
+ k++;
+ }
+ while (comp(this,m)>=0);
+
+ while (k>0)
+ {
+ m.shr(1);
+
+ r.copy(this);
+ r.sub(m);
+ r.norm();
+ cmove(r,(int)(1-((r.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1)));
+
+ k--;
+ }
+ return new BIG(this);
+ }
+
+/* return this/c */
+ public BIG div(BIG c)
+ {
+ int d,k=0;
+ DBIG m=new DBIG(c);
+ DBIG dr=new DBIG(0);
+ BIG r=new BIG(0);
+ BIG a=new BIG(0);
+ BIG e=new BIG(1);
+ norm();
+
+ while (comp(this,m)>=0)
+ {
+ e.fshl(1);
+ m.shl(1);
+ k++;
+ }
+
+ while (k>0)
+ {
+ m.shr(1);
+ e.shr(1);
+
+ dr.copy(this);
+ dr.sub(m);
+ dr.norm();
+ d=(int)(1-((dr.w[BIG.DNLEN-1]>>(BIG.CHUNK-1))&1));
+ cmove(dr,d);
+ r.copy(a);
+ r.add(e);
+ r.norm();
+ a.cmove(r,d);
+ k--;
+ }
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/SECP256K1/ECDH.java b/src/main/java/org/apache/milagro/amcl/SECP256K1/ECDH.java
new file mode 100644
index 0000000..485dfeb
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/SECP256K1/ECDH.java
@@ -0,0 +1,594 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve API high-level functions */
+
+package org.apache.milagro.amcl.SECP256K1;
+
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.HASH256;
+import org.apache.milagro.amcl.HASH384;
+import org.apache.milagro.amcl.HASH512;
+import org.apache.milagro.amcl.AES;
+
+public final class ECDH {
+ public static final int INVALID_PUBLIC_KEY=-2;
+ public static final int ERROR=-3;
+ public static final int INVALID=-4;
+ public static final int EFS=BIG.MODBYTES;
+ public static final int EGS=BIG.MODBYTES;
+// public static final int EAS=16;
+// public static final int EBS=16;
+
+// public static final int SHA256=32;
+// public static final int SHA384=48;
+// public static final int SHA512=64;
+
+
+// public static final int HASH_TYPE=SHA512;
+
+
+/* Convert Integer to n-byte array */
+ public static byte[] inttoBytes(int n,int len)
+ {
+ int i;
+ byte[] b=new byte[len];
+
+ for (i=0;i<len;i++) b[i]=0;
+ i=len;
+ while (n>0 && i>0)
+ {
+ i--;
+ b[i]=(byte)(n&0xff);
+ n/=256;
+ }
+ return b;
+ }
+
+ public static byte[] hashit(int sha,byte[] A,int n,byte[] B,int pad)
+ {
+ byte[] R=null;
+
+ if (sha==ECP.SHA256)
+ {
+ HASH256 H=new HASH256();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA384)
+ {
+ HASH384 H=new HASH384();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (sha==ECP.SHA512)
+ {
+ HASH512 H=new HASH512();
+ H.process_array(A); if (n>0) H.process_num(n);
+ if (B!=null) H.process_array(B);
+ R=H.hash();
+ }
+ if (R==null) return null;
+
+ if (pad==0) return R;
+/* If pad>0 output is truncated or padded to pad bytes */
+ byte[] W=new byte[pad];
+ if (pad<=sha)
+ {
+ for (int i=0;i<pad;i++) W[i]=R[i];
+ }
+ else
+ {
+ for (int i=0;i<sha;i++) W[i+pad-sha]=R[i];
+ for (int i=0;i<pad-sha;i++) W[i]=0;
+
+ //for (int i=0;i<sha;i++) W[i]=R[i];
+ //for (int i=sha;i<pad;i++) W[i]=0;
+ }
+ return W;
+ }
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+ public static byte[] KDF1(int sha,byte[] Z,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output K in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=0;counter<cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,null,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+ return K;
+ }
+
+ public static byte[] KDF2(int sha,byte[] Z,byte[] P,int olen)
+ {
+/* NOTE: the parameter olen is the length of the output k in bytes */
+ int hlen=sha;
+ byte[] K=new byte[olen];
+ byte[] B;
+ int counter,cthreshold,k=0;
+
+ for (int i=0;i<K.length;i++) K[i]=0;
+
+ cthreshold=olen/hlen; if (olen%hlen!=0) cthreshold++;
+
+ for (counter=1;counter<=cthreshold;counter++)
+ {
+ B=hashit(sha,Z,counter,P,0);
+ if (k+hlen>olen) for (int i=0;i<olen%hlen;i++) K[k++]=B[i];
+ else for (int i=0;i<hlen;i++) K[k++]=B[i];
+ }
+
+ return K;
+ }
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+ public static byte[] PBKDF2(int sha,byte[] Pass,byte[] Salt,int rep,int olen)
+ {
+ int i,j,k,len,d,opt;
+ d=olen/sha; if (olen%sha!=0) d++;
+ byte[] F=new byte[sha];
+ byte[] U=new byte[sha];
+ byte[] S=new byte[Salt.length+4];
+
+ byte[] K=new byte[d*sha];
+ opt=0;
+
+ for (i=1;i<=d;i++)
+ {
+ for (j=0;j<Salt.length;j++) S[j]=Salt[j];
+ byte[] N=inttoBytes(i,4);
+ for (j=0;j<4;j++) S[Salt.length+j]=N[j];
+
+ HMAC(sha,S,Pass,F);
+
+ for (j=0;j<sha;j++) U[j]=F[j];
+ for (j=2;j<=rep;j++)
+ {
+ HMAC(sha,U,Pass,U);
+ for (k=0;k<sha;k++) F[k]^=U[k];
+ }
+ for (j=0;j<sha;j++) K[opt++]=F[j];
+ }
+ byte[] key=new byte[olen];
+ for (i=0;i<olen;i++) key[i]=K[i];
+ return key;
+ }
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen */
+ public static int HMAC(int sha,byte[] M,byte[] K,byte[] tag)
+ {
+ /* Input is from an octet m *
+ * olen is requested output length in bytes. k is the key *
+ * The output is the calculated tag */
+ int b=64;
+ if (sha>32) b=128;
+ byte[] B;
+ byte[] K0=new byte[b];
+ int olen=tag.length;
+
+ //b=K0.length;
+ if (olen<4 /*|| olen>sha*/) return 0;
+
+ for (int i=0;i<b;i++) K0[i]=0;
+
+ if (K.length > b)
+ {
+ B=hashit(sha,K,0,null,0);
+ for (int i=0;i<sha;i++) K0[i]=B[i];
+ }
+ else
+ for (int i=0;i<K.length;i++ ) K0[i]=K[i];
+
+ for (int i=0;i<b;i++) K0[i]^=0x36;
+ B=hashit(sha,K0,0,M,0);
+
+ for (int i=0;i<b;i++) K0[i]^=0x6a;
+ B=hashit(sha,K0,0,B,olen);
+
+ for (int i=0;i<olen;i++) tag[i]=B[i];
+
+ return 1;
+ }
+
+/* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */
+ public static byte[] AES_CBC_IV0_ENCRYPT(byte[] K,byte[] M)
+ { /* AES CBC encryption, with Null IV and key K */
+ /* Input is from an octet string M, output is to an octet string C */
+ /* Input is padded as necessary to make up a full final block */
+ AES a=new AES();
+ boolean fin;
+ int i,j,ipt,opt;
+ byte[] buff=new byte[16];
+ int clen=16+(M.length/16)*16;
+
+ byte[] C=new byte[clen];
+ int padlen;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ ipt=opt=0;
+ fin=false;
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ if (ipt<M.length) buff[i]=M[ipt++];
+ else {fin=true; break;}
+ }
+ if (fin) break;
+ a.encrypt(buff);
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ }
+
+/* last block, filled up to i-th index */
+
+ padlen=16-i;
+ for (j=i;j<16;j++) buff[j]=(byte)padlen;
+
+ a.encrypt(buff);
+
+ for (i=0;i<16;i++)
+ C[opt++]=buff[i];
+ a.end();
+ return C;
+ }
+
+/* returns plaintext if all consistent, else returns null string */
+ public static byte[] AES_CBC_IV0_DECRYPT(byte[] K,byte[] C)
+ { /* padding is removed */
+ AES a=new AES();
+ int i,ipt,opt,ch;
+ byte[] buff=new byte[16];
+ byte[] MM=new byte[C.length];
+ boolean fin,bad;
+ int padlen;
+ ipt=opt=0;
+
+ a.init(AES.CBC,K.length,K,null);
+
+ if (C.length==0) return new byte[0];
+ ch=C[ipt++];
+
+ fin=false;
+
+ for(;;)
+ {
+ for (i=0;i<16;i++)
+ {
+ buff[i]=(byte)ch;
+ if (ipt>=C.length) {fin=true; break;}
+ else ch=C[ipt++];
+ }
+ a.decrypt(buff);
+ if (fin) break;
+ for (i=0;i<16;i++)
+ MM[opt++]=buff[i];
+ }
+
+ a.end();
+ bad=false;
+ padlen=buff[15];
+ if (i!=15 || padlen<1 || padlen>16) bad=true;
+ if (padlen>=2 && padlen<=16)
+ for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=true;
+
+ if (!bad) for (i=0;i<16-padlen;i++)
+ MM[opt++]=buff[i];
+
+ if (bad) return new byte[0];
+
+ byte[] M=new byte[opt];
+ for (i=0;i<opt;i++) M[i]=MM[i];
+
+ return M;
+ }
+
+/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
+ * where S is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in S
+ * otherwise it is generated randomly internally */
+ public static int KEY_PAIR_GENERATE(RAND RNG,byte[] S,byte[] W)
+ {
+ BIG r,s;
+ ECP G,WP;
+ int res=0;
+ // byte[] T=new byte[EFS];
+
+ G=ECP.generator();
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (RNG==null)
+ {
+ s=BIG.fromBytes(S);
+ s.mod(r);
+ }
+ else
+ {
+ s=BIG.randomnum(r,RNG);
+ }
+
+ //if (ROM.AES_S>0)
+ //{
+ // s.mod2m(2*ROM.AES_S);
+ //}
+ s.toBytes(S);
+
+ WP=G.mul(s);
+ WP.toBytes(W,false); // To use point compression on public keys, change to true
+
+ return res;
+ }
+
+/* validate public key. */
+ public static int PUBLIC_KEY_VALIDATE(byte[] W)
+ {
+ BIG r,q,k;
+ ECP WP=ECP.fromBytes(W);
+ int nb,res=0;
+
+ r=new BIG(ROM.CURVE_Order);
+
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+
+ if (res==0)
+ {
+
+ q=new BIG(ROM.Modulus);
+ nb=q.nbits();
+ k=new BIG(1); k.shl((nb+4)/2);
+ k.add(q);
+ k.div(r);
+
+ while (k.parity()==0)
+ {
+ k.shr(1);
+ WP.dbl();
+ }
+
+ if (!k.isunity()) WP=WP.mul(k);
+ if (WP.is_infinity()) res=INVALID_PUBLIC_KEY;
+ }
+ return res;
+ }
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+ public static int SVDP_DH(byte[] S,byte[] WD,byte[] Z)
+ {
+ BIG r,s,wx,wy,z;
+ int valid;
+ ECP W;
+ int res=0;
+ byte[] T=new byte[EFS];
+
+ s=BIG.fromBytes(S);
+
+ W=ECP.fromBytes(WD);
+ if (W.is_infinity()) res=ERROR;
+
+ if (res==0)
+ {
+ r=new BIG(ROM.CURVE_Order);
+ s.mod(r);
+
+ W=W.mul(s);
+ if (W.is_infinity()) res=ERROR;
+ else
+ {
+ W.getX().toBytes(T);
+ for (int i=0;i<EFS;i++) Z[i]=T[i];
+ }
+ }
+ return res;
+ }
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+ public static int SP_DSA(int sha,RAND RNG,byte[] S,byte[] F,byte[] C,byte[] D)
+ {
+ byte[] T=new byte[EFS];
+ BIG r,s,f,c,d,u,vx,w;
+ ECP G,V;
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ s=BIG.fromBytes(S);
+ f=BIG.fromBytes(B);
+
+ c=new BIG(0);
+ d=new BIG(0);
+ V=new ECP();
+
+ do {
+ u=BIG.randomnum(r,RNG);
+ w=BIG.randomnum(r,RNG); /* side channel masking */
+ //if (ROM.AES_S>0)
+ //{
+ // u.mod2m(2*ROM.AES_S);
+ //}
+ V.copy(G);
+ V=V.mul(u);
+ vx=V.getX();
+ c.copy(vx);
+ c.mod(r);
+ if (c.iszilch()) continue;
+
+ u.copy(BIG.modmul(u,w,r));
+
+ u.invmodp(r);
+ d.copy(BIG.modmul(s,c,r));
+ d.add(f);
+
+ d.copy(BIG.modmul(d,w,r));
+
+ d.copy(BIG.modmul(u,d,r));
+ } while (d.iszilch());
+
+ c.toBytes(T);
+ for (int i=0;i<EFS;i++) C[i]=T[i];
+ d.toBytes(T);
+ for (int i=0;i<EFS;i++) D[i]=T[i];
+ return 0;
+ }
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+ public static int VP_DSA(int sha,byte[] W,byte[] F, byte[] C,byte[] D)
+ {
+ BIG r,f,c,d,h2;
+ int res=0;
+ ECP G,WP,P;
+ int valid;
+
+ byte[] B=hashit(sha,F,0,null,BIG.MODBYTES);
+
+ G=ECP.generator();
+ r=new BIG(ROM.CURVE_Order);
+
+ c=BIG.fromBytes(C);
+ d=BIG.fromBytes(D);
+ f=BIG.fromBytes(B);
+
+ if (c.iszilch() || BIG.comp(c,r)>=0 || d.iszilch() || BIG.comp(d,r)>=0)
+ res=INVALID;
+
+ if (res==0)
+ {
+ d.invmodp(r);
+ f.copy(BIG.modmul(f,d,r));
+ h2=BIG.modmul(c,d,r);
+
+ WP=ECP.fromBytes(W);
+ if (WP.is_infinity()) res=ERROR;
+ else
+ {
+ P=new ECP();
+ P.copy(WP);
+ P=P.mul2(h2,G,f);
+ if (P.is_infinity()) res=INVALID;
+ else
+ {
+ d=P.getX();
+ d.mod(r);
+ if (BIG.comp(d,c)!=0) res=INVALID;
+ }
+ }
+ }
+
+ return res;
+ }
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+ public static byte[] ECIES_ENCRYPT(int sha,byte[] P1,byte[] P2,RAND RNG,byte[] W,byte[] M,byte[] V,byte[] T)
+ {
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] U=new byte[EGS];
+
+ if (KEY_PAIR_GENERATE(RNG,U,V)!=0) return new byte[0];
+ if (SVDP_DH(U,W,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] C=AES_CBC_IV0_ENCRYPT(K1,M);
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,T);
+
+ return C;
+ }
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+ public static byte[] ECIES_DECRYPT(int sha,byte[] P1,byte[] P2,byte[] V,byte[] C,byte[] T,byte[] U)
+ {
+
+ int i,len;
+
+ byte[] Z=new byte[EFS];
+ byte[] VZ=new byte[3*EFS+1];
+ byte[] K1=new byte[ECP.AESKEY];
+ byte[] K2=new byte[ECP.AESKEY];
+ byte[] TAG=new byte[T.length];
+
+ if (SVDP_DH(U,V,Z)!=0) return new byte[0];
+
+ for (i=0;i<2*EFS+1;i++) VZ[i]=V[i];
+ for (i=0;i<EFS;i++) VZ[2*EFS+1+i]=Z[i];
+
+ byte[] K=KDF2(sha,VZ,P1,2*ECP.AESKEY);
+
+ for (i=0;i<ECP.AESKEY;i++) {K1[i]=K[i]; K2[i]=K[ECP.AESKEY+i];}
+
+ byte[] M=AES_CBC_IV0_DECRYPT(K1,C);
+
+ if (M.length==0) return M;
+
+ byte[] L2=inttoBytes(P2.length,8);
+
+ byte[] AC=new byte[C.length+P2.length+8];
+
+ for (i=0;i<C.length;i++) AC[i]=C[i];
+ for (i=0;i<P2.length;i++) AC[C.length+i]=P2[i];
+ for (i=0;i<8;i++) AC[C.length+P2.length+i]=L2[i];
+
+ HMAC(sha,AC,K2,TAG);
+
+ boolean same=true;
+ for (i=0;i<T.length;i++) if (T[i]!=TAG[i]) same=false;
+ if (!same) return new byte[0];
+
+ return M;
+
+ }
+}
diff --git a/src/main/java/org/apache/milagro/amcl/SECP256K1/ECP.java b/src/main/java/org/apache/milagro/amcl/SECP256K1/ECP.java
new file mode 100644
index 0000000..84a10d3
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/SECP256K1/ECP.java
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Elliptic Curve Point class */
+
+package org.apache.milagro.amcl.SECP256K1;
+
+public final class ECP {
+
+ public static final int WEIERSTRASS=0;
+ public static final int EDWARDS=1;
+ public static final int MONTGOMERY=2;
+ public static final int NOT=0;
+ public static final int BN=1;
+ public static final int BLS=2;
+ public static final int D_TYPE=0;
+ public static final int M_TYPE=1;
+ public static final int POSITIVEX=0;
+ public static final int NEGATIVEX=1;
+
+ public static final int CURVETYPE=WEIERSTRASS;
+ public static final int CURVE_PAIRING_TYPE=NOT;
+ public static final int SEXTIC_TWIST=NOT;
+ public static final int SIGN_OF_X=NOT;
+
+ public static final int SHA256=32;
+ public static final int SHA384=48;
+ public static final int SHA512=64;
+
+ public static final int HASH_TYPE=32;
+ public static final int AESKEY=16;
+
+ private FP x;
+ private FP y;
+ private FP z;
+// private boolean INF;
+
+/* Constructor - set to O */
+ public ECP() {
+ //INF=true;
+ x=new FP(0);
+ y=new FP(1);
+ if (CURVETYPE==EDWARDS)
+ {
+ z=new FP(1);
+ }
+ else
+ {
+ z=new FP(0);
+ }
+ }
+
+ public ECP(ECP e) {
+ this.x = new FP(e.x);
+ this.y = new FP(e.y);
+ this.z = new FP(e.z);
+ }
+
+/* test for O point-at-infinity */
+ public boolean is_infinity() {
+// if (INF) return true; // Edits made
+ if (CURVETYPE==EDWARDS)
+ {
+ return (x.iszilch() && y.equals(z));
+ }
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ return (x.iszilch() && z.iszilch());
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return z.iszilch();
+ }
+ return true;
+ }
+/* Conditional swap of P and Q dependant on d */
+ private void cswap(ECP Q,int d)
+ {
+ x.cswap(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cswap(Q.y,d);
+ z.cswap(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // bd=bd&(INF^Q.INF);
+ // INF^=bd;
+ // Q.INF^=bd;
+ // }
+ }
+
+/* Conditional move of Q to P dependant on d */
+ private void cmove(ECP Q,int d)
+ {
+ x.cmove(Q.x,d);
+ if (CURVETYPE!=MONTGOMERY) y.cmove(Q.y,d);
+ z.cmove(Q.z,d);
+ // if (CURVETYPE!=EDWARDS)
+ // {
+ // boolean bd;
+ // if (d==0) bd=false;
+ // else bd=true;
+ // INF^=(INF^Q.INF)&bd;
+ // }
+ }
+
+/* return 1 if b==c, no branching */
+ private static int teq(int b,int c)
+ {
+ int x=b^c;
+ x-=1; // if x=0, x now -1
+ return ((x>>31)&1);
+ }
+
+/* Constant time select from pre-computed table */
+ private void select(ECP W[],int b)
+ {
+ ECP MP=new ECP();
+ int m=b>>31;
+ int babs=(b^m)-m;
+
+ babs=(babs-1)/2;
+ cmove(W[0],teq(babs,0)); // conditional move
+ cmove(W[1],teq(babs,1));
+ cmove(W[2],teq(babs,2));
+ cmove(W[3],teq(babs,3));
+ cmove(W[4],teq(babs,4));
+ cmove(W[5],teq(babs,5));
+ cmove(W[6],teq(babs,6));
+ cmove(W[7],teq(babs,7));
+
+ MP.copy(this);
+ MP.neg();
+ cmove(MP,(int)(m&1));
+ }
+
+/* Test P == Q */
+ public boolean equals(ECP Q) {
+// if (is_infinity() && Q.is_infinity()) return true;
+// if (is_infinity() || Q.is_infinity()) return false;
+
+ FP a=new FP(0); // Edits made
+ FP b=new FP(0);
+ a.copy(x); a.mul(Q.z);
+ b.copy(Q.x); b.mul(z);
+ if (!a.equals(b)) return false;
+ if (CURVETYPE!=MONTGOMERY)
+ {
+ a.copy(y); a.mul(Q.z);
+ b.copy(Q.y); b.mul(z);
+ if (!a.equals(b)) return false;
+ }
+ return true;
+ }
+
+/* this=P */
+ public void copy(ECP P)
+ {
+ x.copy(P.x);
+ if (CURVETYPE!=MONTGOMERY) y.copy(P.y);
+ z.copy(P.z);
+ //INF=P.INF;
+ }
+/* this=-this */
+ public void neg() {
+// if (is_infinity()) return;
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ y.neg(); y.norm();
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+ x.neg(); x.norm();
+ }
+ return;
+ }
+/* set this=O */
+ public void inf() {
+// INF=true;
+ x.zero();
+ if (CURVETYPE!=MONTGOMERY) y.one();
+ if (CURVETYPE!=EDWARDS) z.zero();
+ else z.one();
+ }
+
+/* Calculate RHS of curve equation */
+ public static FP RHS(FP x) {
+ x.norm();
+ FP r=new FP(x);
+ r.sqr();
+
+ if (CURVETYPE==WEIERSTRASS)
+ { // x^3+Ax+B
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ r.mul(x);
+ if (ROM.CURVE_A==-3)
+ {
+ FP cx=new FP(x);
+ cx.imul(3);
+ cx.neg(); cx.norm();
+ r.add(cx);
+ }
+ r.add(b);
+ }
+ if (CURVETYPE==EDWARDS)
+ { // (Ax^2-1)/(Bx^2-1)
+ FP b=new FP(new BIG(ROM.CURVE_B));
+
+ FP one=new FP(1);
+ b.mul(r);
+ b.sub(one);
+ b.norm();
+ if (ROM.CURVE_A==-1) r.neg();
+ r.sub(one); r.norm();
+ b.inverse();
+
+ r.mul(b);
+ }
+ if (CURVETYPE==MONTGOMERY)
+ { // x^3+Ax^2+x
+ FP x3=new FP(0);
+ x3.copy(r);
+ x3.mul(x);
+ r.imul(ROM.CURVE_A);
+ r.add(x3);
+ r.add(x);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* set (x,y) from two BIGs */
+ public ECP(BIG ix,BIG iy) {
+ x=new FP(ix);
+ y=new FP(iy);
+ z=new FP(1);
+ FP rhs=RHS(x);
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ if (rhs.jacobi()!=1) inf();
+ //if (rhs.jacobi()==1) INF=false;
+ //else inf();
+ }
+ else
+ {
+ FP y2=new FP(y);
+ y2.sqr();
+ if (!y2.equals(rhs)) inf();
+ //if (y2.equals(rhs)) INF=false;
+ //else inf();
+ }
+ }
+/* set (x,y) from BIG and a bit */
+ public ECP(BIG ix,int s) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ FP ny=rhs.sqrt();
+ if (ny.redc().parity()!=s) ny.neg();
+ y.copy(ny);
+ //INF=false;
+ }
+ else inf();
+ }
+
+/* set from x - calculate y from curve equation */
+ public ECP(BIG ix) {
+ x=new FP(ix);
+ FP rhs=RHS(x);
+ y=new FP(0);
+ z=new FP(1);
+ if (rhs.jacobi()==1)
+ {
+ if (CURVETYPE!=MONTGOMERY) y.copy(rhs.sqrt());
+ //INF=false;
+ }
+ else inf(); //INF=true;
+ }
+
+/* set to affine - from (x,y,z) to (x,y) */
+ public void affine() {
+ if (is_infinity()) return; //
+ FP one=new FP(1);
+ if (z.equals(one)) return;
+ z.inverse();
+ x.mul(z); x.reduce();
+ if (CURVETYPE!=MONTGOMERY) // Edits made
+ {
+ y.mul(z); y.reduce();
+ }
+ z.copy(one);
+ }
+/* extract x as a BIG */
+ public BIG getX()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.x.redc();
+ }
+/* extract y as a BIG */
+ public BIG getY()
+ {
+ ECP W=new ECP(this);
+ W.affine();
+ return W.y.redc();
+ }
+
+/* get sign of Y */
+ public int getS()
+ {
+ //affine();
+ BIG y=getY();
+ return y.parity();
+ }
+/* extract x as an FP */
+ public FP getx()
+ {
+ return x;
+ }
+/* extract y as an FP */
+ public FP gety()
+ {
+ return y;
+ }
+/* extract z as an FP */
+ public FP getz()
+ {
+ return z;
+ }
+/* convert to byte array */
+ public void toBytes(byte[] b,boolean compress)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ ECP W=new ECP(this);
+ W.affine();
+
+ W.x.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+1]=t[i];
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ b[0]=0x06;
+ return;
+ }
+
+ if (compress)
+ {
+ b[0]=0x02;
+ if (y.redc().parity()==1) b[0]=0x03;
+ return;
+ }
+
+ b[0]=0x04;
+
+ W.y.redc().toBytes(t);
+ for (int i=0;i<BIG.MODBYTES;i++) b[i+BIG.MODBYTES+1]=t[i];
+ }
+/* convert from byte array to point */
+ public static ECP fromBytes(byte[] b)
+ {
+ byte[] t=new byte[BIG.MODBYTES];
+ BIG p=new BIG(ROM.Modulus);
+
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+1];
+ BIG px=BIG.fromBytes(t);
+ if (BIG.comp(px,p)>=0) return new ECP();
+
+ if (CURVETYPE==MONTGOMERY)
+ {
+ return new ECP(px);
+ }
+
+ if (b[0]==0x04)
+ {
+ for (int i=0;i<BIG.MODBYTES;i++) t[i]=b[i+BIG.MODBYTES+1];
+ BIG py=BIG.fromBytes(t);
+ if (BIG.comp(py,p)>=0) return new ECP();
+ return new ECP(px,py);
+ }
+
+ if (b[0]==0x02 || b[0]==0x03)
+ {
+ return new ECP(px,(int)(b[0]&1));
+ }
+ return new ECP();
+ }
+/* convert to hex string */
+ public String toString() {
+ ECP W=new ECP(this);
+ W.affine();
+ if (W.is_infinity()) return "infinity";
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+")";
+ }
+
+/* convert to hex string */
+ public String toRawString() {
+ //if (is_infinity()) return "infinity";
+ //affine();
+ ECP W=new ECP(this);
+ if (CURVETYPE==MONTGOMERY) return "("+W.x.redc().toString()+","+W.z.redc().toString()+")";
+ else return "("+W.x.redc().toString()+","+W.y.redc().toString()+","+W.z.redc().toString()+")";
+ }
+
+/* this*=2 */
+ public void dbl() {
+// if (INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+ if (ROM.CURVE_A==0)
+ {
+//System.out.println("Into dbl");
+ FP t0=new FP(y); /*** Change ***/ // Edits made
+ t0.sqr();
+ FP t1=new FP(y);
+ t1.mul(z);
+ FP t2=new FP(z);
+ t2.sqr();
+
+ z.copy(t0);
+ z.add(t0); z.norm();
+ z.add(z); z.add(z); z.norm();
+ t2.imul(3*ROM.CURVE_B_I);
+
+ FP x3=new FP(t2);
+ x3.mul(z);
+
+ FP y3=new FP(t0);
+ y3.add(t2); y3.norm();
+ z.mul(t1);
+ t1.copy(t2); t1.add(t2); t2.add(t1);
+ t0.sub(t2); t0.norm(); y3.mul(t0); y3.add(x3);
+ t1.copy(x); t1.mul(y);
+ x.copy(t0); x.norm(); x.mul(t1); x.add(x);
+ x.norm();
+ y.copy(y3); y.norm();
+//System.out.println("Out of dbl");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP z3=new FP(z);
+ FP y3=new FP(0);
+ FP x3=new FP(0);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.sqr(); //1 x^2
+ t1.sqr(); //2 y^2
+ t2.sqr(); //3
+
+ t3.mul(y); //4
+ t3.add(t3); t3.norm();//5
+ z3.mul(x); //6
+ z3.add(z3); z3.norm();//7
+ y3.copy(t2);
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //8
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ y3.sub(z3); //y3.norm(); //9 ***
+ x3.copy(y3); x3.add(y3); x3.norm();//10
+
+ y3.add(x3); //y3.norm();//11
+ x3.copy(t1); x3.sub(y3); x3.norm();//12
+ y3.add(t1); y3.norm();//13
+ y3.mul(x3); //14
+ x3.mul(t3); //15
+ t3.copy(t2); t3.add(t2); //t3.norm(); //16
+ t2.add(t3); //t2.norm(); //17
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ z3.sub(t2); //z3.norm();//19
+ z3.sub(t0); z3.norm();//20 ***
+ t3.copy(z3); t3.add(z3); //t3.norm();//21
+
+ z3.add(t3); z3.norm(); //22
+ t3.copy(t0); t3.add(t0); //t3.norm(); //23
+ t0.add(t3); //t0.norm();//24
+ t0.sub(t2); t0.norm();//25
+
+ t0.mul(z3);//26
+ y3.add(t0); //y3.norm();//27
+ t0.copy(y); t0.mul(z);//28
+ t0.add(t0); t0.norm(); //29
+ z3.mul(t0);//30
+ x3.sub(z3); //x3.norm();//31
+ t0.add(t0); t0.norm();//32
+ t1.add(t1); t1.norm();//33
+ z3.copy(t0); z3.mul(t1);//34
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into dbl");
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP H=new FP(z);
+ FP J=new FP(0);
+
+ x.mul(y); x.add(x); x.norm();
+ C.sqr();
+ D.sqr();
+
+ if (ROM.CURVE_A==-1) C.neg();
+
+ y.copy(C); y.add(D); y.norm();
+ H.sqr(); H.add(H);
+
+ z.copy(y);
+ J.copy(y);
+
+ J.sub(H); J.norm();
+ x.mul(J);
+
+ C.sub(D); C.norm();
+ y.mul(C);
+ z.mul(J);
+//System.out.println("Out of dbl");
+ }
+ if (CURVETYPE==MONTGOMERY)
+ {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP AA=new FP(0);
+ FP BB=new FP(0);
+ FP C=new FP(0);
+
+ A.add(z); A.norm();
+ AA.copy(A); AA.sqr();
+ B.sub(z); B.norm();
+ BB.copy(B); BB.sqr();
+ C.copy(AA); C.sub(BB); C.norm();
+ x.copy(AA); x.mul(BB);
+
+ A.copy(C); A.imul((ROM.CURVE_A+2)/4);
+
+ BB.add(A); BB.norm();
+ z.copy(BB); z.mul(C);
+ }
+ return;
+ }
+
+/* this+=Q */
+ public void add(ECP Q) {
+// if (INF)
+// {
+// copy(Q);
+// return;
+// }
+// if (Q.INF) return;
+
+ if (CURVETYPE==WEIERSTRASS)
+ {
+
+
+ if (ROM.CURVE_A==0)
+ {
+// Edits made
+//System.out.println("Into add");
+ int b=3*ROM.CURVE_B_I;
+ FP t0=new FP(x);
+ t0.mul(Q.x);
+ FP t1=new FP(y);
+ t1.mul(Q.y);
+ FP t2=new FP(z);
+ t2.mul(Q.z);
+ FP t3=new FP(x);
+ t3.add(y); t3.norm();
+ FP t4=new FP(Q.x);
+ t4.add(Q.y); t4.norm();
+ t3.mul(t4);
+ t4.copy(t0); t4.add(t1);
+
+ t3.sub(t4); t3.norm();
+ t4.copy(y);
+ t4.add(z); t4.norm();
+ FP x3=new FP(Q.y);
+ x3.add(Q.z); x3.norm();
+
+ t4.mul(x3);
+ x3.copy(t1);
+ x3.add(t2);
+
+ t4.sub(x3); t4.norm();
+ x3.copy(x); x3.add(z); x3.norm();
+ FP y3=new FP(Q.x);
+ y3.add(Q.z); y3.norm();
+ x3.mul(y3);
+ y3.copy(t0);
+ y3.add(t2);
+ y3.rsub(x3); y3.norm();
+ x3.copy(t0); x3.add(t0);
+ t0.add(x3); t0.norm();
+ t2.imul(b);
+
+ FP z3=new FP(t1); z3.add(t2); z3.norm();
+ t1.sub(t2); t1.norm();
+ y3.imul(b);
+
+ x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2);
+ y3.mul(t0); t1.mul(z3); y3.add(t1);
+ t0.mul(t3); z3.mul(t4); z3.add(t0);
+
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+//System.out.println("Out of add");
+ }
+ else
+ {
+ FP t0=new FP(x);
+ FP t1=new FP(y);
+ FP t2=new FP(z);
+ FP t3=new FP(x);
+ FP t4=new FP(Q.x);
+ FP z3=new FP(0);
+ FP y3=new FP(Q.x);
+ FP x3=new FP(Q.y);
+ FP b=new FP(0);
+
+ if (ROM.CURVE_B_I==0)
+ b.copy(new FP(new BIG(ROM.CURVE_B)));
+
+ t0.mul(Q.x); //1
+ t1.mul(Q.y); //2
+ t2.mul(Q.z); //3
+
+ t3.add(y); t3.norm(); //4
+ t4.add(Q.y); t4.norm();//5
+ t3.mul(t4);//6
+ t4.copy(t0); t4.add(t1); //t4.norm(); //7
+ t3.sub(t4); t3.norm(); //8
+ t4.copy(y); t4.add(z); t4.norm();//9
+ x3.add(Q.z); x3.norm();//10
+ t4.mul(x3); //11
+ x3.copy(t1); x3.add(t2); //x3.norm();//12
+
+ t4.sub(x3); t4.norm();//13
+ x3.copy(x); x3.add(z); x3.norm(); //14
+ y3.add(Q.z); y3.norm();//15
+
+ x3.mul(y3); //16
+ y3.copy(t0); y3.add(t2); //y3.norm();//17
+
+ y3.rsub(x3); y3.norm(); //18
+ z3.copy(t2);
+
+
+ if (ROM.CURVE_B_I==0)
+ z3.mul(b); //18
+ else
+ z3.imul(ROM.CURVE_B_I);
+
+ x3.copy(y3); x3.sub(z3); x3.norm(); //20
+ z3.copy(x3); z3.add(x3); //z3.norm(); //21
+
+ x3.add(z3); //x3.norm(); //22
+ z3.copy(t1); z3.sub(x3); z3.norm(); //23
+ x3.add(t1); x3.norm(); //24
+
+ if (ROM.CURVE_B_I==0)
+ y3.mul(b); //18
+ else
+ y3.imul(ROM.CURVE_B_I);
+
+ t1.copy(t2); t1.add(t2); //t1.norm();//26
+ t2.add(t1); //t2.norm();//27
+
+ y3.sub(t2); //y3.norm(); //28
+
+ y3.sub(t0); y3.norm(); //29
+ t1.copy(y3); t1.add(y3); //t1.norm();//30
+ y3.add(t1); y3.norm(); //31
+
+ t1.copy(t0); t1.add(t0); //t1.norm(); //32
+ t0.add(t1); //t0.norm();//33
+ t0.sub(t2); t0.norm();//34
+ t1.copy(t4); t1.mul(y3);//35
+ t2.copy(t0); t2.mul(y3);//36
+ y3.copy(x3); y3.mul(z3);//37
+ y3.add(t2); //y3.norm();//38
+ x3.mul(t3);//39
+ x3.sub(t1);//40
+ z3.mul(t4);//41
+ t1.copy(t3); t1.mul(t0);//42
+ z3.add(t1);
+ x.copy(x3); x.norm();
+ y.copy(y3); y.norm();
+ z.copy(z3); z.norm();
+ }
+ }
+ if (CURVETYPE==EDWARDS)
+ {
+//System.out.println("Into add");
+ FP A=new FP(z);
+ FP B=new FP(0);
+ FP C=new FP(x);
+ FP D=new FP(y);
+ FP E=new FP(0);
+ FP F=new FP(0);
+ FP G=new FP(0);
+
+ A.mul(Q.z);
+ B.copy(A); B.sqr();
+ C.mul(Q.x);
+ D.mul(Q.y);
+
+ E.copy(C); E.mul(D);
+
+ if (ROM.CURVE_B_I==0)
+ {
+ FP b=new FP(new BIG(ROM.CURVE_B));
+ E.mul(b);
+ }
+ else
+ E.imul(ROM.CURVE_B_I);
+
+ F.copy(B); F.sub(E);
+ G.copy(B); G.add(E);
+
+ if (ROM.CURVE_A==1)
+ {
+ E.copy(D); E.sub(C);
+ }
+ C.add(D);
+
+ B.copy(x); B.add(y);
+ D.copy(Q.x); D.add(Q.y); B.norm(); D.norm();
+ B.mul(D);
+ B.sub(C); B.norm(); F.norm();
+ B.mul(F);
+ x.copy(A); x.mul(B); G.norm();
+ if (ROM.CURVE_A==1)
+ {
+ E.norm(); C.copy(E); C.mul(G);
+ }
+ if (ROM.CURVE_A==-1)
+ {
+ C.norm(); C.mul(G);
+ }
+ y.copy(A); y.mul(C);
+
+ z.copy(F);
+ z.mul(G);
+//System.out.println("Out of add");
+ }
+ return;
+ }
+
+/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+ public void dadd(ECP Q,ECP W) {
+ FP A=new FP(x);
+ FP B=new FP(x);
+ FP C=new FP(Q.x);
+ FP D=new FP(Q.x);
+ FP DA=new FP(0);
+ FP CB=new FP(0);
+
+ A.add(z);
+ B.sub(z);
+
+ C.add(Q.z);
+ D.sub(Q.z);
+ A.norm();
+
+ D.norm();
+ DA.copy(D); DA.mul(A);
+
+ C.norm();
+ B.norm();
+ CB.copy(C); CB.mul(B);
+
+ A.copy(DA); A.add(CB);
+ A.norm(); A.sqr();
+ B.copy(DA); B.sub(CB);
+ B.norm(); B.sqr();
+
+ x.copy(A);
+ z.copy(W.x); z.mul(B);
+ }
+/* this-=Q */
+ public void sub(ECP Q) {
+ ECP NQ=new ECP(Q);
+ NQ.neg();
+ add(NQ);
+ }
+
+/* constant time multiply by small integer of length bts - use ladder */
+ public ECP pinmul(int e,int bts) {
+ if (CURVETYPE==MONTGOMERY)
+ return this.mul(new BIG(e));
+ else
+ {
+ int nb,i,b;
+ ECP P=new ECP();
+ ECP R0=new ECP();
+ ECP R1=new ECP(); R1.copy(this);
+
+ for (i=bts-1;i>=0;i--)
+ {
+ b=(e>>i)&1;
+ P.copy(R1);
+ P.add(R0);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+ }
+ P.copy(R0);
+ P.affine();
+ return P;
+ }
+ }
+
+/* return e.this */
+
+ public ECP mul(BIG e) {
+ if (e.iszilch() || is_infinity()) return new ECP();
+ ECP P=new ECP();
+ if (CURVETYPE==MONTGOMERY)
+ {
+/* use Ladder */
+ int nb,i,b;
+ ECP D=new ECP();
+ ECP R0=new ECP(); R0.copy(this);
+ ECP R1=new ECP(); R1.copy(this);
+ R1.dbl();
+
+ D.copy(this); D.affine();
+ nb=e.nbits();
+ for (i=nb-2;i>=0;i--)
+ {
+ b=e.bit(i);
+ P.copy(R1);
+
+ P.dadd(R0,D);
+ R0.cswap(R1,b);
+ R1.copy(P);
+ R0.dbl();
+ R0.cswap(R1,b);
+
+ }
+
+ P.copy(R0);
+ }
+ else
+ {
+// fixed size windows
+ int i,b,nb,m,s,ns;
+ BIG mt=new BIG();
+ BIG t=new BIG();
+ ECP Q=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+
+ //affine();
+
+// precompute table
+ Q.copy(this);
+
+ Q.dbl();
+ W[0]=new ECP();
+ W[0].copy(this);
+
+ for (i=1;i<8;i++)
+ {
+ W[i]=new ECP();
+ W[i].copy(W[i-1]);
+ W[i].add(Q);
+ }
+
+// make exponent odd - add 2P if even, P if odd
+ t.copy(e);
+ s=t.parity();
+ t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
+ t.cmove(mt,s);
+ Q.cmove(this,ns);
+ C.copy(Q);
+
+ nb=1+(t.nbits()+3)/4;
+
+// convert exponent to signed 4-bit window
+ for (i=0;i<nb;i++)
+ {
+ w[i]=(byte)(t.lastbits(5)-16);
+ t.dec(w[i]); t.norm();
+ t.fshr(4);
+ }
+ w[nb]=(byte)t.lastbits(5);
+
+ P.copy(W[(w[nb]-1)/2]);
+ for (i=nb-1;i>=0;i--)
+ {
+ Q.select(W,w[i]);
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.dbl();
+ P.add(Q);
+ }
+ P.sub(C); /* apply correction */
+ }
+ P.affine();
+ return P;
+ }
+
+/* Return e.this+f.Q */
+
+ public ECP mul2(BIG e,ECP Q,BIG f) {
+ BIG te=new BIG();
+ BIG tf=new BIG();
+ BIG mt=new BIG();
+ ECP S=new ECP();
+ ECP T=new ECP();
+ ECP C=new ECP();
+ ECP[] W=new ECP[8];
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+1)/2];
+ int i,s,ns,nb;
+ byte a,b;
+
+ //affine();
+ //Q.affine();
+
+ te.copy(e);
+ tf.copy(f);
+
+// precompute table
+ W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
+ W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
+ S.copy(Q); S.dbl();
+ W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
+ W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
+ T.copy(this); T.dbl();
+ W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
+ W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
+ W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
+ W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
+
+// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+ s=te.parity();
+ te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
+ te.cmove(mt,s);
+ T.cmove(this,ns);
+ C.copy(T);
+
+ s=tf.parity();
+ tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
+ tf.cmove(mt,s);
+ S.cmove(Q,ns);
+ C.add(S);
+
+ mt.copy(te); mt.add(tf); mt.norm();
+ nb=1+(mt.nbits()+1)/2;
+
+// convert exponent to signed 2-bit window
+ for (i=0;i<nb;i++)
+ {
+ a=(byte)(te.lastbits(3)-4);
+ te.dec(a); te.norm();
+ te.fshr(2);
+ b=(byte)(tf.lastbits(3)-4);
+ tf.dec(b); tf.norm();
+ tf.fshr(2);
+ w[i]=(byte)(4*a+b);
+ }
+ w[nb]=(byte)(4*te.lastbits(3)+tf.lastbits(3));
+ S.copy(W[(w[nb]-1)/2]);
+
+ for (i=nb-1;i>=0;i--)
+ {
+ T.select(W,w[i]);
+ S.dbl();
+ S.dbl();
+ S.add(T);
+ }
+ S.sub(C); /* apply correction */
+ S.affine();
+ return S;
+ }
+
+// multiply a point by the curves cofactor
+ public void cfp()
+ {
+ int cf=ROM.CURVE_Cof_I;
+ if (cf==1) return;
+ if (cf==4)
+ {
+ dbl(); dbl();
+ //affine();
+ return;
+ }
+ if (cf==8)
+ {
+ dbl(); dbl(); dbl();
+ //affine();
+ return;
+ }
+ BIG c=new BIG(ROM.CURVE_Cof);
+ copy(mul(c));
+ }
+
+/* Map byte string to curve point */
+ public static ECP mapit(byte[] h)
+ {
+ BIG q=new BIG(ROM.Modulus);
+ BIG x=BIG.fromBytes(h);
+ x.mod(q);
+ ECP P;
+
+ while (true)
+ {
+ while (true)
+ {
+ if (CURVETYPE!=MONTGOMERY)
+ P=new ECP(x,0);
+ else
+ P=new ECP(x);
+ x.inc(1); x.norm();
+ if (!P.is_infinity()) break;
+ }
+ P.cfp();
+ if (!P.is_infinity()) break;
+ }
+ return P;
+ }
+
+ public static ECP generator()
+ {
+ ECP G;
+ BIG gx,gy;
+ gx=new BIG(ROM.CURVE_Gx);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ gy=new BIG(ROM.CURVE_Gy);
+ G=new ECP(gx,gy);
+ }
+ else
+ G=new ECP(gx);
+ return G;
+ }
+
+/*
+ public static void main(String[] args) {
+
+ BIG Gx=new BIG(ROM.CURVE_Gx);
+ BIG Gy;
+ ECP P;
+ if (CURVETYPE!=MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
+ BIG r=new BIG(ROM.CURVE_Order);
+
+ //r.dec(7);
+
+ System.out.println("Gx= "+Gx.toString());
+ if (CURVETYPE!=MONTGOMERY) System.out.println("Gy= "+Gy.toString());
+
+ if (CURVETYPE!=MONTGOMERY) P=new ECP(Gx,Gy);
+ else P=new ECP(Gx);
+
+ System.out.println("P= "+P.toString());
+
+ ECP R=P.mul(r);
+ //for (int i=0;i<10000;i++)
+ // R=P.mul(r);
+
+ System.out.println("R= "+R.toString());
+ } */
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/SECP256K1/FP.java b/src/main/java/org/apache/milagro/amcl/SECP256K1/FP.java
new file mode 100644
index 0000000..5405395
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/SECP256K1/FP.java
@@ -0,0 +1,526 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Finite Field arithmetic */
+/* AMCL mod p functions */
+
+package org.apache.milagro.amcl.SECP256K1;
+
+public final class FP {
+
+ public static final int NOT_SPECIAL=0;
+ public static final int PSEUDO_MERSENNE=1;
+ public static final int MONTGOMERY_FRIENDLY=2;
+ public static final int GENERALISED_MERSENNE=3;
+
+ public static final int MODBITS=256; /* Number of bits in Modulus */
+ public static final int MOD8=7; /* Modulus mod 8 */
+ public static final int MODTYPE=NOT_SPECIAL;
+
+ public static final int FEXCESS =((int)1<<24); // BASEBITS*NLEN-MODBITS or 2^30 max!
+ public static final long OMASK=(long)(-1)<<(MODBITS%BIG.BASEBITS);
+ public static final int TBITS=MODBITS%BIG.BASEBITS; // Number of active bits in top word
+ public static final long TMASK=((long)1<<TBITS)-1;
+
+
+ public final BIG x;
+ //public BIG p=new BIG(ROM.Modulus);
+ //public BIG r2modp=new BIG(ROM.R2modp);
+ public int XES;
+
+/**************** 64-bit specific ************************/
+
+/* reduce a DBIG to a BIG using the appropriate form of the modulus */
+ public static BIG mod(DBIG d)
+ {
+ if (MODTYPE==PSEUDO_MERSENNE)
+ {
+ BIG b;
+ long v,tw;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+
+ v=t.pmul((int)ROM.MConst);
+
+ t.add(b);
+ t.norm();
+
+ tw=t.w[BIG.NLEN-1];
+ t.w[BIG.NLEN-1]&=FP.TMASK;
+ t.w[0]+=(ROM.MConst*((tw>>TBITS)+(v<<(BIG.BASEBITS-TBITS))));
+
+ t.norm();
+ return t;
+ }
+ if (FP.MODTYPE==MONTGOMERY_FRIENDLY)
+ {
+ BIG b;
+ long[] cr=new long[2];
+ for (int i=0;i<BIG.NLEN;i++)
+ {
+ cr=BIG.muladd(d.w[i],ROM.MConst-1,d.w[i],d.w[BIG.NLEN+i-1]);
+ d.w[BIG.NLEN+i]+=cr[0];
+ d.w[BIG.NLEN+i-1]=cr[1];
+ }
+
+ b=new BIG(0);
+ for (int i=0;i<BIG.NLEN;i++ )
+ b.w[i]=d.w[BIG.NLEN+i];
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==GENERALISED_MERSENNE)
+ { // GoldiLocks Only
+ BIG b;
+ BIG t=d.split(MODBITS);
+ b=new BIG(d);
+ b.add(t);
+ DBIG dd=new DBIG(t);
+ dd.shl(MODBITS/2);
+
+ BIG tt=dd.split(MODBITS);
+ BIG lo=new BIG(dd);
+ b.add(tt);
+ b.add(lo);
+ b.norm();
+ tt.shl(MODBITS/2);
+ b.add(tt);
+
+ long carry=b.w[BIG.NLEN-1]>>TBITS;
+ b.w[BIG.NLEN-1]&=FP.TMASK;
+ b.w[0]+=carry;
+
+ b.w[224/BIG.BASEBITS]+=carry<<(224%BIG.BASEBITS);
+ b.norm();
+ return b;
+ }
+ if (MODTYPE==NOT_SPECIAL)
+ {
+ return BIG.monty(new BIG(ROM.Modulus),ROM.MConst,d);
+ }
+
+ return new BIG(0);
+ }
+
+
+
+/*********************************************************/
+
+
+/* Constructors */
+ public FP(int a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP()
+ {
+ x=new BIG(0);
+ XES=1;
+ }
+
+ public FP(BIG a)
+ {
+ x=new BIG(a);
+ nres();
+ }
+
+ public FP(FP a)
+ {
+ x=new BIG(a.x);
+ XES=a.XES;
+ }
+
+/* convert to string */
+ public String toString()
+ {
+ String s=redc().toString();
+ return s;
+ }
+
+ public String toRawString()
+ {
+ String s=x.toRawString();
+ return s;
+ }
+
+/* convert to Montgomery n-residue form */
+ public void nres()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=BIG.mul(x,new BIG(ROM.R2modp)); /*** Change ***/
+ x.copy(mod(d));
+ XES=2;
+ }
+ else XES=1;
+ }
+
+/* convert back to regular form */
+ public BIG redc()
+ {
+ if (MODTYPE!=PSEUDO_MERSENNE && MODTYPE!=GENERALISED_MERSENNE)
+ {
+ DBIG d=new DBIG(x);
+ return mod(d);
+ }
+ else
+ {
+ BIG r=new BIG(x);
+ return r;
+ }
+ }
+
+/* test this=0? */
+ public boolean iszilch() {
+ FP z=new FP(this);
+ z.reduce();
+ return z.x.iszilch();
+
+ }
+
+/* copy from FP b */
+ public void copy(FP b)
+ {
+ x.copy(b.x);
+ XES=b.XES;
+ }
+
+/* set this=0 */
+ public void zero()
+ {
+ x.zero();
+ XES=1;
+ }
+
+/* set this=1 */
+ public void one()
+ {
+ x.one(); nres();
+ }
+
+/* normalise this */
+ public void norm()
+ {
+ x.norm();
+ }
+
+/* swap FPs depending on d */
+ public void cswap(FP b,int d)
+ {
+ x.cswap(b.x,d);
+ int t,c=d;
+ c=~(c-1);
+ t=c&(XES^b.XES);
+ XES^=t;
+ b.XES^=t;
+ }
+
+/* copy FPs depending on d */
+ public void cmove(FP b,int d)
+ {
+ x.cmove(b.x,d);
+ XES^=(XES^b.XES)&(-d);
+
+ }
+
+/* this*=b mod Modulus */
+ public void mul(FP b)
+ {
+ if ((long)XES*b.XES>(long)FEXCESS) reduce();
+
+ DBIG d=BIG.mul(x,b.x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this*=c mod Modulus, where c is a small int */
+ public void imul(int c)
+ {
+// norm();
+ boolean s=false;
+ if (c<0)
+ {
+ c=-c;
+ s=true;
+ }
+
+ if (MODTYPE==PSEUDO_MERSENNE || MODTYPE==GENERALISED_MERSENNE)
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+ else
+ {
+ if (XES*c<=FEXCESS)
+ {
+ x.pmul(c);
+ XES*=c;
+ }
+ else
+ { // this is not good
+ FP n=new FP(c);
+ mul(n);
+ }
+ }
+
+/*
+ if (c<=BIG.NEXCESS && XES*c<=FEXCESS)
+ {
+ x.imul(c);
+ XES*=c;
+ x.norm();
+ }
+ else
+ {
+ DBIG d=x.pxmul(c);
+ x.copy(mod(d));
+ XES=2;
+ }
+*/
+ if (s) {neg(); norm();}
+
+ }
+
+/* this*=this mod Modulus */
+ public void sqr()
+ {
+ DBIG d;
+ if ((long)XES*XES>(long)FEXCESS) reduce();
+
+ d=BIG.sqr(x);
+ x.copy(mod(d));
+ XES=2;
+ }
+
+/* this+=b */
+ public void add(FP b) {
+ x.add(b.x);
+ XES+=b.XES;
+ if (XES>FEXCESS) reduce();
+ }
+
+// https://graphics.stanford.edu/~seander/bithacks.html
+// constant time log to base 2 (or number of bits in)
+
+ private static int logb2(int v)
+ {
+ int r;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+ return r;
+ }
+
+/* this = -this mod Modulus */
+ public void neg()
+ {
+ int sb;
+ BIG m=new BIG(ROM.Modulus);
+
+ sb=logb2(XES-1);
+ m.fshl(sb);
+ x.rsub(m);
+
+ XES=(1<<sb);
+ if (XES>FEXCESS) reduce();
+ }
+
+/* this-=b */
+ public void sub(FP b)
+ {
+ FP n=new FP(b);
+ n.neg();
+ this.add(n);
+ }
+
+ public void rsub(FP b)
+ {
+ FP n=new FP(this);
+ n.neg();
+ this.copy(b);
+ this.add(n);
+ }
+
+/* this/=2 mod Modulus */
+ public void div2()
+ {
+ if (x.parity()==0)
+ x.fshr(1);
+ else
+ {
+ x.add(new BIG(ROM.Modulus));
+ x.norm();
+ x.fshr(1);
+ }
+ }
+
+/* this=1/this mod Modulus */
+ public void inverse()
+ {
+/*
+ BIG r=redc();
+ r.invmodp(p);
+ x.copy(r);
+ nres();
+*/
+ BIG m2=new BIG(ROM.Modulus);
+ m2.dec(2); m2.norm();
+ copy(pow(m2));
+
+ }
+
+/* return TRUE if this==a */
+ public boolean equals(FP a)
+ {
+ FP f=new FP(this);
+ FP s=new FP(a);
+ f.reduce();
+ s.reduce();
+ if (BIG.comp(f.x,s.x)==0) return true;
+ return false;
+ }
+
+/* reduce this mod Modulus */
+ public void reduce()
+ {
+ x.mod(new BIG(ROM.Modulus));
+ XES=1;
+ }
+
+ public FP pow(BIG e)
+ {
+ byte[] w=new byte[1+(BIG.NLEN*BIG.BASEBITS+3)/4];
+ FP [] tb=new FP[16];
+ BIG t=new BIG(e);
+ t.norm();
+ int nb=1+(t.nbits()+3)/4;
+
+ for (int i=0;i<nb;i++)
+ {
+ int lsbs=t.lastbits(4);
+ t.dec(lsbs);
+ t.norm();
+ w[i]=(byte)lsbs;
+ t.fshr(4);
+ }
+ tb[0]=new FP(1);
+ tb[1]=new FP(this);
+ for (int i=2;i<16;i++)
+ {
+ tb[i]=new FP(tb[i-1]);
+ tb[i].mul(this);
+ }
+ FP r=new FP(tb[w[nb-1]]);
+ for (int i=nb-2;i>=0;i--)
+ {
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.sqr();
+ r.mul(tb[w[i]]);
+ }
+ r.reduce();
+ return r;
+ }
+
+/* return this^e mod Modulus
+ public FP pow(BIG e)
+ {
+ int bt;
+ FP r=new FP(1);
+ e.norm();
+ x.norm();
+ FP m=new FP(this);
+ while (true)
+ {
+ bt=e.parity();
+ e.fshr(1);
+ if (bt==1) r.mul(m);
+ if (e.iszilch()) break;
+ m.sqr();
+ }
+ r.x.mod(p);
+ return r;
+ } */
+
+/* return sqrt(this) mod Modulus */
+ public FP sqrt()
+ {
+ reduce();
+ BIG b=new BIG(ROM.Modulus);
+ if (MOD8==5)
+ {
+ b.dec(5); b.norm(); b.shr(3);
+ FP i=new FP(this); i.x.shl(1);
+ FP v=i.pow(b);
+ i.mul(v); i.mul(v);
+ i.x.dec(1);
+ FP r=new FP(this);
+ r.mul(v); r.mul(i);
+ r.reduce();
+ return r;
+ }
+ else
+ {
+ b.inc(1); b.norm(); b.shr(2);
+ return pow(b);
+ }
+ }
+
+/* return jacobi symbol (this/Modulus) */
+ public int jacobi()
+ {
+ BIG w=redc();
+ return w.jacobi(new BIG(ROM.Modulus));
+ }
+/*
+ public static void main(String[] args) {
+ BIG m=new BIG(ROM.Modulus);
+ BIG x=new BIG(3);
+ BIG e=new BIG(m);
+ e.dec(1);
+
+ System.out.println("m= "+m.nbits());
+
+
+ BIG r=x.powmod(e,m);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+ BIG.cswap(m,r,0);
+
+ System.out.println("m= "+m.toString());
+ System.out.println("r= "+r.toString());
+
+// FP y=new FP(3);
+// FP s=y.pow(e);
+// System.out.println("s= "+s.toString());
+
+ } */
+}
diff --git a/src/main/java/org/apache/milagro/amcl/SECP256K1/ROM.java b/src/main/java/org/apache/milagro/amcl/SECP256K1/ROM.java
new file mode 100644
index 0000000..014ebc9
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/SECP256K1/ROM.java
@@ -0,0 +1,43 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+
+package org.apache.milagro.amcl.SECP256K1;
+
+public class ROM
+{
+
+// Base Bits= 56
+public static final long[] Modulus= {0xFFFFFEFFFFFC2FL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFL};
+public static final long[] R2modp= {0xA1000000000000L,0x7A2000E90L,0x1L,0x0L,0x0L};
+public static final long MConst= 0x38091DD2253531L;
+
+public static final int CURVE_Cof_I= 1;
+public static final long[] CURVE_Cof= {0x1L,0x0L,0x0L,0x0L,0x0L};
+public static final int CURVE_A= 0;
+public static final int CURVE_B_I= 7;
+public static final long[] CURVE_B= {0x7L,0x0L,0x0L,0x0L,0x0L};
+public static final long[] CURVE_Order= {0xD25E8CD0364141L,0xDCE6AF48A03BBFL,0xFFFFFFFFFEBAAEL,0xFFFFFFFFFFFFFFL,0xFFFFFFFFL};
+public static final long[] CURVE_Gx= {0xF2815B16F81798L,0xFCDB2DCE28D959L,0x95CE870B07029BL,0xF9DCBBAC55A062L,0x79BE667EL};
+public static final long[] CURVE_Gy= {0x47D08FFB10D4B8L,0xB448A68554199CL,0xFC0E1108A8FD17L,0x26A3C4655DA4FBL,0x483ADA77L};
+
+}
+
diff --git a/src/main/java/org/apache/milagro/amcl/SHA3.java b/src/main/java/org/apache/milagro/amcl/SHA3.java
new file mode 100644
index 0000000..cf335cd
--- /dev/null
+++ b/src/main/java/org/apache/milagro/amcl/SHA3.java
@@ -0,0 +1,255 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/*
+ * Implementation of the Secure Hashing Algorithm SHA-3
+
+ * Generates a message digest. It should be impossible to come
+ * come up with two messages that hash to the same value ("collision free").
+ *
+ * For use with byte-oriented messages only.
+ */
+
+
+package org.apache.milagro.amcl;
+
+public class SHA3 {
+ private long length;
+ private int rate,len;
+ private long[][] S=new long[5][5];
+
+/* Constructor */
+ public SHA3(int olen)
+ {
+ init(olen);
+ }
+
+ public static final int HASH224=28;
+ public static final int HASH256=32;
+ public static final int HASH384=48;
+ public static final int HASH512=64;
+
+ public static final int SHAKE128=16;
+ public static final int SHAKE256=32;
+
+ public static final long[] RC={
+ 0x0000000000000001L,0x0000000000008082L,0x800000000000808AL,0x8000000080008000L,
+ 0x000000000000808BL,0x0000000080000001L,0x8000000080008081L,0x8000000000008009L,
+ 0x000000000000008AL,0x0000000000000088L,0x0000000080008009L,0x000000008000000AL,
+ 0x000000008000808BL,0x800000000000008BL,0x8000000000008089L,0x8000000000008003L,
+ 0x8000000000008002L,0x8000000000000080L,0x000000000000800AL,0x800000008000000AL,
+ 0x8000000080008081L,0x8000000000008080L,0x0000000080000001L,0x8000000080008008L};
+
+ private static final int ROUNDS=24;
+
+
+ private static long rotl(long x,int n)
+ {
+ return (((x)<<n) | ((x)>>>(64-n)));
+ }
+
+ private void transform()
+ { /* basic transformation step */
+ int i,j,k;
+ long[] C=new long[5];
+ long[] D=new long[5];
+ long[][] B=new long[5][5];
+
+ for (k=0;k<ROUNDS;k++)
+ {
+ C[0]=S[0][0]^S[0][1]^S[0][2]^S[0][3]^S[0][4];
+ C[1]=S[1][0]^S[1][1]^S[1][2]^S[1][3]^S[1][4];
+ C[2]=S[2][0]^S[2][1]^S[2][2]^S[2][3]^S[2][4];
+ C[3]=S[3][0]^S[3][1]^S[3][2]^S[3][3]^S[3][4];
+ C[4]=S[4][0]^S[4][1]^S[4][2]^S[4][3]^S[4][4];
+
+ D[0]=C[4]^rotl(C[1],1);
+ D[1]=C[0]^rotl(C[2],1);
+ D[2]=C[1]^rotl(C[3],1);
+ D[3]=C[2]^rotl(C[4],1);
+ D[4]=C[3]^rotl(C[0],1);
+
+ for (i=0;i<5;i++)
+ for (j=0;j<5;j++)
+ S[i][j]^=D[i];
+
+ B[0][0]=S[0][0];
+ B[1][3]=rotl(S[0][1],36);
+ B[2][1]=rotl(S[0][2],3);
+ B[3][4]=rotl(S[0][3],41);
+ B[4][2]=rotl(S[0][4],18);
+
+ B[0][2]=rotl(S[1][0],1);
+ B[1][0]=rotl(S[1][1],44);
+ B[2][3]=rotl(S[1][2],10);
+ B[3][1]=rotl(S[1][3],45);
+ B[4][4]=rotl(S[1][4],2);
+
+ B[0][4]=rotl(S[2][0],62);
+ B[1][2]=rotl(S[2][1],6);
+ B[2][0]=rotl(S[2][2],43);
+ B[3][3]=rotl(S[2][3],15);
+ B[4][1]=rotl(S[2][4],61);
+
+ B[0][1]=rotl(S[3][0],28);
+ B[1][4]=rotl(S[3][1],55);
+ B[2][2]=rotl(S[3][2],25);
+ B[3][0]=rotl(S[3][3],21);
+ B[4][3]=rotl(S[3][4],56);
+
+ B[0][3]=rotl(S[4][0],27);
+ B[1][1]=rotl(S[4][1],20);
+ B[2][4]=rotl(S[4][2],39);
+ B[3][2]=rotl(S[4][3],8);
+ B[4][0]=rotl(S[4][4],14);
+
+ for (i=0;i<5;i++)
+ for (j=0;j<5;j++)
+ S[i][j]=B[i][j]^(~B[(i+1)%5][j]&B[(i+2)%5][j]);
+
+ S[0][0]^=RC[k];
+ }
+ }
+
+/* Re-Initialize. olen is output length in bytes -
+ should be 28, 32, 48 or 64 (224, 256, 384, 512 bits resp.) */
+
+
+/* Initialise Hash function */
+ public void init(int olen)
+ { /* initialise */
+ int i,j;
+ for (i=0;i<5;i++)
+ for (j=0;j<5;j++)
+ S[i][j]=0; /* 5x5x8 bytes = 200 bytes of state */
+ length=0;
+ len=olen;
+ rate=200-2*olen; /* number of bytes consumed in one gulp. Note that some bytes in the
+ state ("capacity") are not touched. Gulps are smaller for larger digests.
+ Important that olen<rate */
+ }
+
+/* process a single byte */
+ public void process(int byt)
+ { /* process the next message byte */
+ int i,j,b,cnt;
+ cnt=(int)(length%rate);
+ b=cnt%8;
+ cnt/=8;
+ i=cnt%5; j=cnt/5; /* process by columns! */
+ S[i][j]^=((long)(byt&0xff)<<(8*b));
+ length++;
+ if ((length%rate)==0) transform();
+ }
+
+/* squeeze the sponge */
+ public byte[] squeeze(byte[] buff,int olen)
+ {
+ boolean done;
+ int i,j,k,m=0;
+ long el;
+/* extract by columns */
+ done=false;
+ for (;;)
+ {
+ for (j=0;j<5;j++)
+ {
+ for (i=0;i<5;i++)
+ {
+ el=S[i][j];
+ for (k=0;k<8;k++)
+ {
+ buff[m++]=(byte)(el&0xff);
+ if (m>=olen || (m%rate)==0) {done=true; break;}
+ el>>>=8;
+ }
+ if (done) break;
+ }
+ if (done) break;
+ }
+ if (m>=olen) break;
+ done=false;
+ transform();
+ }
+ return buff;
+ }
+
+ public void hash(byte[] digest)
+ { /* generate a SHA3 hash of appropriate size */
+ int q=rate-(int)(length%rate);
+ if (q==1) process(0x86);
+ else
+ {
+ process(0x06); /* 0x06 for SHA-3 */
+ while (length%rate!=rate-1) process(0x00);
+ process(0x80); /* this will force a final transform */
+ }
+ squeeze(digest,len);
+ }
+
+ public void shake(byte[] digest,int olen)
+ { /* SHAKE out a buffer of variable length olen */
+ int q=rate-(int)(length%rate);
+ if (q==1) process(0x9f);
+ else
+ {
+ process(0x1f); // 0x06 for SHA-3 !!!!
+ while (length%rate!=rate-1) process(0x00);
+ process(0x80); /* this will force a final transform */
+ }
+ squeeze(digest,olen);
+ }
+
+/* test program: should produce digests */
+
+//916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18
+//afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185
+//98be04516c04cc73593fef3ed0352ea9f6443942d6950e29a372a681c3deaf4535423709b02843948684e029010badcc0acd8303fc85fdad3eabf4f78cae165635f57afd28810fc2
+
+/*
+ public static void main(String[] args) {
+
+ byte[] test="abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu".getBytes();
+ byte[] digest=new byte[100];
+ int i;
+
+ SHA3 sh256=new SHA3(SHA3.HASH256);
+ for (i=0;i<test.length;i++)
+ sh256.process(test[i]);
+ sh256.hash(digest);
+ for (i=0;i<32;i++) System.out.format("%02x",digest[i]);
+ System.out.println("");
+
+ SHA3 sh512=new SHA3(SHA3.HASH512);
+ for (i=0;i<test.length;i++)
+ sh512.process(test[i]);
+ sh512.hash(digest);
+ for (i=0;i<64;i++) System.out.format("%02x",digest[i]);
+ System.out.println("");
+
+ SHA3 sk256=new SHA3(SHA3.SHAKE256);
+ for (i=0;i<test.length;i++)
+ sk256.process(test[i]);
+ sk256.shake(digest,72);
+ for (i=0;i<72;i++) System.out.format("%02x",digest[i]);
+ System.out.println("");
+
+ }
+*/
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/milagro/amcl/ANSSI/TestECDH.java b/src/test/java/org/apache/milagro/amcl/ANSSI/TestECDH.java
new file mode 100644
index 0000000..c71d32a
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/ANSSI/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.ANSSI; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BLS24/TestECDH.java b/src/test/java/org/apache/milagro/amcl/BLS24/TestECDH.java
new file mode 100644
index 0000000..9abfde3
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BLS24/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.BLS24; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BLS24/TestMPIN192.java b/src/test/java/org/apache/milagro/amcl/BLS24/TestMPIN192.java
new file mode 100644
index 0000000..a2c33d1
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BLS24/TestMPIN192.java
@@ -0,0 +1,297 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.BLS24; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+
+public class TestMPIN192 extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+ static boolean PERMITS=true;
+ static boolean PINERROR=true;
+ static boolean FULL=true;
+ static boolean SINGLE_PASS=false;
+
+ public static void testMPIN()
+ {
+ RAND rng=new RAND();
+ int EGS=MPIN192.EGS;
+ int EFS=MPIN192.EFS;
+ int G1S=2*EFS+1; /* Group 1 Size */
+ int G2S=8*EFS; /* Group 2 Size */
+
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S = new byte[EGS];
+ byte[] SST = new byte[G2S];
+ byte[] TOKEN = new byte[G1S];
+ byte[] PERMIT = new byte[G1S];
+ byte[] SEC = new byte[G1S];
+ byte[] xID = new byte[G1S];
+ byte[] xCID = new byte[G1S];
+ byte[] X= new byte[EGS];
+ byte[] Y= new byte[EGS];
+ byte[] E=new byte[24*EFS];
+ byte[] F=new byte[24*EFS];
+ byte[] HID=new byte[G1S];
+ byte[] HTID=new byte[G1S];
+
+ byte[] G1=new byte[24*EFS];
+ byte[] G2=new byte[24*EFS];
+ byte[] R=new byte[EGS];
+ byte[] Z=new byte[G1S];
+ byte[] W=new byte[EGS];
+ byte[] T=new byte[G1S];
+ byte[] CK=new byte[ECP.AESKEY];
+ byte[] SK=new byte[ECP.AESKEY];
+
+ byte[] HSID=null;
+ byte[] RAW=new byte[100];
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ System.out.println("Testing MPIN code");
+
+/* Trusted Authority set-up */
+
+ MPIN192.RANDOM_GENERATE(rng,S);
+ System.out.print("Master Secret s: 0x"); printBinary(S);
+
+ /* Create Client Identity */
+ String IDstr = "testUser@miracl.com";
+ byte[] CLIENT_ID = IDstr.getBytes();
+
+ byte[] HCID=MPIN192.HASH_ID(sha,CLIENT_ID,EFS); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+ System.out.print("Client ID Hash= "); printBinary(HCID);
+ System.out.print("Client ID= "); printBinary(CLIENT_ID);
+
+/* Client and Server are issued secrets by DTA */
+
+ MPIN192.GET_CLIENT_SECRET(S,HCID,TOKEN);
+ System.out.print("Client Secret CS: 0x");
+ printBinary(TOKEN);
+
+ MPIN192.GET_SERVER_SECRET(S,SST);
+ System.out.print("Server Secret SS: 0x"); printBinary(SST);
+
+
+/* Client extracts PIN from secret to create Token */
+ int pin=1234;
+ System.out.println("Client extracts PIN= "+pin);
+ int rtn=MPIN192.EXTRACT_PIN(sha,CLIENT_ID,pin,TOKEN);
+ if (rtn != 0)
+ fail("FAILURE: EXTRACT_PIN rtn: " + rtn);
+
+ System.out.print("Client Token TK: 0x"); printBinary(TOKEN);
+
+ if (FULL)
+ {
+ MPIN192.PRECOMPUTE(TOKEN,HCID,G1,G2);
+ }
+ int date;
+ if (PERMITS)
+ {
+ date=MPIN192.today();
+/* Client gets "Time Token" permit from DTA */
+ MPIN192.GET_CLIENT_PERMIT(sha,date,S,HCID,PERMIT);
+ System.out.print("Time Permit TP: 0x"); printBinary(PERMIT);
+
+/* This encoding makes Time permit look random - Elligator squared */
+ MPIN192.ENCODING(rng,PERMIT);
+ System.out.print("Encoded Time Permit TP: 0x"); printBinary(PERMIT);
+ MPIN192.DECODING(PERMIT);
+ System.out.print("Decoded Time Permit TP: 0x"); printBinary(PERMIT);
+ }
+ else date=0;
+
+// System.out.print("\nPIN= ");
+// Scanner scan=new Scanner(System.in);
+// pin=scan.nextInt();
+
+ pin=1234;
+
+/* Set date=0 and PERMIT=null if time permits not in use
+
+Client First pass: Inputs CLIENT_ID, optional RNG, pin, TOKEN and PERMIT. Output xID =x .H(CLIENT_ID) and re-combined secret SEC
+If PERMITS are is use, then date!=0 and PERMIT is added to secret and xCID = x.(H(CLIENT_ID)+H(date|H(CLIENT_ID)))
+Random value x is supplied externally if RNG=null, otherwise generated and passed out by RNG
+
+IMPORTANT: To save space and time..
+If Time Permits OFF set xCID = null, HTID=null and use xID and HID only
+If Time permits are ON, AND pin error detection is required then all of xID, xCID, HID and HTID are required
+If Time permits are ON, AND pin error detection is NOT required, set xID=null, HID=null and use xCID and HTID only.
+
+
+*/
+
+ byte[] pxID=xID;
+ byte[] pxCID=xCID;
+ byte[] pHID=HID;
+ byte[] pHTID=HTID;
+ byte[] pE=E;
+ byte[] pF=F;
+ byte[] pPERMIT=PERMIT;
+ byte[] prHID;
+
+ if (date!=0)
+ {
+
+ prHID=pHTID;
+ if (!PINERROR)
+ {
+ pxID=null;
+ // pHID=null; // new
+ }
+ }
+ else
+ {
+ prHID=pHID;
+ pPERMIT=null;
+ pxCID=null;
+ pHTID=null;
+ }
+ if (!PINERROR)
+ {
+ pE=null;
+ pF=null;
+ }
+
+ if (SINGLE_PASS)
+ {
+ System.out.println("MPIN Single Pass");
+ int timeValue = MPIN192.GET_TIME();
+ rtn=MPIN192.CLIENT(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT,timeValue,Y);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN192.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN192.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ rtn=MPIN192.SERVER(sha,date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF,CLIENT_ID,timeValue);
+ if (rtn != 0)
+ fail("FAILURE: SERVER rtn: " + rtn);
+
+ if (FULL)
+ {
+ HSID=MPIN192.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN192.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+ }
+ else
+ {
+ System.out.println("MPIN Multi Pass");
+ /* Send U=x.ID to server, and recreate secret from token and pin */
+ rtn=MPIN192.CLIENT_1(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_1 rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN192.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN192.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ /* Server calculates H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+ MPIN192.SERVER_1(sha,date,CLIENT_ID,pHID,pHTID);
+
+ /* Server generates Random number Y and sends it to Client */
+ MPIN192.RANDOM_GENERATE(rng,Y);
+
+ if (FULL)
+ {
+ HSID=MPIN192.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN192.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+
+ /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+ rtn=MPIN192.CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_2 rtn: " + rtn);
+
+ /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+ /* If PIN error not required, set E and F = null */
+
+ rtn=MPIN192.SERVER_2(date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF);
+
+ if (rtn != 0)
+ fail("FAILURE: SERVER_2 rtn: " + rtn);
+ }
+
+ if (rtn == MPIN192.BAD_PIN)
+ {
+ if (PINERROR)
+ {
+ int err=MPIN192.KANGAROO(E,F);
+ if (err!=0) fail("Client PIN is out by "+err);
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+ }
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+
+ }
+ else System.out.println("Server says - PIN is good! You really are "+IDstr);
+
+
+ if (FULL)
+ {
+ byte[] H=MPIN192.HASH_ALL(sha,HCID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN192.CLIENT_KEY(sha,G1,G2,pin,R,X,H,T,CK);
+ System.out.print("Client Key = 0x"); printBinary(CK);
+
+ H=MPIN192.HASH_ALL(sha,HSID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN192.SERVER_KEY(sha,Z,SST,W,H,pHID,pxID,pxCID,SK);
+ System.out.print("Server Key = 0x"); printBinary(SK);
+ }
+ System.out.println("");
+ }
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ mpin(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BLS381/TestECDH.java b/src/test/java/org/apache/milagro/amcl/BLS381/TestECDH.java
new file mode 100644
index 0000000..f653823
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BLS381/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.BLS381; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BLS381/TestMPIN.java b/src/test/java/org/apache/milagro/amcl/BLS381/TestMPIN.java
new file mode 100644
index 0000000..2312afc
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BLS381/TestMPIN.java
@@ -0,0 +1,297 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.BLS381; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+
+public class TestMPIN extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+ static boolean PERMITS=true;
+ static boolean PINERROR=true;
+ static boolean FULL=true;
+ static boolean SINGLE_PASS=false;
+
+ public static void testMPIN()
+ {
+ RAND rng=new RAND();
+ int EGS=MPIN.EGS;
+ int EFS=MPIN.EFS;
+ int G1S=2*EFS+1; /* Group 1 Size */
+ int G2S=4*EFS; /* Group 2 Size */
+
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S = new byte[EGS];
+ byte[] SST = new byte[G2S];
+ byte[] TOKEN = new byte[G1S];
+ byte[] PERMIT = new byte[G1S];
+ byte[] SEC = new byte[G1S];
+ byte[] xID = new byte[G1S];
+ byte[] xCID = new byte[G1S];
+ byte[] X= new byte[EGS];
+ byte[] Y= new byte[EGS];
+ byte[] E=new byte[12*EFS];
+ byte[] F=new byte[12*EFS];
+ byte[] HID=new byte[G1S];
+ byte[] HTID=new byte[G1S];
+
+ byte[] G1=new byte[12*EFS];
+ byte[] G2=new byte[12*EFS];
+ byte[] R=new byte[EGS];
+ byte[] Z=new byte[G1S];
+ byte[] W=new byte[EGS];
+ byte[] T=new byte[G1S];
+ byte[] CK=new byte[ECP.AESKEY];
+ byte[] SK=new byte[ECP.AESKEY];
+
+ byte[] HSID=null;
+ byte[] RAW=new byte[100];
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ System.out.println("Testing MPIN code");
+
+/* Trusted Authority set-up */
+
+ MPIN.RANDOM_GENERATE(rng,S);
+ System.out.print("Master Secret s: 0x"); printBinary(S);
+
+ /* Create Client Identity */
+ String IDstr = "testUser@miracl.com";
+ byte[] CLIENT_ID = IDstr.getBytes();
+
+ byte[] HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+ System.out.print("Client ID Hash= "); printBinary(HCID);
+ System.out.print("Client ID= "); printBinary(CLIENT_ID);
+
+/* Client and Server are issued secrets by DTA */
+
+ MPIN.GET_CLIENT_SECRET(S,HCID,TOKEN);
+ System.out.print("Client Secret CS: 0x");
+ printBinary(TOKEN);
+
+ MPIN.GET_SERVER_SECRET(S,SST);
+ System.out.print("Server Secret SS: 0x"); printBinary(SST);
+
+
+/* Client extracts PIN from secret to create Token */
+ int pin=1234;
+ System.out.println("Client extracts PIN= "+pin);
+ int rtn=MPIN.EXTRACT_PIN(sha,CLIENT_ID,pin,TOKEN);
+ if (rtn != 0)
+ fail("FAILURE: EXTRACT_PIN rtn: " + rtn);
+
+ System.out.print("Client Token TK: 0x"); printBinary(TOKEN);
+
+ if (FULL)
+ {
+ MPIN.PRECOMPUTE(TOKEN,HCID,G1,G2);
+ }
+ int date;
+ if (PERMITS)
+ {
+ date=MPIN.today();
+/* Client gets "Time Token" permit from DTA */
+ MPIN.GET_CLIENT_PERMIT(sha,date,S,HCID,PERMIT);
+ System.out.print("Time Permit TP: 0x"); printBinary(PERMIT);
+
+/* This encoding makes Time permit look random - Elligator squared */
+ MPIN.ENCODING(rng,PERMIT);
+ System.out.print("Encoded Time Permit TP: 0x"); printBinary(PERMIT);
+ MPIN.DECODING(PERMIT);
+ System.out.print("Decoded Time Permit TP: 0x"); printBinary(PERMIT);
+ }
+ else date=0;
+
+// System.out.print("\nPIN= ");
+// Scanner scan=new Scanner(System.in);
+// pin=scan.nextInt();
+
+ pin=1234;
+
+/* Set date=0 and PERMIT=null if time permits not in use
+
+Client First pass: Inputs CLIENT_ID, optional RNG, pin, TOKEN and PERMIT. Output xID =x .H(CLIENT_ID) and re-combined secret SEC
+If PERMITS are is use, then date!=0 and PERMIT is added to secret and xCID = x.(H(CLIENT_ID)+H(date|H(CLIENT_ID)))
+Random value x is supplied externally if RNG=null, otherwise generated and passed out by RNG
+
+IMPORTANT: To save space and time..
+If Time Permits OFF set xCID = null, HTID=null and use xID and HID only
+If Time permits are ON, AND pin error detection is required then all of xID, xCID, HID and HTID are required
+If Time permits are ON, AND pin error detection is NOT required, set xID=null, HID=null and use xCID and HTID only.
+
+
+*/
+
+ byte[] pxID=xID;
+ byte[] pxCID=xCID;
+ byte[] pHID=HID;
+ byte[] pHTID=HTID;
+ byte[] pE=E;
+ byte[] pF=F;
+ byte[] pPERMIT=PERMIT;
+ byte[] prHID;
+
+ if (date!=0)
+ {
+
+ prHID=pHTID;
+ if (!PINERROR)
+ {
+ pxID=null;
+ // pHID=null; // new
+ }
+ }
+ else
+ {
+ prHID=pHID;
+ pPERMIT=null;
+ pxCID=null;
+ pHTID=null;
+ }
+ if (!PINERROR)
+ {
+ pE=null;
+ pF=null;
+ }
+
+ if (SINGLE_PASS)
+ {
+ System.out.println("MPIN Single Pass");
+ int timeValue = MPIN.GET_TIME();
+ rtn=MPIN.CLIENT(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT,timeValue,Y);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ rtn=MPIN.SERVER(sha,date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF,CLIENT_ID,timeValue);
+ if (rtn != 0)
+ fail("FAILURE: SERVER rtn: " + rtn);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+ }
+ else
+ {
+ System.out.println("MPIN Multi Pass");
+ /* Send U=x.ID to server, and recreate secret from token and pin */
+ rtn=MPIN.CLIENT_1(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_1 rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ /* Server calculates H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+ MPIN.SERVER_1(sha,date,CLIENT_ID,pHID,pHTID);
+
+ /* Server generates Random number Y and sends it to Client */
+ MPIN.RANDOM_GENERATE(rng,Y);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+
+ /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+ rtn=MPIN.CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_2 rtn: " + rtn);
+
+ /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+ /* If PIN error not required, set E and F = null */
+
+ rtn=MPIN.SERVER_2(date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF);
+
+ if (rtn != 0)
+ fail("FAILURE: SERVER_2 rtn: " + rtn);
+ }
+
+ if (rtn == MPIN.BAD_PIN)
+ {
+ if (PINERROR)
+ {
+ int err=MPIN.KANGAROO(E,F);
+ if (err!=0) fail("Client PIN is out by "+err);
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+ }
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+
+ }
+ else System.out.println("Server says - PIN is good! You really are "+IDstr);
+
+
+ if (FULL)
+ {
+ byte[] H=MPIN.HASH_ALL(sha,HCID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.CLIENT_KEY(sha,G1,G2,pin,R,X,H,T,CK);
+ System.out.print("Client Key = 0x"); printBinary(CK);
+
+ H=MPIN.HASH_ALL(sha,HSID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.SERVER_KEY(sha,Z,SST,W,H,pHID,pxID,pxCID,SK);
+ System.out.print("Server Key = 0x"); printBinary(SK);
+ }
+ System.out.println("");
+ }
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ mpin(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BLS383/TestECDH.java b/src/test/java/org/apache/milagro/amcl/BLS383/TestECDH.java
new file mode 100644
index 0000000..2e3920e
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BLS383/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.BLS383; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BLS383/TestMPIN.java b/src/test/java/org/apache/milagro/amcl/BLS383/TestMPIN.java
new file mode 100644
index 0000000..8d99bd4
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BLS383/TestMPIN.java
@@ -0,0 +1,297 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.BLS383; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+
+public class TestMPIN extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+ static boolean PERMITS=true;
+ static boolean PINERROR=true;
+ static boolean FULL=true;
+ static boolean SINGLE_PASS=false;
+
+ public static void testMPIN()
+ {
+ RAND rng=new RAND();
+ int EGS=MPIN.EGS;
+ int EFS=MPIN.EFS;
+ int G1S=2*EFS+1; /* Group 1 Size */
+ int G2S=4*EFS; /* Group 2 Size */
+
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S = new byte[EGS];
+ byte[] SST = new byte[G2S];
+ byte[] TOKEN = new byte[G1S];
+ byte[] PERMIT = new byte[G1S];
+ byte[] SEC = new byte[G1S];
+ byte[] xID = new byte[G1S];
+ byte[] xCID = new byte[G1S];
+ byte[] X= new byte[EGS];
+ byte[] Y= new byte[EGS];
+ byte[] E=new byte[12*EFS];
+ byte[] F=new byte[12*EFS];
+ byte[] HID=new byte[G1S];
+ byte[] HTID=new byte[G1S];
+
+ byte[] G1=new byte[12*EFS];
+ byte[] G2=new byte[12*EFS];
+ byte[] R=new byte[EGS];
+ byte[] Z=new byte[G1S];
+ byte[] W=new byte[EGS];
+ byte[] T=new byte[G1S];
+ byte[] CK=new byte[ECP.AESKEY];
+ byte[] SK=new byte[ECP.AESKEY];
+
+ byte[] HSID=null;
+ byte[] RAW=new byte[100];
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ System.out.println("Testing MPIN code");
+
+/* Trusted Authority set-up */
+
+ MPIN.RANDOM_GENERATE(rng,S);
+ System.out.print("Master Secret s: 0x"); printBinary(S);
+
+ /* Create Client Identity */
+ String IDstr = "testUser@miracl.com";
+ byte[] CLIENT_ID = IDstr.getBytes();
+
+ byte[] HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+ System.out.print("Client ID Hash= "); printBinary(HCID);
+ System.out.print("Client ID= "); printBinary(CLIENT_ID);
+
+/* Client and Server are issued secrets by DTA */
+
+ MPIN.GET_CLIENT_SECRET(S,HCID,TOKEN);
+ System.out.print("Client Secret CS: 0x");
+ printBinary(TOKEN);
+
+ MPIN.GET_SERVER_SECRET(S,SST);
+ System.out.print("Server Secret SS: 0x"); printBinary(SST);
+
+
+/* Client extracts PIN from secret to create Token */
+ int pin=1234;
+ System.out.println("Client extracts PIN= "+pin);
+ int rtn=MPIN.EXTRACT_PIN(sha,CLIENT_ID,pin,TOKEN);
+ if (rtn != 0)
+ fail("FAILURE: EXTRACT_PIN rtn: " + rtn);
+
+ System.out.print("Client Token TK: 0x"); printBinary(TOKEN);
+
+ if (FULL)
+ {
+ MPIN.PRECOMPUTE(TOKEN,HCID,G1,G2);
+ }
+ int date;
+ if (PERMITS)
+ {
+ date=MPIN.today();
+/* Client gets "Time Token" permit from DTA */
+ MPIN.GET_CLIENT_PERMIT(sha,date,S,HCID,PERMIT);
+ System.out.print("Time Permit TP: 0x"); printBinary(PERMIT);
+
+/* This encoding makes Time permit look random - Elligator squared */
+ MPIN.ENCODING(rng,PERMIT);
+ System.out.print("Encoded Time Permit TP: 0x"); printBinary(PERMIT);
+ MPIN.DECODING(PERMIT);
+ System.out.print("Decoded Time Permit TP: 0x"); printBinary(PERMIT);
+ }
+ else date=0;
+
+// System.out.print("\nPIN= ");
+// Scanner scan=new Scanner(System.in);
+// pin=scan.nextInt();
+
+ pin=1234;
+
+/* Set date=0 and PERMIT=null if time permits not in use
+
+Client First pass: Inputs CLIENT_ID, optional RNG, pin, TOKEN and PERMIT. Output xID =x .H(CLIENT_ID) and re-combined secret SEC
+If PERMITS are is use, then date!=0 and PERMIT is added to secret and xCID = x.(H(CLIENT_ID)+H(date|H(CLIENT_ID)))
+Random value x is supplied externally if RNG=null, otherwise generated and passed out by RNG
+
+IMPORTANT: To save space and time..
+If Time Permits OFF set xCID = null, HTID=null and use xID and HID only
+If Time permits are ON, AND pin error detection is required then all of xID, xCID, HID and HTID are required
+If Time permits are ON, AND pin error detection is NOT required, set xID=null, HID=null and use xCID and HTID only.
+
+
+*/
+
+ byte[] pxID=xID;
+ byte[] pxCID=xCID;
+ byte[] pHID=HID;
+ byte[] pHTID=HTID;
+ byte[] pE=E;
+ byte[] pF=F;
+ byte[] pPERMIT=PERMIT;
+ byte[] prHID;
+
+ if (date!=0)
+ {
+
+ prHID=pHTID;
+ if (!PINERROR)
+ {
+ pxID=null;
+ // pHID=null; // new
+ }
+ }
+ else
+ {
+ prHID=pHID;
+ pPERMIT=null;
+ pxCID=null;
+ pHTID=null;
+ }
+ if (!PINERROR)
+ {
+ pE=null;
+ pF=null;
+ }
+
+ if (SINGLE_PASS)
+ {
+ System.out.println("MPIN Single Pass");
+ int timeValue = MPIN.GET_TIME();
+ rtn=MPIN.CLIENT(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT,timeValue,Y);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ rtn=MPIN.SERVER(sha,date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF,CLIENT_ID,timeValue);
+ if (rtn != 0)
+ fail("FAILURE: SERVER rtn: " + rtn);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+ }
+ else
+ {
+ System.out.println("MPIN Multi Pass");
+ /* Send U=x.ID to server, and recreate secret from token and pin */
+ rtn=MPIN.CLIENT_1(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_1 rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ /* Server calculates H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+ MPIN.SERVER_1(sha,date,CLIENT_ID,pHID,pHTID);
+
+ /* Server generates Random number Y and sends it to Client */
+ MPIN.RANDOM_GENERATE(rng,Y);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+
+ /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+ rtn=MPIN.CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_2 rtn: " + rtn);
+
+ /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+ /* If PIN error not required, set E and F = null */
+
+ rtn=MPIN.SERVER_2(date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF);
+
+ if (rtn != 0)
+ fail("FAILURE: SERVER_2 rtn: " + rtn);
+ }
+
+ if (rtn == MPIN.BAD_PIN)
+ {
+ if (PINERROR)
+ {
+ int err=MPIN.KANGAROO(E,F);
+ if (err!=0) fail("Client PIN is out by "+err);
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+ }
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+
+ }
+ else System.out.println("Server says - PIN is good! You really are "+IDstr);
+
+
+ if (FULL)
+ {
+ byte[] H=MPIN.HASH_ALL(sha,HCID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.CLIENT_KEY(sha,G1,G2,pin,R,X,H,T,CK);
+ System.out.print("Client Key = 0x"); printBinary(CK);
+
+ H=MPIN.HASH_ALL(sha,HSID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.SERVER_KEY(sha,Z,SST,W,H,pHID,pxID,pxCID,SK);
+ System.out.print("Server Key = 0x"); printBinary(SK);
+ }
+ System.out.println("");
+ }
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ mpin(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BLS461/TestECDH.java b/src/test/java/org/apache/milagro/amcl/BLS461/TestECDH.java
new file mode 100644
index 0000000..e1514a0
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BLS461/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.BLS461; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BLS461/TestMPIN.java b/src/test/java/org/apache/milagro/amcl/BLS461/TestMPIN.java
new file mode 100644
index 0000000..2915222
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BLS461/TestMPIN.java
@@ -0,0 +1,297 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.BLS461; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+
+public class TestMPIN extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+ static boolean PERMITS=true;
+ static boolean PINERROR=true;
+ static boolean FULL=true;
+ static boolean SINGLE_PASS=false;
+
+ public static void testMPIN()
+ {
+ RAND rng=new RAND();
+ int EGS=MPIN.EGS;
+ int EFS=MPIN.EFS;
+ int G1S=2*EFS+1; /* Group 1 Size */
+ int G2S=4*EFS; /* Group 2 Size */
+
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S = new byte[EGS];
+ byte[] SST = new byte[G2S];
+ byte[] TOKEN = new byte[G1S];
+ byte[] PERMIT = new byte[G1S];
+ byte[] SEC = new byte[G1S];
+ byte[] xID = new byte[G1S];
+ byte[] xCID = new byte[G1S];
+ byte[] X= new byte[EGS];
+ byte[] Y= new byte[EGS];
+ byte[] E=new byte[12*EFS];
+ byte[] F=new byte[12*EFS];
+ byte[] HID=new byte[G1S];
+ byte[] HTID=new byte[G1S];
+
+ byte[] G1=new byte[12*EFS];
+ byte[] G2=new byte[12*EFS];
+ byte[] R=new byte[EGS];
+ byte[] Z=new byte[G1S];
+ byte[] W=new byte[EGS];
+ byte[] T=new byte[G1S];
+ byte[] CK=new byte[ECP.AESKEY];
+ byte[] SK=new byte[ECP.AESKEY];
+
+ byte[] HSID=null;
+ byte[] RAW=new byte[100];
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ System.out.println("Testing MPIN code");
+
+/* Trusted Authority set-up */
+
+ MPIN.RANDOM_GENERATE(rng,S);
+ System.out.print("Master Secret s: 0x"); printBinary(S);
+
+ /* Create Client Identity */
+ String IDstr = "testUser@miracl.com";
+ byte[] CLIENT_ID = IDstr.getBytes();
+
+ byte[] HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+ System.out.print("Client ID Hash= "); printBinary(HCID);
+ System.out.print("Client ID= "); printBinary(CLIENT_ID);
+
+/* Client and Server are issued secrets by DTA */
+
+ MPIN.GET_CLIENT_SECRET(S,HCID,TOKEN);
+ System.out.print("Client Secret CS: 0x");
+ printBinary(TOKEN);
+
+ MPIN.GET_SERVER_SECRET(S,SST);
+ System.out.print("Server Secret SS: 0x"); printBinary(SST);
+
+
+/* Client extracts PIN from secret to create Token */
+ int pin=1234;
+ System.out.println("Client extracts PIN= "+pin);
+ int rtn=MPIN.EXTRACT_PIN(sha,CLIENT_ID,pin,TOKEN);
+ if (rtn != 0)
+ fail("FAILURE: EXTRACT_PIN rtn: " + rtn);
+
+ System.out.print("Client Token TK: 0x"); printBinary(TOKEN);
+
+ if (FULL)
+ {
+ MPIN.PRECOMPUTE(TOKEN,HCID,G1,G2);
+ }
+ int date;
+ if (PERMITS)
+ {
+ date=MPIN.today();
+/* Client gets "Time Token" permit from DTA */
+ MPIN.GET_CLIENT_PERMIT(sha,date,S,HCID,PERMIT);
+ System.out.print("Time Permit TP: 0x"); printBinary(PERMIT);
+
+/* This encoding makes Time permit look random - Elligator squared */
+ MPIN.ENCODING(rng,PERMIT);
+ System.out.print("Encoded Time Permit TP: 0x"); printBinary(PERMIT);
+ MPIN.DECODING(PERMIT);
+ System.out.print("Decoded Time Permit TP: 0x"); printBinary(PERMIT);
+ }
+ else date=0;
+
+// System.out.print("\nPIN= ");
+// Scanner scan=new Scanner(System.in);
+// pin=scan.nextInt();
+
+ pin=1234;
+
+/* Set date=0 and PERMIT=null if time permits not in use
+
+Client First pass: Inputs CLIENT_ID, optional RNG, pin, TOKEN and PERMIT. Output xID =x .H(CLIENT_ID) and re-combined secret SEC
+If PERMITS are is use, then date!=0 and PERMIT is added to secret and xCID = x.(H(CLIENT_ID)+H(date|H(CLIENT_ID)))
+Random value x is supplied externally if RNG=null, otherwise generated and passed out by RNG
+
+IMPORTANT: To save space and time..
+If Time Permits OFF set xCID = null, HTID=null and use xID and HID only
+If Time permits are ON, AND pin error detection is required then all of xID, xCID, HID and HTID are required
+If Time permits are ON, AND pin error detection is NOT required, set xID=null, HID=null and use xCID and HTID only.
+
+
+*/
+
+ byte[] pxID=xID;
+ byte[] pxCID=xCID;
+ byte[] pHID=HID;
+ byte[] pHTID=HTID;
+ byte[] pE=E;
+ byte[] pF=F;
+ byte[] pPERMIT=PERMIT;
+ byte[] prHID;
+
+ if (date!=0)
+ {
+
+ prHID=pHTID;
+ if (!PINERROR)
+ {
+ pxID=null;
+ // pHID=null; // new
+ }
+ }
+ else
+ {
+ prHID=pHID;
+ pPERMIT=null;
+ pxCID=null;
+ pHTID=null;
+ }
+ if (!PINERROR)
+ {
+ pE=null;
+ pF=null;
+ }
+
+ if (SINGLE_PASS)
+ {
+ System.out.println("MPIN Single Pass");
+ int timeValue = MPIN.GET_TIME();
+ rtn=MPIN.CLIENT(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT,timeValue,Y);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ rtn=MPIN.SERVER(sha,date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF,CLIENT_ID,timeValue);
+ if (rtn != 0)
+ fail("FAILURE: SERVER rtn: " + rtn);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+ }
+ else
+ {
+ System.out.println("MPIN Multi Pass");
+ /* Send U=x.ID to server, and recreate secret from token and pin */
+ rtn=MPIN.CLIENT_1(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_1 rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ /* Server calculates H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+ MPIN.SERVER_1(sha,date,CLIENT_ID,pHID,pHTID);
+
+ /* Server generates Random number Y and sends it to Client */
+ MPIN.RANDOM_GENERATE(rng,Y);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+
+ /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+ rtn=MPIN.CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_2 rtn: " + rtn);
+
+ /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+ /* If PIN error not required, set E and F = null */
+
+ rtn=MPIN.SERVER_2(date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF);
+
+ if (rtn != 0)
+ fail("FAILURE: SERVER_2 rtn: " + rtn);
+ }
+
+ if (rtn == MPIN.BAD_PIN)
+ {
+ if (PINERROR)
+ {
+ int err=MPIN.KANGAROO(E,F);
+ if (err!=0) fail("Client PIN is out by "+err);
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+ }
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+
+ }
+ else System.out.println("Server says - PIN is good! You really are "+IDstr);
+
+
+ if (FULL)
+ {
+ byte[] H=MPIN.HASH_ALL(sha,HCID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.CLIENT_KEY(sha,G1,G2,pin,R,X,H,T,CK);
+ System.out.print("Client Key = 0x"); printBinary(CK);
+
+ H=MPIN.HASH_ALL(sha,HSID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.SERVER_KEY(sha,Z,SST,W,H,pHID,pxID,pxCID,SK);
+ System.out.print("Server Key = 0x"); printBinary(SK);
+ }
+ System.out.println("");
+ }
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ mpin(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BLS48/TestECDH.java b/src/test/java/org/apache/milagro/amcl/BLS48/TestECDH.java
new file mode 100644
index 0000000..d2f4ded
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BLS48/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.BLS48; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BLS48/TestMPIN256.java b/src/test/java/org/apache/milagro/amcl/BLS48/TestMPIN256.java
new file mode 100644
index 0000000..8dcfe75
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BLS48/TestMPIN256.java
@@ -0,0 +1,297 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.BLS48; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+
+public class TestMPIN256 extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+ static boolean PERMITS=true;
+ static boolean PINERROR=true;
+ static boolean FULL=true;
+ static boolean SINGLE_PASS=false;
+
+ public static void testMPIN()
+ {
+ RAND rng=new RAND();
+ int EGS=MPIN256.EGS;
+ int EFS=MPIN256.EFS;
+ int G1S=2*EFS+1; /* Group 1 Size */
+ int G2S=16*EFS; /* Group 2 Size */
+
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S = new byte[EGS];
+ byte[] SST = new byte[G2S];
+ byte[] TOKEN = new byte[G1S];
+ byte[] PERMIT = new byte[G1S];
+ byte[] SEC = new byte[G1S];
+ byte[] xID = new byte[G1S];
+ byte[] xCID = new byte[G1S];
+ byte[] X= new byte[EGS];
+ byte[] Y= new byte[EGS];
+ byte[] E=new byte[48*EFS];
+ byte[] F=new byte[48*EFS];
+ byte[] HID=new byte[G1S];
+ byte[] HTID=new byte[G1S];
+
+ byte[] G1=new byte[48*EFS];
+ byte[] G2=new byte[48*EFS];
+ byte[] R=new byte[EGS];
+ byte[] Z=new byte[G1S];
+ byte[] W=new byte[EGS];
+ byte[] T=new byte[G1S];
+ byte[] CK=new byte[ECP.AESKEY];
+ byte[] SK=new byte[ECP.AESKEY];
+
+ byte[] HSID=null;
+ byte[] RAW=new byte[100];
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ System.out.println("Testing MPIN code");
+
+/* Trusted Authority set-up */
+
+ MPIN256.RANDOM_GENERATE(rng,S);
+ System.out.print("Master Secret s: 0x"); printBinary(S);
+
+ /* Create Client Identity */
+ String IDstr = "testUser@miracl.com";
+ byte[] CLIENT_ID = IDstr.getBytes();
+
+ byte[] HCID=MPIN256.HASH_ID(sha,CLIENT_ID,EFS); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+ System.out.print("Client ID Hash= "); printBinary(HCID);
+ System.out.print("Client ID= "); printBinary(CLIENT_ID);
+
+/* Client and Server are issued secrets by DTA */
+
+ MPIN256.GET_CLIENT_SECRET(S,HCID,TOKEN);
+ System.out.print("Client Secret CS: 0x");
+ printBinary(TOKEN);
+
+ MPIN256.GET_SERVER_SECRET(S,SST);
+ System.out.print("Server Secret SS: 0x"); printBinary(SST);
+
+
+/* Client extracts PIN from secret to create Token */
+ int pin=1234;
+ System.out.println("Client extracts PIN= "+pin);
+ int rtn=MPIN256.EXTRACT_PIN(sha,CLIENT_ID,pin,TOKEN);
+ if (rtn != 0)
+ fail("FAILURE: EXTRACT_PIN rtn: " + rtn);
+
+ System.out.print("Client Token TK: 0x"); printBinary(TOKEN);
+
+ if (FULL)
+ {
+ MPIN256.PRECOMPUTE(TOKEN,HCID,G1,G2);
+ }
+ int date;
+ if (PERMITS)
+ {
+ date=MPIN256.today();
+/* Client gets "Time Token" permit from DTA */
+ MPIN256.GET_CLIENT_PERMIT(sha,date,S,HCID,PERMIT);
+ System.out.print("Time Permit TP: 0x"); printBinary(PERMIT);
+
+/* This encoding makes Time permit look random - Elligator squared */
+ MPIN256.ENCODING(rng,PERMIT);
+ System.out.print("Encoded Time Permit TP: 0x"); printBinary(PERMIT);
+ MPIN256.DECODING(PERMIT);
+ System.out.print("Decoded Time Permit TP: 0x"); printBinary(PERMIT);
+ }
+ else date=0;
+
+// System.out.print("\nPIN= ");
+// Scanner scan=new Scanner(System.in);
+// pin=scan.nextInt();
+
+ pin=1234;
+
+/* Set date=0 and PERMIT=null if time permits not in use
+
+Client First pass: Inputs CLIENT_ID, optional RNG, pin, TOKEN and PERMIT. Output xID =x .H(CLIENT_ID) and re-combined secret SEC
+If PERMITS are is use, then date!=0 and PERMIT is added to secret and xCID = x.(H(CLIENT_ID)+H(date|H(CLIENT_ID)))
+Random value x is supplied externally if RNG=null, otherwise generated and passed out by RNG
+
+IMPORTANT: To save space and time..
+If Time Permits OFF set xCID = null, HTID=null and use xID and HID only
+If Time permits are ON, AND pin error detection is required then all of xID, xCID, HID and HTID are required
+If Time permits are ON, AND pin error detection is NOT required, set xID=null, HID=null and use xCID and HTID only.
+
+
+*/
+
+ byte[] pxID=xID;
+ byte[] pxCID=xCID;
+ byte[] pHID=HID;
+ byte[] pHTID=HTID;
+ byte[] pE=E;
+ byte[] pF=F;
+ byte[] pPERMIT=PERMIT;
+ byte[] prHID;
+
+ if (date!=0)
+ {
+
+ prHID=pHTID;
+ if (!PINERROR)
+ {
+ pxID=null;
+ // pHID=null; // new
+ }
+ }
+ else
+ {
+ prHID=pHID;
+ pPERMIT=null;
+ pxCID=null;
+ pHTID=null;
+ }
+ if (!PINERROR)
+ {
+ pE=null;
+ pF=null;
+ }
+
+ if (SINGLE_PASS)
+ {
+ System.out.println("MPIN Single Pass");
+ int timeValue = MPIN256.GET_TIME();
+ rtn=MPIN256.CLIENT(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT,timeValue,Y);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN256.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN256.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ rtn=MPIN256.SERVER(sha,date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF,CLIENT_ID,timeValue);
+ if (rtn != 0)
+ fail("FAILURE: SERVER rtn: " + rtn);
+
+ if (FULL)
+ {
+ HSID=MPIN256.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN256.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+ }
+ else
+ {
+ System.out.println("MPIN Multi Pass");
+ /* Send U=x.ID to server, and recreate secret from token and pin */
+ rtn=MPIN256.CLIENT_1(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_1 rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN256.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN256.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ /* Server calculates H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+ MPIN256.SERVER_1(sha,date,CLIENT_ID,pHID,pHTID);
+
+ /* Server generates Random number Y and sends it to Client */
+ MPIN256.RANDOM_GENERATE(rng,Y);
+
+ if (FULL)
+ {
+ HSID=MPIN256.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN256.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+
+ /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+ rtn=MPIN256.CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_2 rtn: " + rtn);
+
+ /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+ /* If PIN error not required, set E and F = null */
+
+ rtn=MPIN256.SERVER_2(date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF);
+
+ if (rtn != 0)
+ fail("FAILURE: SERVER_2 rtn: " + rtn);
+ }
+
+ if (rtn == MPIN256.BAD_PIN)
+ {
+ if (PINERROR)
+ {
+ int err=MPIN256.KANGAROO(E,F);
+ if (err!=0) fail("Client PIN is out by "+err);
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+ }
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+
+ }
+ else System.out.println("Server says - PIN is good! You really are "+IDstr);
+
+
+ if (FULL)
+ {
+ byte[] H=MPIN256.HASH_ALL(sha,HCID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN256.CLIENT_KEY(sha,G1,G2,pin,R,X,H,T,CK);
+ System.out.print("Client Key = 0x"); printBinary(CK);
+
+ H=MPIN256.HASH_ALL(sha,HSID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN256.SERVER_KEY(sha,Z,SST,W,H,pHID,pxID,pxCID,SK);
+ System.out.print("Server Key = 0x"); printBinary(SK);
+ }
+ System.out.println("");
+ }
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ mpin(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BN254/TestECDH.java b/src/test/java/org/apache/milagro/amcl/BN254/TestECDH.java
new file mode 100644
index 0000000..7a98c55
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BN254/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.BN254; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BN254/TestMPIN.java b/src/test/java/org/apache/milagro/amcl/BN254/TestMPIN.java
new file mode 100644
index 0000000..dcd963a
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BN254/TestMPIN.java
@@ -0,0 +1,297 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.BN254; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+
+public class TestMPIN extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+ static boolean PERMITS=true;
+ static boolean PINERROR=true;
+ static boolean FULL=true;
+ static boolean SINGLE_PASS=false;
+
+ public static void testMPIN()
+ {
+ RAND rng=new RAND();
+ int EGS=MPIN.EGS;
+ int EFS=MPIN.EFS;
+ int G1S=2*EFS+1; /* Group 1 Size */
+ int G2S=4*EFS; /* Group 2 Size */
+
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S = new byte[EGS];
+ byte[] SST = new byte[G2S];
+ byte[] TOKEN = new byte[G1S];
+ byte[] PERMIT = new byte[G1S];
+ byte[] SEC = new byte[G1S];
+ byte[] xID = new byte[G1S];
+ byte[] xCID = new byte[G1S];
+ byte[] X= new byte[EGS];
+ byte[] Y= new byte[EGS];
+ byte[] E=new byte[12*EFS];
+ byte[] F=new byte[12*EFS];
+ byte[] HID=new byte[G1S];
+ byte[] HTID=new byte[G1S];
+
+ byte[] G1=new byte[12*EFS];
+ byte[] G2=new byte[12*EFS];
+ byte[] R=new byte[EGS];
+ byte[] Z=new byte[G1S];
+ byte[] W=new byte[EGS];
+ byte[] T=new byte[G1S];
+ byte[] CK=new byte[ECP.AESKEY];
+ byte[] SK=new byte[ECP.AESKEY];
+
+ byte[] HSID=null;
+ byte[] RAW=new byte[100];
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ System.out.println("Testing MPIN code");
+
+/* Trusted Authority set-up */
+
+ MPIN.RANDOM_GENERATE(rng,S);
+ System.out.print("Master Secret s: 0x"); printBinary(S);
+
+ /* Create Client Identity */
+ String IDstr = "testUser@miracl.com";
+ byte[] CLIENT_ID = IDstr.getBytes();
+
+ byte[] HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+ System.out.print("Client ID Hash= "); printBinary(HCID);
+ System.out.print("Client ID= "); printBinary(CLIENT_ID);
+
+/* Client and Server are issued secrets by DTA */
+
+ MPIN.GET_CLIENT_SECRET(S,HCID,TOKEN);
+ System.out.print("Client Secret CS: 0x");
+ printBinary(TOKEN);
+
+ MPIN.GET_SERVER_SECRET(S,SST);
+ System.out.print("Server Secret SS: 0x"); printBinary(SST);
+
+
+/* Client extracts PIN from secret to create Token */
+ int pin=1234;
+ System.out.println("Client extracts PIN= "+pin);
+ int rtn=MPIN.EXTRACT_PIN(sha,CLIENT_ID,pin,TOKEN);
+ if (rtn != 0)
+ fail("FAILURE: EXTRACT_PIN rtn: " + rtn);
+
+ System.out.print("Client Token TK: 0x"); printBinary(TOKEN);
+
+ if (FULL)
+ {
+ MPIN.PRECOMPUTE(TOKEN,HCID,G1,G2);
+ }
+ int date;
+ if (PERMITS)
+ {
+ date=MPIN.today();
+/* Client gets "Time Token" permit from DTA */
+ MPIN.GET_CLIENT_PERMIT(sha,date,S,HCID,PERMIT);
+ System.out.print("Time Permit TP: 0x"); printBinary(PERMIT);
+
+/* This encoding makes Time permit look random - Elligator squared */
+ MPIN.ENCODING(rng,PERMIT);
+ System.out.print("Encoded Time Permit TP: 0x"); printBinary(PERMIT);
+ MPIN.DECODING(PERMIT);
+ System.out.print("Decoded Time Permit TP: 0x"); printBinary(PERMIT);
+ }
+ else date=0;
+
+// System.out.print("\nPIN= ");
+// Scanner scan=new Scanner(System.in);
+// pin=scan.nextInt();
+
+ pin=1234;
+
+/* Set date=0 and PERMIT=null if time permits not in use
+
+Client First pass: Inputs CLIENT_ID, optional RNG, pin, TOKEN and PERMIT. Output xID =x .H(CLIENT_ID) and re-combined secret SEC
+If PERMITS are is use, then date!=0 and PERMIT is added to secret and xCID = x.(H(CLIENT_ID)+H(date|H(CLIENT_ID)))
+Random value x is supplied externally if RNG=null, otherwise generated and passed out by RNG
+
+IMPORTANT: To save space and time..
+If Time Permits OFF set xCID = null, HTID=null and use xID and HID only
+If Time permits are ON, AND pin error detection is required then all of xID, xCID, HID and HTID are required
+If Time permits are ON, AND pin error detection is NOT required, set xID=null, HID=null and use xCID and HTID only.
+
+
+*/
+
+ byte[] pxID=xID;
+ byte[] pxCID=xCID;
+ byte[] pHID=HID;
+ byte[] pHTID=HTID;
+ byte[] pE=E;
+ byte[] pF=F;
+ byte[] pPERMIT=PERMIT;
+ byte[] prHID;
+
+ if (date!=0)
+ {
+
+ prHID=pHTID;
+ if (!PINERROR)
+ {
+ pxID=null;
+ // pHID=null; // new
+ }
+ }
+ else
+ {
+ prHID=pHID;
+ pPERMIT=null;
+ pxCID=null;
+ pHTID=null;
+ }
+ if (!PINERROR)
+ {
+ pE=null;
+ pF=null;
+ }
+
+ if (SINGLE_PASS)
+ {
+ System.out.println("MPIN Single Pass");
+ int timeValue = MPIN.GET_TIME();
+ rtn=MPIN.CLIENT(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT,timeValue,Y);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ rtn=MPIN.SERVER(sha,date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF,CLIENT_ID,timeValue);
+ if (rtn != 0)
+ fail("FAILURE: SERVER rtn: " + rtn);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+ }
+ else
+ {
+ System.out.println("MPIN Multi Pass");
+ /* Send U=x.ID to server, and recreate secret from token and pin */
+ rtn=MPIN.CLIENT_1(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_1 rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ /* Server calculates H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+ MPIN.SERVER_1(sha,date,CLIENT_ID,pHID,pHTID);
+
+ /* Server generates Random number Y and sends it to Client */
+ MPIN.RANDOM_GENERATE(rng,Y);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+
+ /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+ rtn=MPIN.CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_2 rtn: " + rtn);
+
+ /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+ /* If PIN error not required, set E and F = null */
+
+ rtn=MPIN.SERVER_2(date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF);
+
+ if (rtn != 0)
+ fail("FAILURE: SERVER_2 rtn: " + rtn);
+ }
+
+ if (rtn == MPIN.BAD_PIN)
+ {
+ if (PINERROR)
+ {
+ int err=MPIN.KANGAROO(E,F);
+ if (err!=0) fail("Client PIN is out by "+err);
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+ }
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+
+ }
+ else System.out.println("Server says - PIN is good! You really are "+IDstr);
+
+
+ if (FULL)
+ {
+ byte[] H=MPIN.HASH_ALL(sha,HCID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.CLIENT_KEY(sha,G1,G2,pin,R,X,H,T,CK);
+ System.out.print("Client Key = 0x"); printBinary(CK);
+
+ H=MPIN.HASH_ALL(sha,HSID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.SERVER_KEY(sha,Z,SST,W,H,pHID,pxID,pxCID,SK);
+ System.out.print("Server Key = 0x"); printBinary(SK);
+ }
+ System.out.println("");
+ }
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ mpin(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BN254CX/TestECDH.java b/src/test/java/org/apache/milagro/amcl/BN254CX/TestECDH.java
new file mode 100644
index 0000000..580ab93
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BN254CX/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.BN254CX; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BN254CX/TestMPIN.java b/src/test/java/org/apache/milagro/amcl/BN254CX/TestMPIN.java
new file mode 100644
index 0000000..dc9e688
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BN254CX/TestMPIN.java
@@ -0,0 +1,297 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.BN254CX; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+
+public class TestMPIN extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+ static boolean PERMITS=true;
+ static boolean PINERROR=true;
+ static boolean FULL=true;
+ static boolean SINGLE_PASS=false;
+
+ public static void testMPIN()
+ {
+ RAND rng=new RAND();
+ int EGS=MPIN.EGS;
+ int EFS=MPIN.EFS;
+ int G1S=2*EFS+1; /* Group 1 Size */
+ int G2S=4*EFS; /* Group 2 Size */
+
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S = new byte[EGS];
+ byte[] SST = new byte[G2S];
+ byte[] TOKEN = new byte[G1S];
+ byte[] PERMIT = new byte[G1S];
+ byte[] SEC = new byte[G1S];
+ byte[] xID = new byte[G1S];
+ byte[] xCID = new byte[G1S];
+ byte[] X= new byte[EGS];
+ byte[] Y= new byte[EGS];
+ byte[] E=new byte[12*EFS];
+ byte[] F=new byte[12*EFS];
+ byte[] HID=new byte[G1S];
+ byte[] HTID=new byte[G1S];
+
+ byte[] G1=new byte[12*EFS];
+ byte[] G2=new byte[12*EFS];
+ byte[] R=new byte[EGS];
+ byte[] Z=new byte[G1S];
+ byte[] W=new byte[EGS];
+ byte[] T=new byte[G1S];
+ byte[] CK=new byte[ECP.AESKEY];
+ byte[] SK=new byte[ECP.AESKEY];
+
+ byte[] HSID=null;
+ byte[] RAW=new byte[100];
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ System.out.println("Testing MPIN code");
+
+/* Trusted Authority set-up */
+
+ MPIN.RANDOM_GENERATE(rng,S);
+ System.out.print("Master Secret s: 0x"); printBinary(S);
+
+ /* Create Client Identity */
+ String IDstr = "testUser@miracl.com";
+ byte[] CLIENT_ID = IDstr.getBytes();
+
+ byte[] HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+ System.out.print("Client ID Hash= "); printBinary(HCID);
+ System.out.print("Client ID= "); printBinary(CLIENT_ID);
+
+/* Client and Server are issued secrets by DTA */
+
+ MPIN.GET_CLIENT_SECRET(S,HCID,TOKEN);
+ System.out.print("Client Secret CS: 0x");
+ printBinary(TOKEN);
+
+ MPIN.GET_SERVER_SECRET(S,SST);
+ System.out.print("Server Secret SS: 0x"); printBinary(SST);
+
+
+/* Client extracts PIN from secret to create Token */
+ int pin=1234;
+ System.out.println("Client extracts PIN= "+pin);
+ int rtn=MPIN.EXTRACT_PIN(sha,CLIENT_ID,pin,TOKEN);
+ if (rtn != 0)
+ fail("FAILURE: EXTRACT_PIN rtn: " + rtn);
+
+ System.out.print("Client Token TK: 0x"); printBinary(TOKEN);
+
+ if (FULL)
+ {
+ MPIN.PRECOMPUTE(TOKEN,HCID,G1,G2);
+ }
+ int date;
+ if (PERMITS)
+ {
+ date=MPIN.today();
+/* Client gets "Time Token" permit from DTA */
+ MPIN.GET_CLIENT_PERMIT(sha,date,S,HCID,PERMIT);
+ System.out.print("Time Permit TP: 0x"); printBinary(PERMIT);
+
+/* This encoding makes Time permit look random - Elligator squared */
+ MPIN.ENCODING(rng,PERMIT);
+ System.out.print("Encoded Time Permit TP: 0x"); printBinary(PERMIT);
+ MPIN.DECODING(PERMIT);
+ System.out.print("Decoded Time Permit TP: 0x"); printBinary(PERMIT);
+ }
+ else date=0;
+
+// System.out.print("\nPIN= ");
+// Scanner scan=new Scanner(System.in);
+// pin=scan.nextInt();
+
+ pin=1234;
+
+/* Set date=0 and PERMIT=null if time permits not in use
+
+Client First pass: Inputs CLIENT_ID, optional RNG, pin, TOKEN and PERMIT. Output xID =x .H(CLIENT_ID) and re-combined secret SEC
+If PERMITS are is use, then date!=0 and PERMIT is added to secret and xCID = x.(H(CLIENT_ID)+H(date|H(CLIENT_ID)))
+Random value x is supplied externally if RNG=null, otherwise generated and passed out by RNG
+
+IMPORTANT: To save space and time..
+If Time Permits OFF set xCID = null, HTID=null and use xID and HID only
+If Time permits are ON, AND pin error detection is required then all of xID, xCID, HID and HTID are required
+If Time permits are ON, AND pin error detection is NOT required, set xID=null, HID=null and use xCID and HTID only.
+
+
+*/
+
+ byte[] pxID=xID;
+ byte[] pxCID=xCID;
+ byte[] pHID=HID;
+ byte[] pHTID=HTID;
+ byte[] pE=E;
+ byte[] pF=F;
+ byte[] pPERMIT=PERMIT;
+ byte[] prHID;
+
+ if (date!=0)
+ {
+
+ prHID=pHTID;
+ if (!PINERROR)
+ {
+ pxID=null;
+ // pHID=null; // new
+ }
+ }
+ else
+ {
+ prHID=pHID;
+ pPERMIT=null;
+ pxCID=null;
+ pHTID=null;
+ }
+ if (!PINERROR)
+ {
+ pE=null;
+ pF=null;
+ }
+
+ if (SINGLE_PASS)
+ {
+ System.out.println("MPIN Single Pass");
+ int timeValue = MPIN.GET_TIME();
+ rtn=MPIN.CLIENT(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT,timeValue,Y);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ rtn=MPIN.SERVER(sha,date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF,CLIENT_ID,timeValue);
+ if (rtn != 0)
+ fail("FAILURE: SERVER rtn: " + rtn);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+ }
+ else
+ {
+ System.out.println("MPIN Multi Pass");
+ /* Send U=x.ID to server, and recreate secret from token and pin */
+ rtn=MPIN.CLIENT_1(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_1 rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ /* Server calculates H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+ MPIN.SERVER_1(sha,date,CLIENT_ID,pHID,pHTID);
+
+ /* Server generates Random number Y and sends it to Client */
+ MPIN.RANDOM_GENERATE(rng,Y);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+
+ /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+ rtn=MPIN.CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_2 rtn: " + rtn);
+
+ /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+ /* If PIN error not required, set E and F = null */
+
+ rtn=MPIN.SERVER_2(date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF);
+
+ if (rtn != 0)
+ fail("FAILURE: SERVER_2 rtn: " + rtn);
+ }
+
+ if (rtn == MPIN.BAD_PIN)
+ {
+ if (PINERROR)
+ {
+ int err=MPIN.KANGAROO(E,F);
+ if (err!=0) fail("Client PIN is out by "+err);
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+ }
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+
+ }
+ else System.out.println("Server says - PIN is good! You really are "+IDstr);
+
+
+ if (FULL)
+ {
+ byte[] H=MPIN.HASH_ALL(sha,HCID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.CLIENT_KEY(sha,G1,G2,pin,R,X,H,T,CK);
+ System.out.print("Client Key = 0x"); printBinary(CK);
+
+ H=MPIN.HASH_ALL(sha,HSID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.SERVER_KEY(sha,Z,SST,W,H,pHID,pxID,pxCID,SK);
+ System.out.print("Server Key = 0x"); printBinary(SK);
+ }
+ System.out.println("");
+ }
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ mpin(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/BRAINPOOL/TestECDH.java b/src/test/java/org/apache/milagro/amcl/BRAINPOOL/TestECDH.java
new file mode 100644
index 0000000..ae45c75
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/BRAINPOOL/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.BRAINPOOL; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/C25519/TestECDH.java b/src/test/java/org/apache/milagro/amcl/C25519/TestECDH.java
new file mode 100644
index 0000000..b4f7123
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/C25519/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.C25519; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/C41417/TestECDH.java b/src/test/java/org/apache/milagro/amcl/C41417/TestECDH.java
new file mode 100644
index 0000000..894cc21
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/C41417/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.C41417; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/ED25519/TestECDH.java b/src/test/java/org/apache/milagro/amcl/ED25519/TestECDH.java
new file mode 100644
index 0000000..9373149
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/ED25519/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.ED25519; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/FP256BN/TestECDH.java b/src/test/java/org/apache/milagro/amcl/FP256BN/TestECDH.java
new file mode 100644
index 0000000..1b9c199
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/FP256BN/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.FP256BN; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/FP256BN/TestMPIN.java b/src/test/java/org/apache/milagro/amcl/FP256BN/TestMPIN.java
new file mode 100644
index 0000000..6b140d7
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/FP256BN/TestMPIN.java
@@ -0,0 +1,297 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.FP256BN; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+
+public class TestMPIN extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+ static boolean PERMITS=true;
+ static boolean PINERROR=true;
+ static boolean FULL=true;
+ static boolean SINGLE_PASS=false;
+
+ public static void testMPIN()
+ {
+ RAND rng=new RAND();
+ int EGS=MPIN.EGS;
+ int EFS=MPIN.EFS;
+ int G1S=2*EFS+1; /* Group 1 Size */
+ int G2S=4*EFS; /* Group 2 Size */
+
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S = new byte[EGS];
+ byte[] SST = new byte[G2S];
+ byte[] TOKEN = new byte[G1S];
+ byte[] PERMIT = new byte[G1S];
+ byte[] SEC = new byte[G1S];
+ byte[] xID = new byte[G1S];
+ byte[] xCID = new byte[G1S];
+ byte[] X= new byte[EGS];
+ byte[] Y= new byte[EGS];
+ byte[] E=new byte[12*EFS];
+ byte[] F=new byte[12*EFS];
+ byte[] HID=new byte[G1S];
+ byte[] HTID=new byte[G1S];
+
+ byte[] G1=new byte[12*EFS];
+ byte[] G2=new byte[12*EFS];
+ byte[] R=new byte[EGS];
+ byte[] Z=new byte[G1S];
+ byte[] W=new byte[EGS];
+ byte[] T=new byte[G1S];
+ byte[] CK=new byte[ECP.AESKEY];
+ byte[] SK=new byte[ECP.AESKEY];
+
+ byte[] HSID=null;
+ byte[] RAW=new byte[100];
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ System.out.println("Testing MPIN code");
+
+/* Trusted Authority set-up */
+
+ MPIN.RANDOM_GENERATE(rng,S);
+ System.out.print("Master Secret s: 0x"); printBinary(S);
+
+ /* Create Client Identity */
+ String IDstr = "testUser@miracl.com";
+ byte[] CLIENT_ID = IDstr.getBytes();
+
+ byte[] HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+ System.out.print("Client ID Hash= "); printBinary(HCID);
+ System.out.print("Client ID= "); printBinary(CLIENT_ID);
+
+/* Client and Server are issued secrets by DTA */
+
+ MPIN.GET_CLIENT_SECRET(S,HCID,TOKEN);
+ System.out.print("Client Secret CS: 0x");
+ printBinary(TOKEN);
+
+ MPIN.GET_SERVER_SECRET(S,SST);
+ System.out.print("Server Secret SS: 0x"); printBinary(SST);
+
+
+/* Client extracts PIN from secret to create Token */
+ int pin=1234;
+ System.out.println("Client extracts PIN= "+pin);
+ int rtn=MPIN.EXTRACT_PIN(sha,CLIENT_ID,pin,TOKEN);
+ if (rtn != 0)
+ fail("FAILURE: EXTRACT_PIN rtn: " + rtn);
+
+ System.out.print("Client Token TK: 0x"); printBinary(TOKEN);
+
+ if (FULL)
+ {
+ MPIN.PRECOMPUTE(TOKEN,HCID,G1,G2);
+ }
+ int date;
+ if (PERMITS)
+ {
+ date=MPIN.today();
+/* Client gets "Time Token" permit from DTA */
+ MPIN.GET_CLIENT_PERMIT(sha,date,S,HCID,PERMIT);
+ System.out.print("Time Permit TP: 0x"); printBinary(PERMIT);
+
+/* This encoding makes Time permit look random - Elligator squared */
+ MPIN.ENCODING(rng,PERMIT);
+ System.out.print("Encoded Time Permit TP: 0x"); printBinary(PERMIT);
+ MPIN.DECODING(PERMIT);
+ System.out.print("Decoded Time Permit TP: 0x"); printBinary(PERMIT);
+ }
+ else date=0;
+
+// System.out.print("\nPIN= ");
+// Scanner scan=new Scanner(System.in);
+// pin=scan.nextInt();
+
+ pin=1234;
+
+/* Set date=0 and PERMIT=null if time permits not in use
+
+Client First pass: Inputs CLIENT_ID, optional RNG, pin, TOKEN and PERMIT. Output xID =x .H(CLIENT_ID) and re-combined secret SEC
+If PERMITS are is use, then date!=0 and PERMIT is added to secret and xCID = x.(H(CLIENT_ID)+H(date|H(CLIENT_ID)))
+Random value x is supplied externally if RNG=null, otherwise generated and passed out by RNG
+
+IMPORTANT: To save space and time..
+If Time Permits OFF set xCID = null, HTID=null and use xID and HID only
+If Time permits are ON, AND pin error detection is required then all of xID, xCID, HID and HTID are required
+If Time permits are ON, AND pin error detection is NOT required, set xID=null, HID=null and use xCID and HTID only.
+
+
+*/
+
+ byte[] pxID=xID;
+ byte[] pxCID=xCID;
+ byte[] pHID=HID;
+ byte[] pHTID=HTID;
+ byte[] pE=E;
+ byte[] pF=F;
+ byte[] pPERMIT=PERMIT;
+ byte[] prHID;
+
+ if (date!=0)
+ {
+
+ prHID=pHTID;
+ if (!PINERROR)
+ {
+ pxID=null;
+ // pHID=null; // new
+ }
+ }
+ else
+ {
+ prHID=pHID;
+ pPERMIT=null;
+ pxCID=null;
+ pHTID=null;
+ }
+ if (!PINERROR)
+ {
+ pE=null;
+ pF=null;
+ }
+
+ if (SINGLE_PASS)
+ {
+ System.out.println("MPIN Single Pass");
+ int timeValue = MPIN.GET_TIME();
+ rtn=MPIN.CLIENT(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT,timeValue,Y);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ rtn=MPIN.SERVER(sha,date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF,CLIENT_ID,timeValue);
+ if (rtn != 0)
+ fail("FAILURE: SERVER rtn: " + rtn);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+ }
+ else
+ {
+ System.out.println("MPIN Multi Pass");
+ /* Send U=x.ID to server, and recreate secret from token and pin */
+ rtn=MPIN.CLIENT_1(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_1 rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ /* Server calculates H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+ MPIN.SERVER_1(sha,date,CLIENT_ID,pHID,pHTID);
+
+ /* Server generates Random number Y and sends it to Client */
+ MPIN.RANDOM_GENERATE(rng,Y);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+
+ /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+ rtn=MPIN.CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_2 rtn: " + rtn);
+
+ /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+ /* If PIN error not required, set E and F = null */
+
+ rtn=MPIN.SERVER_2(date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF);
+
+ if (rtn != 0)
+ fail("FAILURE: SERVER_2 rtn: " + rtn);
+ }
+
+ if (rtn == MPIN.BAD_PIN)
+ {
+ if (PINERROR)
+ {
+ int err=MPIN.KANGAROO(E,F);
+ if (err!=0) fail("Client PIN is out by "+err);
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+ }
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+
+ }
+ else System.out.println("Server says - PIN is good! You really are "+IDstr);
+
+
+ if (FULL)
+ {
+ byte[] H=MPIN.HASH_ALL(sha,HCID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.CLIENT_KEY(sha,G1,G2,pin,R,X,H,T,CK);
+ System.out.print("Client Key = 0x"); printBinary(CK);
+
+ H=MPIN.HASH_ALL(sha,HSID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.SERVER_KEY(sha,Z,SST,W,H,pHID,pxID,pxCID,SK);
+ System.out.print("Server Key = 0x"); printBinary(SK);
+ }
+ System.out.println("");
+ }
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ mpin(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/FP512BN/TestECDH.java b/src/test/java/org/apache/milagro/amcl/FP512BN/TestECDH.java
new file mode 100644
index 0000000..0e2013e
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/FP512BN/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.FP512BN; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/FP512BN/TestMPIN.java b/src/test/java/org/apache/milagro/amcl/FP512BN/TestMPIN.java
new file mode 100644
index 0000000..2fa297d
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/FP512BN/TestMPIN.java
@@ -0,0 +1,297 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.FP512BN; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+
+public class TestMPIN extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+
+ static boolean PERMITS=true;
+ static boolean PINERROR=true;
+ static boolean FULL=true;
+ static boolean SINGLE_PASS=false;
+
+ public static void testMPIN()
+ {
+ RAND rng=new RAND();
+ int EGS=MPIN.EGS;
+ int EFS=MPIN.EFS;
+ int G1S=2*EFS+1; /* Group 1 Size */
+ int G2S=4*EFS; /* Group 2 Size */
+
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S = new byte[EGS];
+ byte[] SST = new byte[G2S];
+ byte[] TOKEN = new byte[G1S];
+ byte[] PERMIT = new byte[G1S];
+ byte[] SEC = new byte[G1S];
+ byte[] xID = new byte[G1S];
+ byte[] xCID = new byte[G1S];
+ byte[] X= new byte[EGS];
+ byte[] Y= new byte[EGS];
+ byte[] E=new byte[12*EFS];
+ byte[] F=new byte[12*EFS];
+ byte[] HID=new byte[G1S];
+ byte[] HTID=new byte[G1S];
+
+ byte[] G1=new byte[12*EFS];
+ byte[] G2=new byte[12*EFS];
+ byte[] R=new byte[EGS];
+ byte[] Z=new byte[G1S];
+ byte[] W=new byte[EGS];
+ byte[] T=new byte[G1S];
+ byte[] CK=new byte[ECP.AESKEY];
+ byte[] SK=new byte[ECP.AESKEY];
+
+ byte[] HSID=null;
+ byte[] RAW=new byte[100];
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ System.out.println("Testing MPIN code");
+
+/* Trusted Authority set-up */
+
+ MPIN.RANDOM_GENERATE(rng,S);
+ System.out.print("Master Secret s: 0x"); printBinary(S);
+
+ /* Create Client Identity */
+ String IDstr = "testUser@miracl.com";
+ byte[] CLIENT_ID = IDstr.getBytes();
+
+ byte[] HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+ System.out.print("Client ID Hash= "); printBinary(HCID);
+ System.out.print("Client ID= "); printBinary(CLIENT_ID);
+
+/* Client and Server are issued secrets by DTA */
+
+ MPIN.GET_CLIENT_SECRET(S,HCID,TOKEN);
+ System.out.print("Client Secret CS: 0x");
+ printBinary(TOKEN);
+
+ MPIN.GET_SERVER_SECRET(S,SST);
+ System.out.print("Server Secret SS: 0x"); printBinary(SST);
+
+
+/* Client extracts PIN from secret to create Token */
+ int pin=1234;
+ System.out.println("Client extracts PIN= "+pin);
+ int rtn=MPIN.EXTRACT_PIN(sha,CLIENT_ID,pin,TOKEN);
+ if (rtn != 0)
+ fail("FAILURE: EXTRACT_PIN rtn: " + rtn);
+
+ System.out.print("Client Token TK: 0x"); printBinary(TOKEN);
+
+ if (FULL)
+ {
+ MPIN.PRECOMPUTE(TOKEN,HCID,G1,G2);
+ }
+ int date;
+ if (PERMITS)
+ {
+ date=MPIN.today();
+/* Client gets "Time Token" permit from DTA */
+ MPIN.GET_CLIENT_PERMIT(sha,date,S,HCID,PERMIT);
+ System.out.print("Time Permit TP: 0x"); printBinary(PERMIT);
+
+/* This encoding makes Time permit look random - Elligator squared */
+ MPIN.ENCODING(rng,PERMIT);
+ System.out.print("Encoded Time Permit TP: 0x"); printBinary(PERMIT);
+ MPIN.DECODING(PERMIT);
+ System.out.print("Decoded Time Permit TP: 0x"); printBinary(PERMIT);
+ }
+ else date=0;
+
+// System.out.print("\nPIN= ");
+// Scanner scan=new Scanner(System.in);
+// pin=scan.nextInt();
+
+ pin=1234;
+
+/* Set date=0 and PERMIT=null if time permits not in use
+
+Client First pass: Inputs CLIENT_ID, optional RNG, pin, TOKEN and PERMIT. Output xID =x .H(CLIENT_ID) and re-combined secret SEC
+If PERMITS are is use, then date!=0 and PERMIT is added to secret and xCID = x.(H(CLIENT_ID)+H(date|H(CLIENT_ID)))
+Random value x is supplied externally if RNG=null, otherwise generated and passed out by RNG
+
+IMPORTANT: To save space and time..
+If Time Permits OFF set xCID = null, HTID=null and use xID and HID only
+If Time permits are ON, AND pin error detection is required then all of xID, xCID, HID and HTID are required
+If Time permits are ON, AND pin error detection is NOT required, set xID=null, HID=null and use xCID and HTID only.
+
+
+*/
+
+ byte[] pxID=xID;
+ byte[] pxCID=xCID;
+ byte[] pHID=HID;
+ byte[] pHTID=HTID;
+ byte[] pE=E;
+ byte[] pF=F;
+ byte[] pPERMIT=PERMIT;
+ byte[] prHID;
+
+ if (date!=0)
+ {
+
+ prHID=pHTID;
+ if (!PINERROR)
+ {
+ pxID=null;
+ // pHID=null; // new
+ }
+ }
+ else
+ {
+ prHID=pHID;
+ pPERMIT=null;
+ pxCID=null;
+ pHTID=null;
+ }
+ if (!PINERROR)
+ {
+ pE=null;
+ pF=null;
+ }
+
+ if (SINGLE_PASS)
+ {
+ System.out.println("MPIN Single Pass");
+ int timeValue = MPIN.GET_TIME();
+ rtn=MPIN.CLIENT(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT,timeValue,Y);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ rtn=MPIN.SERVER(sha,date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF,CLIENT_ID,timeValue);
+ if (rtn != 0)
+ fail("FAILURE: SERVER rtn: " + rtn);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+ }
+ else
+ {
+ System.out.println("MPIN Multi Pass");
+ /* Send U=x.ID to server, and recreate secret from token and pin */
+ rtn=MPIN.CLIENT_1(sha,date,CLIENT_ID,rng,X,pin,TOKEN,SEC,pxID,pxCID,pPERMIT);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_1 rtn: " + rtn);
+
+ if (FULL)
+ {
+ HCID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,1,R,HCID,Z); /* Also Send Z=r.ID to Server, remember random r */
+ }
+
+ /* Server calculates H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+ MPIN.SERVER_1(sha,date,CLIENT_ID,pHID,pHTID);
+
+ /* Server generates Random number Y and sends it to Client */
+ MPIN.RANDOM_GENERATE(rng,Y);
+
+ if (FULL)
+ {
+ HSID=MPIN.HASH_ID(sha,CLIENT_ID,EFS);
+ MPIN.GET_G1_MULTIPLE(rng,0,W,prHID,T); /* Also send T=w.ID to client, remember random w */
+ }
+
+ /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+ rtn=MPIN.CLIENT_2(X,Y,SEC);
+ if (rtn != 0)
+ fail("FAILURE: CLIENT_2 rtn: " + rtn);
+
+ /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+ /* If PIN error not required, set E and F = null */
+
+ rtn=MPIN.SERVER_2(date,pHID,pHTID,Y,SST,pxID,pxCID,SEC,pE,pF);
+
+ if (rtn != 0)
+ fail("FAILURE: SERVER_2 rtn: " + rtn);
+ }
+
+ if (rtn == MPIN.BAD_PIN)
+ {
+ if (PINERROR)
+ {
+ int err=MPIN.KANGAROO(E,F);
+ if (err!=0) fail("Client PIN is out by "+err);
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+ }
+ else fail("Server says - Bad Pin. I don't know you. Feck off");
+
+ }
+ else System.out.println("Server says - PIN is good! You really are "+IDstr);
+
+
+ if (FULL)
+ {
+ byte[] H=MPIN.HASH_ALL(sha,HCID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.CLIENT_KEY(sha,G1,G2,pin,R,X,H,T,CK);
+ System.out.print("Client Key = 0x"); printBinary(CK);
+
+ H=MPIN.HASH_ALL(sha,HSID,pxID,pxCID,SEC,Y,Z,T,EFS);
+ MPIN.SERVER_KEY(sha,Z,SST,W,H,pHID,pxID,pxCID,SK);
+ System.out.print("Server Key = 0x"); printBinary(SK);
+ }
+ System.out.println("");
+ }
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ mpin(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/GOLDILOCKS/TestECDH.java b/src/test/java/org/apache/milagro/amcl/GOLDILOCKS/TestECDH.java
new file mode 100644
index 0000000..1046f3e
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/GOLDILOCKS/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.GOLDILOCKS; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/HIFIVE/TestECDH.java b/src/test/java/org/apache/milagro/amcl/HIFIVE/TestECDH.java
new file mode 100644
index 0000000..8a5733c
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/HIFIVE/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.HIFIVE; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/NIST256/TestECDH.java b/src/test/java/org/apache/milagro/amcl/NIST256/TestECDH.java
new file mode 100644
index 0000000..ab2ba14
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/NIST256/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.NIST256; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/NIST384/TestECDH.java b/src/test/java/org/apache/milagro/amcl/NIST384/TestECDH.java
new file mode 100644
index 0000000..be5a5d5
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/NIST384/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.NIST384; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/NIST521/TestECDH.java b/src/test/java/org/apache/milagro/amcl/NIST521/TestECDH.java
new file mode 100644
index 0000000..1c20eff
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/NIST521/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.NIST521; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/NUMS256E/TestECDH.java b/src/test/java/org/apache/milagro/amcl/NUMS256E/TestECDH.java
new file mode 100644
index 0000000..4711a89
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/NUMS256E/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.NUMS256E; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/NUMS256W/TestECDH.java b/src/test/java/org/apache/milagro/amcl/NUMS256W/TestECDH.java
new file mode 100644
index 0000000..0ad38ea
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/NUMS256W/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.NUMS256W; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/NUMS384E/TestECDH.java b/src/test/java/org/apache/milagro/amcl/NUMS384E/TestECDH.java
new file mode 100644
index 0000000..addbcc7
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/NUMS384E/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.NUMS384E; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/NUMS384W/TestECDH.java b/src/test/java/org/apache/milagro/amcl/NUMS384W/TestECDH.java
new file mode 100644
index 0000000..f556ae1
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/NUMS384W/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.NUMS384W; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/NUMS512E/TestECDH.java b/src/test/java/org/apache/milagro/amcl/NUMS512E/TestECDH.java
new file mode 100644
index 0000000..5950ce1
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/NUMS512E/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.NUMS512E; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/NUMS512W/TestECDH.java b/src/test/java/org/apache/milagro/amcl/NUMS512W/TestECDH.java
new file mode 100644
index 0000000..bf29666
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/NUMS512W/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.NUMS512W; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}
diff --git a/src/test/java/org/apache/milagro/amcl/RSA2048/TestRSA.java b/src/test/java/org/apache/milagro/amcl/RSA2048/TestRSA.java
new file mode 100644
index 0000000..392a339
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/RSA2048/TestRSA.java
@@ -0,0 +1,111 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.RSA2048;
+
+import java.util.Scanner;
+import junit.framework.TestCase;
+import org.apache.milagro.amcl.RAND;
+
+public class TestRSA extends TestCase
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testRSA()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ int i;
+ int RFS=RSA.RFS;
+ int sha=RSA.HASH_TYPE;
+
+ String message="Hello World\n";
+
+ public_key pub=new public_key(FF.FFLEN);
+ private_key priv=new private_key(FF.HFLEN);
+
+ byte[] ML=new byte[RFS];
+ byte[] C=new byte[RFS];
+ byte[] S=new byte[RFS];
+
+
+ System.out.println("Testing RSA code");
+
+ System.out.println("Generating public/private key pair");
+ RSA.KEY_PAIR(rng,65537,priv,pub);
+
+ byte[] M=message.getBytes();
+ System.out.print("Encrypting test string\n");
+
+
+ byte[] E=RSA.OAEP_ENCODE(sha,M,rng,null); /* OAEP encode message M to E */
+
+ if (E.length==0) {
+ fail("Encoding failed");
+ }
+
+ RSA.ENCRYPT(pub,E,C); /* encrypt encoded message */
+ System.out.print("Ciphertext= 0x"); printBinary(C);
+
+ System.out.print("Decrypting test string\n");
+ RSA.DECRYPT(priv,C,ML);
+ byte[] MS=RSA.OAEP_DECODE(sha,null,ML); /* OAEP decode message */
+
+ message=new String(MS);
+ System.out.print(message);
+
+ System.out.println("Signing message");
+ RSA.PKCS15(sha,M,C);
+
+ RSA.DECRYPT(priv,C,S); /* create signature in S */
+
+ System.out.print("Signature= 0x"); printBinary(S);
+
+ RSA.ENCRYPT(pub,S,ML);
+
+ boolean cmp=true;
+ if (C.length!=ML.length) cmp=false;
+ else
+ {
+ for (int j=0;j<C.length;j++)
+ if (C[j]!=ML[j]) cmp=false;
+ }
+ if (cmp) System.out.println("Signature is valid");
+ else fail("Signature is INVALID");
+
+ RSA.PRIVATE_KEY_KILL(priv);
+ System.out.println("");
+
+ }
+}
diff --git a/src/test/java/org/apache/milagro/amcl/RSA3072/TestRSA.java b/src/test/java/org/apache/milagro/amcl/RSA3072/TestRSA.java
new file mode 100644
index 0000000..61109aa
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/RSA3072/TestRSA.java
@@ -0,0 +1,111 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.RSA3072;
+
+import java.util.Scanner;
+import junit.framework.TestCase;
+import org.apache.milagro.amcl.RAND;
+
+public class TestRSA extends TestCase
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testRSA()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ int i;
+ int RFS=RSA.RFS;
+ int sha=RSA.HASH_TYPE;
+
+ String message="Hello World\n";
+
+ public_key pub=new public_key(FF.FFLEN);
+ private_key priv=new private_key(FF.HFLEN);
+
+ byte[] ML=new byte[RFS];
+ byte[] C=new byte[RFS];
+ byte[] S=new byte[RFS];
+
+
+ System.out.println("Testing RSA code");
+
+ System.out.println("Generating public/private key pair");
+ RSA.KEY_PAIR(rng,65537,priv,pub);
+
+ byte[] M=message.getBytes();
+ System.out.print("Encrypting test string\n");
+
+
+ byte[] E=RSA.OAEP_ENCODE(sha,M,rng,null); /* OAEP encode message M to E */
+
+ if (E.length==0) {
+ fail("Encoding failed");
+ }
+
+ RSA.ENCRYPT(pub,E,C); /* encrypt encoded message */
+ System.out.print("Ciphertext= 0x"); printBinary(C);
+
+ System.out.print("Decrypting test string\n");
+ RSA.DECRYPT(priv,C,ML);
+ byte[] MS=RSA.OAEP_DECODE(sha,null,ML); /* OAEP decode message */
+
+ message=new String(MS);
+ System.out.print(message);
+
+ System.out.println("Signing message");
+ RSA.PKCS15(sha,M,C);
+
+ RSA.DECRYPT(priv,C,S); /* create signature in S */
+
+ System.out.print("Signature= 0x"); printBinary(S);
+
+ RSA.ENCRYPT(pub,S,ML);
+
+ boolean cmp=true;
+ if (C.length!=ML.length) cmp=false;
+ else
+ {
+ for (int j=0;j<C.length;j++)
+ if (C[j]!=ML[j]) cmp=false;
+ }
+ if (cmp) System.out.println("Signature is valid");
+ else fail("Signature is INVALID");
+
+ RSA.PRIVATE_KEY_KILL(priv);
+ System.out.println("");
+
+ }
+}
diff --git a/src/test/java/org/apache/milagro/amcl/RSA4096/TestRSA.java b/src/test/java/org/apache/milagro/amcl/RSA4096/TestRSA.java
new file mode 100644
index 0000000..2b44a68
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/RSA4096/TestRSA.java
@@ -0,0 +1,111 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+package org.apache.milagro.amcl.RSA4096;
+
+import java.util.Scanner;
+import junit.framework.TestCase;
+import org.apache.milagro.amcl.RAND;
+
+public class TestRSA extends TestCase
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testRSA()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+
+ int i;
+ int RFS=RSA.RFS;
+ int sha=RSA.HASH_TYPE;
+
+ String message="Hello World\n";
+
+ public_key pub=new public_key(FF.FFLEN);
+ private_key priv=new private_key(FF.HFLEN);
+
+ byte[] ML=new byte[RFS];
+ byte[] C=new byte[RFS];
+ byte[] S=new byte[RFS];
+
+
+ System.out.println("Testing RSA code");
+
+ System.out.println("Generating public/private key pair");
+ RSA.KEY_PAIR(rng,65537,priv,pub);
+
+ byte[] M=message.getBytes();
+ System.out.print("Encrypting test string\n");
+
+
+ byte[] E=RSA.OAEP_ENCODE(sha,M,rng,null); /* OAEP encode message M to E */
+
+ if (E.length==0) {
+ fail("Encoding failed");
+ }
+
+ RSA.ENCRYPT(pub,E,C); /* encrypt encoded message */
+ System.out.print("Ciphertext= 0x"); printBinary(C);
+
+ System.out.print("Decrypting test string\n");
+ RSA.DECRYPT(priv,C,ML);
+ byte[] MS=RSA.OAEP_DECODE(sha,null,ML); /* OAEP decode message */
+
+ message=new String(MS);
+ System.out.print(message);
+
+ System.out.println("Signing message");
+ RSA.PKCS15(sha,M,C);
+
+ RSA.DECRYPT(priv,C,S); /* create signature in S */
+
+ System.out.print("Signature= 0x"); printBinary(S);
+
+ RSA.ENCRYPT(pub,S,ML);
+
+ boolean cmp=true;
+ if (C.length!=ML.length) cmp=false;
+ else
+ {
+ for (int j=0;j<C.length;j++)
+ if (C[j]!=ML[j]) cmp=false;
+ }
+ if (cmp) System.out.println("Signature is valid");
+ else fail("Signature is INVALID");
+
+ RSA.PRIVATE_KEY_KILL(priv);
+ System.out.println("");
+
+ }
+}
diff --git a/src/test/java/org/apache/milagro/amcl/SECP256K1/TestECDH.java b/src/test/java/org/apache/milagro/amcl/SECP256K1/TestECDH.java
new file mode 100644
index 0000000..832e272
--- /dev/null
+++ b/src/test/java/org/apache/milagro/amcl/SECP256K1/TestECDH.java
@@ -0,0 +1,192 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* test driver and function exerciser for ECDH/ECIES/ECDSA API Functions */
+
+package org.apache.milagro.amcl.SECP256K1; //
+
+import java.util.Scanner;
+import junit.framework.TestCase; //
+import org.apache.milagro.amcl.RAND;
+import org.apache.milagro.amcl.AES;
+
+public class TestECDH extends TestCase //
+{
+ private static void printBinary(byte[] array)
+ {
+ int i;
+ for (i=0;i<array.length;i++)
+ {
+ System.out.printf("%02x", array[i]);
+ }
+ System.out.println();
+ }
+
+ public static void testECDH()
+ {
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+ int i,j=0,res;
+ int result;
+ String pp=new String("M0ng00se");
+
+ rng.clean();
+ for (i=0;i<100;i++) RAW[i]=(byte)(i);
+ rng.seed(100,RAW);
+
+ int EGS=ECDH.EGS;
+ int EFS=ECDH.EFS;
+ int EAS=AES.KS;
+ int sha=ECP.HASH_TYPE;
+
+ byte[] S1=new byte[EGS];
+ byte[] W0=new byte[2*EFS+1];
+ byte[] W1=new byte[2*EFS+1];
+ byte[] Z0=new byte[EFS];
+ byte[] Z1=new byte[EFS];
+
+ byte[] SALT=new byte[8];
+ byte[] P1=new byte[3];
+ byte[] P2=new byte[4];
+ byte[] V=new byte[2*EFS+1];
+ byte[] M=new byte[17];
+ byte[] T=new byte[12];
+ byte[] CS=new byte[EGS];
+ byte[] DS=new byte[EGS];
+
+ for (i=0;i<8;i++) SALT[i]=(byte)(i+1); // set Salt
+
+ System.out.println("Testing ECDH code");
+ System.out.println("Alice's Passphrase= "+pp);
+ byte[] PW=pp.getBytes();
+
+/* private key S0 of size EGS bytes derived from Password and Salt */
+
+ byte[] S0=ECDH.PBKDF2(sha,PW,SALT,1000,EGS);
+
+ System.out.print("Alice's private key= 0x");
+ printBinary(S0);
+
+/* Generate Key pair S/W */
+ ECDH.KEY_PAIR_GENERATE(null,S0,W0);
+
+ System.out.print("Alice's public key= 0x");
+ printBinary(W0);
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W0);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+/* Random private key for other party */
+ ECDH.KEY_PAIR_GENERATE(rng,S1,W1);
+
+ System.out.print("Servers private key= 0x");
+ printBinary(S1);
+
+ System.out.print("Servers public key= 0x");
+ printBinary(W1);
+
+
+ res=ECDH.PUBLIC_KEY_VALIDATE(W1);
+ if (res!=0)
+ {
+ fail("ECP Public Key is invalid!");
+ }
+
+/* Calculate common key using DH - IEEE 1363 method */
+
+ ECDH.SVDP_DH(S0,W1,Z0);
+ ECDH.SVDP_DH(S1,W0,Z1);
+
+ boolean same=true;
+ for (i=0;i<EFS;i++)
+ if (Z0[i]!=Z1[i]) same=false;
+
+ if (!same)
+ {
+ fail("*** ECPSVDP-DH Failed");
+ }
+
+ byte[] KEY=ECDH.KDF2(sha,Z0,null,EAS);
+
+ System.out.print("Alice's DH Key= 0x"); printBinary(KEY);
+ System.out.print("Servers DH Key= 0x"); printBinary(KEY);
+
+ if (ECP.CURVETYPE!=ECP.MONTGOMERY)
+ {
+ System.out.println("Testing ECIES");
+
+ P1[0]=0x0; P1[1]=0x1; P1[2]=0x2;
+ P2[0]=0x0; P2[1]=0x1; P2[2]=0x2; P2[3]=0x3;
+
+ for (i=0;i<=16;i++) M[i]=(byte)i;
+
+ byte[] C=ECDH.ECIES_ENCRYPT(sha,P1,P2,rng,W1,M,V,T);
+
+ System.out.println("Ciphertext= ");
+ System.out.print("V= 0x"); printBinary(V);
+ System.out.print("C= 0x"); printBinary(C);
+ System.out.print("T= 0x"); printBinary(T);
+
+
+ M=ECDH.ECIES_DECRYPT(sha,P1,P2,V,C,T,S1);
+ if (M.length==0)
+ {
+ fail("*** ECIES Decryption Failed");
+ }
+ else System.out.println("Decryption succeeded");
+
+ System.out.print("Message is 0x"); printBinary(M);
+
+ System.out.println("Testing ECDSA");
+
+ if (ECDH.SP_DSA(sha,rng,S0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Signature Failed");
+ }
+ System.out.println("Signature= ");
+ System.out.print("C= 0x"); printBinary(CS);
+ System.out.print("D= 0x"); printBinary(DS);
+
+ if (ECDH.VP_DSA(sha,W0,M,CS,DS)!=0)
+ {
+ fail("***ECDSA Verification Failed");
+ }
+ else System.out.println("ECDSA Signature/Verification succeeded "+j);
+ System.out.println("");
+
+ }
+ }
+
+/*
+ public static void main(String[] args)
+ {
+
+ byte[] RAW=new byte[100];
+ RAND rng=new RAND();
+
+ rng.clean();
+ for (int i=0;i<100;i++) RAW[i]=(byte)(i);
+
+ rng.seed(100,RAW);
+ ecdh(rng);
+
+ } */
+}