Esempio n. 1
0
        public static void curve25519_keygen(byte[] curve25519_pubkey_out,
                                             byte[] curve25519_privkey_in)
        {
            Ge_p3 ed = new Ge_p3(); /* Ed25519 pubkey point */

            int[] ed_y               = new int[10];
            int[] ed_y_plus_one      = new int[10];
            int[] one_minus_ed_y     = new int[10];
            int[] inv_one_minus_ed_y = new int[10];
            int[] mont_x             = new int[10];

            /* Perform a fixed-base multiplication of the Edwards base point,
             * (which is efficient due to precalculated tables), then convert
             * to the Curve25519 montgomery-format public key.  In particular,
             * convert Curve25519's "montgomery" x-coordinate into an Ed25519
             * "edwards" y-coordinate:
             *
             * mont_x = (ed_y + 1) / (1 - ed_y)
             *
             * with projective coordinates:
             *
             * mont_x = (ed_y + ed_z) / (ed_z - ed_y)
             *
             * NOTE: ed_y=1 is converted to mont_x=0 since fe_invert is mod-exp
             */

            Ge_scalarmult_base.ge_scalarmult_base(ed, curve25519_privkey_in);
            Fe_add.fe_add(ed_y_plus_one, ed.Y, ed.Z);
            Fe_sub.fe_sub(one_minus_ed_y, ed.Z, ed.Y);
            Fe_invert.fe_invert(inv_one_minus_ed_y, one_minus_ed_y);
            Fe_mul.fe_mul(mont_x, ed_y_plus_one, inv_one_minus_ed_y);
            Fe_tobytes.fe_tobytes(curve25519_pubkey_out, mont_x);
        }
Esempio n. 2
0
        public static int curve25519_sign(ISha512 sha512provider, byte[] signature_out,
                                          byte[] curve25519_privkey,
                                          byte[] msg, int msg_len,
                                          byte[] random)
        {
            Ge_p3 ed_pubkey_point = new Ge_p3();        /* Ed25519 pubkey point */

            byte[] ed_pubkey = new byte[32];            /* Ed25519 encoded pubkey */
            byte[] sigbuf    = new byte[msg_len + 128]; /* working buffer */
            byte   sign_bit  = 0;

            /* Convert the Curve25519 privkey to an Ed25519 public key */
            Ge_scalarmult_base.ge_scalarmult_base(ed_pubkey_point, curve25519_privkey);
            Ge_p3_tobytes.ge_p3_tobytes(ed_pubkey, ed_pubkey_point);
            sign_bit = (byte)(ed_pubkey[31] & 0x80);

            /* Perform an Ed25519 signature with explicit private key */
            sign_modified.crypto_sign_modified(sha512provider, sigbuf, msg, msg_len, curve25519_privkey,
                                               ed_pubkey, random);
            Array.Copy(sigbuf, 0, signature_out, 0, 64);

            /* Encode the sign bit into signature (in unused high bit of S) */
            signature_out[63] &= 0x7F; /* bit should be zero already, but just in case */
            signature_out[63] |= sign_bit;
            return(0);
        }
Esempio n. 3
0
        public static int vxed25519_verify(ISha512 sha512provider,
                                           byte[] vrf_out,
                                           byte[] signature,
                                           byte[] curve25519_pubkey,
                                           byte[] msg, int msg_len)
        {
            int[]  u          = new int[10];
            int[]  y          = new int[10];
            byte[] ed_pubkey  = new byte[32];
            byte[] strict     = new byte[32];
            byte[] verifybuf  = new byte[crypto_additions.MAX_MSG_LEN + 160]; /* working buffer */
            byte[] verifybuf2 = new byte[crypto_additions.MAX_MSG_LEN + 160]; /* working buffer #2 ?? !!! */
            Ge_p3  Bv         = new Ge_p3();

            if (msg_len > crypto_additions.MAX_MSG_LEN)
            {
                return(-1);
            }

            /* Convert the Curve25519 public key into an Ed25519 public key.
             *
             * y = (u - 1) / (u + 1)
             *
             * NOTE: u=-1 is converted to y=0 since fe_invert is mod-exp
             */
            Fe_frombytes.fe_frombytes(u, curve25519_pubkey);
            Fe_tobytes.fe_tobytes(strict, u);
            if (Crypto_verify_32.crypto_verify_32(strict, curve25519_pubkey) != 0)
            {
                return(0);
            }
            Fe_montx_to_edy.fe_montx_to_edy(y, u);
            Fe_tobytes.fe_tobytes(ed_pubkey, y);

            Elligator.calculate_Bv(sha512provider, Bv, verifybuf, ed_pubkey, msg, msg_len);

            Array.Copy(signature, 0, verifybuf, 0, 96);
            Array.Copy(msg, 0, verifybuf, 96, msg_len);

            /* Then perform a signature verification, return 0 on success */
            /* The below call has a strange API: */
            /* verifybuf = V || h || s || message */
            /* verifybuf2 = used as buffer, gets the VRF output if success */
            if (Vopen_modified.crypto_vsign_open_modified(sha512provider, verifybuf2, verifybuf, 96 + msg_len, ed_pubkey, Bv) == 0)
            {
                Array.Copy(verifybuf2, 0, vrf_out, 0, 32);
                return(0);
            }
            else
            {
                //memset(vrf_out, 0, 32);
                return(-1);
            }
        }
Esempio n. 4
0
        //CONVERT #include <string.h>
        //CONVERT #include "crypto_sign.h"
        //CONVERT #include "crypto_hash_sha512.h"
        //CONVERT #include "crypto_verify_32.h"
        //CONVERT #include "ge.h"
        //CONVERT #include "sc.h"

        public static int crypto_sign_open(
            ISha512 sha512provider,
            byte[] m, long mlen,
            byte[] sm, long smlen,
            byte[] pk
            )
        {
            byte[] pkcopy = new byte[32];
            byte[] rcopy  = new byte[32];
            byte[] scopy  = new byte[32];
            byte[] h      = new byte[64];
            byte[] rcheck = new byte[32];
            Ge_p3  A      = new Ge_p3();
            Ge_p2  R      = new Ge_p2();

            if (smlen < 64)
            {
                return(-1);
            }
            if ((sm[63] & 224) != 0)
            {
                return(-1);
            }
            if (Ge_frombytes.ge_frombytes_negate_vartime(A, pk) != 0)
            {
                return(-1);
            }

            byte[] pubkeyhash = new byte[64];
            sha512provider.calculateDigest(pubkeyhash, pk, 32);

            Array.Copy(pk, 0, pkcopy, 0, 32);
            Array.Copy(sm, 0, rcopy, 0, 32);
            Array.Copy(sm, 32, scopy, 0, 32);

            Array.Copy(sm, 0, m, 0, (int)smlen);
            Array.Copy(pkcopy, 0, m, 32, 32);
            sha512provider.calculateDigest(h, m, smlen);
            Sc_reduce.sc_reduce(h);

            Ge_double_scalarmult.ge_double_scalarmult_vartime(R, h, A, scopy);
            Ge_tobytes.ge_tobytes(rcheck, R);
            if (Crypto_verify_32.crypto_verify_32(rcheck, rcopy) == 0)
            {
                Array.Copy(m, 64, m, 0, (int)(smlen - 64));
                //memset(m + smlen - 64,0,64);
                return(0);
            }

            //badsig:
            //memset(m,0,smlen);
            return(-1);
        }
Esempio n. 5
0
        public static void calculate_Bv_and_V(ISha512 sha512provider,
                                              Ge_p3 Bv,
                                              byte[] V,
                                              byte[] buf,
                                              byte[] a,
                                              byte[] A,
                                              byte[] msg, int msg_len)
        {
            Ge_p3 p3 = new Ge_p3();

            calculate_Bv(sha512provider, Bv, buf, A, msg, msg_len);
            Ge_scalarmult.ge_scalarmult(p3, a, Bv);
            Ge_p3_tobytes.ge_p3_tobytes(V, p3);
        }
        //CONVERT #include <string.h>
        //CONVERT #include "crypto_sign.h"
        //CONVERT #include "crypto_hash_sha512.h"
        //CONVERT #include "ge.h"
        //CONVERT #include "sc.h"
        //CONVERT #include "zeroize.h"

        /* NEW: Compare to pristine crypto_sign()
         * Uses explicit private key for nonce derivation and as scalar,
         * instead of deriving both from a master key.
         */
        public static int crypto_sign_modified(
            ISha512 sha512provider,
            byte[] sm,
            byte[] m, long mlen,
            byte[] sk, byte[] pk,
            byte[] random
            )
        {
            byte[] nonce = new byte[64];
            byte[] hram  = new byte[64];
            Ge_p3  R     = new Ge_p3();
            int    count = 0;

            Array.Copy(m, 0, sm, 64, (int)mlen);
            Array.Copy(sk, 0, sm, 32, 32);

            /* NEW : add prefix to separate hash uses - see .h */
            sm[0] = (byte)0xFE;
            for (count = 1; count < 32; count++)
            {
                sm[count] = (byte)0xFF;
            }

            /* NEW: add suffix of random data */
            Array.Copy(random, 0, sm, (int)(mlen + 64), 64);

            sha512provider.calculateDigest(nonce, sm, mlen + 128);
            Array.Copy(pk, 0, sm, 32, 32);

            Sc_reduce.sc_reduce(nonce);
            Ge_scalarmult_base.ge_scalarmult_base(R, nonce);
            Ge_p3_tobytes.ge_p3_tobytes(sm, R);

            sha512provider.calculateDigest(hram, sm, mlen + 64);
            Sc_reduce.sc_reduce(hram);
            byte[] S = new byte[32];
            Sc_muladd.sc_muladd(S, hram, sk, nonce); /* NEW: Use privkey directly */
            Array.Copy(S, 0, sm, 32, 32);

            /* Erase any traces of private scalar or
             * nonce left in the stack from sc_muladd */
            //zeroize_stack();
            Zeroize.zeroize(nonce, 64);
            return(0);
        }
Esempio n. 7
0
        public static int uxed25519_sign(ISha512 sha512provider,
                                         byte[] signature_out,
                                         byte[] curve25519_privkey,
                                         byte[] msg, int msg_len,
                                         byte[] random)
        {
            // just return -1 as this shouldn't be used
            return(-1);

            byte[] a    = new byte[32];
            byte[] aneg = new byte[32];
            byte[] A    = new byte[32];

            Ge_p3 Bu = new Ge_p3();
            Ge_p3 ed_pubkey_point = new Ge_p3();

            byte[] sigbuf   = new byte[crypto_additions.MAX_MSG_LEN + 160]; /* working buffer */
            byte   sign_bit = 0;

            if (msg_len > crypto_additions.MAX_MSG_LEN)
            {
                //memset(signature_out, 0, 96);
                return(-1);
            }

            /* Convert the Curve25519 privkey to an Ed25519 public key */
            Ge_scalarmult_base.ge_scalarmult_base(ed_pubkey_point, curve25519_privkey);
            Ge_p3_tobytes.ge_p3_tobytes(A, ed_pubkey_point);

            /* Force Edwards sign bit to zero */
            sign_bit = (byte)((A[31] & 0x80) >> 7);
            Array.Copy(curve25519_privkey, 0, a, 0, 32);
            Sc_neg.sc_neg(aneg, a);
            Sc_cmov.sc_cmov(a, aneg, sign_bit);
            A[31] &= 0x7F;

            //Elligator.calculate_Bv_and_V(sha512provider, Bu, signature_out, sigbuf, a, msg, msg_len);

            /* Perform an Ed25519 signature with explicit private key */
            usign_modified.crypto_usign_modified(sha512provider, sigbuf, msg, msg_len, a, A, random, Bu, signature_out /*U*/);
            Array.Copy(sigbuf, 0, signature_out, 32, 64);

            Zeroize.zeroize(a, 32);
            return(0);
        }
Esempio n. 8
0
        public static int uxed25519_verify(ISha512 sha512provider, byte[] signature, byte[] curve25519_pubkey, byte[] msg, int msg_len)
        {
            // just return -1 as this shouldn't be used
            return(-1);

            int[] u = new int[10];
            int[] y = new int[10];

            byte[] ed_pubkey   = new byte[32];
            long   some_retval = 0;

            byte[] verifybuf  = new byte[crypto_additions.MAX_MSG_LEN + 160]; /* working buffer */
            byte[] verifybuf2 = new byte[crypto_additions.MAX_MSG_LEN + 160]; /* working buffer #2 ?? !!! */
            Ge_p3  Bu         = new Ge_p3();

            if (msg_len > crypto_additions.MAX_MSG_LEN)
            {
                return(-1);
            }

            //Elligator.calculate_Bv(sha512provider, Bu, verifybuf, msg, msg_len);

            /* Convert the Curve25519 public key (u) into an Ed25519 public key.
             *
             * y = (u - 1) / (u + 1)
             *
             * NOTE: u=-1 is converted to y=0 since fe_invert is mod-exp
             */
            Fe_frombytes.fe_frombytes(u, curve25519_pubkey);
            Fe_montx_to_edy.fe_montx_to_edy(y, u);
            Fe_tobytes.fe_tobytes(ed_pubkey, y);

            Array.Copy(signature, 0, verifybuf, 0, 96);
            Array.Copy(msg, 0, verifybuf, 96, msg_len);

            /* Then perform a signature verification, return 0 on success */
            /* The below call has a strange API: */
            /* verifybuf = U || h || s || message */

            /* verifybuf2 = internal to next call gets a copy of verifybuf, S gets
             * replaced with pubkey for hashing, then the whole thing gets zeroized
             * (if bad sig), or contains a copy of msg (good sig) */
            return(uopen_modified.crypto_usign_open_modified(sha512provider, verifybuf2, some_retval, verifybuf, 96 + msg_len, ed_pubkey, Bu));
        }
Esempio n. 9
0
        public static void hash_to_point(ISha512 sha512provider, Ge_p3 p, byte[] iIn, int in_len)
        {
            byte[] hash = new byte[64];
            int[]  h    = new int[10];
            int[]  u    = new int[10];
            byte   sign_bit;
            Ge_p3  p3 = new Ge_p3();

            sha512provider.calculateDigest(hash, iIn, in_len);

            /* take the high bit as Edwards sign bit */
            sign_bit  = (byte)(((uint)hash[31] & 0x80) >> 7);
            hash[31] &= 0x7F;
            Fe_frombytes.fe_frombytes(h, hash);
            elligator(u, h);

            Ge_montx_to_p3.ge_montx_to_p3(p3, u, sign_bit);
            Ge_scalarmult_cofactor.ge_scalarmult_cofactor(p, p3);
        }
Esempio n. 10
0
        public static void calculate_Bv(ISha512 sha512provider,
                                        Ge_p3 Bv,
                                        byte[] buf,
                                        byte[] A,
                                        byte[] msg, int msg_len)
        {
            int count;

            /* Calculate SHA512(label(2) || A || msg) */
            buf[0] = 0xFD;
            for (count = 1; count < 32; count++)
            {
                buf[count] = 0xFF;
            }
            Array.Copy(A, 0, buf, 32, 32);
            Array.Copy(msg, 0, buf, 64, msg_len);

            hash_to_point(sha512provider, Bv, buf, 64 + msg_len);
        }
Esempio n. 11
0
        public static int xed25519_sign(ISha512 sha512provider,
                                        byte[] signature_out,
                                        byte[] curve25519_privkey,
                                        byte[] msg, int msg_len,
                                        byte[] random)
        {
            byte[] a               = new byte[32];
            byte[] A               = new byte[32];
            byte[] aneg            = new byte[32];
            Ge_p3  ed_pubkey_point = new Ge_p3();

            // see link below
            byte[] sigbuf   = new byte[msg_len + 128]; /* working buffer */
            byte   sign_bit = 0;

            // this should be different but whatever
            // https://github.com/WhisperSystems/curve25519-java/commit/2f388f601afdac6a78a19ced2f0629da1ff9800f#diff-6e488e4e28814b3fa524b6781fcaf912R19
            //if (msg_len > crypto_additions.MAX_MSG_LEN)
            //{
            //    //memset(signature_out, 0, 64);
            //    return -1;
            //}

            /* Convert the Curve25519 privkey to an Ed25519 public key */
            Ge_scalarmult_base.ge_scalarmult_base(ed_pubkey_point, curve25519_privkey);
            Ge_p3_tobytes.ge_p3_tobytes(A, ed_pubkey_point);

            /* Force Edwards sign bit to zero */
            sign_bit = (byte)((A[31] & 0x80) >> 7);
            Array.Copy(curve25519_privkey, 0, a, 0, 32);
            Sc_neg.sc_neg(aneg, a);
            Sc_cmov.sc_cmov(a, aneg, sign_bit);
            A[31] &= 0x7F;

            /* Perform an Ed25519 signature with explicit private key */
            sign_modified.crypto_sign_modified(sha512provider, sigbuf, msg, msg_len, a, A, random);
            Array.Copy(sigbuf, 0, signature_out, 0, 64);

            Zeroize.zeroize(a, 32);
            Zeroize.zeroize(aneg, 32);
            return(0);
        }
Esempio n. 12
0
        public static int crypto_vsign_modified(ISha512 sha512provider,
                                                byte[] sm,
                                                byte[] M, int Mlen,
                                                byte[] a,
                                                byte[] A,
                                                byte[] random,
                                                Ge_p3 Bv,
                                                byte[] V)
        {
            byte[] r     = new byte[64];
            byte[] h     = new byte[64];
            Ge_p3  R     = new Ge_p3();
            Ge_p3  Rv    = new Ge_p3();
            int    count = 0;

            /* r = SHA512(label(3) || a || V || random(64)) */
            sm[0] = 0xFC;
            for (count = 1; count < 32; count++)
            {
                sm[count] = 0xFF;
            }

            Array.Copy(a, 0, sm, 32, 32); /* Use privkey directly for nonce derivation */
            Array.Copy(V, 0, sm, 64, 32);

            Array.Copy(random, 0, sm, 96, 64); /* Add suffix of random data */
            sha512provider.calculateDigest(r, sm, 160);

            Sc_reduce.sc_reduce(r);
            Ge_scalarmult_base.ge_scalarmult_base(R, r);
            Ge_scalarmult.ge_scalarmult(Rv, r, Bv);

            /* h = SHA512(label(4) || A || V || R || Rv || M) */
            sm[0] = 0xFB;
            Array.Copy(A, 0, sm, 32, 32);
            Array.Copy(V, 0, sm, 64, 32);
            byte[] R1 = new byte[32];
            Array.Copy(sm, 96, R1, 0, 32);
            Ge_p3_tobytes.ge_p3_tobytes(R1, R);
            Array.Copy(R1, 0, sm, 96, 32);
            byte[] R2 = new byte[32];
            Array.Copy(sm, 128, R2, 0, 32);
            Ge_p3_tobytes.ge_p3_tobytes(R2, Rv);
            Array.Copy(R2, 0, sm, 128, 32);
            Array.Copy(M, 0, sm, 160, Mlen);

            sha512provider.calculateDigest(h, sm, Mlen + 160);
            Sc_reduce.sc_reduce(h);

            Array.Copy(h, 0, sm, 0, 32);
            byte[] S = new byte[32];
            Array.Copy(sm, 32, S, 0, 32);
            Sc_muladd.sc_muladd(S, h, a, r);
            Array.Copy(S, 0, sm, 32, 32);

            /* Erase any traces of private scalar or
             * nonce left in stack from sc_muladd. */
            //zeroize_stack();
            Zeroize.zeroize(r, 64);
            return(0);
        }
        public static int crypto_vsign_open_modified(ISha512 sha512provider,
                                                     byte[] m,
                                                     byte[] sm, long smlen,
                                                     byte[] pk, Ge_p3 Bv)
        {
            Ge_p3 Vneg   = new Ge_p3();
            Ge_p3 V      = new Ge_p3();
            Ge_p3 Aneg   = new Ge_p3();
            Ge_p3 A      = new Ge_p3();
            Ge_p3 c_V    = new Ge_p3();
            Ge_p3 c_A    = new Ge_p3();
            Ge_p3 h_Vneg = new Ge_p3();
            Ge_p3 s_Bv   = new Ge_p3();

            byte[] h = new byte[32];
            byte[] s = new byte[32];
            Ge_p2  R = new Ge_p2();

            byte[] hcheck     = new byte[64];
            byte[] vrf_output = new byte[64];
            int    count;

            if (smlen < 96)
            {
                return(-1);
            }
            if ((sm[63] & 224) != 0) /* strict parsing of h */
            {
                return(-1);
            }
            if ((sm[95] & 224) != 0) /* strict parsing of s */
            {
                return(-1);
            }

            /* Load -A */
            if (Ge_frombytes.ge_frombytes_negate_vartime(Aneg, pk) != 0)
            {
                return(-1);
            }

            /* Load -V, h, s */
            if (Ge_frombytes.ge_frombytes_negate_vartime(Vneg, sm) != 0)
            {
                return(-1);
            }
            Array.Copy(sm, 32, h, 0, 32);
            Array.Copy(sm, 64, s, 0, 32);
            if ((h[31] & 224) != 0) /* strict parsing of h */
            {
                return(-1);
            }
            if ((s[31] & 224) != 0) /* strict parsing of s */
            {
                return(-1);
            }

            Ge_neg.ge_neg(A, Aneg);
            Ge_neg.ge_neg(V, Vneg);
            Ge_scalarmult_cofactor.ge_scalarmult_cofactor(c_A, A);
            Ge_scalarmult_cofactor.ge_scalarmult_cofactor(c_V, V);
            if (Ge_isneutral.ge_isneutral(c_A) != 0 ||
                Ge_isneutral.ge_isneutral(c_V) != 0 ||
                Ge_isneutral.ge_isneutral(Bv) != 0)
            {
                return(-1);
            }

            // R = (s*B) + (h * -A))
            Ge_double_scalarmult.ge_double_scalarmult_vartime(R, h, Aneg, s);

            // s * Bv
            Ge_scalarmult.ge_scalarmult(s_Bv, s, Bv);

            // h * -V
            Ge_scalarmult.ge_scalarmult(h_Vneg, h, Vneg);

            // Rv = (sc * Bv) + (hc * (-V))
            Ge_p1p1   Rp1p1        = new Ge_p1p1();
            Ge_p3     Rv           = new Ge_p3();
            Ge_cached h_Vnegcached = new Ge_cached();

            Ge_p3_to_cached.ge_p3_to_cached(h_Vnegcached, h_Vneg);
            Ge_add.ge_add(Rp1p1, s_Bv, h_Vnegcached);
            Ge_p1p1_to_p3.ge_p1p1_to_p3(Rv, Rp1p1);

            // Check h == SHA512(label(4) || A || V || R || Rv || M)
            m[0] = 0xFB; // label 4
            for (count = 1; count < 32; count++)
            {
                m[count] = 0xFF;
            }
            Array.Copy(pk, 0, m, 32, 32);
            byte[] M = new byte[32];
            Array.Copy(m, 64, M, 0, 32);
            Ge_p3_tobytes.ge_p3_tobytes(M, V);
            Array.Copy(M, 0, m, 64, 32);
            byte[] M2 = new byte[32];
            Array.Copy(m, 96, M2, 0, 32);
            Ge_tobytes.ge_tobytes(M2, R);
            Array.Copy(M2, 0, m, 96, 32);
            byte[] M3 = new byte[32];
            Array.Copy(m, 128, M3, 0, 32);
            Ge_p3_tobytes.ge_p3_tobytes(M3, Rv);
            Array.Copy(M3, 0, m, 128, 32);
            Array.Copy(sm, 96, m, 160, (int)smlen - 96);

            sha512provider.calculateDigest(hcheck, m, smlen + 64);
            Sc_reduce.sc_reduce(hcheck);

            if (Crypto_verify_32.crypto_verify_32(hcheck, h) == 0)
            {
                byte[] M4 = new byte[32];
                Array.Copy(m, 32, M4, 0, 32);
                Ge_p3_tobytes.ge_p3_tobytes(M4, c_V);
                Array.Copy(M4, 0, m, 32, 32);
                m[0] = 0xFA; // label 5
                sha512provider.calculateDigest(vrf_output, m, 64);
                Array.Copy(vrf_output, 0, m, 0, 32);
                return(0);
            }

            //badsig
            //memset(m, 0, 32);
            return(-1);
        }
Esempio n. 14
0
        public static int crypto_usign_open_modified(ISha512 sha512provider, byte[] m, long mlen, byte[] sm, long smlen, byte[] pk, Ge_p3 Bu)
        {
            Ge_p3 U = new Ge_p3();

            byte[] h      = new byte[64];
            byte[] s      = new byte[64];
            byte[] strict = new byte[64];
            Ge_p3  A      = new Ge_p3();
            Ge_p2  R      = new Ge_p2();

            byte[] hcheck = new byte[64];
            int    count;

            if (smlen < 96)
            {
                return(-1);
            }
            if ((sm[63] & 224) != 0) /* strict parsing of h */
            {
                return(-1);
            }
            if ((sm[95] & 224) != 0) /* strict parsing of s */
            {
                return(-1);
            }

            /* Load -A */
            if (Ge_frombytes.ge_frombytes_negate_vartime(A, pk) != 0)
            {
                return(-1);
            }

            /* Load -U, h, s */
            Ge_frombytes.ge_frombytes_negate_vartime(U, sm);
            Array.Copy(sm, 32, h, 0, 32);
            Array.Copy(sm, 64, s, 0, 32);

            /* Insist that s and h are reduced scalars (strict parsing) */
            Array.Copy(h, 0, strict, 0, 64);
            Sc_reduce.sc_reduce(strict);
            if (!Arrays.isEqual(strict, h, 32))
            {
                return(-1);
            }
            Array.Copy(s, 0, strict, 0, 64);
            Sc_reduce.sc_reduce(strict);
            if (!Arrays.isEqual(strict, s, 32))
            {
                return(-1);
            }

            /* Reject U (actually -U) if small order */
            if (Ge_is_small_order.ge_is_small_order(U) != 0)
            {
                return(-1);
            }

            // R = sB + h(-A)
            Ge_double_scalarmult.ge_double_scalarmult_vartime(R, h, A, s);

            // Ru = sBu + h(-U)
            Ge_p3 sBu = new Ge_p3();
            Ge_p3 hU  = new Ge_p3();

            // sBu
            Ge_scalarmult.ge_scalarmult(sBu, s, Bu);

            // h(-U)
            Ge_scalarmult.ge_scalarmult(hU, h, U);

            // Ru = sBu + h(-U)
            Ge_p1p1   Rp1p1    = new Ge_p1p1();
            Ge_p3     Ru       = new Ge_p3();
            Ge_cached hUcached = new Ge_cached();

            Ge_p3_to_cached.ge_p3_to_cached(hUcached, hU);
            Ge_add.ge_add(Rp1p1, sBu, hUcached);
            Ge_p1p1_to_p3.ge_p1p1_to_p3(Ru, Rp1p1);


            // Check h == SHA512(label(4) || A || U || R || Ru || M)
            m[0] = 0xFB;
            for (count = 1; count < 32; count++)
            {
                m[count] = 0xFF;
            }
            Array.Copy(pk, 0, m, 32, 32);
            /* undo the negation for U */
            Fe_neg.fe_neg(U.X, U.X);
            Fe_neg.fe_neg(U.T, U.T);
            byte[] M = new byte[32];
            Array.Copy(m, 64, M, 0, 32);
            Ge_p3_tobytes.ge_p3_tobytes(M, U);
            Array.Copy(M, 0, m, 64, 32);
            byte[] M2 = new byte[32];
            Array.Copy(m, 96, M2, 0, 32);
            Ge_tobytes.ge_tobytes(M2, R);
            Array.Copy(M2, 0, m, 96, 32);
            byte[] M3 = new byte[32];
            Array.Copy(m, 128, M3, 0, 32);
            Ge_p3_tobytes.ge_p3_tobytes(M3, Ru);
            Array.Copy(M3, 0, m, 128, 32);
            Array.Copy(sm, 96, m, 160, (int)smlen - 96);

            sha512provider.calculateDigest(hcheck, m, smlen + 64);
            Sc_reduce.sc_reduce(hcheck);

            if (Crypto_verify_32.crypto_verify_32(hcheck, h) == 0)
            {
                Array.Copy(m, 64, m, 0, (int)smlen - 64);
                //memset(m + smlen - 64,0,64);
                //*mlen = smlen - 64;
                return(0);
            }

            //badsig:
            //*mlen = -1;
            //memset(m,0,smlen);
            return(-1);
        }