amcl-milagro 3.2.6

The Apache Milagro Cryptographic Library (version 3)
Documentation
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

extern crate amcl;

use std::io;
use std::str;

use amcl::rand::RAND;
use amcl::types::CurveType;

pub fn printbinary(array: &[u8]) {
    for i in 0..array.len() {
        print!("{:02X}", array[i])
    }
    println!("")
}

fn ecdh_ed25519(mut rng: &mut RAND) {
    //use amcl::ed25519;
    use amcl::ed25519::ecdh;
    use amcl::ed25519::ecp;

    let pw = "M0ng00se";
    let pp: &[u8] = b"M0ng00se";
    const EFS: usize = ecdh::EFS;
    const EGS: usize = ecdh::EGS;
    const EAS: usize = ecp::AESKEY;

    let sha = ecp::HASH_TYPE;
    let mut salt: [u8; 8] = [0; 8];
    let mut s1: [u8; EGS] = [0; EGS];
    let mut w0: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
    let mut w1: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
    let mut z0: [u8; EFS] = [0; EFS];
    let mut z1: [u8; EFS] = [0; EFS];
    let mut key: [u8; EAS] = [0; EAS];
    let mut cs: [u8; EGS] = [0; EGS];
    let mut ds: [u8; EGS] = [0; EGS];
    let mut m: Vec<u8> = vec![0; 32]; // array that could be of any length. So use heap.
    let mut p1: [u8; 3] = [0; 3];
    let mut p2: [u8; 4] = [0; 4];
    let mut v: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
    let mut t: [u8; 12] = [0; 12];

    for i in 0..8 {
        salt[i] = (i + 1) as u8
    } // set Salt

    println!("\nTesting ECDH/ECDSA/ECIES");
    println!("Alice's Passphrase= {}", pw);

    let mut s0: [u8; EFS] = [0; EGS];
    ecdh::pbkdf2(sha, pp, &salt, 1000, EGS, &mut s0);

    print!("Alice's private key= 0x");
    printbinary(&s0);

    /* Generate Key pair S/W */
    ecdh::key_pair_generate(None, &mut s0, &mut w0);

    print!("Alice's public key= 0x");
    printbinary(&w0);

    let mut res = ecdh::public_key_validate(&w0);
    if res != 0 {
        println!("ECP Public Key is invalid!");
        return;
    }

    /* Random private key for other party */
    ecdh::key_pair_generate(Some(&mut rng), &mut s1, &mut w1);

    print!("Servers private key= 0x");
    printbinary(&s1);

    print!("Servers public key= 0x");
    printbinary(&w1);

    res = ecdh::public_key_validate(&w1);
    if res != 0 {
        println!("ECP Public Key is invalid!");
        return;
    }
    /* Calculate common key using DH - IEEE 1363 method */

    ecdh::ecpsvdp_dh(&s0, &w1, &mut z0);
    ecdh::ecpsvdp_dh(&s1, &w0, &mut z1);

    let mut same = true;
    for i in 0..EFS {
        if z0[i] != z1[i] {
            same = false
        }
    }

    if !same {
        println!("*** ECPSVDP-DH Failed");
        return;
    }

    ecdh::kdf2(sha, &z0, None, EAS, &mut key);

    print!("Alice's DH Key=  0x");
    printbinary(&key);
    print!("Servers DH Key=  0x");
    printbinary(&key);

    if ecp::CURVETYPE != CurveType::MONTGOMERY {
        for i in 0..17 {
            m[i] = i as u8
        }

        println!("Testing ECIES");

        p1[0] = 0x0;
        p1[1] = 0x1;
        p1[2] = 0x2;
        p2[0] = 0x0;
        p2[1] = 0x1;
        p2[2] = 0x2;
        p2[3] = 0x3;

        let cc = ecdh::ecies_encrypt(sha, &p1, &p2, &mut rng, &w1, &m[0..17], &mut v, &mut t);

        if let Some(mut c) = cc {
            println!("Ciphertext= ");
            print!("V= 0x");
            printbinary(&v);
            print!("C= 0x");
            printbinary(&c);
            print!("T= 0x");
            printbinary(&t);

            let mm = ecdh::ecies_decrypt(sha, &p1, &p2, &v, &mut c, &t, &s1);
            if let Some(rm) = mm {
                println!("Decryption succeeded");
                print!("Message is 0x");
                printbinary(&rm);
            } else {
                println!("*** ECIES Decryption Failed");
                return;
            }
        } else {
            println!("*** ECIES Encryption Failed");
            return;
        }

        println!("Testing ECDSA");

        if ecdh::ecpsp_dsa(sha, &mut rng, &s0, &m[0..17], &mut cs, &mut ds) != 0 {
            println!("***ECDSA Signature Failed");
            return;
        }
        println!("Signature= ");
        print!("C= 0x");
        printbinary(&cs);
        print!("D= 0x");
        printbinary(&ds);

        if ecdh::ecpvp_dsa(sha, &w0, &m[0..17], &cs, &ds) != 0 {
            println!("***ECDSA Verification Failed");
            return;
        } else {
            println!("ECDSA Signature/Verification succeeded ")
        }
    }
}

fn ecdh_nist256(mut rng: &mut RAND) {
    //use amcl::nist256;
    use amcl::nist256::ecdh;
    use amcl::nist256::ecp;

    let pw = "M0ng00se";
    let pp: &[u8] = b"M0ng00se";
    const EFS: usize = ecdh::EFS;
    const EGS: usize = ecdh::EGS;
    const EAS: usize = ecp::AESKEY;

    let sha = ecp::HASH_TYPE;
    let mut salt: [u8; 8] = [0; 8];
    let mut s1: [u8; EGS] = [0; EGS];
    let mut w0: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
    let mut w1: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
    let mut z0: [u8; EFS] = [0; EFS];
    let mut z1: [u8; EFS] = [0; EFS];
    let mut key: [u8; EAS] = [0; EAS];
    let mut cs: [u8; EGS] = [0; EGS];
    let mut ds: [u8; EGS] = [0; EGS];
    let mut m: Vec<u8> = vec![0; 32]; // array that could be of any length. So use heap.
    let mut p1: [u8; 3] = [0; 3];
    let mut p2: [u8; 4] = [0; 4];
    let mut v: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
    let mut t: [u8; 12] = [0; 12];

    for i in 0..8 {
        salt[i] = (i + 1) as u8
    } // set Salt

    println!("\nTesting ECDH/ECDSA/ECIES");
    println!("Alice's Passphrase= {}", pw);

    let mut s0: [u8; EFS] = [0; EGS];
    ecdh::pbkdf2(sha, pp, &salt, 1000, EGS, &mut s0);

    print!("Alice's private key= 0x");
    printbinary(&s0);

    /* Generate Key pair S/W */
    ecdh::key_pair_generate(None, &mut s0, &mut w0);

    print!("Alice's public key= 0x");
    printbinary(&w0);

    let mut res = ecdh::public_key_validate(&w0);
    if res != 0 {
        println!("ECP Public Key is invalid!");
        return;
    }

    /* Random private key for other party */
    ecdh::key_pair_generate(Some(&mut rng), &mut s1, &mut w1);

    print!("Servers private key= 0x");
    printbinary(&s1);

    print!("Servers public key= 0x");
    printbinary(&w1);

    res = ecdh::public_key_validate(&w1);
    if res != 0 {
        println!("ECP Public Key is invalid!");
        return;
    }
    /* Calculate common key using DH - IEEE 1363 method */

    ecdh::ecpsvdp_dh(&s0, &w1, &mut z0);
    ecdh::ecpsvdp_dh(&s1, &w0, &mut z1);

    let mut same = true;
    for i in 0..EFS {
        if z0[i] != z1[i] {
            same = false
        }
    }

    if !same {
        println!("*** ECPSVDP-DH Failed");
        return;
    }

    ecdh::kdf2(sha, &z0, None, EAS, &mut key);

    print!("Alice's DH Key=  0x");
    printbinary(&key);
    print!("Servers DH Key=  0x");
    printbinary(&key);

    if ecp::CURVETYPE != CurveType::MONTGOMERY {
        for i in 0..17 {
            m[i] = i as u8
        }

        println!("Testing ECIES");

        p1[0] = 0x0;
        p1[1] = 0x1;
        p1[2] = 0x2;
        p2[0] = 0x0;
        p2[1] = 0x1;
        p2[2] = 0x2;
        p2[3] = 0x3;

        let cc = ecdh::ecies_encrypt(sha, &p1, &p2, &mut rng, &w1, &m[0..17], &mut v, &mut t);

        if let Some(mut c) = cc {
            println!("Ciphertext= ");
            print!("V= 0x");
            printbinary(&v);
            print!("C= 0x");
            printbinary(&c);
            print!("T= 0x");
            printbinary(&t);

            let mm = ecdh::ecies_decrypt(sha, &p1, &p2, &v, &mut c, &t, &s1);
            if let Some(rm) = mm {
                println!("Decryption succeeded");
                print!("Message is 0x");
                printbinary(&rm);
            } else {
                println!("*** ECIES Decryption Failed");
                return;
            }
        } else {
            println!("*** ECIES Encryption Failed");
            return;
        }

        println!("Testing ECDSA");

        if ecdh::ecpsp_dsa(sha, &mut rng, &s0, &m[0..17], &mut cs, &mut ds) != 0 {
            println!("***ECDSA Signature Failed");
            return;
        }
        println!("Signature= ");
        print!("C= 0x");
        printbinary(&cs);
        print!("D= 0x");
        printbinary(&ds);

        if ecdh::ecpvp_dsa(sha, &w0, &m[0..17], &cs, &ds) != 0 {
            println!("***ECDSA Verification Failed");
            return;
        } else {
            println!("ECDSA Signature/Verification succeeded ")
        }
    }
}

fn ecdh_goldilocks(mut rng: &mut RAND) {
    //use amcl::goldilocks;
    use amcl::goldilocks::ecdh;
    use amcl::goldilocks::ecp;

    let pw = "M0ng00se";
    let pp: &[u8] = b"M0ng00se";
    const EFS: usize = ecdh::EFS;
    const EGS: usize = ecdh::EGS;
    const EAS: usize = ecp::AESKEY;

    let sha = ecp::HASH_TYPE;
    let mut salt: [u8; 8] = [0; 8];
    let mut s1: [u8; EGS] = [0; EGS];
    let mut w0: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
    let mut w1: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
    let mut z0: [u8; EFS] = [0; EFS];
    let mut z1: [u8; EFS] = [0; EFS];
    let mut key: [u8; EAS] = [0; EAS];
    let mut cs: [u8; EGS] = [0; EGS];
    let mut ds: [u8; EGS] = [0; EGS];
    let mut m: Vec<u8> = vec![0; 32]; // array that could be of any length. So use heap.
    let mut p1: [u8; 3] = [0; 3];
    let mut p2: [u8; 4] = [0; 4];
    let mut v: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
    let mut t: [u8; 12] = [0; 12];

    for i in 0..8 {
        salt[i] = (i + 1) as u8
    } // set Salt

    println!("\nTesting ECDH/ECDSA/ECIES");
    println!("Alice's Passphrase= {}", pw);

    let mut s0: [u8; EFS] = [0; EGS];
    ecdh::pbkdf2(sha, pp, &salt, 1000, EGS, &mut s0);

    print!("Alice's private key= 0x");
    printbinary(&s0);

    /* Generate Key pair S/W */
    ecdh::key_pair_generate(None, &mut s0, &mut w0);

    print!("Alice's public key= 0x");
    printbinary(&w0);

    let mut res = ecdh::public_key_validate(&w0);
    if res != 0 {
        println!("ECP Public Key is invalid!");
        return;
    }

    /* Random private key for other party */
    ecdh::key_pair_generate(Some(&mut rng), &mut s1, &mut w1);

    print!("Servers private key= 0x");
    printbinary(&s1);

    print!("Servers public key= 0x");
    printbinary(&w1);

    res = ecdh::public_key_validate(&w1);
    if res != 0 {
        println!("ECP Public Key is invalid!");
        return;
    }
    /* Calculate common key using DH - IEEE 1363 method */

    ecdh::ecpsvdp_dh(&s0, &w1, &mut z0);
    ecdh::ecpsvdp_dh(&s1, &w0, &mut z1);

    let mut same = true;
    for i in 0..EFS {
        if z0[i] != z1[i] {
            same = false
        }
    }

    if !same {
        println!("*** ECPSVDP-DH Failed");
        return;
    }

    ecdh::kdf2(sha, &z0, None, EAS, &mut key);

    print!("Alice's DH Key=  0x");
    printbinary(&key);
    print!("Servers DH Key=  0x");
    printbinary(&key);

    if ecp::CURVETYPE != CurveType::MONTGOMERY {
        for i in 0..17 {
            m[i] = i as u8
        }

        println!("Testing ECIES");

        p1[0] = 0x0;
        p1[1] = 0x1;
        p1[2] = 0x2;
        p2[0] = 0x0;
        p2[1] = 0x1;
        p2[2] = 0x2;
        p2[3] = 0x3;

        let cc = ecdh::ecies_encrypt(sha, &p1, &p2, &mut rng, &w1, &m[0..17], &mut v, &mut t);

        if let Some(mut c) = cc {
            println!("Ciphertext= ");
            print!("V= 0x");
            printbinary(&v);
            print!("C= 0x");
            printbinary(&c);
            print!("T= 0x");
            printbinary(&t);

            let mm = ecdh::ecies_decrypt(sha, &p1, &p2, &v, &mut c, &t, &s1);
            if let Some(rm) = mm {
                println!("Decryption succeeded");
                print!("Message is 0x");
                printbinary(&rm);
            } else {
                println!("*** ECIES Decryption Failed");
                return;
            }
        } else {
            println!("*** ECIES Encryption Failed");
            return;
        }

        println!("Testing ECDSA");

        if ecdh::ecpsp_dsa(sha, &mut rng, &s0, &m[0..17], &mut cs, &mut ds) != 0 {
            println!("***ECDSA Signature Failed");
            return;
        }
        println!("Signature= ");
        print!("C= 0x");
        printbinary(&cs);
        print!("D= 0x");
        printbinary(&ds);

        if ecdh::ecpvp_dsa(sha, &w0, &m[0..17], &cs, &ds) != 0 {
            println!("***ECDSA Verification Failed");
            return;
        } else {
            println!("ECDSA Signature/Verification succeeded ")
        }
    }
}

fn mpin_bn254(mut rng: &mut RAND) {
    //use amcl::bn254;
    use amcl::bn254::ecp;
    use amcl::bn254::mpin;
    pub const PERMITS: bool = true;
    pub const PINERROR: bool = true;
    pub const FULL: bool = true;
    //pub const SINGLE_PASS:bool=false;

    const EFS: usize = mpin::EFS;
    const EGS: usize = mpin::EGS;

    let mut s: [u8; EGS] = [0; EGS];
    const RM: usize = EFS as usize;
    let mut hcid: [u8; RM] = [0; RM];
    let mut hsid: [u8; RM] = [0; RM];

    const G1S: usize = 2 * EFS + 1; /* Group 1 Size */
    const G2S: usize = 4 * EFS; /* Group 2 Size */
    const EAS: usize = ecp::AESKEY;

    let mut sst: [u8; G2S] = [0; G2S];
    let mut token: [u8; G1S] = [0; G1S];
    let mut permit: [u8; G1S] = [0; G1S];
    let mut g1: [u8; 12 * EFS] = [0; 12 * EFS];
    let mut g2: [u8; 12 * EFS] = [0; 12 * EFS];
    let mut xid: [u8; G1S] = [0; G1S];
    let mut xcid: [u8; G1S] = [0; G1S];
    let mut x: [u8; EGS] = [0; EGS];
    let mut y: [u8; EGS] = [0; EGS];
    let mut sec: [u8; G1S] = [0; G1S];
    let mut r: [u8; EGS] = [0; EGS];
    let mut z: [u8; G1S] = [0; G1S];
    let mut hid: [u8; G1S] = [0; G1S];
    let mut htid: [u8; G1S] = [0; G1S];
    let mut rhid: [u8; G1S] = [0; G1S];
    let mut w: [u8; EGS] = [0; EGS];
    let mut t: [u8; G1S] = [0; G1S];
    let mut e: [u8; 12 * EFS] = [0; 12 * EFS];
    let mut f: [u8; 12 * EFS] = [0; 12 * EFS];
    let mut h: [u8; RM] = [0; RM];
    let mut ck: [u8; EAS] = [0; EAS];
    let mut sk: [u8; EAS] = [0; EAS];

    let sha = ecp::HASH_TYPE;

    println!("\nTesting MPIN - PIN is 1234");
    /* Trusted Authority set-up */

    mpin::random_generate(&mut rng, &mut s);
    print!("Master Secret s: 0x");
    printbinary(&s);

    /* Create Client Identity */
    let name = "testUser@miracl.com";
    let client_id = name.as_bytes();

    print!("Client ID= ");
    printbinary(&client_id);

    mpin::hash_id(sha, &client_id, &mut hcid); /* Either Client or TA calculates Hash(ID) - you decide! */

    /* Client and Server are issued secrets by DTA */
    mpin::get_server_secret(&s, &mut sst);
    print!("Server Secret SS: 0x");
    printbinary(&sst);

    mpin::get_client_secret(&mut s, &hcid, &mut token);
    print!("Client Secret CS: 0x");
    printbinary(&token);

    /* Client extracts PIN from secret to create Token */
    let pin: i32 = 1234;
    println!("Client extracts PIN= {}", pin);
    let mut rtn = mpin::extract_pin(sha, &client_id, pin, &mut token);
    if rtn != 0 {
        println!("FAILURE: EXTRACT_PIN rtn: {}", rtn);
    }

    print!("Client Token TK: 0x");
    printbinary(&token);

    if FULL {
        mpin::precompute(&token, &hcid, &mut g1, &mut g2);
    }

    let mut date = 0;
    if PERMITS {
        date = mpin::today();
        /* Client gets "Time Token" permit from DTA */

        mpin::get_client_permit(sha, date, &s, &hcid, &mut permit);
        print!("Time Permit TP: 0x");
        printbinary(&permit);

        /* This encoding makes Time permit look random - Elligator squared */
        mpin::encoding(&mut rng, &mut permit);
        print!("Encoded Time Permit TP: 0x");
        printbinary(&permit);
        mpin::decoding(&mut permit);
        print!("Decoded Time Permit TP: 0x");
        printbinary(&permit);
    }

    print!("\nPIN= ");
    let _ = io::Write::flush(&mut io::stdout());
    let mut input_text = String::new();
    let _ = io::stdin().read_line(&mut input_text);

    let pin = input_text.trim().parse::<usize>().unwrap();

    println!("MPIN Multi Pass");
    /* Send U=x.ID to server, and recreate secret from token and pin */
    rtn = mpin::client_1(
        sha,
        date,
        &client_id,
        Some(&mut rng),
        &mut x,
        pin,
        &token,
        &mut sec,
        Some(&mut xid[..]),
        Some(&mut xcid[..]),
        Some(&permit[..]),
    );
    if rtn != 0 {
        println!("FAILURE: CLIENT_1 rtn: {}", rtn);
    }

    if FULL {
        mpin::hash_id(sha, &client_id, &mut hcid);
        mpin::get_g1_multiple(Some(&mut rng), 1, &mut r, &hcid, &mut z); /* Also Send Z=r.ID to Server, remember random r */
    }

    /* Server calculates H(ID) and H(T|H(ID)) (if time mpin::PERMITS enabled), and maps them to points on the curve HID and HTID resp. */

    mpin::server_1(sha, date, &client_id, &mut hid, Some(&mut htid[..]));

    if date != 0 {
        rhid.clone_from_slice(&htid[..]);
    } else {
        rhid.clone_from_slice(&hid[..]);
    }

    /* Server generates Random number Y and sends it to Client */
    mpin::random_generate(&mut rng, &mut y);

    if FULL {
        mpin::hash_id(sha, &client_id, &mut hsid);
        mpin::get_g1_multiple(Some(&mut rng), 0, &mut w, &rhid, &mut t); /* Also send T=w.ID to client, remember random w  */
    }

    /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
    rtn = mpin::client_2(&x, &y, &mut sec);
    if rtn != 0 {
        println!("FAILURE: CLIENT_2 rtn: {}", rtn);
    }

    /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
    /* If PIN error not required, set E and F = null */

    if !PINERROR {
        rtn = mpin::server_2(
            date,
            &hid,
            Some(&htid[..]),
            &y,
            &sst,
            Some(&xid[..]),
            Some(&xcid[..]),
            &sec,
            None,
            None,
        );
    } else {
        rtn = mpin::server_2(
            date,
            &hid,
            Some(&htid[..]),
            &y,
            &sst,
            Some(&xid[..]),
            Some(&xcid[..]),
            &sec,
            Some(&mut e),
            Some(&mut f),
        );
    }

    if rtn == mpin::BAD_PIN {
        println!("Server says - Bad Pin. I don't know you. Feck off.");
        if PINERROR {
            let err = mpin::kangaroo(&e, &f);
            if err != 0 {
                println!("(Client PIN is out by {})", err)
            }
        }
        return;
    } else {
        println!("Server says - PIN is good! You really are {}", name);
    }

    if FULL {
        let mut pxcid = None;
        if PERMITS {
            pxcid = Some(&xcid[..])
        };

        mpin::hash_all(sha, &hcid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
        mpin::client_key(sha, &g1, &g2, pin, &r, &x, &h, &t, &mut ck);
        print!("Client Key =  0x");
        printbinary(&ck);

        mpin::hash_all(sha, &hsid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
        mpin::server_key(sha, &z, &sst, &w, &h, &hid, &xid, pxcid, &mut sk);
        print!("Server Key =  0x");
        printbinary(&sk);
    }
}

fn mpin_bls383(mut rng: &mut RAND) {
    //use amcl::bls383;
    use amcl::bls383::ecp;
    use amcl::bls383::mpin;
    pub const PERMITS: bool = true;
    pub const PINERROR: bool = true;
    pub const FULL: bool = true;
    //pub const SINGLE_PASS:bool=false;

    const EFS: usize = mpin::EFS;
    const EGS: usize = mpin::EGS;

    let mut s: [u8; EGS] = [0; EGS];
    const RM: usize = EFS as usize;
    let mut hcid: [u8; RM] = [0; RM];
    let mut hsid: [u8; RM] = [0; RM];

    const G1S: usize = 2 * EFS + 1; /* Group 1 Size */
    const G2S: usize = 4 * EFS; /* Group 2 Size */
    const EAS: usize = ecp::AESKEY;

    let mut sst: [u8; G2S] = [0; G2S];
    let mut token: [u8; G1S] = [0; G1S];
    let mut permit: [u8; G1S] = [0; G1S];
    let mut g1: [u8; 12 * EFS] = [0; 12 * EFS];
    let mut g2: [u8; 12 * EFS] = [0; 12 * EFS];
    let mut xid: [u8; G1S] = [0; G1S];
    let mut xcid: [u8; G1S] = [0; G1S];
    let mut x: [u8; EGS] = [0; EGS];
    let mut y: [u8; EGS] = [0; EGS];
    let mut sec: [u8; G1S] = [0; G1S];
    let mut r: [u8; EGS] = [0; EGS];
    let mut z: [u8; G1S] = [0; G1S];
    let mut hid: [u8; G1S] = [0; G1S];
    let mut htid: [u8; G1S] = [0; G1S];
    let mut rhid: [u8; G1S] = [0; G1S];
    let mut w: [u8; EGS] = [0; EGS];
    let mut t: [u8; G1S] = [0; G1S];
    let mut e: [u8; 12 * EFS] = [0; 12 * EFS];
    let mut f: [u8; 12 * EFS] = [0; 12 * EFS];
    let mut h: [u8; RM] = [0; RM];
    let mut ck: [u8; EAS] = [0; EAS];
    let mut sk: [u8; EAS] = [0; EAS];

    let sha = ecp::HASH_TYPE;

    println!("\nTesting MPIN - PIN is 1234");
    /* Trusted Authority set-up */

    mpin::random_generate(&mut rng, &mut s);
    print!("Master Secret s: 0x");
    printbinary(&s);

    /* Create Client Identity */
    let name = "testUser@miracl.com";
    let client_id = name.as_bytes();

    print!("Client ID= ");
    printbinary(&client_id);

    mpin::hash_id(sha, &client_id, &mut hcid); /* Either Client or TA calculates Hash(ID) - you decide! */

    /* Client and Server are issued secrets by DTA */
    mpin::get_server_secret(&s, &mut sst);
    print!("Server Secret SS: 0x");
    printbinary(&sst);

    mpin::get_client_secret(&mut s, &hcid, &mut token);
    print!("Client Secret CS: 0x");
    printbinary(&token);

    /* Client extracts PIN from secret to create Token */
    let pin: i32 = 1234;
    println!("Client extracts PIN= {}", pin);
    let mut rtn = mpin::extract_pin(sha, &client_id, pin, &mut token);
    if rtn != 0 {
        println!("FAILURE: EXTRACT_PIN rtn: {}", rtn);
    }

    print!("Client Token TK: 0x");
    printbinary(&token);

    if FULL {
        mpin::precompute(&token, &hcid, &mut g1, &mut g2);
    }

    let mut date = 0;
    if PERMITS {
        date = mpin::today();
        /* Client gets "Time Token" permit from DTA */

        mpin::get_client_permit(sha, date, &s, &hcid, &mut permit);
        print!("Time Permit TP: 0x");
        printbinary(&permit);

        /* This encoding makes Time permit look random - Elligator squared */
        mpin::encoding(&mut rng, &mut permit);
        print!("Encoded Time Permit TP: 0x");
        printbinary(&permit);
        mpin::decoding(&mut permit);
        print!("Decoded Time Permit TP: 0x");
        printbinary(&permit);
    }

    print!("\nPIN= ");
    let _ = io::Write::flush(&mut io::stdout());
    let mut input_text = String::new();
    let _ = io::stdin().read_line(&mut input_text);

    let pin = input_text.trim().parse::<usize>().unwrap();

    println!("MPIN Multi Pass");
    /* Send U=x.ID to server, and recreate secret from token and pin */
    rtn = mpin::client_1(
        sha,
        date,
        &client_id,
        Some(&mut rng),
        &mut x,
        pin,
        &token,
        &mut sec,
        Some(&mut xid[..]),
        Some(&mut xcid[..]),
        Some(&permit[..]),
    );
    if rtn != 0 {
        println!("FAILURE: CLIENT_1 rtn: {}", rtn);
    }

    if FULL {
        mpin::hash_id(sha, &client_id, &mut hcid);
        mpin::get_g1_multiple(Some(&mut rng), 1, &mut r, &hcid, &mut z); /* Also Send Z=r.ID to Server, remember random r */
    }

    /* Server calculates H(ID) and H(T|H(ID)) (if time mpin::PERMITS enabled), and maps them to points on the curve HID and HTID resp. */

    mpin::server_1(sha, date, &client_id, &mut hid, Some(&mut htid[..]));

    if date != 0 {
        rhid.clone_from_slice(&htid[..]);
    } else {
        rhid.clone_from_slice(&hid[..]);
    }

    /* Server generates Random number Y and sends it to Client */
    mpin::random_generate(&mut rng, &mut y);

    if FULL {
        mpin::hash_id(sha, &client_id, &mut hsid);
        mpin::get_g1_multiple(Some(&mut rng), 0, &mut w, &rhid, &mut t); /* Also send T=w.ID to client, remember random w  */
    }

    /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
    rtn = mpin::client_2(&x, &y, &mut sec);
    if rtn != 0 {
        println!("FAILURE: CLIENT_2 rtn: {}", rtn);
    }

    /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
    /* If PIN error not required, set E and F = null */

    if !PINERROR {
        rtn = mpin::server_2(
            date,
            &hid,
            Some(&htid[..]),
            &y,
            &sst,
            Some(&xid[..]),
            Some(&xcid[..]),
            &sec,
            None,
            None,
        );
    } else {
        rtn = mpin::server_2(
            date,
            &hid,
            Some(&htid[..]),
            &y,
            &sst,
            Some(&xid[..]),
            Some(&xcid[..]),
            &sec,
            Some(&mut e),
            Some(&mut f),
        );
    }

    if rtn == mpin::BAD_PIN {
        println!("Server says - Bad Pin. I don't know you. Feck off.");
        if PINERROR {
            let err = mpin::kangaroo(&e, &f);
            if err != 0 {
                println!("(Client PIN is out by {})", err)
            }
        }
        return;
    } else {
        println!("Server says - PIN is good! You really are {}", name);
    }

    if FULL {
        let mut pxcid = None;
        if PERMITS {
            pxcid = Some(&xcid[..])
        };

        mpin::hash_all(sha, &hcid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
        mpin::client_key(sha, &g1, &g2, pin, &r, &x, &h, &t, &mut ck);
        print!("Client Key =  0x");
        printbinary(&ck);

        mpin::hash_all(sha, &hsid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
        mpin::server_key(sha, &z, &sst, &w, &h, &hid, &xid, pxcid, &mut sk);
        print!("Server Key =  0x");
        printbinary(&sk);
    }
}

fn mpin_bls24(mut rng: &mut RAND) {
    //use amcl::bls24;
    use amcl::bls24::ecp;
    use amcl::bls24::mpin192;

    pub const PERMITS: bool = true;
    pub const PINERROR: bool = true;
    pub const FULL: bool = true;
    //pub const SINGLE_PASS:bool=false;

    const EFS: usize = mpin192::EFS;
    const EGS: usize = mpin192::EGS;

    let mut s: [u8; EGS] = [0; EGS];
    const RM: usize = EFS as usize;
    let mut hcid: [u8; RM] = [0; RM];
    let mut hsid: [u8; RM] = [0; RM];

    const G1S: usize = 2 * EFS + 1; /* Group 1 Size */
    const G2S: usize = 8 * EFS; /* Group 2 Size */
    const EAS: usize = ecp::AESKEY;

    let mut sst: [u8; G2S] = [0; G2S];
    let mut token: [u8; G1S] = [0; G1S];
    let mut permit: [u8; G1S] = [0; G1S];
    let mut g1: [u8; 24 * EFS] = [0; 24 * EFS];
    let mut g2: [u8; 24 * EFS] = [0; 24 * EFS];
    let mut xid: [u8; G1S] = [0; G1S];
    let mut xcid: [u8; G1S] = [0; G1S];
    let mut x: [u8; EGS] = [0; EGS];
    let mut y: [u8; EGS] = [0; EGS];
    let mut sec: [u8; G1S] = [0; G1S];
    let mut r: [u8; EGS] = [0; EGS];
    let mut z: [u8; G1S] = [0; G1S];
    let mut hid: [u8; G1S] = [0; G1S];
    let mut htid: [u8; G1S] = [0; G1S];
    let mut rhid: [u8; G1S] = [0; G1S];
    let mut w: [u8; EGS] = [0; EGS];
    let mut t: [u8; G1S] = [0; G1S];
    let mut e: [u8; 24 * EFS] = [0; 24 * EFS];
    let mut f: [u8; 24 * EFS] = [0; 24 * EFS];
    let mut h: [u8; RM] = [0; RM];
    let mut ck: [u8; EAS] = [0; EAS];
    let mut sk: [u8; EAS] = [0; EAS];

    let sha = ecp::HASH_TYPE;

    println!("\nTesting MPIN - PIN is 1234");
    /* Trusted Authority set-up */

    mpin192::random_generate(&mut rng, &mut s);
    print!("Master Secret s: 0x");
    printbinary(&s);

    /* Create Client Identity */
    let name = "testUser@miracl.com";
    let client_id = name.as_bytes();

    print!("Client ID= ");
    printbinary(&client_id);

    mpin192::hash_id(sha, &client_id, &mut hcid); /* Either Client or TA calculates Hash(ID) - you decide! */

    /* Client and Server are issued secrets by DTA */
    mpin192::get_server_secret(&s, &mut sst);
    print!("Server Secret SS: 0x");
    printbinary(&sst);

    mpin192::get_client_secret(&mut s, &hcid, &mut token);
    print!("Client Secret CS: 0x");
    printbinary(&token);

    /* Client extracts PIN from secret to create Token */
    let pin: i32 = 1234;
    println!("Client extracts PIN= {}", pin);
    let mut rtn = mpin192::extract_pin(sha, &client_id, pin, &mut token);
    if rtn != 0 {
        println!("FAILURE: EXTRACT_PIN rtn: {}", rtn);
    }

    print!("Client Token TK: 0x");
    printbinary(&token);

    if FULL {
        mpin192::precompute(&token, &hcid, &mut g1, &mut g2);
    }

    let mut date = 0;
    if PERMITS {
        date = mpin192::today();
        /* Client gets "Time Token" permit from DTA */

        mpin192::get_client_permit(sha, date, &s, &hcid, &mut permit);
        print!("Time Permit TP: 0x");
        printbinary(&permit);

        /* This encoding makes Time permit look random - Elligator squared */
        mpin192::encoding(&mut rng, &mut permit);
        print!("Encoded Time Permit TP: 0x");
        printbinary(&permit);
        mpin192::decoding(&mut permit);
        print!("Decoded Time Permit TP: 0x");
        printbinary(&permit);
    }

    print!("\nPIN= ");
    let _ = io::Write::flush(&mut io::stdout());
    let mut input_text = String::new();
    let _ = io::stdin().read_line(&mut input_text);

    let pin = input_text.trim().parse::<usize>().unwrap();

    println!("MPIN Multi Pass");
    /* Send U=x.ID to server, and recreate secret from token and pin */
    rtn = mpin192::client_1(
        sha,
        date,
        &client_id,
        Some(&mut rng),
        &mut x,
        pin,
        &token,
        &mut sec,
        Some(&mut xid[..]),
        Some(&mut xcid[..]),
        Some(&permit[..]),
    );
    if rtn != 0 {
        println!("FAILURE: CLIENT_1 rtn: {}", rtn);
    }

    if FULL {
        mpin192::hash_id(sha, &client_id, &mut hcid);
        mpin192::get_g1_multiple(Some(&mut rng), 1, &mut r, &hcid, &mut z); /* Also Send Z=r.ID to Server, remember random r */
    }

    /* Server calculates H(ID) and H(T|H(ID)) (if time mpin192::PERMITS enabled), and maps them to points on the curve HID and HTID resp. */

    mpin192::server_1(sha, date, &client_id, &mut hid, Some(&mut htid[..]));

    if date != 0 {
        rhid.clone_from_slice(&htid[..]);
    } else {
        rhid.clone_from_slice(&hid[..]);
    }

    /* Server generates Random number Y and sends it to Client */
    mpin192::random_generate(&mut rng, &mut y);

    if FULL {
        mpin192::hash_id(sha, &client_id, &mut hsid);
        mpin192::get_g1_multiple(Some(&mut rng), 0, &mut w, &rhid, &mut t); /* Also send T=w.ID to client, remember random w  */
    }

    /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
    rtn = mpin192::client_2(&x, &y, &mut sec);
    if rtn != 0 {
        println!("FAILURE: CLIENT_2 rtn: {}", rtn);
    }

    /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
    /* If PIN error not required, set E and F = null */

    if !PINERROR {
        rtn = mpin192::server_2(
            date,
            &hid,
            Some(&htid[..]),
            &y,
            &sst,
            Some(&xid[..]),
            Some(&xcid[..]),
            &sec,
            None,
            None,
        );
    } else {
        rtn = mpin192::server_2(
            date,
            &hid,
            Some(&htid[..]),
            &y,
            &sst,
            Some(&xid[..]),
            Some(&xcid[..]),
            &sec,
            Some(&mut e),
            Some(&mut f),
        );
    }

    if rtn == mpin192::BAD_PIN {
        println!("Server says - Bad Pin. I don't know you. Feck off.");
        if PINERROR {
            let err = mpin192::kangaroo(&e, &f);
            if err != 0 {
                println!("(Client PIN is out by {})", err)
            }
        }
        return;
    } else {
        println!("Server says - PIN is good! You really are {}", name);
    }

    if FULL {
        let mut pxcid = None;
        if PERMITS {
            pxcid = Some(&xcid[..])
        };

        mpin192::hash_all(sha, &hcid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
        mpin192::client_key(sha, &g1, &g2, pin, &r, &x, &h, &t, &mut ck);
        print!("Client Key =  0x");
        printbinary(&ck);

        mpin192::hash_all(sha, &hsid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
        mpin192::server_key(sha, &z, &sst, &w, &h, &hid, &xid, pxcid, &mut sk);
        print!("Server Key =  0x");
        printbinary(&sk);
    }
}

fn mpin_bls48(mut rng: &mut RAND) {
    //use amcl::bls48;
    use amcl::bls48::ecp;
    use amcl::bls48::mpin256;

    pub const PERMITS: bool = true;
    pub const PINERROR: bool = true;
    pub const FULL: bool = true;
    //pub const SINGLE_PASS:bool=false;

    const EFS: usize = mpin256::EFS;
    const EGS: usize = mpin256::EGS;

    let mut s: [u8; EGS] = [0; EGS];
    const RM: usize = EFS as usize;
    let mut hcid: [u8; RM] = [0; RM];
    let mut hsid: [u8; RM] = [0; RM];

    const G1S: usize = 2 * EFS + 1; /* Group 1 Size */
    const G2S: usize = 16 * EFS; /* Group 2 Size */
    const EAS: usize = ecp::AESKEY;

    let mut sst: [u8; G2S] = [0; G2S];
    let mut token: [u8; G1S] = [0; G1S];
    let mut permit: [u8; G1S] = [0; G1S];
    let mut g1: [u8; 48 * EFS] = [0; 48 * EFS];
    let mut g2: [u8; 48 * EFS] = [0; 48 * EFS];
    let mut xid: [u8; G1S] = [0; G1S];
    let mut xcid: [u8; G1S] = [0; G1S];
    let mut x: [u8; EGS] = [0; EGS];
    let mut y: [u8; EGS] = [0; EGS];
    let mut sec: [u8; G1S] = [0; G1S];
    let mut r: [u8; EGS] = [0; EGS];
    let mut z: [u8; G1S] = [0; G1S];
    let mut hid: [u8; G1S] = [0; G1S];
    let mut htid: [u8; G1S] = [0; G1S];
    let mut rhid: [u8; G1S] = [0; G1S];
    let mut w: [u8; EGS] = [0; EGS];
    let mut t: [u8; G1S] = [0; G1S];
    let mut e: [u8; 48 * EFS] = [0; 48 * EFS];
    let mut f: [u8; 48 * EFS] = [0; 48 * EFS];
    let mut h: [u8; RM] = [0; RM];
    let mut ck: [u8; EAS] = [0; EAS];
    let mut sk: [u8; EAS] = [0; EAS];

    let sha = ecp::HASH_TYPE;

    println!("\nTesting MPIN - PIN is 1234");
    /* Trusted Authority set-up */

    mpin256::random_generate(&mut rng, &mut s);
    print!("Master Secret s: 0x");
    printbinary(&s);

    /* Create Client Identity */
    let name = "testUser@miracl.com";
    let client_id = name.as_bytes();

    print!("Client ID= ");
    printbinary(&client_id);

    mpin256::hash_id(sha, &client_id, &mut hcid); /* Either Client or TA calculates Hash(ID) - you decide! */

    /* Client and Server are issued secrets by DTA */
    mpin256::get_server_secret(&s, &mut sst);
    print!("Server Secret SS: 0x");
    printbinary(&sst);

    mpin256::get_client_secret(&mut s, &hcid, &mut token);
    print!("Client Secret CS: 0x");
    printbinary(&token);

    /* Client extracts PIN from secret to create Token */
    let pin: i32 = 1234;
    println!("Client extracts PIN= {}", pin);
    let mut rtn = mpin256::extract_pin(sha, &client_id, pin, &mut token);
    if rtn != 0 {
        println!("FAILURE: EXTRACT_PIN rtn: {}", rtn);
    }

    print!("Client Token TK: 0x");
    printbinary(&token);

    if FULL {
        mpin256::precompute(&token, &hcid, &mut g1, &mut g2);
    }

    let mut date = 0;
    if PERMITS {
        date = mpin256::today();
        /* Client gets "Time Token" permit from DTA */

        mpin256::get_client_permit(sha, date, &s, &hcid, &mut permit);
        print!("Time Permit TP: 0x");
        printbinary(&permit);

        /* This encoding makes Time permit look random - Elligator squared */
        mpin256::encoding(&mut rng, &mut permit);
        print!("Encoded Time Permit TP: 0x");
        printbinary(&permit);
        mpin256::decoding(&mut permit);
        print!("Decoded Time Permit TP: 0x");
        printbinary(&permit);
    }

    print!("\nPIN= ");
    let _ = io::Write::flush(&mut io::stdout());
    let mut input_text = String::new();
    let _ = io::stdin().read_line(&mut input_text);

    let pin = input_text.trim().parse::<usize>().unwrap();

    println!("MPIN Multi Pass");
    /* Send U=x.ID to server, and recreate secret from token and pin */
    rtn = mpin256::client_1(
        sha,
        date,
        &client_id,
        Some(&mut rng),
        &mut x,
        pin,
        &token,
        &mut sec,
        Some(&mut xid[..]),
        Some(&mut xcid[..]),
        Some(&permit[..]),
    );
    if rtn != 0 {
        println!("FAILURE: CLIENT_1 rtn: {}", rtn);
    }

    if FULL {
        mpin256::hash_id(sha, &client_id, &mut hcid);
        mpin256::get_g1_multiple(Some(&mut rng), 1, &mut r, &hcid, &mut z); /* Also Send Z=r.ID to Server, remember random r */
    }

    /* Server calculates H(ID) and H(T|H(ID)) (if time mpin256::PERMITS enabled), and maps them to points on the curve HID and HTID resp. */

    mpin256::server_1(sha, date, &client_id, &mut hid, Some(&mut htid[..]));

    if date != 0 {
        rhid.clone_from_slice(&htid[..]);
    } else {
        rhid.clone_from_slice(&hid[..]);
    }

    /* Server generates Random number Y and sends it to Client */
    mpin256::random_generate(&mut rng, &mut y);

    if FULL {
        mpin256::hash_id(sha, &client_id, &mut hsid);
        mpin256::get_g1_multiple(Some(&mut rng), 0, &mut w, &rhid, &mut t); /* Also send T=w.ID to client, remember random w  */
    }

    /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
    rtn = mpin256::client_2(&x, &y, &mut sec);
    if rtn != 0 {
        println!("FAILURE: CLIENT_2 rtn: {}", rtn);
    }

    /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
    /* If PIN error not required, set E and F = null */

    if !PINERROR {
        rtn = mpin256::server_2(
            date,
            &hid,
            Some(&htid[..]),
            &y,
            &sst,
            Some(&xid[..]),
            Some(&xcid[..]),
            &sec,
            None,
            None,
        );
    } else {
        rtn = mpin256::server_2(
            date,
            &hid,
            Some(&htid[..]),
            &y,
            &sst,
            Some(&xid[..]),
            Some(&xcid[..]),
            &sec,
            Some(&mut e),
            Some(&mut f),
        );
    }

    if rtn == mpin256::BAD_PIN {
        println!("Server says - Bad Pin. I don't know you. Feck off.");
        if PINERROR {
            let err = mpin256::kangaroo(&e, &f);
            if err != 0 {
                println!("(Client PIN is out by {})", err)
            }
        }
        return;
    } else {
        println!("Server says - PIN is good! You really are {}", name);
    }

    if FULL {
        let mut pxcid = None;
        if PERMITS {
            pxcid = Some(&xcid[..])
        };

        mpin256::hash_all(sha, &hcid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
        mpin256::client_key(sha, &g1, &g2, pin, &r, &x, &h, &t, &mut ck);
        print!("Client Key =  0x");
        printbinary(&ck);

        mpin256::hash_all(sha, &hsid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
        mpin256::server_key(sha, &z, &sst, &w, &h, &hid, &xid, pxcid, &mut sk);
        print!("Server Key =  0x");
        printbinary(&sk);
    }
}

fn rsa_2048(mut rng: &mut RAND) {
    //use amcl::rsa2048;
    use amcl::rsa2048::ff;
    use amcl::rsa2048::rsa;

    let sha = rsa::HASH_TYPE;
    let message: &[u8] = b"Hello World\n";
    const RFS: usize = rsa::RFS;

    let mut pbc = rsa::new_public_key(ff::FFLEN);
    let mut prv = rsa::new_private_key(ff::HFLEN);

    let mut ml: [u8; RFS] = [0; RFS];
    let mut ms: [u8; RFS] = [0; RFS];
    let mut c: [u8; RFS] = [0; RFS];
    let mut s: [u8; RFS] = [0; RFS];
    let mut e: [u8; RFS] = [0; RFS];

    println!("\nTesting RSA");
    println!("Generating public/private key pair");
    rsa::key_pair(&mut rng, 65537, &mut prv, &mut pbc);

    println!("Encrypting test string\n");
    rsa::oaep_encode(sha, &message, &mut rng, None, &mut e); /* OAEP encode message M to E  */

    rsa::encrypt(&pbc, &e, &mut c); /* encrypt encoded message */
    print!("Ciphertext= 0x");
    printbinary(&c);

    println!("Decrypting test string");
    rsa::decrypt(&prv, &c, &mut ml);
    let mlen = rsa::oaep_decode(sha, None, &mut ml); /* OAEP decode message  */

    let mess = str::from_utf8(&ml[0..mlen]).unwrap();
    print!("{}", &mess);

    println!("Signing message");
    rsa::pkcs15(sha, message, &mut c);

    rsa::decrypt(&prv, &c, &mut s); /* create signature in S */

    print!("Signature= 0x");
    printbinary(&s);

    rsa::encrypt(&pbc, &s, &mut ms);

    let mut cmp = true;
    if c.len() != ms.len() {
        cmp = false;
    } else {
        for j in 0..c.len() {
            if c[j] != ms[j] {
                cmp = false
            }
        }
    }
    if cmp {
        println!("Signature is valid");
    } else {
        println!("Signature is INVALID");
    }

    rsa::private_key_kill(&mut prv);
}

//#[test]
fn main() {
    let mut raw: [u8; 100] = [0; 100];

    let mut rng = RAND::new();
    rng.clean();
    for i in 0..100 {
        raw[i] = i as u8
    }

    rng.seed(100, &raw);

    ecdh_ed25519(&mut rng);
    ecdh_nist256(&mut rng);
    ecdh_goldilocks(&mut rng);
    mpin_bn254(&mut rng);
    mpin_bls383(&mut rng);
    mpin_bls24(&mut rng);
    mpin_bls48(&mut rng);
    rsa_2048(&mut rng);
}