You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@milagro.apache.org by km...@apache.org on 2020/05/05 06:39:18 UTC

[incubator-milagro-crypto-rust] 32/44: Write hash to curve functions for bls381 G1 and G2

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

kmccusker pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/incubator-milagro-crypto-rust.git

commit 17c208e2950553ac05e6fef7a9012c99e6c19369
Author: Kirk Baird <ba...@outlook.com>
AuthorDate: Sun Apr 5 22:50:01 2020 +1000

    Write hash to curve functions for bls381 G1 and G2
    
    Signed-off-by: Kirk Baird <ba...@outlook.com>
---
 Cargo.lock                                         | 662 +++++++++++++++++++++
 Cargo.toml                                         |  16 +-
 src/big.rs                                         |  11 +-
 src/bls.rs                                         |   6 +-
 src/bls256.rs                                      |   6 +-
 src/bls381.rs                                      | 424 +++++++++++++
 src/bls381/iso.rs                                  | 335 +++++++++++
 src/dbig.rs                                        |  21 +-
 src/ecdh.rs                                        |   3 +-
 src/ecp.rs                                         |  23 +
 src/ecp2.rs                                        |  45 +-
 src/errors.rs                                      |   4 +
 src/fp.rs                                          |  76 ++-
 src/fp2.rs                                         |  23 +
 src/hash256.rs                                     | 266 ++++++++-
 src/hash384.rs                                     | 228 +++++--
 src/hash512.rs                                     | 296 ++++++++-
 src/hash_to_curve.rs                               | 307 ++++++++++
 src/lib.rs                                         |  45 +-
 src/nhs.rs                                         |   1 -
 src/roms/{rom_bls381_32.rs => rom_bls381g1_32.rs}  |  36 +-
 src/roms/{rom_bls381_64.rs => rom_bls381g1_64.rs}  |  38 +-
 src/roms/{rom_bls381_32.rs => rom_bls381g2_32.rs}  |  38 +-
 src/roms/{rom_bls381_64.rs => rom_bls381g2_64.rs}  |  38 +-
 .../BLS12381G1_XMD:SHA-256_SSWU_NU_.json           |  77 +++
 .../BLS12381G1_XMD:SHA-256_SSWU_RO_.json           |  97 +++
 .../BLS12381G1_XMD:SHA-256_SVDW_NU_.json           |  77 +++
 .../BLS12381G1_XMD:SHA-256_SVDW_RO_.json           |  97 +++
 .../BLS12381G2_XMD:SHA-256_SSWU_NU_.json           |  77 +++
 .../BLS12381G2_XMD:SHA-256_SSWU_RO_.json           |  97 +++
 .../BLS12381G2_XMD:SHA-256_SVDW_NU_.json           |  77 +++
 .../BLS12381G2_XMD:SHA-256_SVDW_RO_.json           |  97 +++
 src/test_utils/mod.rs                              |  20 +
 src/test_utils/test_vector_structs.rs              |  44 ++
 34 files changed, 3550 insertions(+), 158 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index ccba62a..6f9d42d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,4 +3,666 @@
 [[package]]
 name = "amcl"
 version = "0.2.0"
+dependencies = [
+ "criterion 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
+]
 
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bstr"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "byteorder"
+version = "1.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cast"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "clap"
+version = "2.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "criterion"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "criterion-plot 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "oorandom 11.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "plotters 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tinytemplate 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "csv"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bstr 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "csv-core"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "either"
+version = "1.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "getrandom"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "hex"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "itertools"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "js-sys"
+version = "0.3.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "wasm-bindgen 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "log"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "maybe-uninit"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "memchr"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "memoffset"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "oorandom"
+version = "11.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "plotters"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "js-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
+ "web-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rayon"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex"
+version = "1.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-util 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tinytemplate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "walkdir"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-macro 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-shared 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-macro-support 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-backend 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-shared 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "web-sys"
+version = "0.3.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "js-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
+"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+"checksum bstr 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41"
+"checksum bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187"
+"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
+"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
+"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
+"checksum criterion 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc755679c12bda8e5523a71e4d654b6bf2e14bd838dfc48cde6559a05caf7d1"
+"checksum criterion-plot 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a01e15e0ea58e8234f96146b1f91fa9d0e4dd7a38da93ff7a75d42c0b9d3a545"
+"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
+"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
+"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
+"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
+"checksum csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
+"checksum csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
+"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
+"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
+"checksum hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e"
+"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
+"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
+"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
+"checksum js-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055"
+"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+"checksum libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
+"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
+"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
+"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
+"checksum memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8"
+"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
+"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
+"checksum oorandom 11.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebcec7c9c2a95cacc7cd0ecb89d8a8454eca13906f6deb55258ffff0adeb9405"
+"checksum plotters 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "4e3bb8da247d27ae212529352020f3e5ee16e83c0c258061d27b08ab92675eeb"
+"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
+"checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
+"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
+"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098"
+"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9"
+"checksum regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3"
+"checksum regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
+"checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
+"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+"checksum ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76"
+"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+"checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399"
+"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c"
+"checksum serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9"
+"checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03"
+"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+"checksum tinytemplate 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "57a3c6667d3e65eb1bc3aed6fd14011c6cbc3a0665218ab7f5daf040b9ec371a"
+"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
+"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
+"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+"checksum wasm-bindgen 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f"
+"checksum wasm-bindgen-backend 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd"
+"checksum wasm-bindgen-macro 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4"
+"checksum wasm-bindgen-macro-support 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931"
+"checksum wasm-bindgen-shared 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639"
+"checksum web-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb"
+"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+"checksum winapi-util 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e"
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
index 834aa83..132aec6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,15 +8,20 @@ license = "Apache-2.0"
 repository = "https://github.com/milagro-crypto/amcl"
 
 [dependencies]
+hex = "0.3"
+lazy_static = "1.2.0"
 
-[lib]
-name = "amcl"
-path = "src/lib.rs"
+[dev-dependencies]
+serde = "1.0"
+serde_json = "1.0"
+serde_derive = "1.0"
+criterion = "0.3.0"
+rand = "0.7.2"
 
 [features]
 default = ["bn254"]
 all = [
-  "anssi","bls24","bls48","bls381","bls383","bls461","bn254","bn254cx",
+  "anssi","bls24","bls48","bls381g1","bls381g2","bls383","bls461","bn254","bn254cx",
   "brainpool","c25519","c41417","ed25519","fp256BN","fp512BN","goldilocks","hifive",
   "nist256","nist384","nist521","nums256e","nums256w","nums384e","nums384w","nums512e",
   "nums512w","rsa2048","rsa3072","rsa4096","secp256k1",
@@ -24,7 +29,8 @@ all = [
 anssi = []
 bls24 = []
 bls48 = []
-bls381 = []
+bls381g1 = []
+bls381g2 = []
 bls383 = []
 bls461 = []
 bn254 = []
diff --git a/src/big.rs b/src/big.rs
index e4eca10..0c57d0a 100644
--- a/src/big.rs
+++ b/src/big.rs
@@ -436,7 +436,16 @@ impl Big {
     /// Convert from byte array starting at index `n` to Big
     pub fn frombytearray(b: &[u8], n: usize) -> Big {
         let mut m = Big::new();
-        for i in 0..(MODBYTES as usize) {
+
+        // Restrict length
+        let max_big = MODBYTES;
+        let len = if b.len() >= max_big {
+            max_big as usize
+        } else {
+            b.len()
+        };
+
+        for i in 0..len {
             m.fshl(8);
             m.w[0] += (b[i + n] & 0xff) as Chunk;
         }
diff --git a/src/bls.rs b/src/bls.rs
index 1bc71cb..c956441 100644
--- a/src/bls.rs
+++ b/src/bls.rs
@@ -17,13 +17,13 @@ specific language governing permissions and limitations
 under the License.
 */
 
-use super::ecp::ECP;
-use super::ecp2::ECP2;
-use std::str;
 use super::big;
 use super::big::Big;
+use super::ecp::ECP;
+use super::ecp2::ECP2;
 use super::pair;
 use super::rom;
+use std::str;
 
 use rand::RAND;
 use sha3::SHA3;
diff --git a/src/bls256.rs b/src/bls256.rs
index 11fb5a1..785ad2d 100644
--- a/src/bls256.rs
+++ b/src/bls256.rs
@@ -17,13 +17,13 @@ specific language governing permissions and limitations
 under the License.
 */
 
-use super::ecp::ECP;
-use super::ecp8::ECP8;
-use std::str;
 use super::big;
 use super::big::Big;
+use super::ecp::ECP;
+use super::ecp8::ECP8;
 use super::pair256;
 use super::rom;
+use std::str;
 
 use rand::RAND;
 use sha3::SHA3;
diff --git a/src/bls381.rs b/src/bls381.rs
new file mode 100644
index 0000000..de56701
--- /dev/null
+++ b/src/bls381.rs
@@ -0,0 +1,424 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/// BLS12-381
+///
+/// An implementation of BLS12-381 as specified by the following standard:
+/// https://github.com/cfrg/draft-irtf-cfrg-bls-signature
+pub mod iso;
+
+use self::iso::{iso11_to_ecp, iso3_to_ecp2};
+use super::big::Big;
+use super::ecp::ECP;
+use super::ecp2::ECP2;
+use super::fp::FP;
+use super::fp2::FP2;
+use super::hash_to_curve::*;
+use super::pair;
+use super::rom::*;
+
+use rand::RAND;
+use sha3::SHA3;
+use sha3::SHAKE256;
+use std::str;
+
+// BLS API Functions
+pub const BFS: usize = MODBYTES as usize;
+pub const BGS: usize = MODBYTES as usize;
+pub const BLS_OK: isize = 0;
+pub const BLS_FAIL: isize = -1;
+
+// Hash a message to an ECP point, using SHA3
+#[allow(non_snake_case)]
+fn bls_hashit(m: &str) -> ECP {
+    let mut sh = SHA3::new(SHAKE256);
+    let mut hm: [u8; BFS] = [0; BFS];
+    let t = m.as_bytes();
+    for i in 0..m.len() {
+        sh.process(t[i]);
+    }
+    sh.shake(&mut hm, BFS);
+    let P = ECP::mapit(&hm);
+    P
+}
+
+/// Generate key pair, private key s, public key w
+pub fn key_pair_generate(mut rng: &mut RAND, s: &mut [u8], w: &mut [u8]) -> isize {
+    let q = Big::new_ints(&CURVE_ORDER);
+    let g = ECP2::generator();
+    let sc = Big::randomnum(&q, &mut rng);
+    sc.tobytes(s);
+    pair::g2mul(&g, &sc).tobytes(w);
+    BLS_OK
+}
+
+/// Sign message m using private key s to produce signature sig.
+pub fn sign(sig: &mut [u8], m: &str, s: &[u8]) -> isize {
+    let d = bls_hashit(m);
+    let mut sc = Big::frombytes(&s);
+    pair::g1mul(&d, &mut sc).tobytes(sig, true);
+    BLS_OK
+}
+
+/// Verify signature given message m, the signature sig, and the public key w
+pub fn verify(sig: &[u8], m: &str, w: &[u8]) -> isize {
+    let hm = bls_hashit(m);
+    let mut d = ECP::frombytes(&sig);
+    let g = ECP2::generator();
+    let pk = ECP2::frombytes(&w);
+    d.neg();
+
+    // Use new multi-pairing mechanism
+    let mut r = pair::initmp();
+    pair::another(&mut r, &g, &d);
+    pair::another(&mut r, &pk, &hm);
+    let mut v = pair::miller(&r);
+
+    //.. or alternatively
+    //    let mut v = pair::ate2(&g, &d, &pk, &hm);
+
+    v = pair::fexp(&v);
+    if v.isunity() {
+        return BLS_OK;
+    }
+    BLS_FAIL
+}
+
+/*************************************************************************************************
+* Functions for hashing to curve when signatures are on ECP
+*************************************************************************************************/
+/// Hash to Curve
+///
+/// Takes a message as input and converts it to a Curve Point
+/// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3
+pub fn hash_to_curve_g1(msg: &[u8]) -> ECP {
+    let u =
+        hash_to_field_fp(msg, 2, DST).expect("hash to field should not fail for given parameters");
+    let mut q0 = map_to_curve_g1(u[0]);
+    let q1 = map_to_curve_g1(u[1]);
+    q0.add(&q1);
+    let p = q0.mul(&H_EFF_G1);
+    p
+}
+
+// Simplified SWU for Pairing-Friendly Curves
+//
+// Take a field point and map it to a Curve Point.
+// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-6.6.2
+fn map_to_curve_g1(u: FP) -> ECP {
+    let (x, y) = simplified_swu_fp(u);
+    iso11_to_ecp(&x, &y)
+}
+
+/*************************************************************************************************
+* Functions for hashing to curve when signatures are on ECP2
+*************************************************************************************************/
+/// Hash to Curve
+///
+/// Takes a message as input and converts it to a Curve Point
+/// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3
+pub fn hash_to_curve_g2(msg: &[u8]) -> ECP2 {
+    let u =
+        hash_to_field_fp2(msg, 2, DST).expect("hash to field should not fail for given parameters");
+    let mut q0 = map_to_curve_g2(u[0]);
+    let q1 = map_to_curve_g2(u[1]);
+    q0.add(&q1);
+    q0.clear_cofactor();
+    q0
+}
+
+// Simplified SWU for Pairing-Friendly Curves
+//
+// Take a field point and map it to a Curve Point.
+// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-6.6.2
+fn map_to_curve_g2(u: FP2) -> ECP2 {
+    let (x, y) = simplified_swu_fp2(u);
+    iso3_to_ecp2(&x, &y)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::test_utils::*;
+
+    // The following tests were exported from
+    // https://github.com/kwantam/bls_sigs_ref/tree/master/python-impl
+    // Format: [(input, output)]
+    // input: [u0_a, u0_b, u1_a, u1_b]
+    // output: [x_a, x_b, y_a, y_b]
+    pub const TESTS: [([&str; 4], [&str; 4]); 4] =
+        [
+            // Test 0
+            (
+                // Input
+                [
+                    "004ad233c619209060e40059b81e4c1f92796b05aa1bc6358d65e53dc0d657dfbc713d4030b0b6d9234a6634fd1944e7",
+                    "0e2386c82713441bc3b06a460bd81850f4bf376ea89c80b18c0881e855c58dc8e83b2fd23af983f4786508e30c42af01",
+                    "08a6a75e0a8d32f1e096f29047ea879dd34a5504218d7ce92c32c244786822fb73fbf708d167ad86537468249ec6df48",
+                    "07016d0e5e13cd65780042c6f7b4c74ae1c58da438c99582696818b5c229895b893318dcb87d2a65e557d4ebeb408b70"
+                ],
+                // Output
+                [
+                    "04861c41efcc5fc56e62273692b48da25d950d2a0aaffb34eff80e8dbdc2d41ca38555ceb8554368436aea47d16056b5",
+                    "09db5217528c55d982cf05fc54242bdcd25f1ebb73372e00e16d8e0f19dc3aeabdeef2d42d693405a04c37d60961526a",
+                    "177d05b95e7879a7ddbd83c15114b5a4e9846fde72b2263072dc9e60db548ccbadaacb92cc4952d4f47425fe3c5e0172",
+                    "0fc82c99b928ed9df12a74f9215c3df8ae1e9a3fa54c00897889296890b23a0edcbb9653f9170bf715f882b35c0b4647"
+                ]
+            ),
+            // Test 1
+            (
+                // Input
+                [
+                    "083c57b3ee2ecba5bbf874bb03897827f949096efceea00f002c979de7e5e9429fcf1f3323d4c8c548cd6f8ecb1a5c1d",
+                    "0344fdfe8e1401867a275b3bef7e6ec52450968ab8a1293938fe3d5712dda67c85afeb91d85ab83fcdbebba4dc913e44",
+                    "1361b5ee134c6bee4e287e63f852b6e48546dcf0684af7cf3e7653a3427a609f769ce4d9d99a638b6ae432130fa43104",
+                    "18425b12c2ab5de136eb493b88ca950a45cab942505b5dd59a8b3ae8ec34c40ada65ff2719b1fcda9769fb22882002f9"
+                ],
+                // Output
+                [
+                    "15f7a5c1168ad5ab67ff285c80fa8dd932ca88d9f8b3803c6c7b1f525d2dd5d01f2418259ae167c17c514d55e4707ddb",
+                    "04378269c7364a6cefcdafdb87b004d3ebf6853f46687e46f29f23196d47a176c6f858be34c9f9a3608c74e804f6c686",
+                    "023d9d46abe82bc0ac7c104d9519c037ff72893b8371d72ab92378f60a2361d7171df6b33500828c88923ddb1aab7fa5",
+                    "1015adfeece3613836bf82541ea560c701e197b3d081e2c242b217d809f4ac0ca787b402537a66c0d1f6b76e1b19e94b"
+                ]
+            ),
+            // Test 2
+            (
+                // Input
+                [
+                    "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+                    "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+                    "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+                    "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+                ],
+                // Output
+                [
+                    "19da1b4d47efeeb154f8968b43da2125376e0999ba722141419b03fd857490562fa42a5d0973956d1932dd20c1e0a284",
+                    "18426da25dadd359adfda64fbaddac4414da2a841cb467935289877db450fac424361efb2e7fb141b7b98e6b2f888aef",
+                    "0c2f8d431770d9be9b087c36fc5b66bb83ce6372669f48294193ef646105e0f21d17b134e7d1ad9c18f54b81f6a3707b",
+                    "03257c3be77016e69b75905a97871008a6dfd2e324a6748c48d3304380156987bd0905991824936fcfe34ab25c3b6caa"
+                ]
+            ),
+            // Test 3
+            (
+                // Input
+                [
+                    "05495a3dfa360cb809c1904530db1986aea4bf356e634b40b51e0ee5fcb6cb75085a72a0626873a426470067627c6418",
+                    "11e63b587bedb59c2140518565950bdf881d75c0cccdcedcd9f4b71f2cfede3e5fdbe0261b015562d5edeaa11b7b2b76",
+                    "116c87bbeece66871eb6c2a51bc4327b10ffe470b49c28ef8eef624da766caa2cc9ff6c7042b26b2efd3404f5a81a140",
+                    "010450a90c17ba2997b645ef340fb5b207d6c915b34a93d93e75ee905d6d203d4aac046e10bd4d94a215604ade7afa8e"
+                ],
+                // Output
+                [
+                    "0f1614a6e91c3e00799098fded2f2cfd72cb585cbdaec41b478509913c6772266a764f00b24a7f99607948a4b69b4d8f",
+                    "13ca2148705ca7ba49c92ab8985d7babcc8afc6bf8e397fb829f5fe3f49e51c41332ba4389f5ba66667310b22bea16c9",
+                    "026a743ee00eec8c7ef63351f4a3b26b2f029c10130385efc56ce53d0788db32ff5296ab77f9c389bd196cce8fc1e888",
+                    "0d458d80897e922f3e7e15cfa66a0d3645d95788bddb7478af3f1b5ca662c348b0e9ffdb88fabfdb74f103fea0c2d793"
+                ]
+            )
+        ];
+
+    #[test]
+    fn test_map_to_curve_g2() {
+        // Only run when signatures are on G2
+        if BLS_SIG_G1 {
+            return;
+        }
+
+        for test in &TESTS {
+            // Input u0 and u1
+            let a = Big::frombytes(&hex::decode(test.0[0]).unwrap());
+            let b = Big::frombytes(&hex::decode(test.0[1]).unwrap());
+            let u0 = FP2::new_bigs(&a, &b);
+            let a = Big::frombytes(&hex::decode(test.0[2]).unwrap());
+            let b = Big::frombytes(&hex::decode(test.0[3]).unwrap());
+            let u1 = FP2::new_bigs(&a, &b);
+
+            // Map to Curve
+            let (iso3_0_x, iso3_0_y) = simplified_swu_fp2(u0);
+            let (iso3_1_x, iso3_1_y) = simplified_swu_fp2(u1);
+
+            // 3-Isogeny Map
+            let mut q0 = iso3_to_ecp2(&iso3_0_x, &iso3_0_y);
+            let q1 = iso3_to_ecp2(&iso3_1_x, &iso3_1_y);
+            q0.add(&q1);
+
+            // Clear Cofactor
+            q0.clear_cofactor();
+
+            // Check expected values
+            let a = Big::frombytes(&hex::decode(test.1[0]).unwrap());
+            let b = Big::frombytes(&hex::decode(test.1[1]).unwrap());
+            let check_x = FP2::new_bigs(&a, &b);
+            let a = Big::frombytes(&hex::decode(test.1[2]).unwrap());
+            let b = Big::frombytes(&hex::decode(test.1[3]).unwrap());
+            let check_y = FP2::new_bigs(&a, &b);
+            let check_e = ECP2::new_fp2s(&check_x, &check_y);
+
+            assert!(q0.equals(&check_e));
+        }
+    }
+
+    #[test]
+    #[cfg(feature = "bls381g2")]
+    fn test_hash_to_curve_g2() {
+        // Only run when signatures are on G2
+        if BLS_SIG_G1 {
+            return;
+        }
+
+        // Read hash to curve test vector
+        let reader = json_reader(H2C_SUITE);
+        let test_vectors: Bls12381Ro = serde_json::from_reader(reader).unwrap();
+
+        // Iterate through each individual case
+        for case in test_vectors.vectors {
+            // Execute hash to curve
+            let u = hash_to_field_fp2(case.msg.as_bytes(), 2, test_vectors.dst.as_bytes()).unwrap();
+            let q0 = map_to_curve_g2(u[0]);
+            let q1 = map_to_curve_g2(u[1]);
+            let mut r = q0.clone();
+            r.add(&q1);
+            let mut p = r.clone();
+            p.clear_cofactor();
+
+            // Verify hash to curve outputs
+            // Check u
+            assert_eq!(case.u.len(), u.len());
+            for (i, u_str) in case.u.iter().enumerate() {
+                // Convert case 'u[i]' to FP2
+                let u_str_parts: Vec<&str> = u_str.split(',').collect();
+                let a = Big::frombytes(&hex::decode(&u_str_parts[0].get(2..).unwrap()).unwrap());
+                let b = Big::frombytes(&hex::decode(&u_str_parts[1].get(2..).unwrap()).unwrap());
+                let expected_u_i = FP2::new_bigs(&a, &b);
+
+                // Verify u[i]
+                assert_eq!(expected_u_i, u[i]);
+            }
+
+            // Check Q0
+            let x_str_parts: Vec<&str> = case.Q0.x.split(',').collect();
+            let a = Big::frombytes(&hex::decode(&x_str_parts[0].get(2..).unwrap()).unwrap());
+            let b = Big::frombytes(&hex::decode(&x_str_parts[1].get(2..).unwrap()).unwrap());
+            let expected_x = FP2::new_bigs(&a, &b);
+
+            let y_str_parts: Vec<&str> = case.Q0.y.split(',').collect();
+            let a = Big::frombytes(&hex::decode(&y_str_parts[0].get(2..).unwrap()).unwrap());
+            let b = Big::frombytes(&hex::decode(&y_str_parts[1].get(2..).unwrap()).unwrap());
+            let expected_y = FP2::new_bigs(&a, &b);
+
+            let expected_q0 = ECP2::new_fp2s(&expected_x, &expected_y);
+            assert_eq!(expected_q0, q0);
+
+            // Check Q1
+            let x_str_parts: Vec<&str> = case.Q1.x.split(',').collect();
+            let a = Big::frombytes(&hex::decode(&x_str_parts[0].get(2..).unwrap()).unwrap());
+            let b = Big::frombytes(&hex::decode(&x_str_parts[1].get(2..).unwrap()).unwrap());
+            let expected_x = FP2::new_bigs(&a, &b);
+
+            let y_str_parts: Vec<&str> = case.Q1.y.split(',').collect();
+            let a = Big::frombytes(&hex::decode(&y_str_parts[0].get(2..).unwrap()).unwrap());
+            let b = Big::frombytes(&hex::decode(&y_str_parts[1].get(2..).unwrap()).unwrap());
+            let expected_y = FP2::new_bigs(&a, &b);
+
+            let expected_q1 = ECP2::new_fp2s(&expected_x, &expected_y);
+            assert_eq!(expected_q1, q1);
+
+            // Check P
+            let x_str_parts: Vec<&str> = case.P.x.split(',').collect();
+            let a = Big::frombytes(&hex::decode(&x_str_parts[0].get(2..).unwrap()).unwrap());
+            let b = Big::frombytes(&hex::decode(&x_str_parts[1].get(2..).unwrap()).unwrap());
+            let expected_x = FP2::new_bigs(&a, &b);
+
+            let y_str_parts: Vec<&str> = case.P.y.split(',').collect();
+            let a = Big::frombytes(&hex::decode(&y_str_parts[0].get(2..).unwrap()).unwrap());
+            let b = Big::frombytes(&hex::decode(&y_str_parts[1].get(2..).unwrap()).unwrap());
+            let expected_y = FP2::new_bigs(&a, &b);
+
+            let expected_p = ECP2::new_fp2s(&expected_x, &expected_y);
+            assert_eq!(expected_p, p);
+        }
+    }
+
+    #[test]
+    #[cfg(feature = "bls381g1")]
+    fn test_hash_to_curve_g1() {
+        // Only run when signatures are on G2
+        if !BLS_SIG_G1 {
+            return;
+        }
+
+        // Read hash to curve test vector
+        let reader = json_reader(H2C_SUITE);
+        let test_vectors: Bls12381Ro = serde_json::from_reader(reader).unwrap();
+
+        // Iterate through each individual case
+        for case in test_vectors.vectors {
+            // Execute hash to curve
+            let u = hash_to_field_fp(case.msg.as_bytes(), 2, test_vectors.dst.as_bytes()).unwrap();
+            let q0 = map_to_curve_g1(u[0]);
+            let q1 = map_to_curve_g1(u[1]);
+            let mut r = q0.clone();
+            r.add(&q1);
+            let p = r.mul(&H_EFF_G1);
+
+            // Verify hash to curve outputs
+            // Check u
+            assert_eq!(case.u.len(), u.len());
+            for (i, u_str) in case.u.iter().enumerate() {
+                // Convert case 'u[i]' to FP
+                let a = Big::frombytes(&hex::decode(&u_str.get(2..).unwrap()).unwrap());
+                let expected_u_i = FP::new_big(&a);
+
+                // Verify u[i]
+                assert_eq!(expected_u_i, u[i]);
+            }
+
+            // Check Q0
+            let a = Big::frombytes(&hex::decode(&case.Q0.x.get(2..).unwrap()).unwrap());
+            let expected_x = FP::new_big(&a);
+
+            let a = Big::frombytes(&hex::decode(&case.Q0.y.get(2..).unwrap()).unwrap());
+            let expected_y = FP::new_big(&a);
+
+            let expected_q0 = ECP::new_fps(expected_x, expected_y);
+            assert_eq!(expected_q0, q0);
+
+            // Check Q1
+            let a = Big::frombytes(&hex::decode(&case.Q1.x.get(2..).unwrap()).unwrap());
+            let expected_x = FP::new_big(&a);
+
+            let a = Big::frombytes(&hex::decode(&case.Q1.y.get(2..).unwrap()).unwrap());
+            let expected_y = FP::new_big(&a);
+
+            let expected_q1 = ECP::new_fps(expected_x, expected_y);
+            assert_eq!(expected_q1, q1);
+
+            // Check P
+            let a = Big::frombytes(&hex::decode(&case.P.x.get(2..).unwrap()).unwrap());
+            let expected_x = FP::new_big(&a);
+
+            let a = Big::frombytes(&hex::decode(&case.P.y.get(2..).unwrap()).unwrap());
+            let expected_y = FP::new_big(&a);
+
+            let expected_p = ECP::new_fps(expected_x, expected_y);
+            assert_eq!(expected_p, p);
+        }
+    }
+}
diff --git a/src/bls381/iso.rs b/src/bls381/iso.rs
new file mode 100644
index 0000000..b00fb3d
--- /dev/null
+++ b/src/bls381/iso.rs
@@ -0,0 +1,335 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::super::big::Big;
+use super::super::ecp::ECP;
+use super::super::ecp2::ECP2;
+use super::super::fp::FP;
+use super::super::fp2::FP2;
+
+/**************************************************
+* 3-Isogeny Constants
+**************************************************/
+lazy_static! {
+    // ISO-3 Mapping values
+    pub static ref ISO3_XNUM: [FP2; 4] = [
+        FP2::new_bigs(
+            &Big::frombytes(&hex::decode("05c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6").unwrap()),
+            &Big::frombytes(&hex::decode("05c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6").unwrap())
+        ),
+        FP2::new_bigs(
+            &Big::new(),
+            &Big::frombytes(&hex::decode("11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71a").unwrap())
+        ),
+        FP2::new_bigs(
+            &Big::frombytes(&hex::decode("11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71e").unwrap()),
+            &Big::frombytes(&hex::decode("08ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38d").unwrap())
+        ),
+        FP2::new_bigs(
+            &Big::frombytes(&hex::decode("171d6541fa38ccfaed6dea691f5fb614cb14b4e7f4e810aa22d6108f142b85757098e38d0f671c7188e2aaaaaaaa5ed1").unwrap()),
+            &Big::new()
+        )
+    ];
+    pub static ref ISO3_XDEN: [FP2; 4] = [
+        FP2::new_bigs(
+            &Big::new(),
+            &Big::frombytes(&hex::decode("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa63").unwrap())
+        ),
+        FP2::new_bigs(
+            &Big::new_int(12),
+            &Big::frombytes(&hex::decode("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa9f").unwrap())
+        ),
+        FP2::new_int(1),
+        FP2::new(),
+    ];
+    pub static ref ISO3_YNUM: [FP2; 4] = [
+        FP2::new_bigs(
+            &Big::frombytes(&hex::decode("1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706").unwrap()),
+            &Big::frombytes(&hex::decode("1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706").unwrap())
+        ),
+        FP2::new_bigs(
+            &Big::new(),
+            &Big::frombytes(&hex::decode("05c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97be").unwrap())
+        ),
+        FP2::new_bigs(
+            &Big::frombytes(&hex::decode("11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71c").unwrap()),
+            &Big::frombytes(&hex::decode("08ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38f").unwrap())
+        ),
+        FP2::new_bigs(
+            &Big::frombytes(&hex::decode("124c9ad43b6cf79bfbf7043de3811ad0761b0f37a1e26286b0e977c69aa274524e79097a56dc4bd9e1b371c71c718b10").unwrap()),
+            &Big::new()
+        )
+    ];
+    pub static ref ISO3_YDEN: [FP2; 4] = [
+        FP2::new_bigs(
+            &Big::frombytes(&hex::decode("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb").unwrap()),
+            &Big::frombytes(&hex::decode("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb").unwrap())
+        ),
+        FP2::new_bigs(
+            &Big::new(),
+            &Big::frombytes(&hex::decode("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9d3").unwrap())
+        ),
+        FP2::new_bigs(
+            &Big::new_int(18),
+            &Big::frombytes(&hex::decode("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa99").unwrap())
+        ),
+        FP2::new_ints(1, 0)
+    ];
+
+    // ISO-11 Mapping values
+    pub static ref ISO11_XNUM: [FP; 12] = [
+        FP::new_big(
+            &Big::frombytes(&hex::decode("11a05f2b1e833340b809101dd99815856b303e88a2d7005ff2627b56cdb4e2c85610c2d5f2e62d6eaeac1662734649b7").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("17294ed3e943ab2f0588bab22147a81c7c17e75b2f6a8417f565e33c70d1e86b4838f2a6f318c356e834eef1b3cb83bb").unwrap())
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0d54005db97678ec1d1048c5d10a9a1bce032473295983e56878e501ec68e25c958c3e3d2a09729fe0179f9dac9edcb0").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("1778e7166fcc6db74e0609d307e55412d7f5e4656a8dbf25f1b33289f1b330835336e25ce3107193c5b388641d9b6861").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0e99726a3199f4436642b4b3e4118e5499db995a1257fb3f086eeb65982fac18985a286f301e77c451154ce9ac8895d9").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("1630c3250d7313ff01d1201bf7a74ab5db3cb17dd952799b9ed3ab9097e68f90a0870d2dcae73d19cd13c1c66f652983").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0d6ed6553fe44d296a3726c38ae652bfb11586264f0f8ce19008e218f9c86b2a8da25128c1052ecaddd7f225a139ed84").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("17b81e7701abdbe2e8743884d1117e53356de5ab275b4db1a682c62ef0f2753339b7c8f8c8f475af9ccb5618e3f0c88e").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("080d3cf1f9a78fc47b90b33563be990dc43b756ce79f5574a2c596c928c5d1de4fa295f296b74e956d71986a8497e317").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("169b1f8e1bcfa7c42e0c37515d138f22dd2ecb803a0c5c99676314baf4bb1b7fa3190b2edc0327797f241067be390c9e").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("10321da079ce07e272d8ec09d2565b0dfa7dccdde6787f96d50af36003b14866f69b771f8c285decca67df3f1605fb7b").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("06e08c248e260e70bd1e962381edee3d31d79d7e22c837bc23c0bf1bc24c6b68c24b1b80b64d391fa9c8ba2e8ba2d229").unwrap()),
+        ),
+    ];
+    pub static ref ISO11_XDEN: [FP; 11] = [
+        FP::new_big(
+            &Big::frombytes(&hex::decode("08ca8d548cff19ae18b2e62f4bd3fa6f01d5ef4ba35b48ba9c9588617fc8ac62b558d681be343df8993cf9fa40d21b1c").unwrap())
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("12561a5deb559c4348b4711298e536367041e8ca0cf0800c0126c2588c48bf5713daa8846cb026e9e5c8276ec82b3bff").unwrap())
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0b2962fe57a3225e8137e629bff2991f6f89416f5a718cd1fca64e00b11aceacd6a3d0967c94fedcfcc239ba5cb83e19").unwrap())
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("03425581a58ae2fec83aafef7c40eb545b08243f16b1655154cca8abc28d6fd04976d5243eecf5c4130de8938dc62cd8").unwrap())
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("13a8e162022914a80a6f1d5f43e7a07dffdfc759a12062bb8d6b44e833b306da9bd29ba81f35781d539d395b3532a21e").unwrap())
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0e7355f8e4e667b955390f7f0506c6e9395735e9ce9cad4d0a43bcef24b8982f7400d24bc4228f11c02df9a29f6304a5").unwrap())
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0772caacf16936190f3e0c63e0596721570f5799af53a1894e2e073062aede9cea73b3538f0de06cec2574496ee84a3a").unwrap())
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("14a7ac2a9d64a8b230b3f5b074cf01996e7f63c21bca68a81996e1cdf9822c580fa5b9489d11e2d311f7d99bbdcc5a5e").unwrap())
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0a10ecf6ada54f825e920b3dafc7a3cce07f8d1d7161366b74100da67f39883503826692abba43704776ec3a79a1d641").unwrap())
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("095fc13ab9e92ad4476d6e3eb3a56680f682b4ee96f7d03776df533978f31c1593174e4b4b7865002d6384d168ecdd0a").unwrap())
+        ),
+        FP::new_int(1),
+    ];
+    pub static ref ISO11_YNUM: [FP; 16] = [
+        FP::new_big(
+            &Big::frombytes(&hex::decode("090d97c81ba24ee0259d1f094980dcfa11ad138e48a869522b52af6c956543d3cd0c7aee9b3ba3c2be9845719707bb33").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("134996a104ee5811d51036d776fb46831223e96c254f383d0f906343eb67ad34d6c56711962fa8bfe097e75a2e41c696").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("00cc786baa966e66f4a384c86a3b49942552e2d658a31ce2c344be4b91400da7d26d521628b00523b8dfe240c72de1f6").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("01f86376e8981c217898751ad8746757d42aa7b90eeb791c09e4a3ec03251cf9de405aba9ec61deca6355c77b0e5f4cb").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("08cc03fdefe0ff135caf4fe2a21529c4195536fbe3ce50b879833fd221351adc2ee7f8dc099040a841b6daecf2e8fedb").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("16603fca40634b6a2211e11db8f0a6a074a7d0d4afadb7bd76505c3d3ad5544e203f6326c95a807299b23ab13633a5f0").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("04ab0b9bcfac1bbcb2c977d027796b3ce75bb8ca2be184cb5231413c4d634f3747a87ac2460f415ec961f8855fe9d6f2").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0987c8d5333ab86fde9926bd2ca6c674170a05bfe3bdd81ffd038da6c26c842642f64550fedfe935a15e4ca31870fb29").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("09fc4018bd96684be88c9e221e4da1bb8f3abd16679dc26c1e8b6e6a1f20cabe69d65201c78607a360370e577bdba587").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0e1bba7a1186bdb5223abde7ada14a23c42a0ca7915af6fe06985e7ed1e4d43b9b3f7055dd4eba6f2bafaaebca731c30").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("19713e47937cd1be0dfd0b8f1d43fb93cd2fcbcb6caf493fd1183e416389e61031bf3a5cce3fbafce813711ad011c132").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("18b46a908f36f6deb918c143fed2edcc523559b8aaf0c2462e6bfe7f911f643249d9cdf41b44d606ce07c8a4d0074d8e").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0b182cac101b9399d155096004f53f447aa7b12a3426b08ec02710e807b4633f06c851c1919211f20d4c04f00b971ef8").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0245a394ad1eca9b72fc00ae7be315dc757b3b080d4c158013e6632d3c40659cc6cf90ad1c232a6442d9d3f5db980133").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("05c129645e44cf1102a159f748c4a3fc5e673d81d7e86568d9ab0f5d396a7ce46ba1049b6579afb7866b1e715475224b").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("15e6be4e990f03ce4ea50b3b42df2eb5cb181d8f84965a3957add4fa95af01b2b665027efec01c7704b456be69c8b604").unwrap()),
+        ),
+    ];
+    pub static ref ISO11_YDEN: [FP; 16] = [
+        FP::new_big(
+            &Big::frombytes(&hex::decode("16112c4c3a9c98b252181140fad0eae9601a6de578980be6eec3232b5be72e7a07f3688ef60c206d01479253b03663c1").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("1962d75c2381201e1a0cbd6c43c348b885c84ff731c4d59ca4a10356f453e01f78a4260763529e3532f6102c2e49a03d").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("058df3306640da276faaae7d6e8eb15778c4855551ae7f310c35a5dd279cd2eca6757cd636f96f891e2538b53dbf67f2").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("16b7d288798e5395f20d23bf89edb4d1d115c5dbddbcd30e123da489e726af41727364f2c28297ada8d26d98445f5416").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0be0e079545f43e4b00cc912f8228ddcc6d19c9f0f69bbb0542eda0fc9dec916a20b15dc0fd2ededda39142311a5001d").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("08d9e5297186db2d9fb266eaac783182b70152c65550d881c5ecd87b6f0f5a6449f38db9dfa9cce202c6477faaf9b7ac").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("166007c08a99db2fc3ba8734ace9824b5eecfdfa8d0cf8ef5dd365bc400a0051d5fa9c01a58b1fb93d1a1399126a775c").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("16a3ef08be3ea7ea03bcddfabba6ff6ee5a4375efa1f4fd7feb34fd206357132b920f5b00801dee460ee415a15812ed9").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("1866c8ed336c61231a1be54fd1d74cc4f9fb0ce4c6af5920abc5750c4bf39b4852cfe2f7bb9248836b233d9d55535d4a").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("167a55cda70a6e1cea820597d94a84903216f763e13d87bb5308592e7ea7d4fbc7385ea3d529b35e346ef48bb8913f55").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("04d2f259eea405bd48f010a01ad2911d9c6dd039bb61a6290e591b36e636a5c871a5c29f4f83060400f8b49cba8f6aa8").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0accbb67481d033ff5852c1e48c50c477f94ff8aefce42d28c0f9a88cea7913516f968986f7ebbea9684b529e2561092").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0ad6b9514c767fe3c3613144b45f1496543346d98adf02267d5ceef9a00d9b8693000763e3b90ac11e99b138573345cc").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("02660400eb2e4f3b628bdd0d53cd76f2bf565b94e72927c1cb748df27942480e420517bd8714cc80d1fadc1326ed06f7").unwrap()),
+        ),
+        FP::new_big(
+            &Big::frombytes(&hex::decode("0e0fa1d816ddc03e6b24255e0d7819c171c40f65e273b853324efcd6356caa205ca2f570f13497804415473a1d634b8f").unwrap()),
+        ),
+        FP::new_int(1),
+    ];
+}
+
+/// Mapping from 11-Isogeny Curve to BLS12-381 ECP
+///
+/// Adjusted from https://eprint.iacr.org/2019/403
+/// to convert projectives to (XZ, YZ, Z)
+pub fn iso11_to_ecp(iso_x: &FP, iso_y: &FP) -> ECP {
+    let polynomials_coefficients: [&[FP]; 4] =
+        [&*ISO11_XNUM, &*ISO11_XDEN, &*ISO11_YNUM, &*ISO11_YDEN];
+
+    // x-num, x-den, y-num, y-den
+    let mut mapped_vals: [FP; 4] = [FP::new(), FP::new(), FP::new(), FP::new()];
+
+    // Horner caculation for evaluating polynomials
+    for (i, coefficients) in polynomials_coefficients[..].iter().enumerate() {
+        mapped_vals[i] = coefficients[coefficients.len() - 1].clone();
+        for k in coefficients.iter().rev().skip(1) {
+            mapped_vals[i].mul(&iso_x);
+            mapped_vals[i].add(&k);
+        }
+    }
+
+    // y-num multiplied by y
+    mapped_vals[2].mul(&iso_y);
+
+    let mut z = mapped_vals[1].clone(); // x-den
+    z.mul(&mapped_vals[3]); // x-den * y-den
+
+    let mut x = mapped_vals[0].clone(); // x-num
+    x.mul(&mapped_vals[3]); // x-num * y-den
+
+    let mut y = mapped_vals[2].clone(); // y-num
+    y.mul(&mapped_vals[1]); // y-num * x-den
+
+    ECP::new_projective(x, y, z)
+}
+
+/// Mapping from 3-Isogeny Curve to BLS12-381 ECP2
+///
+/// Adjusted from https://eprint.iacr.org/2019/403
+/// to convert projectives to (XZ, YZ, Z)
+pub fn iso3_to_ecp2(iso_x: &FP2, iso_y: &FP2) -> ECP2 {
+    let polynomials_coefficients: [&[FP2; 4]; 4] =
+        [&*ISO3_XNUM, &*ISO3_XDEN, &*ISO3_YNUM, &*ISO3_YDEN];
+
+    // x-num, x-den, y-num, y-den
+    let mut mapped_vals: [FP2; 4] = [FP2::new(), FP2::new(), FP2::new(), FP2::new()];
+
+    // Horner caculation for evaluating polynomials
+    for (i, coefficients) in polynomials_coefficients[..].iter().enumerate() {
+        mapped_vals[i] = coefficients[coefficients.len() - 1].clone();
+        for k in coefficients.iter().rev().skip(1) {
+            mapped_vals[i].mul(&iso_x);
+            mapped_vals[i].add(&k);
+        }
+    }
+
+    // y-num multiplied by y
+    mapped_vals[2].mul(&iso_y);
+
+    let mut z = mapped_vals[1].clone(); // x-den
+    z.mul(&mapped_vals[3]); // x-den * y-den
+
+    let mut x = mapped_vals[0].clone(); // x-num
+    x.mul(&mapped_vals[3]); // x-num * y-den
+
+    let mut y = mapped_vals[2].clone(); // y-num
+    y.mul(&mapped_vals[1]); // y-num * x-den
+
+    ECP2::new_projective(x, y, z)
+}
diff --git a/src/dbig.rs b/src/dbig.rs
index 2479637..26b29bd 100644
--- a/src/dbig.rs
+++ b/src/dbig.rs
@@ -20,7 +20,7 @@ under the License.
 use super::super::arch;
 use super::super::arch::Chunk;
 use super::big;
-use super::big::Big;
+use super::big::{Big, MODBYTES};
 
 #[derive(Copy)]
 pub struct DBig {
@@ -301,4 +301,23 @@ impl DBig {
         }
         s
     }
+
+    // convert from byte array to DBig
+    pub fn frombytes(b: &[u8]) -> DBig {
+        let mut m = DBig::new();
+
+        // Restrict length
+        let max_dbig = 2 * MODBYTES;
+        let len = if b.len() >= max_dbig {
+            max_dbig as usize
+        } else {
+            b.len()
+        };
+
+        for i in 0..len {
+            m.shl(8);
+            m.w[0] += (b[i] & 0xff) as Chunk;
+        }
+        m
+    }
 }
diff --git a/src/ecdh.rs b/src/ecdh.rs
index 934ea82..78e88a9 100644
--- a/src/ecdh.rs
+++ b/src/ecdh.rs
@@ -226,7 +226,7 @@ pub fn pbkdf2(sha: usize, pass: &[u8], salt: &[u8], rep: usize, olen: usize, k:
 /// Calculate HMAC of m using key k. HMAC is tag of length olen (which is length of tag)
 pub fn hmac(sha: usize, m: &[u8], k: &[u8], olen: usize, tag: &mut [u8]) -> bool {
     // Input is from an octet m
-	// olen is requested output length in bytes. k is the key
+    // olen is requested output length in bytes. k is the key
     // The output is the calculated tag
     let mut b: [u8; 64] = [0; 64]; // Not good
     let mut k0: [u8; 128] = [0; 128];
@@ -754,7 +754,6 @@ mod tests {
 
     #[test]
     fn test_ecdh() {
-
         let mut rng = create_rng();
 
         let pw = "M0ng00se";
diff --git a/src/ecp.rs b/src/ecp.rs
index 07e19be..609ab75 100644
--- a/src/ecp.rs
+++ b/src/ecp.rs
@@ -131,6 +131,29 @@ impl ECP {
         return E;
     }
 
+    // construct this from (x,y), set to 0 if not on curve
+    pub fn new_fps(x: FP, y: FP) -> ECP {
+        let mut point = ECP {
+            x,
+            y,
+            z: FP::new_int(1),
+        };
+
+        let rhs = ECP::rhs(&point.x);
+        let mut y2 = point.y.clone();
+        y2.sqr();
+        if !y2.equals(&rhs) {
+            point.inf();
+        }
+        point
+    }
+
+    // Create new point from (x, y, z)
+    // Assumes coordinates are valid
+    pub fn new_projective(x: FP, y: FP, z: FP) -> ECP {
+        ECP { x, y, z }
+    }
+
     /* set this=O */
     pub fn inf(&mut self) {
         self.x.zero();
diff --git a/src/ecp2.rs b/src/ecp2.rs
index b0892e4..25f7175 100644
--- a/src/ecp2.rs
+++ b/src/ecp2.rs
@@ -94,6 +94,11 @@ impl ECP2 {
         return E;
     }
 
+    // Construct from (X, Y, Z) with no gaurentee of correctness.
+    pub fn new_projective(x: FP2, y: FP2, z: FP2) -> ECP2 {
+        ECP2 { x, y, z }
+    }
+
     /* Test this=O? */
     pub fn is_infinity(&self) -> bool {
         self.x.iszilch() && self.z.iszilch()
@@ -722,15 +727,20 @@ impl ECP2 {
             x.inc(1);
             x.norm();
         }
+        Q.clear_cofactor();
+        Q
+    }
+
+    pub fn clear_cofactor(&mut self) {
         let mut X = FP2::new_bigs(&Big::new_ints(&rom::FRA), &Big::new_ints(&rom::FRB));
         if ecp::SEXTIC_TWIST == SexticTwist::MType {
             X.inverse();
             X.norm();
         }
-        x = Big::new_ints(&rom::CURVE_BNX);
+        let x = Big::new_ints(&rom::CURVE_BNX);
 
         if ecp::CURVE_PAIRING_TYPE == CurvePairingType::Bn {
-            let mut T = Q.mul(&x);
+            let mut T = self.mul(&x);
             if ecp::SIGN_OF_X == SignOfX::NegativeX {
                 T.neg();
             }
@@ -740,38 +750,37 @@ impl ECP2 {
             K.add(&T);
 
             K.frob(&X);
-            Q.frob(&X);
-            Q.frob(&X);
-            Q.frob(&X);
-            Q.add(&T);
-            Q.add(&K);
+            self.frob(&X);
+            self.frob(&X);
+            self.frob(&X);
+            self.add(&T);
+            self.add(&K);
             T.frob(&X);
             T.frob(&X);
-            Q.add(&T);
+            self.add(&T);
         }
         if ecp::CURVE_PAIRING_TYPE == CurvePairingType::Bls {
-            let mut xQ = Q.mul(&x);
+            let mut xQ = self.mul(&x);
             let mut x2Q = xQ.mul(&x);
 
             if ecp::SIGN_OF_X == SignOfX::NegativeX {
                 xQ.neg();
             }
             x2Q.sub(&xQ);
-            x2Q.sub(&Q);
+            x2Q.sub(&self);
 
-            xQ.sub(&Q);
+            xQ.sub(&self);
             xQ.frob(&X);
 
-            Q.dbl();
-            Q.frob(&X);
-            Q.frob(&X);
+            self.dbl();
+            self.frob(&X);
+            self.frob(&X);
 
-            Q.add(&x2Q);
-            Q.add(&xQ);
+            self.add(&x2Q);
+            self.add(&xQ);
         }
 
-        Q.affine();
-        return Q;
+        self.affine();
     }
 
     pub fn generator() -> ECP2 {
diff --git a/src/errors.rs b/src/errors.rs
new file mode 100644
index 0000000..71a30fa
--- /dev/null
+++ b/src/errors.rs
@@ -0,0 +1,4 @@
+#[derive(Debug)]
+pub enum AmclError {
+    HashToFieldError,
+}
diff --git a/src/fp.rs b/src/fp.rs
index 95cddff..f1454c5 100644
--- a/src/fp.rs
+++ b/src/fp.rs
@@ -118,7 +118,7 @@ impl FP {
         format!("{} {}", self.xes, self.x.tostring())
     }
 
-    // convert back to regular form
+    /// convert back to regular form
     pub fn redc(&self) -> Big {
         if MODTYPE != ModType::PseudoMersenne && MODTYPE != ModType::GeneralisedMersenne {
             let mut d = DBig::new_scopy(&(self.x));
@@ -127,8 +127,7 @@ impl FP {
         Big::new_copy(&(self.x))
     }
 
-    // reduce a DBig to a Big using the appropriate form of the modulus
-    // dd
+    /// reduce a DBig to a Big using the appropriate form of the modulus
     pub fn modulo(d: &mut DBig) -> Big {
         if MODTYPE == ModType::PseudoMersenne {
             let mut b = Big::new();
@@ -198,12 +197,12 @@ impl FP {
         Big::new()
     }
 
-    // convert to string
+    /// convert to string
     pub fn tostring(&self) -> String {
         self.redc().tostring()
     }
 
-    // reduce this mod Modulus
+    /// reduce this mod Modulus
     pub fn reduce(&mut self) {
         let mut m = Big::new_ints(&rom::MODULUS);
         let mut r = Big::new_copy(&m);
@@ -230,42 +229,43 @@ impl FP {
         self.xes = 1;
     }
 
-    // test this=0?
+    /// test this=0?
     pub fn iszilch(&self) -> bool {
         let mut a = FP::new_copy(self);
         a.reduce();
         a.x.iszilch()
     }
 
-    // copy from FP b
+    /// copy from FP b
     pub fn copy(&mut self, b: &FP) {
         self.x.copy(&(b.x));
         self.xes = b.xes;
     }
 
-    // copy from Big b
+    /// copy from Big b
     pub fn bcopy(&mut self, b: &Big) {
         self.x.copy(&b);
         self.nres();
     }
 
-    // set this=0
+    /// set this=0
     pub fn zero(&mut self) {
         self.x.zero();
         self.xes = 1;
     }
 
-    // set this=1
+    /// set this=1
     pub fn one(&mut self) {
         self.x.one();
         self.nres()
     }
 
-    // normalise this
+    /// normalise this
     pub fn norm(&mut self) {
         self.x.norm();
     }
-    // swap FPs depending on d
+
+    /// swap FPs depending on d
     pub fn cswap(&mut self, b: &mut FP, d: isize) {
         self.x.cswap(&mut (b.x), d);
         let mut c = d as i32;
@@ -275,14 +275,14 @@ impl FP {
         b.xes ^= t;
     }
 
-    // copy FPs depending on d
+    /// copy FPs depending on d
     pub fn cmove(&mut self, b: &FP, d: isize) {
         self.x.cmove(&(b.x), d);
         let c = d as i32;
         self.xes ^= (self.xes ^ b.xes) & (-c);
     }
 
-    // this*=b mod Modulus
+    /// this*=b mod Modulus
     pub fn mul(&mut self, b: &FP) {
         if i64::from(self.xes) * i64::from(b.xes) > i64::from(FEXCESS) {
             self.reduce()
@@ -306,9 +306,9 @@ impl FP {
         ((((v + (v >> 4)) & 0xF0F0F0F).wrapping_mul(0x1010101)) >> 24) as usize
     }
 
-    // find approximation to quotient of a/m
-    // Out by at most 2.
-    // Note that MAXXES is bounded to be 2-bits less than half a word
+    /// Find approximation to quotient of a/m
+    /// Out by at most 2.
+    /// Note that MAXXES is bounded to be 2-bits less than half a word
     fn quo(n: &Big, m: &Big) -> isize {
         let hb = arch::CHUNK / 2;
 
@@ -324,7 +324,7 @@ impl FP {
         }
     }
 
-    // this = -this mod Modulus
+    /// this = -this mod Modulus
     pub fn neg(&mut self) {
         let mut p = Big::new_ints(&rom::MODULUS);
         let sb = FP::logb2((self.xes - 1) as u32);
@@ -337,7 +337,7 @@ impl FP {
         }
     }
 
-    // this*=c mod Modulus, where c is a small int
+    /// this*=c mod Modulus, where c is a small int
     pub fn imul(&mut self, c: isize) {
         let mut cc = c;
         let mut s = false;
@@ -366,7 +366,7 @@ impl FP {
         }
     }
 
-    // self*=self mod Modulus
+    /// self*=self mod Modulus
     pub fn sqr(&mut self) {
         if i64::from(self.xes) * i64::from(self.xes) > i64::from(FEXCESS) {
             self.reduce()
@@ -377,7 +377,7 @@ impl FP {
         self.xes = 2
     }
 
-    // self+=b
+    /// self+=b
     pub fn add(&mut self, b: &FP) {
         self.x.add(&(b.x));
         self.xes += b.xes;
@@ -386,7 +386,7 @@ impl FP {
         }
     }
 
-    // self+=self
+    /// self+=self
     pub fn dbl(&mut self) {
         self.x.dbl();
         self.xes += self.xes;
@@ -395,20 +395,20 @@ impl FP {
         }
     }
 
-    // self-=b
+    /// self-=b
     pub fn sub(&mut self, b: &FP) {
         let mut n = FP::new_copy(b);
         n.neg();
         self.add(&n);
     }
 
-    // self=b-self
+    /// self=b-self
     pub fn rsub(&mut self, b: &FP) {
         self.neg();
         self.add(&b);
     }
 
-    // self/=2 mod Modulus
+    /// self/=2 mod Modulus
     pub fn div2(&mut self) {
         if self.x.parity() == 0 {
             self.x.fshr(1);
@@ -420,8 +420,9 @@ impl FP {
         }
     }
 
-    // See eprint paper https://eprint.iacr.org/2018/1038
-    // return this^(p-3)/4 or this^(p-5)/8
+    /// Return this^(p-3)/4 or this^(p-5)/8
+    ///
+    /// https://eprint.iacr.org/2018/1038
     pub fn fpow(&self) -> FP {
         let ac: [isize; 11] = [1, 2, 3, 6, 12, 15, 30, 60, 120, 240, 255];
         let mut xp: [FP; 11] = [
@@ -570,7 +571,8 @@ impl FP {
         }
         r
     }
-    // self=1/self mod Modulus
+
+    /// self=1/self mod Modulus
     pub fn inverse(&mut self) {
         if MODTYPE == ModType::PseudoMersenne || MODTYPE == ModType::GeneralisedMersenne {
             let mut y = self.fpow();
@@ -594,7 +596,7 @@ impl FP {
         }
     }
 
-    // return TRUE if self==a
+    /// return TRUE if self==a
     pub fn equals(&self, a: &FP) -> bool {
         let mut f = FP::new_copy(self);
         let mut s = FP::new_copy(a);
@@ -606,7 +608,7 @@ impl FP {
         return false;
     }
 
-    // return self^e mod Modulus
+    /// return self^e mod Modulus
     pub fn pow(&mut self, e: &mut Big) -> FP {
         let mut tb: [FP; 16] = [
             FP::new(),
@@ -662,7 +664,7 @@ impl FP {
         return r;
     }
 
-    // return sqrt(this) mod Modulus
+    /// return sqrt(this) mod Modulus
     pub fn sqrt(&mut self) -> FP {
         self.reduce();
 
@@ -702,10 +704,20 @@ impl FP {
             return r;
         }
     }
-    // return jacobi symbol (this/Modulus)
+
+    /// return jacobi symbol (this/Modulus)
     pub fn jacobi(&self) -> isize {
         let p = Big::new_ints(&rom::MODULUS);
         let mut w = self.redc();
         return w.jacobi(&p);
     }
+
+    /// Checks if the field value is negative
+    ///
+    /// Negative if a > -a
+    pub fn is_neg(&mut self) -> bool {
+        let mut neg_a = self.clone();
+        neg_a.neg();
+        Big::comp(&self.redc(), &neg_a.redc()) > 0
+    }
 }
diff --git a/src/fp2.rs b/src/fp2.rs
index 87a0db1..12920f4 100644
--- a/src/fp2.rs
+++ b/src/fp2.rs
@@ -64,6 +64,13 @@ impl FP2 {
         return f;
     }
 
+    pub fn new_ints(a: isize, b: isize) -> FP2 {
+        let mut f = FP2::new();
+        f.a.copy(&FP::new_int(a));
+        f.b.copy(&FP::new_int(b));
+        return f;
+    }
+
     pub fn new_copy(x: &FP2) -> FP2 {
         let mut f = FP2::new();
         f.a.copy(&x.a);
@@ -406,4 +413,20 @@ impl FP2 {
         self.copy(&t);
         self.div2();
     }
+
+    // ((a + b) , (a - b))
+    pub fn spmt(&mut self) {
+        let b = self.b.clone();
+        self.b = self.a.clone();
+        self.a.add(&b);
+        self.b.sub(&b);
+    }
+
+    // b > -b OR if b is 0 then a > -a
+    pub fn is_neg(&mut self) -> bool {
+        if self.b.iszilch() {
+            return self.a.is_neg();
+        }
+        self.b.is_neg()
+    }
 }
diff --git a/src/hash256.rs b/src/hash256.rs
index 5df85d9..5860524 100644
--- a/src/hash256.rs
+++ b/src/hash256.rs
@@ -37,6 +37,15 @@ const HASH256_K: [u32; 64] = [
     0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
 ];
 
+/// The block size of each round.
+const BLOCK_SIZE: usize = 64;
+/// Ipad Byte
+const IPAD_BYTE: u8 = 0x36;
+/// Opad Byte
+const OPAD_BYTE: u8 = 0x5c;
+/// Hash Length in Bytes
+const HASH_BYTES: usize = 32;
+
 pub struct HASH256 {
     length: [u32; 2],
     h: [u32; 8],
@@ -75,7 +84,7 @@ impl HASH256 {
     }
 
     fn transform(&mut self) {
-        /* basic transformation step */
+        // basic transformation step
         for j in 16..64 {
             self.w[j] = HASH256::theta1(self.w[j - 2])
                 .wrapping_add(self.w[j - 7])
@@ -91,7 +100,7 @@ impl HASH256 {
         let mut g = self.h[6];
         let mut hh = self.h[7];
         for j in 0..64 {
-            /* 64 times - mush it up */
+            // 64 times - mush it up
             let t1 = hh
                 .wrapping_add(HASH256::sig1(e))
                 .wrapping_add(HASH256::ch(e, f, g))
@@ -117,9 +126,9 @@ impl HASH256 {
         self.h[7] = self.h[7].wrapping_add(hh);
     }
 
-    /* Initialise Hash function */
+    /// Initialise Hash function
     pub fn init(&mut self) {
-        /* initialise */
+        // initialise
         for i in 0..64 {
             self.w[i] = 0
         }
@@ -145,7 +154,7 @@ impl HASH256 {
         return nh;
     }
 
-    /* process a single byte */
+    /// Process a single byte
     pub fn process(&mut self, byt: u8) {
         /* process the next message byte */
         let cnt = ((self.length[0] / 32) % 16) as usize;
@@ -161,15 +170,14 @@ impl HASH256 {
         }
     }
 
-    /* process an array of bytes */
-
+    /// Process an array of bytes
     pub fn process_array(&mut self, b: &[u8]) {
         for i in 0..b.len() {
             self.process(b[i])
         }
     }
 
-    /* process a 32-bit integer */
+    /// Process a 32-bit integer
     pub fn process_num(&mut self, n: i32) {
         self.process(((n >> 24) & 0xff) as u8);
         self.process(((n >> 16) & 0xff) as u8);
@@ -177,9 +185,9 @@ impl HASH256 {
         self.process((n & 0xff) as u8);
     }
 
-    /* Generate 32-byte Hash */
-    pub fn hash(&mut self) -> [u8; 32] {
-        /* pad message and finish - supply digest */
+    /// Generate 32-byte Hash
+    pub fn hash(&mut self) -> [u8; HASH_BYTES] {
+        // pad message and finish - supply digest
         let mut digest: [u8; 32] = [0; 32];
         let len0 = self.length[0];
         let len1 = self.length[1];
@@ -191,26 +199,238 @@ impl HASH256 {
         self.w[15] = len0;
         self.transform();
         for i in 0..32 {
-            /* convert to bytes */
+            // convert to bytes
             digest[i] = ((self.h[i / 4] >> (8 * (3 - i % 4))) & 0xff) as u8;
         }
         self.init();
         return digest;
     }
+
+    /// Generate a HMAC
+    ///
+    /// https://tools.ietf.org/html/rfc2104
+    pub fn hmac(key: &[u8], text: &[u8]) -> [u8; HASH_BYTES] {
+        let mut k = key.to_vec();
+
+        // Verify length of key < BLOCK_SIZE
+        if k.len() > BLOCK_SIZE {
+            // Reduce key to 32 bytes by hashing
+            let mut hash256 = HASH256::new();
+            hash256.init();
+            hash256.process_array(&k);
+            k = hash256.hash().to_vec();
+        }
+
+        // Prepare inner and outer paddings
+        // inner = (ipad XOR k)
+        // outer = (opad XOR k)
+        let mut inner = vec![IPAD_BYTE; BLOCK_SIZE];
+        let mut outer = vec![OPAD_BYTE; BLOCK_SIZE];
+        for (i, byte) in k.iter().enumerate() {
+            inner[i] = inner[i] ^ byte;
+            outer[i] = outer[i] ^ byte;
+        }
+
+        // Concatenate inner with text = (ipad XOR k || text)
+        inner.extend_from_slice(text);
+
+        // hash inner = H(ipad XOR k || text)
+        let mut hash256 = HASH256::new();
+        hash256.init();
+        hash256.process_array(&inner);
+        let inner = hash256.hash();
+
+        // Concatenate outer with hash of inner = (opad XOR k) || H(ipad XOR k || text)
+        outer.extend_from_slice(&inner);
+
+        // Final hash = H((opad XOR k) || H(ipad XOR k || text))
+        let mut hash256 = HASH256::new();
+        hash256.init();
+        hash256.process_array(&outer);
+        hash256.hash()
+    }
+
+    /// HKDF-Extract
+    ///
+    /// https://tools.ietf.org/html/rfc5869
+    pub fn hkdf_extract(salt: &[u8], ikm: &[u8]) -> [u8; HASH_BYTES] {
+        HASH256::hmac(salt, ikm)
+    }
+
+    /// HKDF-Extend
+    ///
+    /// https://tools.ietf.org/html/rfc5869
+    pub fn hkdf_extend(prk: &[u8], info: &[u8], l: u8) -> Vec<u8> {
+        // n = cieling(l / 32)
+        let mut n = l / (HASH_BYTES as u8);
+        if n * (HASH_BYTES as u8) < l {
+            n += 1;
+        }
+
+        let mut okm: Vec<u8> = vec![];
+        let mut previous = vec![]; // T(0) = []
+
+        for i in 0..n as usize {
+            // Concatenate (T(i) || info || i)
+            let mut text: Vec<u8> = previous;
+            text.extend_from_slice(info);
+            text.push((i + 1) as u8); // Note: i <= 254
+
+            // T(i+1) = HMAC(PRK, T(i) || info || i)
+            previous = HASH256::hmac(prk, &text).to_vec();
+            okm.extend_from_slice(&previous);
+        }
+
+        // Reduce length to size L
+        okm.resize(l as usize, 0);
+        okm
+    }
 }
 
-//248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1
-/*
-fn main() {
-    let s = String::from("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
-    let test = s.into_bytes();
-    let mut sh=HASH256::new();
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_hmac_simple() {
+        let text = [0x0a];
+        let key = [0x0b];
+        let expected =
+            hex::decode("b1746117c186405d121d52866f48270fdeb2177d67f6922f0a031e0101658624")
+                .unwrap();
+
+        let output = HASH256::hmac(&key, &text);
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hmac_empty() {
+        let text = [];
+        let key = [];
+        let expected =
+            hex::decode("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad")
+                .unwrap();
 
-    for i in 0..test.len(){
-        sh.process(test[i]);
+        let output = HASH256::hmac(&key, &text);
+        assert_eq!(expected, output);
     }
 
-    let digest=sh.hash();
-    for i in 0..32 {print!("{:02x}",digest[i])}
+    #[test]
+    fn test_hmac_32_byte_key() {
+        let text = [0x0a];
+        let key = hex::decode("abababababababababababababababababababababababababababababababab")
+            .unwrap();
+        let expected =
+            hex::decode("43997a72e7b3b1c19e5566c940d5f2961c96802b58a3da2acd19dcc1a90a8d05")
+                .unwrap();
+
+        let output = HASH256::hmac(&key, &text);
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hmac_64_byte_key() {
+        let text = [0x0a];
+        let key = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
+        let expected =
+            hex::decode("93a88773df742079e3512f3d10f4f8ac674e24c4eda78df46c2376dd3946750b")
+                .unwrap();
+
+        let output = HASH256::hmac(&key, &text);
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hmac_65_byte_key() {
+        let text = [0x0a];
+        let key = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0B").unwrap();
+        let expected =
+            hex::decode("7c8dd5068bcff3347dd13a7493247444635b51cf000b18f37a74a55cec3413fb")
+                .unwrap();
+
+        let output = HASH256::hmac(&key, &text);
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hmac_65_byte_text() {
+        let text = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0B").unwrap();
+        let key = [0x0b];
+        let expected =
+            hex::decode("f04344808f2fcdafe1c20272a29b1ce4be00c916a2c14700b82b81c6eae9dd96")
+                .unwrap();
+
+        let output = HASH256::hmac(&key, &text);
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hkdf_case_1() {
+        // From https://tools.ietf.org/html/rfc5869
+        let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
+        let salt = hex::decode("000102030405060708090a0b0c").unwrap();
+        let expected_prk =
+            hex::decode("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5")
+                .unwrap();
+
+        let output_prk = HASH256::hkdf_extract(&salt, &ikm).to_vec();
+        assert_eq!(expected_prk, output_prk);
+
+        let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap();
+        let l = 42;
+        let expected_okm = hex::decode(
+            "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865",
+        )
+        .unwrap();
+
+        let output_okm = HASH256::hkdf_extend(&expected_prk, &info, l);
+        assert_eq!(expected_okm, output_okm);
+    }
+
+    #[test]
+    fn test_hkdf_case_2() {
+        // From https://tools.ietf.org/html/rfc5869
+        let ikm = hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f")
+            .unwrap();
+        let salt = hex::decode("606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf")
+            .unwrap();
+        let expected_prk =
+            hex::decode("06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244")
+                .unwrap();
+
+        let output_prk = HASH256::hkdf_extract(&salt, &ikm).to_vec();
+        assert_eq!(expected_prk, output_prk);
+
+        let info = hex::decode("b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")
+            .unwrap();
+        let l = 82;
+        let expected_okm = hex::decode("b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87")
+            .unwrap();
+
+        let output_okm = HASH256::hkdf_extend(&expected_prk, &info, l);
+        assert_eq!(expected_okm, output_okm);
+    }
+
+    #[test]
+    fn test_hkdf_case_3() {
+        // From https://tools.ietf.org/html/rfc5869
+        let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
+        let salt = vec![];
+        let expected_prk =
+            hex::decode("19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04")
+                .unwrap();
+
+        let output_prk = HASH256::hkdf_extract(&salt, &ikm).to_vec();
+        assert_eq!(expected_prk, output_prk);
+
+        let info = vec![];
+        let l = 42;
+        let expected_okm = hex::decode(
+            "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8",
+        )
+        .unwrap();
+
+        let output_okm = HASH256::hkdf_extend(&expected_prk, &info, l);
+        assert_eq!(expected_okm, output_okm);
+    }
 }
-*/
diff --git a/src/hash384.rs b/src/hash384.rs
index 2e3729e..5c349a1 100644
--- a/src/hash384.rs
+++ b/src/hash384.rs
@@ -109,6 +109,15 @@ const HASH384_K: [u64; 80] = [
     0x6c44198c4a475817,
 ];
 
+/// The block size of each round.
+const BLOCK_SIZE: usize = 128;
+/// Ipad Byte
+const IPAD_BYTE: u8 = 0x36;
+/// Opad Byte
+const OPAD_BYTE: u8 = 0x5c;
+/// Hash Length in Bytes
+const HASH_BYTES: usize = 48;
+
 pub struct HASH384 {
     length: [u64; 2],
     h: [u64; 8],
@@ -132,27 +141,27 @@ impl HASH384 {
     }
 
     fn sig0(x: u64) -> u64 {
-        return HASH384::s(28, x) ^ HASH384::s(34, x) ^ HASH384::s(39, x);
+        return Self::s(28, x) ^ Self::s(34, x) ^ Self::s(39, x);
     }
 
     fn sig1(x: u64) -> u64 {
-        return HASH384::s(14, x) ^ HASH384::s(18, x) ^ HASH384::s(41, x);
+        return Self::s(14, x) ^ Self::s(18, x) ^ Self::s(41, x);
     }
 
     fn theta0(x: u64) -> u64 {
-        return HASH384::s(1, x) ^ HASH384::s(8, x) ^ HASH384::r(7, x);
+        return Self::s(1, x) ^ Self::s(8, x) ^ Self::r(7, x);
     }
 
     fn theta1(x: u64) -> u64 {
-        return HASH384::s(19, x) ^ HASH384::s(61, x) ^ HASH384::r(6, x);
+        return Self::s(19, x) ^ Self::s(61, x) ^ Self::r(6, x);
     }
 
     fn transform(&mut self) {
-        /* basic transformation step */
+        // basic transformation step
         for j in 16..80 {
-            self.w[j] = HASH384::theta1(self.w[j - 2])
+            self.w[j] = Self::theta1(self.w[j - 2])
                 .wrapping_add(self.w[j - 7])
-                .wrapping_add(HASH384::theta0(self.w[j - 15]))
+                .wrapping_add(Self::theta0(self.w[j - 15]))
                 .wrapping_add(self.w[j - 16]);
         }
         let mut a = self.h[0];
@@ -166,11 +175,11 @@ impl HASH384 {
         for j in 0..80 {
             /* 64 times - mush it up */
             let t1 = hh
-                .wrapping_add(HASH384::sig1(e))
-                .wrapping_add(HASH384::ch(e, f, g))
+                .wrapping_add(Self::sig1(e))
+                .wrapping_add(Self::ch(e, f, g))
                 .wrapping_add(HASH384_K[j])
                 .wrapping_add(self.w[j]);
-            let t2 = HASH384::sig0(a).wrapping_add(HASH384::maj(a, b, c));
+            let t2 = Self::sig0(a).wrapping_add(Self::maj(a, b, c));
             hh = g;
             g = f;
             f = e;
@@ -190,9 +199,9 @@ impl HASH384 {
         self.h[7] = self.h[7].wrapping_add(hh);
     }
 
-    /* Initialise Hash function */
+    /// Initialise Hash function
     pub fn init(&mut self) {
-        /* initialise */
+        // initialise
         for i in 0..64 {
             self.w[i] = 0
         }
@@ -208,8 +217,8 @@ impl HASH384 {
         self.h[7] = HASH384_H7;
     }
 
-    pub fn new() -> HASH384 {
-        let mut nh = HASH384 {
+    pub fn new() -> Self {
+        let mut nh = Self {
             length: [0; 2],
             h: [0; 8],
             w: [0; 80],
@@ -218,7 +227,7 @@ impl HASH384 {
         return nh;
     }
 
-    /* process a single byte */
+    /// Process a single byte
     pub fn process(&mut self, byt: u8) {
         /* process the next message byte */
         let cnt = ((self.length[0] / 64) % 16) as usize;
@@ -234,15 +243,14 @@ impl HASH384 {
         }
     }
 
-    /* process an array of bytes */
-
+    /// Process an array of bytes
     pub fn process_array(&mut self, b: &[u8]) {
         for i in 0..b.len() {
             self.process(b[i])
         }
     }
 
-    /* process a 32-bit integer */
+    /// Process a 32-bit integer
     pub fn process_num(&mut self, n: i32) {
         self.process(((n >> 24) & 0xff) as u8);
         self.process(((n >> 16) & 0xff) as u8);
@@ -250,10 +258,10 @@ impl HASH384 {
         self.process((n & 0xff) as u8);
     }
 
-    /* Generate 48-byte Hash */
-    pub fn hash(&mut self) -> [u8; 48] {
+    /// Generate 48-byte Hash
+    pub fn hash(&mut self) -> [u8; HASH_BYTES] {
         /* pad message and finish - supply digest */
-        let mut digest: [u8; 48] = [0; 48];
+        let mut digest: [u8; 48] = [0; HASH_BYTES];
         let len0 = self.length[0];
         let len1 = self.length[1];
         self.process(0x80);
@@ -263,26 +271,178 @@ impl HASH384 {
         self.w[14] = len1;
         self.w[15] = len0;
         self.transform();
-        for i in 0..48 {
-            /* convert to bytes */
+        for i in 0..HASH_BYTES {
+            // convert to bytes
             digest[i] = ((self.h[i / 8] >> (8 * (7 - i % 8))) & 0xff) as u8;
         }
         self.init();
         return digest;
     }
+
+    /// Generate a HMAC
+    ///
+    /// https://tools.ietf.org/html/rfc2104
+    pub fn hmac(key: &[u8], text: &[u8]) -> [u8; HASH_BYTES] {
+        let mut k = key.to_vec();
+
+        // Verify length of key < BLOCK_SIZE
+        if k.len() > BLOCK_SIZE {
+            // Reduce key to 64 bytes by hashing
+            let mut hash384 = Self::new();
+            hash384.init();
+            hash384.process_array(&k);
+            k = hash384.hash().to_vec();
+        }
+
+        // Prepare inner and outer paddings
+        // inner = (ipad XOR k)
+        // outer = (opad XOR k)
+        let mut inner = vec![IPAD_BYTE; BLOCK_SIZE];
+        let mut outer = vec![OPAD_BYTE; BLOCK_SIZE];
+        for (i, byte) in k.iter().enumerate() {
+            inner[i] = inner[i] ^ byte;
+            outer[i] = outer[i] ^ byte;
+        }
+
+        // Concatenate inner with text = (ipad XOR k || text)
+        inner.extend_from_slice(text);
+
+        // hash inner = H(ipad XOR k || text)
+        let mut hash384 = Self::new();
+        hash384.init();
+        hash384.process_array(&inner);
+        let inner = hash384.hash();
+
+        // Concatenate outer with hash of inner = (opad XOR k) || H(ipad XOR k || text)
+        outer.extend_from_slice(&inner);
+
+        // Final hash = H((opad XOR k) || H(ipad XOR k || text))
+        let mut hash384 = Self::new();
+        hash384.init();
+        hash384.process_array(&outer);
+        hash384.hash()
+    }
+
+    /// HKDF-Extract
+    ///
+    /// https://tools.ietf.org/html/rfc5869
+    pub fn hkdf_extract(salt: &[u8], ikm: &[u8]) -> [u8; HASH_BYTES] {
+        Self::hmac(salt, ikm)
+    }
+
+    /// HKDF-Extend
+    ///
+    /// https://tools.ietf.org/html/rfc5869
+    pub fn hkdf_extend(prk: &[u8], info: &[u8], l: u8) -> Vec<u8> {
+        // n = cieling(l / 48)
+        let mut n = l / (HASH_BYTES as u8);
+        if n * (HASH_BYTES as u8) < l {
+            n += 1;
+        }
+
+        let mut okm: Vec<u8> = vec![];
+        let mut previous = vec![]; // T(0) = []
+
+        for i in 0..n as usize {
+            // Concatenate (T(i) || info || i)
+            let mut text: Vec<u8> = previous;
+            text.extend_from_slice(info);
+            text.push((i + 1) as u8); // Note: i <= 254
+
+            // T(i+1) = HMAC(PRK, T(i) || info || i)
+            previous = Self::hmac(prk, &text).to_vec();
+            okm.extend_from_slice(&previous);
+        }
+
+        // Reduce length to size L
+        okm.resize(l as usize, 0);
+        okm
+    }
 }
 
-//09330c33f71147e8 3d192fc782cd1b47 53111b173b3b05d2 2fa08086e3b0f712 fcc7c71a557e2db9 66c3e9fa91746039
-/*
-fn main() {
-    let s = String::from("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
-    let test = s.into_bytes();
-    let mut sh=HASH384::new();
+#[cfg(test)]
+mod tests {
+    // TODO: Test HKDF
+    use super::*;
+
+    #[test]
+    fn test_hash384_simple() {
+        let text = [0x01];
+        let mut hash384 = HASH384::new();
+        hash384.init();
+        hash384.process_array(&text);
+        let output = hash384.hash().to_vec();
 
-    for i in 0..test.len(){
-        sh.process(test[i]);
+        let expected =
+            hex::decode("8d2ce87d86f55fcfab770a047b090da23270fa206832dfea7e0c946fff451f819add242374be551b0d6318ed6c7d41d8")
+                .unwrap();
+
+        assert_eq!(expected, output);
     }
 
-    let digest=sh.hash();
-    for i in 0..48 {print!("{:02x}",digest[i])}
-} */
+    #[test]
+    fn test_hash384_empty() {
+        let text = [];
+        let mut hash384 = HASH384::new();
+        hash384.init();
+        hash384.process_array(&text);
+        let output = hash384.hash().to_vec();
+
+        let expected =
+            hex::decode("38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")
+                .unwrap();
+
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hash384_long() {
+        let text = hex::decode("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e01").unwrap();
+        let mut hash384 = HASH384::new();
+        hash384.init();
+        hash384.process_array(&text);
+        let output = hash384.hash().to_vec();
+
+        let expected =
+            hex::decode("1793c4989b4e68154c7159bee9756e5b72dbc0bd57c7583bb09c9a1c111f46fcaf8ef9faf1715e1eff36526c6c15a1f1")
+                .unwrap();
+
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hmac_simple() {
+        let text = [0x01];
+        let key = [0x01];
+        let expected =
+            hex::decode("52650d924c6c3ed9f7b0fc64107e139d0d9254e8ecfb32e5780535897532ccee5272d61ec5d2abd19fa60e9f69f8711d")
+                .unwrap();
+
+        let output = HASH384::hmac(&key, &text).to_vec();
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hmac_empty() {
+        let text = [];
+        let key = [];
+        let expected =
+            hex::decode("6c1f2ee938fad2e24bd91298474382ca218c75db3d83e114b3d4367776d14d3551289e75e8209cd4b792302840234adc")
+                .unwrap();
+
+        let output = HASH384::hmac(&key, &text).to_vec();
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hmac_long() {
+        let text = hex::decode("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e01").unwrap();
+        let key = [0x01];
+        let expected =
+            hex::decode("dee07cba20bcf23f3913c6a885ac08b90702e2c5765f64040b336375c5ad35cce89e9c9f62983be516447e35e65de70c")
+                .unwrap();
+
+        let output = HASH384::hmac(&key, &text).to_vec();
+        assert_eq!(expected, output);
+    }
+}
diff --git a/src/hash512.rs b/src/hash512.rs
index 4eb0689..34970a7 100644
--- a/src/hash512.rs
+++ b/src/hash512.rs
@@ -109,6 +109,15 @@ const HASH512_K: [u64; 80] = [
     0x6c44198c4a475817,
 ];
 
+/// The block size of each round.
+const BLOCK_SIZE: usize = 128;
+/// Ipad Byte
+const IPAD_BYTE: u8 = 0x36;
+/// Opad Byte
+const OPAD_BYTE: u8 = 0x5c;
+/// Hash Length in Bytes
+const HASH_BYTES: usize = 64;
+
 pub struct HASH512 {
     length: [u64; 2],
     h: [u64; 8],
@@ -132,27 +141,27 @@ impl HASH512 {
     }
 
     fn sig0(x: u64) -> u64 {
-        return HASH512::s(28, x) ^ HASH512::s(34, x) ^ HASH512::s(39, x);
+        return Self::s(28, x) ^ Self::s(34, x) ^ Self::s(39, x);
     }
 
     fn sig1(x: u64) -> u64 {
-        return HASH512::s(14, x) ^ HASH512::s(18, x) ^ HASH512::s(41, x);
+        return Self::s(14, x) ^ Self::s(18, x) ^ Self::s(41, x);
     }
 
     fn theta0(x: u64) -> u64 {
-        return HASH512::s(1, x) ^ HASH512::s(8, x) ^ HASH512::r(7, x);
+        return Self::s(1, x) ^ Self::s(8, x) ^ Self::r(7, x);
     }
 
     fn theta1(x: u64) -> u64 {
-        return HASH512::s(19, x) ^ HASH512::s(61, x) ^ HASH512::r(6, x);
+        return Self::s(19, x) ^ Self::s(61, x) ^ Self::r(6, x);
     }
 
     fn transform(&mut self) {
         /* basic transformation step */
         for j in 16..80 {
-            self.w[j] = HASH512::theta1(self.w[j - 2])
+            self.w[j] = Self::theta1(self.w[j - 2])
                 .wrapping_add(self.w[j - 7])
-                .wrapping_add(HASH512::theta0(self.w[j - 15]))
+                .wrapping_add(Self::theta0(self.w[j - 15]))
                 .wrapping_add(self.w[j - 16]);
         }
         let mut a = self.h[0];
@@ -166,11 +175,11 @@ impl HASH512 {
         for j in 0..80 {
             /* 64 times - mush it up */
             let t1 = hh
-                .wrapping_add(HASH512::sig1(e))
-                .wrapping_add(HASH512::ch(e, f, g))
+                .wrapping_add(Self::sig1(e))
+                .wrapping_add(Self::ch(e, f, g))
                 .wrapping_add(HASH512_K[j])
                 .wrapping_add(self.w[j]);
-            let t2 = HASH512::sig0(a).wrapping_add(HASH512::maj(a, b, c));
+            let t2 = Self::sig0(a).wrapping_add(Self::maj(a, b, c));
             hh = g;
             g = f;
             f = e;
@@ -208,8 +217,8 @@ impl HASH512 {
         self.h[7] = HASH512_H7;
     }
 
-    pub fn new() -> HASH512 {
-        let mut nh = HASH512 {
+    pub fn new() -> Self {
+        let mut nh = Self {
             length: [0; 2],
             h: [0; 8],
             w: [0; 80],
@@ -270,19 +279,262 @@ impl HASH512 {
         self.init();
         return digest;
     }
+
+    /// Generate a HMAC
+    ///
+    /// https://tools.ietf.org/html/rfc2104
+    pub fn hmac(key: &[u8], text: &[u8]) -> [u8; 64] {
+        let mut k = key.to_vec();
+
+        // Verify length of key < BLOCK_SIZE
+        if k.len() > BLOCK_SIZE {
+            // Reduce key to 64 bytes by hashing
+            let mut hash512 = Self::new();
+            hash512.init();
+            hash512.process_array(&k);
+            k = hash512.hash().to_vec();
+        }
+
+        // Prepare inner and outer paddings
+        // inner = (ipad XOR k)
+        // outer = (opad XOR k)
+        let mut inner = vec![IPAD_BYTE; BLOCK_SIZE];
+        let mut outer = vec![OPAD_BYTE; BLOCK_SIZE];
+        for (i, byte) in k.iter().enumerate() {
+            inner[i] = inner[i] ^ byte;
+            outer[i] = outer[i] ^ byte;
+        }
+
+        // Concatenate inner with text = (ipad XOR k || text)
+        inner.extend_from_slice(text);
+
+        // hash inner = H(ipad XOR k || text)
+        let mut hash512 = Self::new();
+        hash512.init();
+        hash512.process_array(&inner);
+        let inner = hash512.hash();
+
+        // Concatenate outer with hash of inner = (opad XOR k) || H(ipad XOR k || text)
+        outer.extend_from_slice(&inner);
+
+        // Final hash = H((opad XOR k) || H(ipad XOR k || text))
+        let mut hash512 = Self::new();
+        hash512.init();
+        hash512.process_array(&outer);
+        hash512.hash()
+    }
+
+    /// HKDF-Extract
+    ///
+    /// https://tools.ietf.org/html/rfc5869
+    pub fn hkdf_extract(salt: &[u8], ikm: &[u8]) -> [u8; HASH_BYTES] {
+        Self::hmac(salt, ikm)
+    }
+
+    /// HKDF-Extend
+    ///
+    /// https://tools.ietf.org/html/rfc5869
+    pub fn hkdf_extend(prk: &[u8], info: &[u8], l: u8) -> Vec<u8> {
+        // n = cieling(l / 64)
+        let mut n = l / (HASH_BYTES as u8);
+        if n * (HASH_BYTES as u8) < l {
+            n += 1;
+        }
+
+        let mut okm: Vec<u8> = vec![];
+        let mut previous = vec![]; // T(0) = []
+
+        for i in 0..n as usize {
+            // Concatenate (T(i) || info || i)
+            let mut text: Vec<u8> = previous;
+            text.extend_from_slice(info);
+            text.push((i + 1) as u8); // Note: i <= 254
+
+            // T(i+1) = HMAC(PRK, T(i) || info || i)
+            previous = Self::hmac(prk, &text).to_vec();
+            okm.extend_from_slice(&previous);
+        }
+
+        // Reduce length to size L
+        okm.resize(l as usize, 0);
+        okm
+    }
 }
 
-//8e959b75dae313da 8cf4f72814fc143f 8f7779c6eb9f7fa1 7299aeadb6889018 501d289e4900f7e4 331b99dec4b5433a c7d329eeb6dd2654 5e96e55b874be909
-/*
-fn main() {
-    let s = String::from("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
-    let test = s.into_bytes();
-    let mut sh=HASH512::new();
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_hash512_simple() {
+        let text = [0x01];
+        let mut hash512 = HASH512::new();
+        hash512.init();
+        hash512.process_array(&text);
+        let output = hash512.hash().to_vec();
+
+        let expected =
+            hex::decode("7b54b66836c1fbdd13d2441d9e1434dc62ca677fb68f5fe66a464baadecdbd00576f8d6b5ac3bcc80844b7d50b1cc6603444bbe7cfcf8fc0aa1ee3c636d9e339")
+                .unwrap();
 
-    for i in 0..test.len(){
-        sh.process(test[i]);
+        assert_eq!(expected, output);
     }
 
-    let digest=sh.hash();
-    for i in 0..64 {print!("{:02x}",digest[i])}
-} */
+    #[test]
+    fn test_hash512_empty() {
+        let text = [];
+        let mut hash512 = HASH512::new();
+        hash512.init();
+        hash512.process_array(&text);
+        let output = hash512.hash().to_vec();
+
+        let expected =
+            hex::decode("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")
+                .unwrap();
+
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hash512_long() {
+        let text = hex::decode("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e01").unwrap();
+        let mut hash512 = HASH512::new();
+        hash512.init();
+        hash512.process_array(&text);
+        let output = hash512.hash().to_vec();
+
+        let expected =
+            hex::decode("ca3088651246c66ac9c7a8afd727539ab2d8ce9234b5e1fec311e1e435d6d9eb152e41e8e9ad953dd737d0271ad2b0299cbd6f4eb9536de34c3a01411766c7be")
+                .unwrap();
+
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hmac_simple() {
+        let text = [0x01];
+        let key = [0x01];
+        let expected =
+            hex::decode("503deb5732606d9595e308c8893fe56923fe470fc57021cf252dacb0ad15de020943e139d7a84e77956d34df3cc78142c090b959049a813cb19627c5b49c5761")
+                .unwrap();
+
+        let output = HASH512::hmac(&key, &text).to_vec();
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hmac_empty() {
+        let text = [];
+        let key = [];
+        let expected =
+            hex::decode("b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d47")
+                .unwrap();
+
+        let output = HASH512::hmac(&key, &text).to_vec();
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hmac_long() {
+        let text = hex::decode("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e01").unwrap();
+        let key = [0x01];
+        let expected =
+            hex::decode("d4a8d1b936eb79e6f56b85306e62dea59a54e81690a616e804eaefe2b1e0d7319eecd68494913b3a7e78755a0e1716bb0f0f3b60a810c65f61a909562811d372")
+                .unwrap();
+
+        let output = HASH512::hmac(&key, &text).to_vec();
+        assert_eq!(expected, output);
+    }
+
+    #[test]
+    fn test_hkdf_case_a() {
+        // From https://www.kullo.net/blog/hkdf-sha-512-test-vectors/
+        let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
+        let salt = hex::decode("000102030405060708090a0b0c").unwrap();
+        let expected_prk =
+            hex::decode("665799823737ded04a88e47e54a5890bb2c3d247c7a4254a8e61350723590a26c36238127d8661b88cf80ef802d57e2f7cebcf1e00e083848be19929c61b4237")
+            .unwrap();
+
+        let output_prk = HASH512::hkdf_extract(&salt, &ikm).to_vec();
+        assert_eq!(expected_prk, output_prk);
+
+        let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap();
+        let l = 42;
+        let expected_okm = hex::decode(
+            "832390086cda71fb47625bb5ceb168e4c8e26a1a16ed34d9fc7fe92c1481579338da362cb8d9f925d7cb",
+        )
+        .unwrap();
+
+        let output_okm = HASH512::hkdf_extend(&expected_prk, &info, l);
+        assert_eq!(expected_okm, output_okm);
+    }
+
+    #[test]
+    fn test_hkdf_case_b() {
+        // From https://www.kullo.net/blog/hkdf-sha-512-test-vectors/
+        let ikm = hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f").unwrap();
+        let salt = hex::decode("606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf").unwrap();
+        let expected_prk =
+            hex::decode("35672542907d4e142c00e84499e74e1de08be86535f924e022804ad775dde27ec86cd1e5b7d178c74489bdbeb30712beb82d4f97416c5a94ea81ebdf3e629e4a")
+            .unwrap();
+
+        let output_prk = HASH512::hkdf_extract(&salt, &ikm).to_vec();
+        assert_eq!(expected_prk, output_prk);
+
+        let info = hex::decode("b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff").unwrap();
+        let l = 82;
+        let expected_okm = hex::decode(
+                "ce6c97192805b346e6161e821ed165673b84f400a2b514b2fe23d84cd189ddf1b695b48cbd1c8388441137b3ce28f16aa64ba33ba466b24df6cfcb021ecff235f6a2056ce3af1de44d572097a8505d9e7a93",
+            )
+            .unwrap();
+
+        let output_okm = HASH512::hkdf_extend(&expected_prk, &info, l);
+        assert_eq!(expected_okm, output_okm);
+    }
+
+    #[test]
+    fn test_hkdf_case_c() {
+        // From https://www.kullo.net/blog/hkdf-sha-512-test-vectors/
+        let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
+        let salt = vec![];
+        let expected_prk =
+            hex::decode("fd200c4987ac491313bd4a2a13287121247239e11c9ef82802044b66ef357e5b194498d0682611382348572a7b1611de54764094286320578a863f36562b0df6")
+            .unwrap();
+
+        let output_prk = HASH512::hkdf_extract(&salt, &ikm).to_vec();
+        assert_eq!(expected_prk, output_prk);
+
+        let info = vec![];
+        let l = 42;
+        let expected_okm = hex::decode(
+            "f5fa02b18298a72a8c23898a8703472c6eb179dc204c03425c970e3b164bf90fff22d04836d0e2343bac",
+        )
+        .unwrap();
+
+        let output_okm = HASH512::hkdf_extend(&expected_prk, &info, l);
+        assert_eq!(expected_okm, output_okm);
+    }
+
+    #[test]
+    fn test_hkdf_case_d() {
+        // From https://www.kullo.net/blog/hkdf-sha-512-test-vectors/
+        let ikm = hex::decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c").unwrap();
+        let salt = vec![];
+        let expected_prk =
+            hex::decode("5346b376bf3aa9f84f8f6ed5b1c4f489172e244dac303d12f68ecc766ea600aa88495e7fb605803122fa136924a840b1f0719d2d5f68e29b242299d758ed680c")
+            .unwrap();
+
+        let output_prk = HASH512::hkdf_extract(&salt, &ikm).to_vec();
+        assert_eq!(expected_prk, output_prk);
+
+        let info = vec![];
+        let l = 42;
+        let expected_okm = hex::decode(
+            "1407d46013d98bc6decefcfee55f0f90b0c7f63d68eb1a80eaf07e953cfc0a3a5240a155d6e4daa965bb",
+        )
+        .unwrap();
+
+        let output_okm = HASH512::hkdf_extend(&expected_prk, &info, l);
+        assert_eq!(expected_okm, output_okm);
+    }
+}
diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs
new file mode 100644
index 0000000..fe8d962
--- /dev/null
+++ b/src/hash_to_curve.rs
@@ -0,0 +1,307 @@
+use super::big::Big;
+use super::dbig::DBig;
+use super::fp::FP;
+use super::fp2::FP2;
+use super::rom::{
+    HASH_ALGORITHM, HASH_TYPE, L, MODULUS, SSWU_A1, SSWU_A2, SSWU_B1, SSWU_B2, SSWU_Z1, SSWU_Z2,
+    Z_PAD,
+};
+
+use errors::AmclError;
+use hash256::HASH256;
+use hash384::HASH384;
+use hash512::HASH512;
+
+/// Oversized DST padding
+pub const OVERSIZED_DST: &[u8] = b"H2C-OVERSIZE-DST-";
+
+#[derive(Copy, Clone)]
+pub enum HashAlgorithm {
+    Sha256,
+    Sha384,
+    Sha512,
+}
+
+/// Hash a message
+pub fn hash(msg: &[u8], hash_function: HashAlgorithm) -> Vec<u8> {
+    match hash_function {
+        HashAlgorithm::Sha256 => {
+            let mut hash = HASH256::new();
+            hash.init();
+            hash.process_array(msg);
+            hash.hash().to_vec()
+        }
+        HashAlgorithm::Sha384 => {
+            let mut hash = HASH384::new();
+            hash.init();
+            hash.process_array(msg);
+            hash.hash().to_vec()
+        }
+        HashAlgorithm::Sha512 => {
+            let mut hash = HASH512::new();
+            hash.init();
+            hash.process_array(msg);
+            hash.hash().to_vec()
+        }
+    }
+}
+
+// Hash To Field - Fp
+//
+// Take a message as bytes and convert it to a Field Point
+// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.2
+pub fn hash_to_field_fp(msg: &[u8], count: usize, dst: &[u8]) -> Result<Vec<FP>, AmclError> {
+    let m = 1;
+    let p = Big::new_ints(&MODULUS);
+
+    let len_in_bytes = count * m * L;
+    let pseudo_random_bytes = expand_message_xmd(msg, len_in_bytes, dst, HASH_ALGORITHM)?;
+
+    let mut u: Vec<FP> = Vec::with_capacity(count as usize);
+    for i in 0..count as usize {
+        let elm_offset = L as usize * i * m as usize;
+        let mut dbig = DBig::frombytes(&pseudo_random_bytes[elm_offset..elm_offset + L as usize]);
+        let e: Big = dbig.dmod(&p);
+        u.push(FP::new_big(&e));
+    }
+    Ok(u)
+}
+
+// Hash To Field - Fp2
+//
+// Take a message as bytes and convert it to a vector of Field Points with extension degree 2.
+// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.2
+pub fn hash_to_field_fp2(msg: &[u8], count: usize, dst: &[u8]) -> Result<Vec<FP2>, AmclError> {
+    let m = 2;
+    let p = Big::new_ints(&MODULUS);
+
+    let len_in_bytes = count * m * L;
+
+    let pseudo_random_bytes = expand_message_xmd(msg, len_in_bytes, dst, HASH_ALGORITHM)?;
+
+    let mut u: Vec<FP2> = Vec::with_capacity(count as usize);
+    for i in 0..count as usize {
+        let mut e: Vec<Big> = Vec::with_capacity(m as usize);
+        for j in 0..m as usize {
+            let elm_offset = L as usize * (j + i * m as usize);
+            let mut big =
+                DBig::frombytes(&pseudo_random_bytes[elm_offset..elm_offset + L as usize]);
+            e.push(big.dmod(&p));
+        }
+        u.push(FP2::new_bigs(&e[0], &e[1]));
+    }
+    Ok(u)
+}
+
+// Expand Message XMD
+//
+// Take a message and convert it to pseudo random bytes of specified length
+// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.3.1
+pub fn expand_message_xmd(
+    msg: &[u8],
+    len_in_bytes: usize,
+    dst: &[u8],
+    hash_algorithm: HashAlgorithm,
+) -> Result<Vec<u8>, AmclError> {
+    // ell = ceiling(len_in_bytes / b_in_bytes)
+    let ell = (len_in_bytes + HASH_TYPE - 1) / HASH_TYPE;
+
+    // Error if length of output less than 255 bytes
+    if ell >= 255 {
+        return Err(AmclError::HashToFieldError);
+    }
+
+    // Create DST prime as (dst.len() || dst)
+    let dst_prime = if dst.len() > 256 {
+        // DST too long, shorten to H("H2C-OVERSIZE-DST-" || dst)
+        let mut tmp = OVERSIZED_DST.to_vec();
+        tmp.extend_from_slice(dst);
+        let mut prime = vec![32u8; 1];
+        prime.append(&mut hash(&tmp, hash_algorithm));
+        prime
+    } else {
+        // DST correct size, prepend length as a single byte
+        let mut prime = vec![dst.len() as u8; 1];
+        prime.extend_from_slice(dst);
+        prime
+    };
+
+    let mut pseudo_random_bytes: Vec<u8> = vec![];
+    let mut b: Vec<Vec<u8>> = vec![vec![]; 2];
+
+    // Set b[0] to H(Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime)
+    let mut tmp = Z_PAD.to_vec();
+    tmp.extend_from_slice(msg);
+    let l_i_b_str: &[u8] = &(len_in_bytes as u16).to_be_bytes();
+    tmp.extend_from_slice(l_i_b_str);
+    tmp.push(0u8);
+    tmp.extend_from_slice(&dst_prime);
+    b[0] = hash(&tmp, hash_algorithm);
+
+    // Set b[1] to H(b_0 || I2OSP(1, 1) || DST_prime)
+    tmp = b[0].clone();
+    tmp.push(1u8);
+    tmp.extend_from_slice(&dst_prime);
+    b[1] = hash(&tmp, hash_algorithm);
+
+    pseudo_random_bytes.extend_from_slice(&b[1]);
+
+    for i in 2..=ell {
+        // Set b[i] to H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
+        tmp = b[0]
+            .iter()
+            .enumerate()
+            .map(|(j, b_0)| {
+                // Perform strxor(b[0], b[i-1])
+                b_0 ^ b[i - 1][j] // b[i].len() will all be 32 bytes as they are SHA256 output.
+            })
+            .collect();
+        tmp.push(i as u8); // i < 256
+        tmp.extend_from_slice(&dst_prime);
+        b.push(hash(&tmp, hash_algorithm));
+
+        pseudo_random_bytes.extend_from_slice(&b[i]);
+    }
+
+    // Take required length
+    Ok(pseudo_random_bytes[..len_in_bytes as usize].to_vec())
+}
+
+// Simplified Shallue-van de Woestijne-Ulas Method - Fp
+//
+// Returns projectives as (XZ, YZ, Z)
+// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-6.6.2
+pub fn simplified_swu_fp(u: FP) -> (FP, FP) {
+    // tmp1 = Z * u^2
+    // tv1 = 1 / (Z^2 * u^4 + Z * u^2)
+    let mut tmp1 = u.clone();
+    let is_neg_u = tmp1.is_neg();
+    tmp1.sqr();
+    tmp1.mul(&SSWU_Z1);
+    let mut tv1 = tmp1.clone();
+    tv1.sqr();
+    tv1.add(&tmp1);
+    tv1.inverse();
+
+    // x = (-B / A) * (1 + tv1)
+    let mut x = tv1.clone();
+    x.add(&FP::new_int(1));
+    x.mul(&SSWU_B1); // b * (Z^2 * u^4 + Z * u^2 + 1)
+    x.neg();
+    let mut a_inverse = SSWU_A1.clone();
+    a_inverse.inverse();
+    x.mul(&a_inverse);
+
+    // Deal with case where Z^2 * u^4 + Z * u^2 == 0
+    if tv1.iszilch() {
+        // x = B / (Z * A)
+        x = SSWU_Z1.clone();
+        x.inverse();
+        x.mul(&SSWU_B1);
+        x.mul(&a_inverse);
+    }
+
+    // gx = x^3 + A * x + B
+    let mut gx = x.clone();
+    gx.sqr();
+    gx.add(&SSWU_A1);
+    gx.mul(&x);
+    gx.add(&SSWU_B1);
+
+    // y = sqrt(gx)
+    let mut y = gx.clone();
+    let mut y = y.sqrt();
+
+    // Check y is valid square root
+    let mut y2 = y.clone();
+    y2.sqr();
+    if !gx.equals(&y2) {
+        // x = x * Z^2 * u
+        x.mul(&tmp1);
+
+        // gx = x^3 + A * x + B
+        let mut gx = x.clone();
+        gx.sqr();
+        gx.add(&SSWU_A1);
+        gx.mul(&x);
+        gx.add(&SSWU_B1);
+
+        y = gx.sqrt();
+        y2 = y.clone();
+        y2.sqr();
+        assert_eq!(gx, y2, "Hash to Curve SSWU failure - no square roots");
+    }
+
+    // Negate y if y and t are opposite in sign
+    if is_neg_u != y.is_neg() {
+        y.neg();
+    }
+
+    (x, y)
+}
+
+// Simplified Shallue-van de Woestijne-Ulas Method - Fp2
+//
+// Returns projectives as (X, Y)
+// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-6.6.2
+pub fn simplified_swu_fp2(u: FP2) -> (FP2, FP2) {
+    // tmp1 = Z * u^2
+    // tv1 = 1 / (Z^2 * u^4 + Z * u^2)
+    let mut tmp1 = u.clone();
+    let is_neg_u = tmp1.is_neg();
+    tmp1.sqr();
+    tmp1.mul(&SSWU_Z2);
+    let mut tv1 = tmp1.clone();
+    tv1.sqr();
+    tv1.add(&tmp1);
+    tv1.inverse();
+
+    // x = (-B / A) * (1 + tv1)
+    let mut x = tv1.clone();
+    x.add(&FP2::new_ints(1, 0));
+    x.mul(&SSWU_B2); // b * (Z^2 * u^4 + Z * u^2 + 1)
+    x.neg();
+    let mut a_inverse = SSWU_A2.clone();
+    a_inverse.inverse();
+    x.mul(&a_inverse);
+
+    // Deal with case where Z^2 * u^4 + Z * u^2 == 0
+    if tv1.iszilch() {
+        // x = B / (Z * A)
+        x = SSWU_Z2.clone();
+        x.inverse();
+        x.mul(&SSWU_B2);
+        x.mul(&a_inverse);
+    }
+
+    // gx = x^3 + A * x + B
+    let mut gx = x.clone();
+    gx.sqr();
+    gx.add(&SSWU_A2);
+    gx.mul(&x);
+    gx.add(&SSWU_B2);
+
+    // y = sqrt(gx)
+    let mut y = gx.clone();
+    if !y.sqrt() {
+        // x = x * Z^2 * u
+        x.mul(&tmp1);
+
+        // gx = x^3 + A * x + B
+        let mut gx = x.clone();
+        gx.sqr();
+        gx.add(&SSWU_A2);
+        gx.mul(&x);
+        gx.add(&SSWU_B2);
+
+        y = gx;
+        assert!(y.sqrt(), "Hash to Curve SSWU failure - no square roots");
+    }
+
+    // Negate y if y and t are opposite in sign
+    if is_neg_u != y.is_neg() {
+        y.neg();
+    }
+
+    (x, y)
+}
diff --git a/src/lib.rs b/src/lib.rs
index 9849b2b..6575763 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,12 @@
+#[macro_use]
+extern crate lazy_static;
+
+#[cfg(test)]
+extern crate serde_json;
+#[cfg(test)]
+#[macro_use]
+extern crate serde_derive;
+
 pub mod aes;
 #[cfg(target_pointer_width = "32")]
 #[path = "arch/arch32.rs"]
@@ -5,6 +14,7 @@ pub mod arch;
 #[cfg(target_pointer_width = "64")]
 #[path = "arch/arch64.rs"]
 pub mod arch;
+pub mod errors;
 pub mod gcm;
 pub mod hash256;
 pub mod hash384;
@@ -85,18 +95,42 @@ pub mod bls383 {
     pub mod pair;
 }
 
-#[cfg(feature = "bls381")]
+#[cfg(feature = "bls381g1")]
 #[path = "./"]
-pub mod bls381 {
+pub mod bls381g1 {
     #[cfg(target_pointer_width = "32")]
-    #[path = "roms/rom_bls381_32.rs"]
+    #[path = "roms/rom_bls381g1_32.rs"]
     pub mod rom;
     #[cfg(target_pointer_width = "64")]
-    #[path = "roms/rom_bls381_64.rs"]
+    #[path = "roms/rom_bls381g1_64.rs"]
     pub mod rom;
 
     pub mod big;
-    pub mod bls;
+    pub mod bls381;
+    pub mod dbig;
+    pub mod ecp;
+    pub mod ecp2;
+    pub mod fp;
+    pub mod fp12;
+    pub mod fp2;
+    pub mod fp4;
+    pub mod hash_to_curve;
+    pub mod mpin;
+    pub mod pair;
+}
+
+#[cfg(feature = "bls381g2")]
+#[path = "./"]
+pub mod bls381g2 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_bls381g2_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_bls381g2_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod bls381;
     pub mod dbig;
     pub mod ecp;
     pub mod ecp2;
@@ -104,6 +138,7 @@ pub mod bls381 {
     pub mod fp12;
     pub mod fp2;
     pub mod fp4;
+    pub mod hash_to_curve;
     pub mod mpin;
     pub mod pair;
 }
diff --git a/src/nhs.rs b/src/nhs.rs
index 9a7f856..338582e 100644
--- a/src/nhs.rs
+++ b/src/nhs.rs
@@ -751,5 +751,4 @@ mod tests {
         }
         println!("");
     }
-
 }
diff --git a/src/roms/rom_bls381_32.rs b/src/roms/rom_bls381g1_32.rs
similarity index 82%
copy from src/roms/rom_bls381_32.rs
copy to src/roms/rom_bls381g1_32.rs
index e282e9d..f72806d 100644
--- a/src/roms/rom_bls381_32.rs
+++ b/src/roms/rom_bls381g1_32.rs
@@ -18,7 +18,10 @@ under the License.
 */
 
 use super::super::arch::Chunk;
-use bls381::big::NLEN;
+use super::fp::FP;
+use super::fp2::FP2;
+use super::hash_to_curve::HashAlgorithm;
+use bls381g2::big::{Big, NLEN};
 use types::{CurvePairingType, CurveType, ModType, SexticTwist, SignOfX};
 
 // Base Bits= 29
@@ -205,5 +208,34 @@ pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::Bls;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::MType;
 pub const ATE_BITS: usize = 65;
 pub const SIGN_OF_X: SignOfX = SignOfX::NegativeX;
-pub const HASH_TYPE: usize = 32;
+pub const HASH_ALGORITHM: HashAlgorithm = HashAlgorithm::Sha256; // Hash algorithm for hash to curve
+pub const HASH_TYPE: usize = 32; // Output size of hash algorithm
 pub const AESKEY: usize = 16;
+
+/// Signatures on G1: true, Signatures on G2: false
+pub const BLS_SIG_G1: bool = true;
+
+// BLS Standard Constants
+/// L = ceil(ceil(log2(Q) + 128) / 8)
+pub const L: usize = 64;
+/// Hash to Curve Suite
+pub const H2C_SUITE: &str = "BLS12381G1_XMD:SHA-256_SSWU_RO_";
+/// Domain Separation Tag
+pub const DST: &[u8] = b"BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_POP_";
+/// Z_PAD is a vector of zeros of length equal to the hash block size (64).
+pub const Z_PAD: [u8; 64] = [0u8; 64];
+
+lazy_static! {
+    // G1 h_eff
+    pub static ref H_EFF_G1: Big = Big::frombytes(&mut hex::decode("d201000000010001").unwrap());
+
+    // Curve parameters of G2 ISO-3: y^2 = x^3 + ax + b
+    pub static ref SSWU_A1: FP = FP::new_big(&Big::frombytes(&hex::decode("00144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d").unwrap()));
+    pub static ref SSWU_B1: FP = FP::new_big(&Big::frombytes(&hex::decode("12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0").unwrap()));
+    pub static ref SSWU_Z1: FP = FP::new_int(11);
+
+    // Curve parameters of G2 ISO-3: y^2 = x^3 + ax + b
+    pub static ref SSWU_A2: FP2 = FP2::new_ints(0, 240);
+    pub static ref SSWU_B2: FP2 = FP2::new_ints(1012, 1012);
+    pub static ref SSWU_Z2: FP2 = FP2::new_ints(-2, -1);
+}
diff --git a/src/roms/rom_bls381_64.rs b/src/roms/rom_bls381g1_64.rs
similarity index 76%
copy from src/roms/rom_bls381_64.rs
copy to src/roms/rom_bls381g1_64.rs
index 4d95fdb..51bd4b9 100644
--- a/src/roms/rom_bls381_64.rs
+++ b/src/roms/rom_bls381g1_64.rs
@@ -18,7 +18,10 @@ under the License.
 */
 
 use super::super::arch::Chunk;
-use bls381::big::NLEN;
+use super::fp::FP;
+use super::fp2::FP2;
+use super::hash_to_curve::HashAlgorithm;
+use bls381g1::big::{Big, NLEN};
 use types::{CurvePairingType, CurveType, ModType, SexticTwist, SignOfX};
 
 // Base Bits= 58
@@ -207,5 +210,36 @@ pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::Bls;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::MType;
 pub const ATE_BITS: usize = 65;
 pub const SIGN_OF_X: SignOfX = SignOfX::NegativeX;
-pub const HASH_TYPE: usize = 32;
+pub const HASH_ALGORITHM: HashAlgorithm = HashAlgorithm::Sha256; // Hash algorithm for hash to curve
+pub const HASH_TYPE: usize = 32; // Output size of hash algorithm
 pub const AESKEY: usize = 16;
+
+/// Signatures on G1: true, Signatures on G2: false
+pub const BLS_SIG_G1: bool = true;
+
+// BLS Standard Constants
+/// L = ceil(ceil(log2(Q) + 128) / 8)
+pub const L: usize = 64;
+/// b_in_bytes = ceil(b / 8), where b is bits outputted from SHA256
+pub const B_IN_BYTES: usize = 32;
+/// Hash to Curve Suite
+pub const H2C_SUITE: &str = "BLS12381G1_XMD:SHA-256_SSWU_RO_";
+/// Domain Separation Tag
+pub const DST: &[u8] = b"BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_POP_";
+/// Z_PAD is a vector of zeros of length equal to the hash block size (64).
+pub const Z_PAD: [u8; 64] = [0u8; 64];
+
+lazy_static! {
+    // G1 h_eff
+    pub static ref H_EFF_G1: Big = Big::frombytes(&mut hex::decode("d201000000010001").unwrap());
+
+    // Curve parameters of G2 ISO-3: y^2 = x^3 + ax + b
+    pub static ref SSWU_A1: FP = FP::new_big(&Big::frombytes(&hex::decode("00144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d").unwrap()));
+    pub static ref SSWU_B1: FP = FP::new_big(&Big::frombytes(&hex::decode("12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0").unwrap()));
+    pub static ref SSWU_Z1: FP = FP::new_int(11);
+
+    // Curve parameters of G2 ISO-3: y^2 = x^3 + ax + b
+    pub static ref SSWU_A2: FP2 = FP2::new_ints(0, 240);
+    pub static ref SSWU_B2: FP2 = FP2::new_ints(1012, 1012);
+    pub static ref SSWU_Z2: FP2 = FP2::new_ints(-2, -1);
+}
diff --git a/src/roms/rom_bls381_32.rs b/src/roms/rom_bls381g2_32.rs
similarity index 81%
rename from src/roms/rom_bls381_32.rs
rename to src/roms/rom_bls381g2_32.rs
index e282e9d..af198a8 100644
--- a/src/roms/rom_bls381_32.rs
+++ b/src/roms/rom_bls381g2_32.rs
@@ -18,7 +18,10 @@ under the License.
 */
 
 use super::super::arch::Chunk;
-use bls381::big::NLEN;
+use super::fp::FP;
+use super::fp2::FP2;
+use super::hash_to_curve::HashAlgorithm;
+use bls381g2::big::{Big, NLEN};
 use types::{CurvePairingType, CurveType, ModType, SexticTwist, SignOfX};
 
 // Base Bits= 29
@@ -205,5 +208,36 @@ pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::Bls;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::MType;
 pub const ATE_BITS: usize = 65;
 pub const SIGN_OF_X: SignOfX = SignOfX::NegativeX;
-pub const HASH_TYPE: usize = 32;
+pub const HASH_ALGORITHM: HashAlgorithm = HashAlgorithm::Sha256; // Hash algorithm for hash to curve
+pub const HASH_TYPE: usize = 32; // Output size of hash algorithm
 pub const AESKEY: usize = 16;
+
+/// Signatures on G1: true, Signatures on G2: false
+pub const BLS_SIG_G1: bool = false;
+
+// BLS Standard Constants
+/// L = ceil(ceil(log2(Q) + 128) / 8)
+pub const L: usize = 64;
+/// b_in_bytes = ceil(b / 8), where b is bits outputted from SHA256
+pub const B_IN_BYTES: usize = 32;
+/// Hash to Curve Suite
+pub const H2C_SUITE: &str = "BLS12381G2_XMD:SHA-256_SSWU_RO_";
+/// Domain Separation Tag
+pub const DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
+/// Z_PAD is a vector of zeros of length equal to the hash block size (64).
+pub const Z_PAD: [u8; 64] = [0u8; 64];
+
+lazy_static! {
+    // G1 h_eff
+    pub static ref H_EFF_G1: Big = Big::frombytes(&mut hex::decode("d201000000010001").unwrap());
+
+    // Curve parameters of G2 ISO-3: y^2 = x^3 + ax + b
+    pub static ref SSWU_A1: FP = FP::new_big(&Big::frombytes(&hex::decode("00144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d").unwrap()));
+    pub static ref SSWU_B1: FP = FP::new_big(&Big::frombytes(&hex::decode("12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0").unwrap()));
+    pub static ref SSWU_Z1: FP = FP::new_int(11);
+
+    // Curve parameters of G2 ISO-3: y^2 = x^3 + ax + b
+    pub static ref SSWU_A2: FP2 = FP2::new_ints(0, 240);
+    pub static ref SSWU_B2: FP2 = FP2::new_ints(1012, 1012);
+    pub static ref SSWU_Z2: FP2 = FP2::new_ints(-2, -1);
+}
diff --git a/src/roms/rom_bls381_64.rs b/src/roms/rom_bls381g2_64.rs
similarity index 76%
rename from src/roms/rom_bls381_64.rs
rename to src/roms/rom_bls381g2_64.rs
index 4d95fdb..e3d6815 100644
--- a/src/roms/rom_bls381_64.rs
+++ b/src/roms/rom_bls381g2_64.rs
@@ -18,7 +18,10 @@ under the License.
 */
 
 use super::super::arch::Chunk;
-use bls381::big::NLEN;
+use super::fp::FP;
+use super::fp2::FP2;
+use super::hash_to_curve::HashAlgorithm;
+use bls381g2::big::{Big, NLEN};
 use types::{CurvePairingType, CurveType, ModType, SexticTwist, SignOfX};
 
 // Base Bits= 58
@@ -207,5 +210,36 @@ pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::Bls;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::MType;
 pub const ATE_BITS: usize = 65;
 pub const SIGN_OF_X: SignOfX = SignOfX::NegativeX;
-pub const HASH_TYPE: usize = 32;
+pub const HASH_ALGORITHM: HashAlgorithm = HashAlgorithm::Sha256; // Hash algorithm for hash to curve
+pub const HASH_TYPE: usize = 32; // Output size of hash algorithm
 pub const AESKEY: usize = 16;
+
+/// Signatures on G1: true, Signatures on G2: false
+pub const BLS_SIG_G1: bool = false;
+
+// BLS Standard Constants
+/// L = ceil(ceil(log2(Q) + 128) / 8)
+pub const L: usize = 64;
+/// b_in_bytes = ceil(b / 8), where b is bits outputted from SHA256
+pub const B_IN_BYTES: usize = 32;
+/// Hash to Curve Suite
+pub const H2C_SUITE: &str = "BLS12381G2_XMD:SHA-256_SSWU_RO_";
+/// Domain Separation Tag
+pub const DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
+/// Z_PAD is a vector of zeros of length equal to the hash block size (64).
+pub const Z_PAD: [u8; 64] = [0u8; 64];
+
+lazy_static! {
+    // G1 h_eff
+    pub static ref H_EFF_G1: Big = Big::frombytes(&mut hex::decode("d201000000010001").unwrap());
+
+    // Curve parameters of G2 ISO-3: y^2 = x^3 + ax + b
+    pub static ref SSWU_A1: FP = FP::new_big(&Big::frombytes(&hex::decode("00144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d").unwrap()));
+    pub static ref SSWU_B1: FP = FP::new_big(&Big::frombytes(&hex::decode("12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0").unwrap()));
+    pub static ref SSWU_Z1: FP = FP::new_int(11);
+
+    // Curve parameters of G2 ISO-3: y^2 = x^3 + ax + b
+    pub static ref SSWU_A2: FP2 = FP2::new_ints(0, 240);
+    pub static ref SSWU_B2: FP2 = FP2::new_ints(1012, 1012);
+    pub static ref SSWU_Z2: FP2 = FP2::new_ints(-2, -1);
+}
diff --git a/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SSWU_NU_.json b/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SSWU_NU_.json
new file mode 100644
index 0000000..cba0e19
--- /dev/null
+++ b/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SSWU_NU_.json
@@ -0,0 +1,77 @@
+{
+  "L": "0x40",
+  "Z": "0xb",
+  "ciphersuite": "BLS12381G1_XMD:SHA-256_SSWU_NU_",
+  "curve": "BLS12381G1",
+  "dst": "BLS12381G1_XMD:SHA-256_SSWU_NU_TESTGEN",
+  "expand": "XMD",
+  "field": {
+    "m": "0x1",
+    "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
+  },
+  "hash": "sha256",
+  "k": "0x80",
+  "map": {
+    "name": "SSWU",
+    "sgn0": "sgn0_be"
+  },
+  "randomOracle": false,
+  "vectors": [
+    {
+      "P": {
+        "x": "0x115281bd55a4103f31c8b12000d98149598b72e5da14e953277def263a24bc2e9fd8fa151df73ea3800f9c8cbb9b245c",
+        "y": "0x0796506faf9edbf1957ba8d667a079cab0d3a37e302e5132bd25665b66b26ea8556a0cfb92d6ae2c4890df0029b455ce"
+      },
+      "Q": {
+        "x": "0x0dddf77f320e7848a457358ab8d3b84cbaf19307be26b91a10c211651691cd736b1f59d77aed3954f857f108d6966f5b",
+        "y": "0x0450ab32020649f22a2fca166a1d8a59d4c93f1eb078a4bedd6c48027b9933507a2a8ae4d915305f58ede781283325a9"
+      },
+      "msg": "",
+      "u": [
+        "0x0ccb6bda9b602ab82aae21c0291623e2f639648a6ada1c76d8ffb664130fd18d98a2cc6160624148827a9726678e7cd4"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x04a7a63d24439ade3cd16eaab22583c95b061136bd5013cf109d92983f902c31f49c95cbeb97222577e571e97a68a32e",
+        "y": "0x09a8aa8d6e4b409bbe9a6976c016688269024d6e9d378ed25e8b4986194511f479228fa011ec88b8f4c57a621fc12187"
+      },
+      "Q": {
+        "x": "0x12897a9a513b12303a7f0f3a3cc7c838d16847a31507980945312bede915848159bd390b16b8e378b398e31a385d9180",
+        "y": "0x1372530cc0811d70071e50640281aa8aaf96ee09c01281ccfead92296cb9dacf5054aa51dbea730e46239e709042a15d"
+      },
+      "msg": "abc",
+      "u": [
+        "0x08accd9a1bd4b75bb2e9f014ac354a198cbf607f0061d00a6286f5544cf4f9ecc1439e3194f570cbbc7b96d1a754f231"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x05c59faaf88187f51cd9cc6c20ca47ac66cc38d99af88aef2e82d7f35104168916f200a79562e64bc843f83cdc8a4675",
+        "y": "0x0b10472100a4aaa665f35f044b14a234b8f74990fa029e3dd06aa60b232fd9c232564ceead8cdb72a8a0320fc1071845"
+      },
+      "Q": {
+        "x": "0x08459bd42a955d6e247fce6c81eda0ad9645f9e666d141a71f0afa3fbc509b2c58550fe077d073cc752493400399fddd",
+        "y": "0x169d35a8c6bb915ae910f4c6cde359622746b0c8b2b241b411d0e92ef991d3e6a7b0fafabb93c1de2e3997d6e362ce8a"
+      },
+      "msg": "abcdef0123456789",
+      "u": [
+        "0x0a359cf072db3a39acf22f086d825fcf49d0daf241d98902342380fc5130b44e55de8f684f300bc11c44dee526413363"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x10147709f8d4f6f2fa6f957f6c6533e3bf9069c01be721f9421d88e0f02d8c617d048c6f8b13b81309d1ef6b56eeddc7",
+        "y": "0x1048977c38688f1a3acf48ae319216cb1509b6a29bd1e7f3b2e476088a280e8c97d4a4c147f0203c7b3acb3caa566ae8"
+      },
+      "Q": {
+        "x": "0x08c937d529c01ab2398b85b0bff6da465ed6265d4944dbbef7d383eea40157927082739c7b5417027d2225c6cb9d5ef0",
+        "y": "0x059047d83b5ea1ff7f0665b406acede27f233d3414055cbff25b37614b679f08fd6d807b5956edec6abad36c5321d99e"
+      },
+      "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]
+      "u": [
+        "0x181d09392c52f7740d5eaae52123c1dfa4808343261d8bdbaf19e7773e5cdfd989165cd9ecc795500e5da2437dde2093"
+      ]
+    }
+  ]
+}
diff --git a/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SSWU_RO_.json b/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SSWU_RO_.json
new file mode 100644
index 0000000..43785d3
--- /dev/null
+++ b/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SSWU_RO_.json
@@ -0,0 +1,97 @@
+{
+  "L": "0x40",
+  "Z": "0xb",
+  "ciphersuite": "BLS12381G1_XMD:SHA-256_SSWU_RO_",
+  "curve": "BLS12381G1",
+  "dst": "BLS12381G1_XMD:SHA-256_SSWU_RO_TESTGEN",
+  "expand": "XMD",
+  "field": {
+    "m": "0x1",
+    "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
+  },
+  "hash": "sha256",
+  "k": "0x80",
+  "map": {
+    "name": "SSWU",
+    "sgn0": "sgn0_be"
+  },
+  "randomOracle": true,
+  "vectors": [
+    {
+      "P": {
+        "x": "0x14738daf70f5142df038c9e3be76f5d71b0db6613e5ef55cfe8e43e27f840dc75de97092da617376a9f598e7a0920c47",
+        "y": "0x12645b7cb071943631d062b22ca61a8a3df2a8bdac4e6fcd2c18643ef37a98beacf770ce28cb01c8abf5ed63d1a19b53"
+      },
+      "Q0": {
+        "x": "0x02f2686965a4dd27ccb11119f2e131aefee818744a414d23ecef4db1407991fdf058f0affaee18fd586a9ab81060ae20",
+        "y": "0x0341a16c88a39b3d111b36b7cf885b7147b1d54b9201faaba5b47d7839bcf433cc35bb1f7b8e55aa9382a52fe4d84370"
+      },
+      "Q1": {
+        "x": "0x1357bddd2bc6c8e752f3cf498ffe29ae87d8ff933701ae76f82d2839b0d9aee5229d4fff54dfb8223be0d88fa4485863",
+        "y": "0x09ba0ec3c78cf1e65330721f777b529aef27539642c39be11f459106b890ec5eb4a21c5d94885603e822cfa765170857"
+      },
+      "msg": "",
+      "u": [
+        "0x14700e34d15178550475044b044b4e41ca8d52a655c34f8afea856d21d499f48c9370d2bae4ae8351305493e48d36ab5",
+        "0x17e2da57f6fd3f11dba6119db4cd26b03e63e67b4e42db678d9c41fdfcaff00ba336d8563abcd9da6c17d2e1784ee858"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x01fea27a940188120178dfceec87dca78b745b6e73757be21c54d6cee6f07e3d5a465cf425c9d34dccfa95acffa86bf2",
+        "y": "0x18def9271f5fd253380c764a6818e8b6524c3d35864fcf963d85031225d62bf8cd0abeb326c3c62fec56f6100fa04367"
+      },
+      "Q0": {
+        "x": "0x119cc1d21e3e494d388a8718fe9f8ec6d8ff134486ce5c1f97129797616c4b8125f0dc568c59836cbf064496136438bc",
+        "y": "0x19e6c998825ee57b82c4808e4df477680f0f254c9edce228104422494a4e5d40d11ee676f6b861b6c49cf7de9d777aef"
+      },
+      "Q1": {
+        "x": "0x0d1783f40bd83461b921c3fcd0e9ba326ef75272b122cf44338f0060d7179995a38ea9c66f3ce800e2f693d2634a4524",
+        "y": "0x017b2566d55fa7ee43844f1fa068cb0a11d5889c11607d939da046697c8ba25cf71054c2a8eb2189d3680485a39f5bdd"
+      },
+      "msg": "abc",
+      "u": [
+        "0x10c84aa245c74ee20579a27e63199be5d19cdfb5e44c6b587765931605d7790a1df6e1433f78bcddb4edb8553374f75e",
+        "0x0f73433dcc2b5f9905c49d905bd62e1a1529b057c77194e56d196860d9d645167e0430aec9d3c70de31dd046fcab4a20"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x0bdbca067fc4458a1206ecf3e235b400449c5693dd99e99a9793da076cb65e1b796bc279c892ae1c320c3783e25062d2",
+        "y": "0x12ca3f12b93b0028390a4ef4fa7083cb23f66ca42423e6e53987620e1d57c23a0ad6a14db1f709d0494c7d5122e0632f"
+      },
+      "Q0": {
+        "x": "0x1614d05720a39379fb89469883f90ae3e50995def9e17f8f8566a3f6cfb4fe88267eac1dc7834406fc597965065ef100",
+        "y": "0x1060e5aab331ac4940693a936ea80029bb2c4a3945add7ae35bce805e767af827c4a9ffcb5842fbc50ab234716d895f6"
+      },
+      "Q1": {
+        "x": "0x0f612cda21cee750b1ccff361a4ce047e70d9a9e152e96a60aa29b5d8a5dcd25f7c5bd71bb56bd34e6a8af7532afaa4f",
+        "y": "0x1878f926302468949ef290b4fee621d1172e072eda1b42e366df68fc87f53c35583dbc043009e0b38a04a9b1ff617efe"
+      },
+      "msg": "abcdef0123456789",
+      "u": [
+        "0x11503eb4a558d0d2c5fc7cdddb51ba715c33577cf1a7f2f21a7eee6d2a570332bbbe53ae3392c9f8d8f6c172ae484692",
+        "0x0efd59b8d98be7c491dfdb9d2a669e32e9bb348f8a64dbf7e47708dd5d40f484b1439109a3f96230bf63af72b908c43d"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x0a81ca09b6a8c05712396801e6432a87b14ab1f764fa519e9f515816607283fe2a653a191fc1c8fee89cd30195e7a8e1",
+        "y": "0x11c7f1b59bb552692288da6557d1b5c72a448101faf56dd4125d8422af1425c4ddeecfbd5200525064657a79bdd0c3ed"
+      },
+      "Q0": {
+        "x": "0x0a817078e7f30f08e94a25c2a1947160db1fe52042626660b8252cd339e678a1fecc0e6da60390a203532bd089a426b6",
+        "y": "0x097bd5d6ae3f5b5d0ba5e4099485caa2c505a1d900e4525af10254b3927ae0c82611be944ff8fdc6b278aab9e17ee27c"
+      },
+      "Q1": {
+        "x": "0x1098f203da72c58dca61ffd52a3de82603d3154c527df51c2efe6298ea0eeaa065d57ba3a809b5e32d9d56dade119006",
+        "y": "0x0bcbd9df3505f049476f060c1d1c958fe8b34e426fd7e75424c9e227d9c4d3edbd5eddb8b1e89cc91b4a7bd3275d4d70"
+      },
+      "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]
+      "u": [
+        "0x134dc7f817cc08c5a3128892385ff6e9dd55f5e39d9a2d74ac74058d5dfc025d507806ab5d9254bd2334defbb477400d",
+        "0x0eeaf2c6f4c1ca5cc039d99cb94234f67e65968f36d9dd77e95da55dadd085b50fbb11489167ded9157e5aac0d99d5be"
+      ]
+    }
+  ]
+}
diff --git a/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SVDW_NU_.json b/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SVDW_NU_.json
new file mode 100644
index 0000000..9e9e234
--- /dev/null
+++ b/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SVDW_NU_.json
@@ -0,0 +1,77 @@
+{
+  "L": "0x40",
+  "Z": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa8",
+  "ciphersuite": "BLS12381G1_XMD:SHA-256_SVDW_NU_",
+  "curve": "BLS12381G1",
+  "dst": "BLS12381G1_XMD:SHA-256_SVDW_NU_TESTGEN",
+  "expand": "XMD",
+  "field": {
+    "m": "0x1",
+    "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
+  },
+  "hash": "sha256",
+  "k": "0x80",
+  "map": {
+    "name": "SVDW",
+    "sgn0": "sgn0_be"
+  },
+  "randomOracle": false,
+  "vectors": [
+    {
+      "P": {
+        "x": "0x126690faf7d2cc7838d18f58ae94e9eab1d042fb93ed7245aee5a1a3ecac3f1935df62b807f9bd7be1018492b4ccd087",
+        "y": "0x0d2fe772107e935ddfb916da6dd69cb3c9b112cbd7452a3da77e16620f58c06db9c1eb19ab031995c96dc2d0c16b5031"
+      },
+      "Q": {
+        "x": "0x10c4561604827f4687f7bf5a365411577c6c0298910d6a67685c675f1dd0b59ac5fbb46b729b3389499aaaf5b11bfc06",
+        "y": "0x114963eba7ffc5e3bc4c86a1dbf73b4952bfd60fa94449b2adc81dee0e974e4afd228a1af4fb1d4ede14cdc727deed0c"
+      },
+      "msg": "",
+      "u": [
+        "0x1754372e423dd665efc2703d235a5b42ad5609ee9872c303a0b900b152d5ae8d64c2d67764a6f188b87d61445cf5634c"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x03929609010843a52432b815cb5fb90d6a5ee8a10cc75c70d6f1bd64642a0057d2e69187ea3fff0a34e7e72b1cef5948",
+        "y": "0x196b4734ee0b1845619e8b871c0a2ea556660dab119868ad5542dc5b782b0df61cb6d2a1b2cd174a80cbc9f8cde6d470"
+      },
+      "Q": {
+        "x": "0x0f5b63a00bd63757fab41dbc9b47b679650d903a83934eb920a13097d58dd5f82068c059bf1de52971373c3cc83c427e",
+        "y": "0x03f2547ce0337e25a9fad27335f499b054cc1ab3e1c29366959599b5d362602bedcce56c0e039c4ec0fbbdf041564892"
+      },
+      "msg": "abc",
+      "u": [
+        "0x0a55c5bc65256c488bb068e3b1d5d2fc4acb3017da1f7d9a3a9036eb5880123b0aad219aedb4836eab28e7a85d171054"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x107485fecc3be4b92f8cc35f1782c67742d055c379e6bcb77836396edebe728aec4ecad0290bd1bbd5be7047ccb8554c",
+        "y": "0x0603d69e02982c8a0edc6fd84848fa9d9c75a0d7a372eb1ea2672245ee2d9ed672cd1a564fec3cb77758b966316d5c09"
+      },
+      "Q": {
+        "x": "0x09c7ef760508a7d4a226869cae2c556a8773ebf5d4bbba33b0cffbbba598105bf7549cd33d52ded62be72f718ab0ac9c",
+        "y": "0x0aa4882a7806cac7bb3bd6404000066b7c6ff14dc590efa26f2740b1af2f3be2b50e1d5b99b37d723d9d3b0cf089bf62"
+      },
+      "msg": "abcdef0123456789",
+      "u": [
+        "0x06983540e75c0e7a36976a66cbfee71e05c9ea2cbdb3b0f25aa9329e02ee95895fcde7262dae46c370c148e1027c4819"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x06c214d69a4ecd4fd0230f2dd2a8988daff27c597acee1ec5e6045e0848894d1435f5c143f4226fbc27a2045921c2499",
+        "y": "0x179e0869d8c73643e0bc27ebe3178ee297ad366c9a4f416a0b0ac2265ff763fbd8c0bf37e637d081190352f4c1961f74"
+      },
+      "Q": {
+        "x": "0x0598fd95bcd0a790e6921a51f212f5194781521cf32fcea581be2a5cbfd2a8fda8828141a24168689a273d258b1fe083",
+        "y": "0x04d0dbee266de33d9bee538d188e209476ebe3b34e58a19d0ea6ae542219b3f3549685ef3eef82991feb23ddde098285"
+      },
+      "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]
+      "u": [
+        "0x011a4d12d373940b946029aef4bbdbb880810b25370d1f5b171e2d68091fabc9a4c8fc4f39c9b0ef98c34b5f90070a9d"
+      ]
+    }
+  ]
+}
diff --git a/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SVDW_RO_.json b/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SVDW_RO_.json
new file mode 100644
index 0000000..8384f86
--- /dev/null
+++ b/src/test_utils/hash_to_curve_vectors/BLS12381G1_XMD:SHA-256_SVDW_RO_.json
@@ -0,0 +1,97 @@
+{
+  "L": "0x40",
+  "Z": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa8",
+  "ciphersuite": "BLS12381G1_XMD:SHA-256_SVDW_RO_",
+  "curve": "BLS12381G1",
+  "dst": "BLS12381G1_XMD:SHA-256_SVDW_RO_TESTGEN",
+  "expand": "XMD",
+  "field": {
+    "m": "0x1",
+    "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
+  },
+  "hash": "sha256",
+  "k": "0x80",
+  "map": {
+    "name": "SVDW",
+    "sgn0": "sgn0_be"
+  },
+  "randomOracle": true,
+  "vectors": [
+    {
+      "P": {
+        "x": "0x045f87745ff759f9197e131ad83d47d635dc36a3e0c7e4a1be5e1effe5e63ac69c8f34e6c3aef9c5cf28224922788367",
+        "y": "0x06125886a03f883740a078313d5fa6e4a68b9c0394eb75f77c65fc8b44db3f4ef933ac6adf341bc45fabc7907afcb832"
+      },
+      "Q0": {
+        "x": "0x0d8deaf1a9fe87c5710466d0cd554b243a041a97c15228f1c65be6244b5e1a575f4ab2762a1cc9ff18d45d4f1494e2c6",
+        "y": "0x090dce51930eff6bccfc7cc2920f180c398b0a97085ff9e4841367e701a0f28616bef537203d27b0656f3b7ee52ee0ac"
+      },
+      "Q1": {
+        "x": "0x0a36d0d4051eb32d253b069d1d15c5b69cefbb75bf38d66d43b37432b34f88b2553bf063c6533ca87112c3e95c295ebf",
+        "y": "0x0bdc425e77f44ac13587b17dab2b1b9d3e3501be1fe1c56c7f3701fbe53b43a37263c04fd9aa1b7f58b1b63fe6cb29fd"
+      },
+      "msg": "",
+      "u": [
+        "0x04a74117c448f7aad70bd41328b3856de638c0e10c9ff344295a04b90db0f2afe80ff2da62e091793d6e52bc70b28d47",
+        "0x06f44093874c190fff1e893c847a59601d5d5ac0aebd85ba36b3906a93173e0d2fcf6bed8013ee2679ca337f21f8f053"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x009a357691a6f7b2917d9a34ba64d896d40b49733fcb3207f8c146e20fffb47823198a26b6ceeb01215fc3422908020e",
+        "y": "0x03fe44c894c107a8547826b60f577b90f80c63f899ef9dcff94daadae180ad803609337c9ec97d6d9b8ba306df7a9849"
+      },
+      "Q0": {
+        "x": "0x01b50bac3377c4e764fda5fcbe9bb477c3becb8cf18a3026a88ab2a0fbf6d8164d60d85ac9e67b6712fbb26b10ec4597",
+        "y": "0x11acedae54a20e1f8d58fd074e1b7de957203f56365b52a09824ff9f8d191356914818bdb35faffe8657ad24e1f5db5f"
+      },
+      "Q1": {
+        "x": "0x16bb1fce160d3fb5194b275a907cb03bfbe8713e830f2a573acc26782c65c9a6e88d96c6489c96dc6f65815cb5ab2662",
+        "y": "0x159720d02fef12d1a40b1c91336908d72b0eb1fe54a16a914c5ae9710a8fa8dfad035ac6c39708c88318fe26113f28d5"
+      },
+      "msg": "abc",
+      "u": [
+        "0x145191560d1db38062a0a2e29469d71eb035f888cd4bb2792ddf88ad63c93320738a3e4c6e199bb286e26efaad665a0f",
+        "0x17f7bbcbcf64dedb58af6560e2b2d08a678e4a57a0b042978840abb77f25b33090035675ac30bb461a8955a9d1ebb411"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x04eb09680fe48598533932907810fb7681e60b3689cb138454bec627490c5089b6dd755556e52a36c3817e98b62d7497",
+        "y": "0x1763dd8bf6823d9a22124d22a4ab3d93d8a9603ec80b4a40905b26664b16033fa6e73a6155c9bc4c6faa42bf911ffba1"
+      },
+      "Q0": {
+        "x": "0x164e16e858fa1e1a4ea52152ffe0a42f1e6ae1258dd830eb7aa66434d7d5689da945e29c61766ae213bc1878363a3d4c",
+        "y": "0x077cc4c8f74f33a0e6036d32c4407b3021b0be82e0eba80a1fc119f69a071c52f25764dbd72f2cafb420264c079b7691"
+      },
+      "Q1": {
+        "x": "0x06a856de4eb5651a21c68c7e85796c16e417d7d97cb3e1b74bbc4042b340e647fc41cc570bd160fe9917d32a47bc8b5e",
+        "y": "0x15c4e89949bbdb253d333018a1417150f8a78187c343f5bbdf25d962c513df1d96ee1059facab028549ebe7716164466"
+      },
+      "msg": "abcdef0123456789",
+      "u": [
+        "0x06ed3e25d860fc574e482bbc0a09c5f44216ad44d75ea499cf905efe4bc3c5fd3e94df483d17501395f325d3c8ea7925",
+        "0x1426c02b7de4bedb4a301c7d2d4a270f672a8fc448471f2c3dfcf43846b102e592ad6e055c2d5149fcbc21f5fb6002c0"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x0915842b42c2c4d3b509823c60c1fad834784ff451855f43390b80c3b6985d76aadc6ecfbb4b42a07921410d6821f0bb",
+        "y": "0x052873ee0b444dd8337ce403636d680cff1e9402b7a1ce2ab210bff11a83fe4e14216fe96efe3f344c1a2ec0fc1b2c5f"
+      },
+      "Q0": {
+        "x": "0x15901778217bbe602fe603edfa0518009197445f34dd2184450fb2a1a5088945c32d31f6a9fcd9e3c4333557a11c18a3",
+        "y": "0x126fc2597e155f4aeeacb7a039b5a0012be2cc1433d8c097b6c8e9e6be9b5f279c0decb019a6995345e607a36681f8a3"
+      },
+      "Q1": {
+        "x": "0x0dcb7e280b34f7caf24928ecb126a3bacbfeb4b70612d60bc7cd36467ad58cf33925e9c78635cdb7a024c0a3031846c1",
+        "y": "0x066bbc62980e8bf7664f2b84ee69f3ed2c1a49ef1f8c0d35d709ef1ed149f79cc93a65ccde5194d333710496f087d41d"
+      },
+      "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]
+      "u": [
+        "0x0da5000e88db641d86b6aa2711b88b9bcf45fc95689dec569bd9ae6aac6b19e96620920f3fb806d0e72a111889983c11",
+        "0x0367d0cf2689a9e972f01d9d325a3e1bdcee67af7f39fb71030869457ea93d0039e58bb92cd058e45ced781def78ac79"
+      ]
+    }
+  ]
+}
diff --git a/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SSWU_NU_.json b/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SSWU_NU_.json
new file mode 100644
index 0000000..95bcfc4
--- /dev/null
+++ b/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SSWU_NU_.json
@@ -0,0 +1,77 @@
+{
+  "L": "0x40",
+  "Z": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9,0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa",
+  "ciphersuite": "BLS12381G2_XMD:SHA-256_SSWU_NU_",
+  "curve": "BLS12381G2",
+  "dst": "BLS12381G2_XMD:SHA-256_SSWU_NU_TESTGEN",
+  "expand": "XMD",
+  "field": {
+    "m": "0x2",
+    "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
+  },
+  "hash": "sha256",
+  "k": "0x80",
+  "map": {
+    "name": "SSWU",
+    "sgn0": "sgn0_be"
+  },
+  "randomOracle": false,
+  "vectors": [
+    {
+      "P": {
+        "x": "0x170919c7845a9e623cef297e17484606a3eb2ae21ed8a21ff2b258861daefa3ac36955c0b374c6f4925868920d9c5f0b,0x04264ddf941f7c9ea5ad62027c72b194c6c3f62a92fcdb56ddc9de7990489af1f81c576e7f451c2cd416102253e040f0",
+        "y": "0x0ce03abe6c55ff0640b2b303440d88bd1a2b0cbfe3274b2802c1f58b1085e4dd8795c9c4d9c166d2f033e3c438e7f8a9,0x02d03d852629f70563e3a653ccc2e114439f551a2fd87c8136eb205b84e22c3f40507beccdcdc52c921b69a57968ec7c"
+      },
+      "Q": {
+        "x": "0x029b6b7335975135a3dd653cb5f865f8e1a6fd0e806f83f08078426d294efb72578dc6747b81747d03b5bce9fa9c6d4c,0x0e31914536a751dce017585d51c8c30127cf0abf8cce302faf8ea87de1393a37696df8d999f597b256e8e19a0865817a",
+        "y": "0x0a718016d326692f10e7508c6abc624e1b37da5fd0e4391acf5a19fac36b97a9ae13a79dd2bd0b28c1db20f9d27607e9,0x01df562b2fe9e7281b63a3c136a93773184ad924d9a0a0cd01b51150f175f9dfaa8d009f77df9812636a6de4a1b0a901"
+      },
+      "msg": "",
+      "u": [
+        "0x09367e3b485dda3925e82cc458e5009051281d3e442e94f9ef9feec44ee26375d6dc904dc1aa1f831f2aebd7b437ad12,0x094376a68cdc8f64bd981d59bf762f9b2960df6b135f6e09ceada2fe8d0000bbf04023492796c09f8ef04016a2e8365f"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x16d830a4e12fddfbdaf9a667f94f21e490879fd3ccc5ee6f039cd7c2174fb47ea8027af78779a978d2a921612844587f,0x019a3b47aa956b2b548cc04d9e109dec06642d6e28814f7e35f807e1ce609e2eae3a155af406c842529776d8192f562e",
+        "y": "0x15930174c11aa9b51a5cc3ebfa1ab6377e2318c4ea2df387bdb84b28687a02c86e6401b195bbcabb6e95d6ae43669e12,0x15adde069459ab2012b44c7703119185b96b7f04ad59b39f4f6aea35fdbb9c5c7d876b5f89afb55b67e7da96ad489dc3"
+      },
+      "Q": {
+        "x": "0x0f4c4441758b65035fab9adeb84dc4fcbf48e218085111cbc9e3294dde67ba93411d747d090fbc5aa144900df054ed32,0x0bcf59db7917f351264cbc8825c04e88885b01f8228ecef238e39cd9d7e42c7a6b4aeeabacbfe43ea36f1a148c3517ae",
+        "y": "0x128fae582c1c32dc1199981981c9f2ff42343523192b82d3c010c6b06e419087449196cf4a79caa921db2ab10fc316b5,0x0cff0768048fef20c10c19679deb85fff097befdb18a47a21c2ee57eeebf046d4d9f3bdaff6f9c7d6c80e0700018b86e"
+      },
+      "msg": "abc",
+      "u": [
+        "0x17ecd5d41a860b8886cb1210874b254f59945b089f774dcc14bc1aca7d4e3c975bce0d28510c442e9a932be5880ee5b1,0x0f105595e14847cc9a41fd70deb3240337678b266304100ec261add2585b991c7268bb1a325d2f871b327e8d04fd579b"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x1498937f0ed18c49ebbcdee579b58ce235f3ab03be5dc809e1df25e2e0b4eb4c672f4eaf26df91f3755d6367df55d5be,0x0910b2d55e210122fab2d2dae81e6a440fd22e925e422aaf16a8fd28477bacb12aa888de0faeea203e372a1c1cd9578c",
+        "y": "0x033b1948575e70fed67fb4f7bd86b5452dfc0afeb74ecf5cab4a6872e33f0eade9564d3d5b9fcb9d4c498afda0bc037d,0x102631eb4e684d759312d7eab78598f487c2c10ad3d3552cb43ce6f09a11eb46e551864863077906d3ecfd921f1fe541"
+      },
+      "Q": {
+        "x": "0x110a8c50fb6b2df0146678e80de24089e0d619c45c488e0c688f136963a4190b76647e9e122c18ab7b60a88ca1281e9c,0x17f35d34544ad51d51f2ccfbda142addc678bf5551bf301dce3c3b934ecb6aa78b3814729282755a62e4680083736628",
+        "y": "0x0bd9c15e07f2a2bd5dacae74861afa19ea15d393fe7552d3ba45ef1396d37ff7967bdf67d93ea68baf849710bcd88147,0x18e9a587c3b76f53a62e8919101b6f2b25803333a53b2c8596db44929bd59376f0a170c5debea9f8a378107c2ee1d51b"
+      },
+      "msg": "abcdef0123456789",
+      "u": [
+        "0x032ae17a23a76c94745a5460cd9f1191c0ebeec7adfc4df28b0833e536b7dbabf498dc076ff16cc11c6a6ef5105df693,0x1107a6f450c6c9580c720190b577f52c633cf5f3defb528ae873d3723bccc8fa433014e9120a1da31abc27c674f37ae4"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x18af6eedb7ed3be66c5a1d998ad4d9640f557b189558baec41f6e712ff2a39f795a35494b4b12343b7a1a2b17686d793,0x021f7faa0550e5a5d08338b4c0a5d30240dec7989fc7c77b6ffba9bfd5d64ce45af5aad8da8482bf0da91af4f29d371f",
+        "y": "0x0cc46cea229960bfbe25831162c27f96cf8bb14c017938e35b636987a306521915456fbd40633c6d5a30f61bce52a3f5,0x166c1abec65af593d291dbd05e5d7d28f1a9ffb73751d65f49d76084493f3da707ee2bbf54cf6de5bbaac2ffa0028c31"
+      },
+      "Q": {
+        "x": "0x0cf7bef1339955cf2139e61d3876d22bfcc89c44492b458b2d08a6bb4fa58755739a4b216bfd7604e203f4c31cfcdc00,0x0b80a0dee7817f29f8bf3374a3c033586d695391dc95280b2143d398817bbf9b76547c54b81fd2b8ad24a6afa1dd8524",
+        "y": "0x07bba42c7be028acf9111e51cfa6d98f2949f12635847e938b0f7980705da9abd1d77bbdaf86f90512e0139ac0e57a6b,0x0266340a1aaaf26dd5ec892f33dcb6a217dcde9b8d39dff5d277d93130ee3e33e43a2cb83665fee8648d610da2967fa5"
+      },
+      "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]
+      "u": [
+        "0x0cda6b874f8c41862c078099aa76d607be51d913a2e3f997539a0993bda31892292818c74aa9be035f234df2576fe49a,0x0306162d24592a18fa8de2007d7b69d04bb7a71a5a7965d15bdcbaa4ddf9b599079fbdae9f67d55ab6dba044f9daf179"
+      ]
+    }
+  ]
+}
diff --git a/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SSWU_RO_.json b/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SSWU_RO_.json
new file mode 100644
index 0000000..d108a06
--- /dev/null
+++ b/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SSWU_RO_.json
@@ -0,0 +1,97 @@
+{
+  "L": "0x40",
+  "Z": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9,0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa",
+  "ciphersuite": "BLS12381G2_XMD:SHA-256_SSWU_RO_",
+  "curve": "BLS12381G2",
+  "dst": "BLS12381G2_XMD:SHA-256_SSWU_RO_TESTGEN",
+  "expand": "XMD",
+  "field": {
+    "m": "0x2",
+    "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
+  },
+  "hash": "sha256",
+  "k": "0x80",
+  "map": {
+    "name": "SSWU",
+    "sgn0": "sgn0_be"
+  },
+  "randomOracle": true,
+  "vectors": [
+    {
+      "P": {
+        "x": "0x0d3b02ee071b12d1e79138c3900ca3da7b8021ac462fe6ed68080dc9a5f1c5de46b7fe171e8b3e4e7537e7746757aeca,0x0d4733459fead6a1f30e5f92df08ecfd0db9bcd0f3e2f2de0f00c8f45e081420aa4392eade61eade57d7a68474672fc1",
+        "y": "0x09cc6f7b3074f0c82510e65d8fc58f6033e03ba7358005a13e2bbd7f429b080f29731ef08c3780c9e3c746578b96b05c,0x0011531b8e08900a4f6f612e1e27432961419ce6a5ee3ec904a53588982d36ec4ea37be80b6cb7d986b38faec67dbe44"
+      },
+      "Q0": {
+        "x": "0x06828063f239b9607a9668fc0a59e5391c1bf24ba42ab5b480112666e3835c45f29401bea70d6f529d07e33bc017c1a4,0x04545e6b5318ec1c10f75791bc97142722370b851b7829987ada828de5a9b6353cb2c4f8540bf0821328c983e7eb887e",
+        "y": "0x06fc755b65fd277f7e1e4a6174dc11fedddf1baaefc07d04c3d120e15ca2ec61f489711c4af949bf6a5d26a885db05e3,0x068d9e1e428ed301e425e633603ea9ce5ff02df898aca1957fe5be1faf2ff42b64485cab41370a50c1e7e9742c0a2c6d"
+      },
+      "Q1": {
+        "x": "0x16af5b519e92362ce94c0ef76051510266c5ace22d2cf9c3c1ef20856e4669523df27c30e513444b436cef2082983814,0x03cb00c0a0370bd301cd45b0c08e6feea3adf07052e83a3f2ced57665d7484f5540db310f649387701731ff185b460da",
+        "y": "0x025310a94143c55175eb4929df2d8294b0268f3011850bdf46bdfb82749233e8dbb8070d623e3fb8397b079504d6c5aa,0x087d9d2c1cd911f8cd9844e60011a1d1c02d61a831bfd33b58c436d4305935abd3207c553c0145fd3ae1bf18c82011ce"
+      },
+      "msg": "",
+      "u": [
+        "0x1921dd796efec0b5f2ad9037e73b7470e6e7c85e6b7bf6e6827729442cd01aa55ac765ae451f497873400e90a814105c,0x0838662da53724510bb7d677b7caedbf3c4e19e55b42c9872c2af096272b26cb53cc522413cf4c54c1f7691bcdabfc4d",
+        "0x12bcf2aa2ec29a9dc0b77c942869af340047a27b57f549ecdd2e93fc5e63d0364fba13941725cbbac1d1e8c6e8605d5d,0x04e892eca8729b9a2b88b4b0b33e8c8d4ed52f7d0fc860e41f09de0a05c48f605a7a9d603b60db4f9ee49f888e4f3273"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x0b6d276d0bfbddde617a9ab4c175b07c9c4aecad2cdd6cc9ca541b61334a69c58680ef5692bbad03d2f572838df32b66,0x139e9d78ff6d9d163f979d14a64c5e57f82f1ef7e42ece338b571a9e92c0666f0f6bf1a5fc21e2d32bcb6432eab7037c",
+        "y": "0x022f9ee5d596d06c5f2f735c3c5f743978f79fd57bf7d4291e221227f490d3f276066de9f9edc89c57e048ef4cf0ef72,0x14dd23517516a80d1d840e34f51dfb76946c7670fca0f36ad8ec9bde4ea82dfae119a21b076519bcc1c00152989a4d45"
+      },
+      "Q0": {
+        "x": "0x198d5f5fad8e1594ed98ee3d5f5b24b58e6cb8ae37372f6028e7beffa0e7a16b0958e13f92f322f513b85eacdd88d0c2,0x09288d195828e46e7c058b22115af5c1bde20cb7462a3f9d6488188fa937e2c0bee3aa188622312f87e12b48660a66d2",
+        "y": "0x16a0916a0c42492649701f0520c075d4cc66d74cd8fb4f9c6d2631cac1bb48e357bae0b97bb7f87f1b08539ac944f46a,0x0dd21353972061024a92db26e51cd97246c89a884ecad67a1ff8dd1da73a8397f54d41533ea2ac48ee4f5817cb09dd1a"
+      },
+      "Q1": {
+        "x": "0x17a29041f55a2b23b3a601b8d4f5a3e85b75cdc52650f26470884b8b367b20ef8f7e6f0ee8969b994f4132344a68f023,0x115d814e4f7973e7a960df53b494c2a0a1cd7a42ce6650bd7f46e55a2622dbf6528392ab9c8ff45d3fd97bd7b1dcfe67",
+        "y": "0x15749a8af1d35fcb409ffc3336670a8b47a117f81670394183c316d7bcd2a49b2253ced38b7d00763a2fe4afb51f0336,0x10624b1845ccd669e5edd403d68a603fb43e382c6570953dd9b9add472e422d1098eb45380fa870e14f6927252c4e667"
+      },
+      "msg": "abc",
+      "u": [
+        "0x0b7b2d371fc970671ddf7bc9ca4a70a1bd286af4487b497e460c0b44d405d73db576f8a08d59416cc976d4b1d0100775,0x0e86d0eb2d34c34fe8b2a1f2d999fa3dabcd504fdb4beb57e79756b08fd75b0a82660abc6026ecc4ccf327a522587b38",
+        "0x10376d048c060df1c5017a363144c482892fe2ce0061094327b8bbe49a713ce795726aa23b5402a271e9f1e7b9b6c7ba,0x0117f2ea63015e192d759f11a658a002e06112147d90f00d7429722456b9a1c63fef2dbe8df13168e3bd40af2fb959f3"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x0ded52c30aace28d3e9cc5c1b47861ae4dd4e9cd17622e0f5b9d584af0397cd0e3bae80d4ee2d9d4b18c390f63154dfd,0x046701a03f361a0b8392ca387585f7ee6534dcec9450a035e39dc37387d5ca079b9557447f7d9cad0bd9671cb65ada02",
+        "y": "0x07a5cf56c5ea1d69ad59c0e80cc16c0c1b27f02840b396eb0ea320f70e87f705c6fa70cfeb9719b14badbb058bec5a4c,0x0674d1f7c9e8e84d8d7a07b40231257571c43160fd566e8d24459d17ca52f6068e1b63aaae5359d8869d4abc66de66b6"
+      },
+      "Q0": {
+        "x": "0x1729bbdeef9e902ab2e2bf6f90e3800231397ecc36b0b53d33ecb173bd682ef45a51e691d7c884965fb530cc85d6476d,0x07fd016eb7f3785362f75a0150d9e73d5ae13631c491075d73eab5c3b6ceb8391d909926d0c519fb83fbe889dae667eb",
+        "y": "0x15ee2194b053071cd1d40bacbb2650b5608d22d12ddeeae9fb11921e475ffea6d1c008fc390f231aa14589365c6937c7,0x15345785b7ba1db6cf6ec9f652dede47c86b6837b2c43f3a9e6984f95feecffb84bb5963df655068a0ad6b8d8a762fc6"
+      },
+      "Q1": {
+        "x": "0x18505ec8bfa125df7ea130e702eaa33a89961dd24ad06b3b3452da15f2394d0abec06aa3b4e9433c32fa8a7c6ef874ec,0x17c0d91f4c363a7ff183deeb4308fa5e8d61c0263b9d0ddcf304b2758e2b556695fe20636b4b7a2cd4909c145a81c884",
+        "y": "0x140b4d6603a96ef9de2a71a8ceec992aa72eb8c4f08d28de11310dcfd4d13dbb68734001417d0c1587b9082b593ab9ca,0x0ac81f1093f8be742b331c1c04e9cb0fb75ac72e87ae5da9fa395b043fb83fbdabe9e54331ec3a3a754f845939b118f6"
+      },
+      "msg": "abcdef0123456789",
+      "u": [
+        "0x0022182b07cb11d26cbdab43e0d696297a7dfe1b8dd2fa8ded11f858bf25ab000adb1ec319cbfa42d1107a3ec9528b33,0x01160e11ac26a46322b4867a0d66cbb1d8b8f78e88a3771b7a832d18c65d65297692e9faa1f65719c9ea621578003c37",
+        "0x185e096fa6e05479e1f3ae4148fd4de985c73e414f9a9202d3930d59a09d90d87e545522a91a0d24c6aa3e2363a48a41,0x08e234820b6cdd9229490f5c1e05e82b8fe7b1efab9dfaabe3ea4158f0f8da855daf1e1f5382246187d317ccee520a0e"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x0161130ef4aa2f60f751e6b3dd48ac6e994d2d2613897c5dd26945bc72f33cc2977e1255c3f2dc0f1440d15a71c29b40,0x06db1818f132a61f5fe86d315faa8de4653049ac9cf7fbbc6d9987e5864d82a0156259d56192109bafddd5c30b9f01f5",
+        "y": "0x00f7fab0fedc978b974a38a1755244727b8a4eb31073653fa949594645ad181880d20ff0c91c4375b7e451fe803c9847,0x0964d550ee8752b6db99555ffcd442b4185267f31e3d57435ea73896a7a9fe952bd67f90fd75f4413212ac9640a7672c"
+      },
+      "Q0": {
+        "x": "0x08420c5b8d9f73ddac45b6ce050a8876e5014cb8783bc63a24eebab5e0ca75d547b51025ecfe75f4efadbc8d71c145c5,0x1915fda1fb71039148f5d346f1c36df1630a2f908881f29de32a5c2782eb6eb3c8cbe58f8c1bf8d348319347c6ec7635",
+        "y": "0x0e557684bd3e61db3f96df904c57ee1e8e45f5aecdda654ed741587082ad91860d311cae158569c217c56bbba3d3f25f,0x0acc1f70e15591005ab8bcdc7b1b19e3c16a6ee6c7a17ce83eaa2771971254be34726b266c076abab6b9b477ef790261"
+      },
+      "Q1": {
+        "x": "0x0cabb5826e6bf948e30cbb094b72685aa1d93ea49fdd9d54828b7ffb9df582e3d9405f33b9ae3ad3b6fd51863ff68c56,0x0604d687830b1dba2cd28c644709475c7a5427aa15278df2db06f59a48bcfb52061f77b6f5b637fc345e2bc64aa7e5ba",
+        "y": "0x11e8a23425631218f3249f1870a8b1a17d82f3224602e433ff04a5525e827582ac5898e81972e21618e41e5c5edc03f5,0x18f405a27aaf3803ef88b9f4d3e5d8eed901d980ebbefb71d5816ac2f1975c513965b7556ee44db2f74202ac178c72e4"
+      },
+      "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]
+      "u": [
+        "0x0034f33d3e0b2bb1e396fa3716a02682ebfefe6b99e986c356c725f4bb787714f66fca8aac0581b538ae255aa69aa8b2,0x169a32a329b295e56423a29a2fa15b259f5e27f992c1391b3d333a4a050d8264cf146b1baa641e609ec748d74d6bfcd5",
+        "0x04028afd52de566f85dec8fd409112d34f09ed3b617b31bb23b0a96d76080d1dce671a910785cf63d4efcc20112f4a67,0x06d00ecaf61b0f972b521b223aeed36d1e4e1e6308b36dea9eeeb917619499d06615c275ea39cc4d7db697e4b697d40e"
+      ]
+    }
+  ]
+}
diff --git a/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SVDW_NU_.json b/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SVDW_NU_.json
new file mode 100644
index 0000000..25c4da1
--- /dev/null
+++ b/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SVDW_NU_.json
@@ -0,0 +1,77 @@
+{
+  "L": "0x40",
+  "Z": "0x0,0x1",
+  "ciphersuite": "BLS12381G2_XMD:SHA-256_SVDW_NU_",
+  "curve": "BLS12381G2",
+  "dst": "BLS12381G2_XMD:SHA-256_SVDW_NU_TESTGEN",
+  "expand": "XMD",
+  "field": {
+    "m": "0x2",
+    "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
+  },
+  "hash": "sha256",
+  "k": "0x80",
+  "map": {
+    "name": "SVDW",
+    "sgn0": "sgn0_be"
+  },
+  "randomOracle": false,
+  "vectors": [
+    {
+      "P": {
+        "x": "0x164c24901348f035811139a2ad95042bc85bb4b4481309431cd98503c951e9cb8f29d3c4ad0abeb31a3da4062f4b9027,0x0cbca2904d96a263308df43e2767c4165f0357a5f0abf3419acab6fcf2200002b0b018c574f9253716844947f2752c94",
+        "y": "0x12b642193b7beb7989fe98c06482effb8740d9e744ff317e050f758bf449d6dedfded2eb1b1b3314b8699b9ea41f9fab,0x0219ec9ba08594f6e1582fb6a6ffb795a92551b32191f146296d29497bbafba7b3aae8ab2f012a5780f72cb0d0380a78"
+      },
+      "Q": {
+        "x": "0x033aa78402bfe8ee9117ac5501b55e2d1cc133a36ff2351b0176e7f44b009260f9f984be5aff18207a751fee8347250f,0x0f6512f645a015b2917bcef9407299bc46e57344ec24877b681b2c7b3c8171bbd0efe0074fe5eaa7fc74983494f90521",
+        "y": "0x02ed21e9ce037653ba12b3996f085847db2daf80e0033013a67667c6c53983ee76af4946da08c7186164ed50d225551f,0x05f95d63b6f2d45bc79824359754d23c795f98be958384829ea72b473525f58bef08bbf1c09ad153628f2d3ca9f44bdc"
+      },
+      "msg": "",
+      "u": [
+        "0x12e76b1034fcb8958d47b2ff763642841556e09d524a6e1ac146009e7b0a60e859567d52629ea27abc86996632970e99,0x005f69bf4eb6ef49bd04d4ca394c77b9ad359646e2ed36e013dc9491a64f2d1207734d4b91b53fd71a32d9e966d46dce"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x14662fda486645b71a497576120b99cd0c8fd72c52ade4b13ce4de57ca05acba0facf6bdc1a230539a96e97fbfdef9fd,0x0646f0fb608d82ba68075d6af75c61b762c5b47de2c620822e88acd296fcfecf125113392b582c48c9c4cec645e7e817",
+        "y": "0x097b57eed2bdcc07cf6518cc582af8604d284f51447d572f3c8b3c36c13f2f5ab3448d06a5e433a0a4753163469977b4,0x0d0e9f7c5c51da1a8b3211052b0562b0f19690027d2bf0df3f662ec9743b0b39c6b9be5b5bf6d224f7eff946b3c4a149"
+      },
+      "Q": {
+        "x": "0x065cca134296b189b4587c38490d674900cac3cb7a6e14ff96f415eae6ce4ee51696910182471d2542b86bc707a40230,0x088edc31bb54b91389c1c057cc5b69e62eb06f72c3184834ce60bcdf1ee885ad8e03005230bfb15c89109209d8c7671b",
+        "y": "0x17c0ef47c0c7960baec68b33d1ce4e9a54c2c770956cc43afe93d8b4bb07d5205fca8f785d55010111367b529a13a67b,0x121a0b8aa8dc49fd8da99eb93d9d736e01e65a0e72c9144b3ba87416fdf125a355f38b5d98e98c20744cbae1c344386f"
+      },
+      "msg": "abc",
+      "u": [
+        "0x108fd37b239357d783a30c9aeea3129316f236cfa8e279bbd921ea7c642ffbb69e4f731cbee57b2df7b17e9ec19c8c91,0x110587325ae905e360ad84368fe6aed5b8636bab77eaac3921c0468154c5d72ddd3ca2e9d57d393d55929344a34215c2"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x0816a09925c45e39c3c04d3fdb331613f7308a0d5dec8e5496e4aec5e5a67458532f25dd07c6a793bc4be8be17a9fd56,0x0addce3d065de3f0e1b4ccad3e503b570412818094d1639329e7f4c6ad759bdcaff01f234ec3f1ebe71209a0773e4ca9",
+        "y": "0x1214b5de271352309fd0091b72eecdb6224f58af9c76ae47a2b21512fe194d5c08278bbfbbd3468ba4ef3abbdb0ffcf0,0x105bd74edfcf83e10663800fc4aca1f2de2e3197b6ad91c246c3c9828e24c44384747b95a8d156da7063df00546a6bad"
+      },
+      "Q": {
+        "x": "0x02da0a79d42b538d42ffc67176681d80ca8d0dc26974d3a30440ee92a362c13bbf737b414fbdf2fd8ce635a396e79ff0,0x0e91dd04cd2d2e112e503efec26f84183fca55101c36fa41226c72eab8e398a189c9cf2f4eda77f7c9a38a09a563b2f1",
+        "y": "0x0d3d3ca0d4ec0aff793a69c22e2abf333e8efc5a72b1d5179c19479e191daff895c95f32669672a1d3df38b1a184f8e8,0x069ab87711da7286d7c75f8cdd4b1e2719554d7866139e948851bc14d7e9124f6f969118bdd2ae3db9d8f997b59717e2"
+      },
+      "msg": "abcdef0123456789",
+      "u": [
+        "0x183b05a0844e652153f50ef0ea7e12bd9174707faf7fa3ac3d4408e8f06b0c8bae4aa322a71614f7d0380986632f2261,0x011b40d28c312f5f62289fe083c493c84ac5c2e66f8be782c1d50493d30bab14fc88966dfe4d51f53049d1e7b221e0e7"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x0eb76ebb9399768c509108314b557659a38bc8187e7ab742adf8c672da98e2a7c9714e0e885070ccb7fbbabebf7a7bbb,0x04049cf80dd5f3a2734402d6ef6ce67f35189a4ace622dae7cf02b8b9662cd58f05dde5b6920fb93c6126c5895e9a3a5",
+        "y": "0x022d29c941c40481c8496f4b1df9fb708103c3170e99ffe41942e79ce8b0e35ecdce0e9281da60a685bafe07f9a635a7,0x023c57e63304ec8997a2b35ab78a2d060ffde49ff0235058ffdbf129946672e518bb31506c53dad9c8a30b751b6181bf"
+      },
+      "Q": {
+        "x": "0x1764c36ebfdb830ad6bed870087c5969cc95e16ca57d572047f8bb17ba961d2264c86726ef69ce6122a4459a6025461c,0x0d27880f35312ee235f2dc9208cc679679261e7f909072c5b71b14e369e20dc0c52459ace34c23aa6842e9b4663073e7",
+        "y": "0x07e98c5d7eadaa88299ee4ecccf848ca9492db725c45696596c50000cdf4852cee8d4a0e3ec0ca4d7b048ab0d92a4dee,0x00b35c992495fa745f8a3b765a2e7d7ee06e7c4cdd6d26be78b06231a98cf9fd6b585d24b6f7a659c45e1acd64082af5"
+      },
+      "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]
+      "u": [
+        "0x09619086497c2a6933c45e2d330c560562d87d3b2f77f1d2da51525fb630a0ba2bd03ea9ebeb65778a65b29f1092a99d,0x0c3c45a61a7938b2a8a645c48c37a2e2e957b5ef5bbe3f661e8bdcb50e962a548862335c8503d73c1d28c5dc598ea29f"
+      ]
+    }
+  ]
+}
diff --git a/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SVDW_RO_.json b/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SVDW_RO_.json
new file mode 100644
index 0000000..6d1955a
--- /dev/null
+++ b/src/test_utils/hash_to_curve_vectors/BLS12381G2_XMD:SHA-256_SVDW_RO_.json
@@ -0,0 +1,97 @@
+{
+  "L": "0x40",
+  "Z": "0x0,0x1",
+  "ciphersuite": "BLS12381G2_XMD:SHA-256_SVDW_RO_",
+  "curve": "BLS12381G2",
+  "dst": "BLS12381G2_XMD:SHA-256_SVDW_RO_TESTGEN",
+  "expand": "XMD",
+  "field": {
+    "m": "0x2",
+    "p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
+  },
+  "hash": "sha256",
+  "k": "0x80",
+  "map": {
+    "name": "SVDW",
+    "sgn0": "sgn0_be"
+  },
+  "randomOracle": true,
+  "vectors": [
+    {
+      "P": {
+        "x": "0x0d2b7cb4e1b0f001de23bda54652814186434637442e61bd7ae665f78e3e8429a3b0abf727b6ffc1a7d5d7f5683c2517,0x0c6519468850b1b6b34c2ac7a67166d9c2b842df09105c8644d6d9be03880c56452e26dc82ee93d0db99946acc2675f4",
+        "y": "0x063e54fd52fde42a5d4a0739ce89956deb0aa4721237b581a79af5e6e847dc047e0b0a41502975e4c15bed99cdcdf0e8,0x0a04fe752f02d57217f2eca100582f9f60fe464aaec2e94624ff22a2522d562fc251e3e962b00f2a7cb544bad462487e"
+      },
+      "Q0": {
+        "x": "0x0440b705d56079686eace2e3d2fc6c26ed4349ddb574bbb13ef5c9c25c7a757b43a1243ecfc62f9cdd169d6360c21cad,0x061b98df942271495e7259a7010ed74c6bdf0f83fe225c7e60fa91b3699d32a99afa440f1ec380aacc8d703649631246",
+        "y": "0x12964591a5a374d4ed6f1306e1889d8ab5259e88928cf0900c6f4fc64ca2cd7d1cc992166f0515c7ce53dd52efe3ded2,0x1440790373a4369524884f589eff8c3ddeb82ad6032446072e75c3b09a300bfd9303a4794f6676ff2c73c796f9110557"
+      },
+      "Q1": {
+        "x": "0x01146e5fe16047ea58369ff4e6f53ada22c23260cb46b4ac1b24a7054691835c6f45e3a8218ed67c68a2c492b35a89b6,0x196e511b97c1f3802e37478224bb5ce12082cee5dd8e5630ff194d8537c7b30ace746d39c051cf3f6a836a861f0fca71",
+        "y": "0x16860436cd82bd22c166a92a38bae79036e357ce185ddcc481cef46edf398d377b5d85f38325be63270cf09c31329583,0x067e5d9c24201fa688bb9101b96fff337625778d0a6cb00495fa5aa8840bfed134b7b1bbd03979538b9b9bb5222b8a99"
+      },
+      "msg": "",
+      "u": [
+        "0x178a86886823673336e71d5cb95ef38381506d64e1251fe3f66c2fae08f5c1b5d1f01a0f09cbb29e8d776345c7601941,0x1200915fc80bdf41b2723f5051a642fee4f548ebdd6f90da9d34d8f477fd17f84921be12497be94b9061bcc9e977a958",
+        "0x0c2b3e012f715a94fc08bd3757ba7c979d848c9719264d2a6b07f03be4f236da0f5017ee8b92fa4fac3ab88c64cce667,0x099e1f36e1dada16e9277160ba74552efaa0c939a629c16fc8ebda75421af560a7ede7cd4423f00ffc1a12c07ab05fa0"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x0c2e41838e536c79576d6b34974204b591f0127354eac121b79029886a405615b273a6e1a78476d5d824d781b885af26,0x0dca051f4d106f072564729f74969ab9e557760e14c67c55ed38fb7e2f3f4b26af1ce227c963fd06a5c5c2745f082415",
+        "y": "0x12e8f2e906c3bac97820bd1cfad278d03321ba3d650a93cea80d4eea70271aff8f145dc6d7c32e4e945b4a213a551871,0x006d694e36dc47f3761ffc22d8a3cc66a9abe8851ad7dc42630a573f569692d46de02256b9bf98f1066be5ce38d97836"
+      },
+      "Q0": {
+        "x": "0x056eff68c169148bf3cf4a1e3dcd47d05f172f54f0f0906759b98ee48897090c16127085c2f9a16dec9ef0b73e1e98ae,0x0245cec38af51ed121d2d2f9ba21469335fb2eddc2c084386477c53d2999c65182d43285464927b5f63465599955b283",
+        "y": "0x1597b2110dc452b8083728a6ed061340b27cef4b2f96c7c4c74e3f090577407f2d8e72dd12e5a642eb0e0116d50d2900,0x040a92bbe9662fa154a7706b0b208bbc5083e2909de5e0feb243c4aeb4c2abd9201d970fd586fe9fc3f7b70deeca38c0"
+      },
+      "Q1": {
+        "x": "0x0a231b6efadfd4cd9243b2d5f8d9820943f6616bd34dc48b677783f0d298211d06b9f25e7ebaf5c3d53009c99b2371bb,0x0397e8e732c84a631df79723dc4ce49aeb1a08857cfd4272ca2b41d840862588fb75ac2555f127b3852942d5be5d8f32",
+        "y": "0x0576ad544c7dff5cfb6fe52857e89f1ad5ee0f703826471a18c6e8a28f538a87de1d02cca1a4aa7c1a90f0f2e1efe9fa,0x16eae9ad925c008d00832bad8830eaf05a2e693ddcf1fbb5d2702515fbbac163f0b12369e5466bcb30153616cf1a9021"
+      },
+      "msg": "abc",
+      "u": [
+        "0x0ecb785eea491ba407b29812dd080692d7f654fa9be80e1b930f90a2764157ec6522aec0ebeca35a440b524b1efe2ade,0x02f7de24231dbff773ec0ebdf9b1b7d84713ef0eff9ff8bc356163c34bc42e373abebb437568b9b8a7a9622f52f8f64d",
+        "0x09ed2dafb8141db58cf0e3038c1b5969742a15dfd3a8de689309ad49477a8c4d45f0b3029d216bef8197a615b89ee53d,0x0da9396afba6386ea945d8881d5d1b4e892ff506940d11f1c14e11008650abf458d6423185935f13e5304ac325996fed"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x103b9ec29f230ce504d06b3d9efbebd5ef5cda2bbdbd09e66ed9979a84dd4ccbb86bdfdb4e9673b75af702cd933b9938,0x17bb4ca24579cf87c16a554bc92497a67390971ad25d60a09befa7799b01676cdb30308ec0ddae9e4a5ca485200bc01f",
+        "y": "0x161a632c2847e27715f550ea93ac9897dab5d7ad483cbf180efefaa232f2bba706bff16b1f1c3e0b9da9ff39e160e42f,0x047f0b3db455080f13114bb2bbe37d40abdc9850bdc724f27a9ffa7bbd8307739fa1b803d69c30da64586c102842a3ae"
+      },
+      "Q0": {
+        "x": "0x0ad81631edcdf51ac0df8426a5d49192326f94244df93b8f990b9d23c2fd654b8f310a8bc0b136b649271a2ed598aeb7,0x106a7d7e84ca907f28cdf2e2ab98a49d53aa70f512afc0c764fdffe778ff3538992546decf48142a790700c6eee5db70",
+        "y": "0x0d9c4f07a23d596b3735a7e275a4a8ed3ac7b6849d83adf78b2743c6cb180232d5f180f7e32422c5e81039f9d48f9cb3,0x0b4ee768a0d0525872db9e4c511da298691aa7a3360a38cfb89691da7c9a314a8d461e8cc8935d08b6fed60ef149e9d9"
+      },
+      "Q1": {
+        "x": "0x1351d14d9737ba5ded8fee48ee1e45c823eaec7b58600a5af7c3543c0b450c565db1e1d37732182dbcbbc31ea33e3e1f,0x0c5ddcd88ebee0861a91dde563a6785752fbc5f4e079665ca2c0567dac8f203ed81f609e27e223938a55534ba8693657",
+        "y": "0x146bbafa353ed101672c21e98fb2e1aa6f438c918d963b4f05e80c36cac3769ad25b49720b8566e1ea1eecff77b010cb,0x02613eb3f365b783461d0edc9b3212495ae6f4de190150b94bbecaa6d631a6ccdf09335ac6f6aba57d70ce06d1a6d615"
+      },
+      "msg": "abcdef0123456789",
+      "u": [
+        "0x15788d0f014f083f4a6bad1e3ec01905fb81328fac060d575f9220ddb7fc495f6cca48e8f46c69153ef0152caa692bbe,0x0c50eaf8096785caee1e08ff3d9d46b8e4c1a1600406d4ab8c9c96c74c4d2b6b90fb5ddb0bdf7adabe0b176f75005df7",
+        "0x0274a965f4d5b3c5e1d35e426df580885eb9aeb4d0997afcb51d2aa908b7a7a5d2b608b4b054fbf77eb2ec8f6854d192,0x07be630ece05ab3536328fc3ccbbc7b8af99542ce0a7241bcf03723f9b45cbe16d3003dd28fcaaa4558f8f8f261a654a"
+      ]
+    },
+    {
+      "P": {
+        "x": "0x125730d27604ffa5f1be4e6357f2dacf59803b6b8ce43b81eb2e42010e0765ea149ed52e5d8ad0847617bb87a3cfdbbc,0x033008748ad6e6d95b68e86d8e786609c1011729606e45ab0b5691eceb3d4c72a80e36792f74e907309f3550bd7b9a6e",
+        "y": "0x180de292e84a7ecc8361490a3f0d8bcf834e12d68529e437df782111f01c8c73c53c9a502eac6aa9dffceae1b7df55b8,0x07225c86be63f0b3eb8acf88ff4133fa9bb8c0b1d50c997ad33f57dcb9cd09cc4676da6f37fd7f4bfaa06bfca9bdb772"
+      },
+      "Q0": {
+        "x": "0x17dab789594c9400a35e44e124a04eb15a273b7ff3385aeb5767cecf3a5d6ce03bee39e9b3d9eedef8dfc1c064465f04,0x0d2b1ae115243ae13e9018e065bfb4215671e2ea86792e78858f012264b642591f4839972f58c00309cbf54a6f2809c5",
+        "y": "0x19d7a9d430474d353c6ccf18db52263cd2fa4685e0194b3ec55672dfea3645c08feb3643dc8a4e995406e3b1108e7275,0x03ac4fde5674418babfbbc6bad412d789f018ad49135ef84e6cbb1b63eb9fb61d7d12caa9ccb4e710d1badff2fe47515"
+      },
+      "Q1": {
+        "x": "0x08d580532d837ce1cd78df4e3668b123eafd519b93e359ba64e0281740742649b76c960e5390e1fd4ea4abcc84afcaca,0x12a30881e5bca0c8f3f8159b1490e215a380eb70c71f14570e886f6ba2de770e26ce6634e00c9a98e14665f61820209f",
+        "y": "0x13192225f9eea2cb2c342e39654a115e5f4b002943cea6067d429e685317be5034edcfe71897728402fc28abb725eca9,0x01c62e5d5325ec325e75e7e0a60a86d489ee543b0709ccd76807cafd1d8041486c185a89f6ca72b96cb7eb193a3d5ef5"
+      },
+      "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]
+      "u": [
+        "0x00a73d9f99c991b8a95d533111700cb468bb9dfc7ecfa8879d278efd4f62907dc735a137ed26eac6fbd4730d16c525e7,0x011de2ac3ddd0fce82f81ff1506a4da615a545f5f1a38d76493489d043dec0298423741a607fd45b57526b2dbf7c0512",
+        "0x054e5964345b7e40826826b6c0773ae205596eed3f430649873afb7ff36a0a4583d947b5e1dcaaf64b62067814724329,0x0c022493d6de52b27b95ba1097b803e2834ce69555e25bda8df9a60bbd7cb8ef2d11eea6ea85856626a1f35c2ccccb95"
+      ]
+    }
+  ]
+}
diff --git a/src/test_utils/mod.rs b/src/test_utils/mod.rs
index 4c85c50..3ed8643 100644
--- a/src/test_utils/mod.rs
+++ b/src/test_utils/mod.rs
@@ -1,5 +1,13 @@
+use std::fs::File;
+use std::io::BufReader;
+use std::path::PathBuf;
+
 use crate::rand::RAND;
 
+mod test_vector_structs;
+
+pub use self::test_vector_structs::*;
+
 pub fn printbinary(array: &[u8]) {
     for i in 0..array.len() {
         print!("{:02X}", array[i])
@@ -19,3 +27,15 @@ pub fn create_rng() -> RAND {
     rng.seed(100, &raw);
     rng
 }
+
+// Reads the json test files
+pub fn json_reader(file_name: &str) -> BufReader<File> {
+    let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+    file_path_buf.push("src/test_utils/hash_to_curve_vectors/");
+    let mut file_name = String::from(file_name);
+    file_name.push_str(".json");
+    file_path_buf.push(file_name);
+
+    let file = File::open(file_path_buf).unwrap();
+    BufReader::new(file)
+}
diff --git a/src/test_utils/test_vector_structs.rs b/src/test_utils/test_vector_structs.rs
new file mode 100644
index 0000000..5ea34fd
--- /dev/null
+++ b/src/test_utils/test_vector_structs.rs
@@ -0,0 +1,44 @@
+#[derive(Deserialize)]
+pub struct Field {
+    pub m: String,
+    pub p: String,
+}
+
+#[derive(Deserialize)]
+pub struct Map {
+    pub name: String,
+    pub sgn0: String,
+}
+
+#[derive(Deserialize)]
+pub struct Point {
+    pub x: String,
+    pub y: String,
+}
+
+#[derive(Deserialize)]
+#[allow(non_snake_case)]
+pub struct Bls12381Ro {
+    pub L: String,
+    pub Z: String,
+    pub ciphersuite: String,
+    pub curve: String,
+    pub dst: String,
+    pub expand: String,
+    pub field: Field,
+    pub hash: String,
+    pub k: String,
+    pub map: Map,
+    pub randomOracle: bool,
+    pub vectors: Vec<Bls12381RoVectors>,
+}
+
+#[derive(Deserialize)]
+#[allow(non_snake_case)]
+pub struct Bls12381RoVectors {
+    pub P: Point,
+    pub Q0: Point,
+    pub Q1: Point,
+    pub msg: String,
+    pub u: Vec<String>,
+}