You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@milagro.apache.org by br...@apache.org on 2018/11/08 01:41:48 UTC

[incubator-milagro-java] branch master created (now fdf3902)

This is an automated email from the ASF dual-hosted git repository.

brianspector pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-milagro-java.git.


      at fdf3902  update code

This branch includes the following new commits:

     new fdf3902  update code

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[incubator-milagro-java] 01/01: update code

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brianspector pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-milagro-java.git

commit fdf39026d82c33ba0be2392e3303c06f828f143b
Author: Brian Spector <br...@gmail.com>
AuthorDate: Thu Nov 8 01:40:12 2018 +0000

    update code
---
 .gitignore                                         |   25 +
 .travis.yml                                        |   10 +
 AMCL.pdf                                           |  Bin 0 -> 423451 bytes
 LICENSE                                            |  201 ++++
 README.md                                          |   74 ++
 VERSION                                            |    1 +
 build.gradle                                       |  104 ++
 examples/README.md                                 |   11 +
 examples/TestECC.java                              |  174 +++
 examples/TestMPIN.java                             |  267 +++++
 gradle.properties                                  |    6 +
 gradle/wrapper/gradle-wrapper.jar                  |  Bin 0 -> 54329 bytes
 gradle/wrapper/gradle-wrapper.properties           |    5 +
 gradlew                                            |  172 +++
 gradlew.bat                                        |   84 ++
 settings.gradle                                    |   18 +
 src/main/java/org/apache/milagro/amcl/AES.java     |  695 ++++++++++++
 .../java/org/apache/milagro/amcl/ANSSI/BIG.java    |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/ANSSI/DBIG.java   |  279 +++++
 .../java/org/apache/milagro/amcl/ANSSI/ECDH.java   |  594 +++++++++++
 .../java/org/apache/milagro/amcl/ANSSI/ECP.java    | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/ANSSI/FP.java     |  526 ++++++++++
 .../java/org/apache/milagro/amcl/ANSSI/ROM.java    |   43 +
 .../java/org/apache/milagro/amcl/BLS24/BIG.java    |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS24/DBIG.java   |  279 +++++
 .../java/org/apache/milagro/amcl/BLS24/ECDH.java   |  594 +++++++++++
 .../java/org/apache/milagro/amcl/BLS24/ECP.java    | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS24/ECP4.java   |  768 ++++++++++++++
 .../java/org/apache/milagro/amcl/BLS24/FP.java     |  526 ++++++++++
 .../java/org/apache/milagro/amcl/BLS24/FP2.java    |  425 ++++++++
 .../java/org/apache/milagro/amcl/BLS24/FP24.java   |  851 +++++++++++++++
 .../java/org/apache/milagro/amcl/BLS24/FP4.java    |  721 +++++++++++++
 .../java/org/apache/milagro/amcl/BLS24/FP8.java    |  656 ++++++++++++
 .../org/apache/milagro/amcl/BLS24/MPIN192.java     |  806 ++++++++++++++
 .../org/apache/milagro/amcl/BLS24/PAIR192.java     |  550 ++++++++++
 .../java/org/apache/milagro/amcl/BLS24/ROM.java    |   60 ++
 .../java/org/apache/milagro/amcl/BLS381/BIG.java   |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS381/DBIG.java  |  279 +++++
 .../java/org/apache/milagro/amcl/BLS381/ECDH.java  |  594 +++++++++++
 .../java/org/apache/milagro/amcl/BLS381/ECP.java   | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS381/ECP2.java  |  796 ++++++++++++++
 .../java/org/apache/milagro/amcl/BLS381/FP.java    |  526 ++++++++++
 .../java/org/apache/milagro/amcl/BLS381/FP12.java  |  907 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS381/FP2.java   |  425 ++++++++
 .../java/org/apache/milagro/amcl/BLS381/FP4.java   |  721 +++++++++++++
 .../java/org/apache/milagro/amcl/BLS381/MPIN.java  |  823 +++++++++++++++
 .../java/org/apache/milagro/amcl/BLS381/PAIR.java  |  817 ++++++++++++++
 .../java/org/apache/milagro/amcl/BLS381/ROM.java   |   57 +
 .../java/org/apache/milagro/amcl/BLS383/BIG.java   |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS383/DBIG.java  |  279 +++++
 .../java/org/apache/milagro/amcl/BLS383/ECDH.java  |  594 +++++++++++
 .../java/org/apache/milagro/amcl/BLS383/ECP.java   | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS383/ECP2.java  |  796 ++++++++++++++
 .../java/org/apache/milagro/amcl/BLS383/FP.java    |  526 ++++++++++
 .../java/org/apache/milagro/amcl/BLS383/FP12.java  |  907 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS383/FP2.java   |  425 ++++++++
 .../java/org/apache/milagro/amcl/BLS383/FP4.java   |  721 +++++++++++++
 .../java/org/apache/milagro/amcl/BLS383/MPIN.java  |  823 +++++++++++++++
 .../java/org/apache/milagro/amcl/BLS383/PAIR.java  |  817 ++++++++++++++
 .../java/org/apache/milagro/amcl/BLS383/ROM.java   |   55 +
 .../java/org/apache/milagro/amcl/BLS461/BIG.java   |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS461/DBIG.java  |  279 +++++
 .../java/org/apache/milagro/amcl/BLS461/ECDH.java  |  594 +++++++++++
 .../java/org/apache/milagro/amcl/BLS461/ECP.java   | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS461/ECP2.java  |  796 ++++++++++++++
 .../java/org/apache/milagro/amcl/BLS461/FP.java    |  526 ++++++++++
 .../java/org/apache/milagro/amcl/BLS461/FP12.java  |  907 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS461/FP2.java   |  425 ++++++++
 .../java/org/apache/milagro/amcl/BLS461/FP4.java   |  721 +++++++++++++
 .../java/org/apache/milagro/amcl/BLS461/MPIN.java  |  823 +++++++++++++++
 .../java/org/apache/milagro/amcl/BLS461/PAIR.java  |  817 ++++++++++++++
 .../java/org/apache/milagro/amcl/BLS461/ROM.java   |   56 +
 .../java/org/apache/milagro/amcl/BLS48/BIG.java    |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS48/DBIG.java   |  279 +++++
 .../java/org/apache/milagro/amcl/BLS48/ECDH.java   |  594 +++++++++++
 .../java/org/apache/milagro/amcl/BLS48/ECP.java    | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS48/ECP8.java   |  930 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS48/FP.java     |  526 ++++++++++
 .../java/org/apache/milagro/amcl/BLS48/FP16.java   |  563 ++++++++++
 .../java/org/apache/milagro/amcl/BLS48/FP2.java    |  425 ++++++++
 .../java/org/apache/milagro/amcl/BLS48/FP4.java    |  721 +++++++++++++
 .../java/org/apache/milagro/amcl/BLS48/FP48.java   | 1057 +++++++++++++++++++
 .../java/org/apache/milagro/amcl/BLS48/FP8.java    |  656 ++++++++++++
 .../org/apache/milagro/amcl/BLS48/MPIN256.java     |  815 ++++++++++++++
 .../org/apache/milagro/amcl/BLS48/PAIR256.java     |  628 +++++++++++
 .../java/org/apache/milagro/amcl/BLS48/ROM.java    |   68 ++
 .../java/org/apache/milagro/amcl/BN254/BIG.java    |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BN254/DBIG.java   |  279 +++++
 .../java/org/apache/milagro/amcl/BN254/ECDH.java   |  594 +++++++++++
 .../java/org/apache/milagro/amcl/BN254/ECP.java    | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/BN254/ECP2.java   |  796 ++++++++++++++
 .../java/org/apache/milagro/amcl/BN254/FP.java     |  526 ++++++++++
 .../java/org/apache/milagro/amcl/BN254/FP12.java   |  907 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BN254/FP2.java    |  425 ++++++++
 .../java/org/apache/milagro/amcl/BN254/FP4.java    |  721 +++++++++++++
 .../java/org/apache/milagro/amcl/BN254/MPIN.java   |  823 +++++++++++++++
 .../java/org/apache/milagro/amcl/BN254/PAIR.java   |  817 ++++++++++++++
 .../java/org/apache/milagro/amcl/BN254/ROM.java    |   55 +
 .../java/org/apache/milagro/amcl/BN254CX/BIG.java  |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BN254CX/DBIG.java |  279 +++++
 .../java/org/apache/milagro/amcl/BN254CX/ECDH.java |  594 +++++++++++
 .../java/org/apache/milagro/amcl/BN254CX/ECP.java  | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/BN254CX/ECP2.java |  796 ++++++++++++++
 .../java/org/apache/milagro/amcl/BN254CX/FP.java   |  526 ++++++++++
 .../java/org/apache/milagro/amcl/BN254CX/FP12.java |  907 ++++++++++++++++
 .../java/org/apache/milagro/amcl/BN254CX/FP2.java  |  425 ++++++++
 .../java/org/apache/milagro/amcl/BN254CX/FP4.java  |  721 +++++++++++++
 .../java/org/apache/milagro/amcl/BN254CX/MPIN.java |  823 +++++++++++++++
 .../java/org/apache/milagro/amcl/BN254CX/PAIR.java |  817 ++++++++++++++
 .../java/org/apache/milagro/amcl/BN254CX/ROM.java  |   58 +
 .../org/apache/milagro/amcl/BRAINPOOL/BIG.java     |  917 ++++++++++++++++
 .../org/apache/milagro/amcl/BRAINPOOL/DBIG.java    |  279 +++++
 .../org/apache/milagro/amcl/BRAINPOOL/ECDH.java    |  594 +++++++++++
 .../org/apache/milagro/amcl/BRAINPOOL/ECP.java     | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/BRAINPOOL/FP.java |  526 ++++++++++
 .../org/apache/milagro/amcl/BRAINPOOL/ROM.java     |   43 +
 .../java/org/apache/milagro/amcl/C25519/BIG.java   |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/C25519/DBIG.java  |  279 +++++
 .../java/org/apache/milagro/amcl/C25519/ECDH.java  |  594 +++++++++++
 .../java/org/apache/milagro/amcl/C25519/ECP.java   | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/C25519/FP.java    |  526 ++++++++++
 .../java/org/apache/milagro/amcl/C25519/ROM.java   |   42 +
 .../java/org/apache/milagro/amcl/C41417/BIG.java   |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/C41417/DBIG.java  |  279 +++++
 .../java/org/apache/milagro/amcl/C41417/ECDH.java  |  594 +++++++++++
 .../java/org/apache/milagro/amcl/C41417/ECP.java   | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/C41417/FP.java    |  526 ++++++++++
 .../java/org/apache/milagro/amcl/C41417/ROM.java   |   44 +
 .../java/org/apache/milagro/amcl/ED25519/BIG.java  |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/ED25519/DBIG.java |  279 +++++
 .../java/org/apache/milagro/amcl/ED25519/ECDH.java |  594 +++++++++++
 .../java/org/apache/milagro/amcl/ED25519/ECP.java  | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/ED25519/FP.java   |  526 ++++++++++
 .../java/org/apache/milagro/amcl/ED25519/ROM.java  |   43 +
 .../java/org/apache/milagro/amcl/FP256BN/BIG.java  |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/FP256BN/DBIG.java |  279 +++++
 .../java/org/apache/milagro/amcl/FP256BN/ECDH.java |  594 +++++++++++
 .../java/org/apache/milagro/amcl/FP256BN/ECP.java  | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/FP256BN/ECP2.java |  796 ++++++++++++++
 .../java/org/apache/milagro/amcl/FP256BN/FP.java   |  526 ++++++++++
 .../java/org/apache/milagro/amcl/FP256BN/FP12.java |  907 ++++++++++++++++
 .../java/org/apache/milagro/amcl/FP256BN/FP2.java  |  425 ++++++++
 .../java/org/apache/milagro/amcl/FP256BN/FP4.java  |  721 +++++++++++++
 .../java/org/apache/milagro/amcl/FP256BN/MPIN.java |  823 +++++++++++++++
 .../java/org/apache/milagro/amcl/FP256BN/PAIR.java |  817 ++++++++++++++
 .../java/org/apache/milagro/amcl/FP256BN/ROM.java  |   55 +
 .../java/org/apache/milagro/amcl/FP512BN/BIG.java  |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/FP512BN/DBIG.java |  279 +++++
 .../java/org/apache/milagro/amcl/FP512BN/ECDH.java |  594 +++++++++++
 .../java/org/apache/milagro/amcl/FP512BN/ECP.java  | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/FP512BN/ECP2.java |  796 ++++++++++++++
 .../java/org/apache/milagro/amcl/FP512BN/FP.java   |  526 ++++++++++
 .../java/org/apache/milagro/amcl/FP512BN/FP12.java |  907 ++++++++++++++++
 .../java/org/apache/milagro/amcl/FP512BN/FP2.java  |  425 ++++++++
 .../java/org/apache/milagro/amcl/FP512BN/FP4.java  |  721 +++++++++++++
 .../java/org/apache/milagro/amcl/FP512BN/MPIN.java |  823 +++++++++++++++
 .../java/org/apache/milagro/amcl/FP512BN/PAIR.java |  817 ++++++++++++++
 .../java/org/apache/milagro/amcl/FP512BN/ROM.java  |   56 +
 src/main/java/org/apache/milagro/amcl/GCM.java     |  376 +++++++
 .../org/apache/milagro/amcl/GOLDILOCKS/BIG.java    |  917 ++++++++++++++++
 .../org/apache/milagro/amcl/GOLDILOCKS/DBIG.java   |  279 +++++
 .../org/apache/milagro/amcl/GOLDILOCKS/ECDH.java   |  594 +++++++++++
 .../org/apache/milagro/amcl/GOLDILOCKS/ECP.java    | 1109 ++++++++++++++++++++
 .../org/apache/milagro/amcl/GOLDILOCKS/FP.java     |  526 ++++++++++
 .../org/apache/milagro/amcl/GOLDILOCKS/ROM.java    |   44 +
 src/main/java/org/apache/milagro/amcl/HASH256.java |  218 ++++
 src/main/java/org/apache/milagro/amcl/HASH384.java |  229 ++++
 src/main/java/org/apache/milagro/amcl/HASH512.java |  232 ++++
 .../java/org/apache/milagro/amcl/HIFIVE/BIG.java   |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/HIFIVE/DBIG.java  |  279 +++++
 .../java/org/apache/milagro/amcl/HIFIVE/ECDH.java  |  594 +++++++++++
 .../java/org/apache/milagro/amcl/HIFIVE/ECP.java   | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/HIFIVE/FP.java    |  526 ++++++++++
 .../java/org/apache/milagro/amcl/HIFIVE/ROM.java   |   43 +
 src/main/java/org/apache/milagro/amcl/NHS.java     |  577 ++++++++++
 .../java/org/apache/milagro/amcl/NIST256/BIG.java  |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/NIST256/DBIG.java |  279 +++++
 .../java/org/apache/milagro/amcl/NIST256/ECDH.java |  594 +++++++++++
 .../java/org/apache/milagro/amcl/NIST256/ECP.java  | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/NIST256/FP.java   |  526 ++++++++++
 .../java/org/apache/milagro/amcl/NIST256/ROM.java  |   43 +
 .../java/org/apache/milagro/amcl/NIST384/BIG.java  |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/NIST384/DBIG.java |  279 +++++
 .../java/org/apache/milagro/amcl/NIST384/ECDH.java |  594 +++++++++++
 .../java/org/apache/milagro/amcl/NIST384/ECP.java  | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/NIST384/FP.java   |  526 ++++++++++
 .../java/org/apache/milagro/amcl/NIST384/ROM.java  |   44 +
 .../java/org/apache/milagro/amcl/NIST521/BIG.java  |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/NIST521/DBIG.java |  279 +++++
 .../java/org/apache/milagro/amcl/NIST521/ECDH.java |  594 +++++++++++
 .../java/org/apache/milagro/amcl/NIST521/ECP.java  | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/NIST521/FP.java   |  526 ++++++++++
 .../java/org/apache/milagro/amcl/NIST521/ROM.java  |   44 +
 .../java/org/apache/milagro/amcl/NUMS256E/BIG.java |  917 ++++++++++++++++
 .../org/apache/milagro/amcl/NUMS256E/DBIG.java     |  279 +++++
 .../org/apache/milagro/amcl/NUMS256E/ECDH.java     |  594 +++++++++++
 .../java/org/apache/milagro/amcl/NUMS256E/ECP.java | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/NUMS256E/FP.java  |  526 ++++++++++
 .../java/org/apache/milagro/amcl/NUMS256E/ROM.java |   42 +
 .../java/org/apache/milagro/amcl/NUMS256W/BIG.java |  917 ++++++++++++++++
 .../org/apache/milagro/amcl/NUMS256W/DBIG.java     |  279 +++++
 .../org/apache/milagro/amcl/NUMS256W/ECDH.java     |  594 +++++++++++
 .../java/org/apache/milagro/amcl/NUMS256W/ECP.java | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/NUMS256W/FP.java  |  526 ++++++++++
 .../java/org/apache/milagro/amcl/NUMS256W/ROM.java |   45 +
 .../java/org/apache/milagro/amcl/NUMS384E/BIG.java |  917 ++++++++++++++++
 .../org/apache/milagro/amcl/NUMS384E/DBIG.java     |  279 +++++
 .../org/apache/milagro/amcl/NUMS384E/ECDH.java     |  594 +++++++++++
 .../java/org/apache/milagro/amcl/NUMS384E/ECP.java | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/NUMS384E/FP.java  |  526 ++++++++++
 .../java/org/apache/milagro/amcl/NUMS384E/ROM.java |   40 +
 .../java/org/apache/milagro/amcl/NUMS384W/BIG.java |  917 ++++++++++++++++
 .../org/apache/milagro/amcl/NUMS384W/DBIG.java     |  279 +++++
 .../org/apache/milagro/amcl/NUMS384W/ECDH.java     |  594 +++++++++++
 .../java/org/apache/milagro/amcl/NUMS384W/ECP.java | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/NUMS384W/FP.java  |  526 ++++++++++
 .../java/org/apache/milagro/amcl/NUMS384W/ROM.java |   55 +
 .../java/org/apache/milagro/amcl/NUMS512E/BIG.java |  917 ++++++++++++++++
 .../org/apache/milagro/amcl/NUMS512E/DBIG.java     |  279 +++++
 .../org/apache/milagro/amcl/NUMS512E/ECDH.java     |  594 +++++++++++
 .../java/org/apache/milagro/amcl/NUMS512E/ECP.java | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/NUMS512E/FP.java  |  526 ++++++++++
 .../java/org/apache/milagro/amcl/NUMS512E/ROM.java |   40 +
 .../java/org/apache/milagro/amcl/NUMS512W/BIG.java |  917 ++++++++++++++++
 .../org/apache/milagro/amcl/NUMS512W/DBIG.java     |  279 +++++
 .../org/apache/milagro/amcl/NUMS512W/ECDH.java     |  594 +++++++++++
 .../java/org/apache/milagro/amcl/NUMS512W/ECP.java | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/NUMS512W/FP.java  |  526 ++++++++++
 .../java/org/apache/milagro/amcl/NUMS512W/ROM.java |   41 +
 src/main/java/org/apache/milagro/amcl/RAND.java    |  163 +++
 .../java/org/apache/milagro/amcl/RSA2048/BIG.java  |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/RSA2048/DBIG.java |  279 +++++
 .../java/org/apache/milagro/amcl/RSA2048/FF.java   | 1028 ++++++++++++++++++
 .../java/org/apache/milagro/amcl/RSA2048/RSA.java  |  369 +++++++
 .../apache/milagro/amcl/RSA2048/private_key.java   |   16 +
 .../apache/milagro/amcl/RSA2048/public_key.java    |   14 +
 .../java/org/apache/milagro/amcl/RSA3072/BIG.java  |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/RSA3072/DBIG.java |  279 +++++
 .../java/org/apache/milagro/amcl/RSA3072/FF.java   | 1028 ++++++++++++++++++
 .../java/org/apache/milagro/amcl/RSA3072/RSA.java  |  369 +++++++
 .../apache/milagro/amcl/RSA3072/private_key.java   |   16 +
 .../apache/milagro/amcl/RSA3072/public_key.java    |   14 +
 .../java/org/apache/milagro/amcl/RSA4096/BIG.java  |  917 ++++++++++++++++
 .../java/org/apache/milagro/amcl/RSA4096/DBIG.java |  279 +++++
 .../java/org/apache/milagro/amcl/RSA4096/FF.java   | 1028 ++++++++++++++++++
 .../java/org/apache/milagro/amcl/RSA4096/RSA.java  |  369 +++++++
 .../apache/milagro/amcl/RSA4096/private_key.java   |   16 +
 .../apache/milagro/amcl/RSA4096/public_key.java    |   14 +
 .../org/apache/milagro/amcl/SECP256K1/BIG.java     |  917 ++++++++++++++++
 .../org/apache/milagro/amcl/SECP256K1/DBIG.java    |  279 +++++
 .../org/apache/milagro/amcl/SECP256K1/ECDH.java    |  594 +++++++++++
 .../org/apache/milagro/amcl/SECP256K1/ECP.java     | 1109 ++++++++++++++++++++
 .../java/org/apache/milagro/amcl/SECP256K1/FP.java |  526 ++++++++++
 .../org/apache/milagro/amcl/SECP256K1/ROM.java     |   43 +
 src/main/java/org/apache/milagro/amcl/SHA3.java    |  255 +++++
 .../org/apache/milagro/amcl/ANSSI/TestECDH.java    |  192 ++++
 .../org/apache/milagro/amcl/BLS24/TestECDH.java    |  192 ++++
 .../org/apache/milagro/amcl/BLS24/TestMPIN192.java |  297 ++++++
 .../org/apache/milagro/amcl/BLS381/TestECDH.java   |  192 ++++
 .../org/apache/milagro/amcl/BLS381/TestMPIN.java   |  297 ++++++
 .../org/apache/milagro/amcl/BLS383/TestECDH.java   |  192 ++++
 .../org/apache/milagro/amcl/BLS383/TestMPIN.java   |  297 ++++++
 .../org/apache/milagro/amcl/BLS461/TestECDH.java   |  192 ++++
 .../org/apache/milagro/amcl/BLS461/TestMPIN.java   |  297 ++++++
 .../org/apache/milagro/amcl/BLS48/TestECDH.java    |  192 ++++
 .../org/apache/milagro/amcl/BLS48/TestMPIN256.java |  297 ++++++
 .../org/apache/milagro/amcl/BN254/TestECDH.java    |  192 ++++
 .../org/apache/milagro/amcl/BN254/TestMPIN.java    |  297 ++++++
 .../org/apache/milagro/amcl/BN254CX/TestECDH.java  |  192 ++++
 .../org/apache/milagro/amcl/BN254CX/TestMPIN.java  |  297 ++++++
 .../apache/milagro/amcl/BRAINPOOL/TestECDH.java    |  192 ++++
 .../org/apache/milagro/amcl/C25519/TestECDH.java   |  192 ++++
 .../org/apache/milagro/amcl/C41417/TestECDH.java   |  192 ++++
 .../org/apache/milagro/amcl/ED25519/TestECDH.java  |  192 ++++
 .../org/apache/milagro/amcl/FP256BN/TestECDH.java  |  192 ++++
 .../org/apache/milagro/amcl/FP256BN/TestMPIN.java  |  297 ++++++
 .../org/apache/milagro/amcl/FP512BN/TestECDH.java  |  192 ++++
 .../org/apache/milagro/amcl/FP512BN/TestMPIN.java  |  297 ++++++
 .../apache/milagro/amcl/GOLDILOCKS/TestECDH.java   |  192 ++++
 .../org/apache/milagro/amcl/HIFIVE/TestECDH.java   |  192 ++++
 .../org/apache/milagro/amcl/NIST256/TestECDH.java  |  192 ++++
 .../org/apache/milagro/amcl/NIST384/TestECDH.java  |  192 ++++
 .../org/apache/milagro/amcl/NIST521/TestECDH.java  |  192 ++++
 .../org/apache/milagro/amcl/NUMS256E/TestECDH.java |  192 ++++
 .../org/apache/milagro/amcl/NUMS256W/TestECDH.java |  192 ++++
 .../org/apache/milagro/amcl/NUMS384E/TestECDH.java |  192 ++++
 .../org/apache/milagro/amcl/NUMS384W/TestECDH.java |  192 ++++
 .../org/apache/milagro/amcl/NUMS512E/TestECDH.java |  192 ++++
 .../org/apache/milagro/amcl/NUMS512W/TestECDH.java |  192 ++++
 .../org/apache/milagro/amcl/RSA2048/TestRSA.java   |  111 ++
 .../org/apache/milagro/amcl/RSA3072/TestRSA.java   |  111 ++
 .../org/apache/milagro/amcl/RSA4096/TestRSA.java   |  111 ++
 .../apache/milagro/amcl/SECP256K1/TestECDH.java    |  192 ++++
 293 files changed, 152068 insertions(+)

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
Binary files /dev/null and b/AMCL.pdf 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
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar 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
... 144149 lines suppressed ...