コード例 #1
0
/* Functions to support M-Pin Full */

    public static int PRECOMPUTE(sbyte[] TOKEN, sbyte[] CID, sbyte[] G1, sbyte[] G2)
    {
        ECP  P, T;
        FP12 g;

        T = ECP.fromBytes(TOKEN);
        if (T.is_infinity())
        {
            return(INVALID_POINT);
        }

        P = mapit(CID);

        ECP2 Q = new ECP2(new FP2(new BIG(ROM.CURVE_Pxa), new BIG(ROM.CURVE_Pxb)), new FP2(new BIG(ROM.CURVE_Pya), new BIG(ROM.CURVE_Pyb)));

        g = PAIR.ate(Q, T);
        g = PAIR.fexp(g);
        g.toBytes(G1);

        g = PAIR.ate(Q, P);
        g = PAIR.fexp(g);
        g.toBytes(G2);

        return(0);
    }
コード例 #2
0
/*
 * W=x*H(G);
 * if RNG == NULL then X is passed in
 * if RNG != NULL the X is passed out
 * if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
 */
    public static int GET_G1_MULTIPLE(RAND rng, int type, sbyte[] X, sbyte[] G, sbyte[] W)
    {
        BIG x;
        BIG r = new BIG(ROM.CURVE_Order);

        if (rng != null)
        {
            x = BIG.randomnum(r, rng);
            x.toBytes(X);
        }
        else
        {
            x = BIG.fromBytes(X);
        }
        ECP P;

        if (type == 0)
        {
            P = ECP.fromBytes(G);
            if (P.is_infinity())
            {
                return(INVALID_POINT);
            }
        }
        else
        {
            P = mapit(G);
        }

        PAIR.G1mul(P, x).toBytes(W);
        return(0);
    }
コード例 #3
0
ファイル: ECP.cs プロジェクト: ratranqu/milagro-crypto
/* constant time multiply by small integer of length bts - use ladder */
    public ECP pinmul(int e, int bts)
    {
        if (ROM.CURVETYPE == ROM.MONTGOMERY)
        {
            return(this.mul(new BIG(e)));
        }
        else
        {
            int nb, i, b;
            ECP P  = new ECP();
            ECP R0 = new ECP();
            ECP R1 = new ECP();
            R1.copy(this);

            for (i = bts - 1; i >= 0; i--)
            {
                b = (e >> i) & 1;
                P.copy(R1);
                P.add(R0);
                R0.cswap(R1, b);
                R1.copy(P);
                R0.dbl();
                R0.cswap(R1, b);
            }
            P.copy(R0);
            P.affine();
            return(P);
        }
    }
コード例 #4
0
/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
    public static int ECPVP_DSA(sbyte[] W, sbyte[] F, sbyte[] C, sbyte[] D)
    {
        BIG r, gx, gy, f, c, d, h2;
        int res = 0;
        ECP G, WP, P;

        HASH H = new HASH();

        H.process_array(F);
        sbyte[] B = H.hash();

        gx = new BIG(ROM.CURVE_Gx);
        gy = new BIG(ROM.CURVE_Gy);

        G = new ECP(gx, gy);
        r = new BIG(ROM.CURVE_Order);

        c = BIG.fromBytes(C);
        d = BIG.fromBytes(D);
        f = BIG.fromBytes(B);

        if (c.iszilch() || BIG.comp(c, r) >= 0 || d.iszilch() || BIG.comp(d, r) >= 0)
        {
            res = INVALID;
        }

        if (res == 0)
        {
            d.invmodp(r);
            f.copy(BIG.modmul(f, d, r));
            h2 = BIG.modmul(c, d, r);

            WP = ECP.fromBytes(W);
            if (WP.is_infinity())
            {
                res = ERROR;
            }
            else
            {
                P = new ECP();
                P.copy(WP);
                P = P.mul2(h2, G, f);
                if (P.is_infinity())
                {
                    res = INVALID;
                }
                else
                {
                    d = P.X;
                    d.mod(r);
                    if (BIG.comp(d, c) != 0)
                    {
                        res = INVALID;
                    }
                }
            }
        }

        return(res);
    }
コード例 #5
0
/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
    public static int GET_CLIENT_PERMIT(int date, sbyte[] S, sbyte[] CID, sbyte[] CTT)
    {
        sbyte[] h = hashit(date, CID);
        ECP     P = mapit(h);

        BIG s = BIG.fromBytes(S);

        PAIR.G1mul(P, s).toBytes(CTT);
        return(0);
    }
コード例 #6
0
ファイル: WeakBB.cs プロジェクト: mvaneijk/fabric-sdk-net
        /**
         * Verify a WBB signature for a certain message
         *
         * @param pk  Public key
         * @param sig Signature
         * @param m   Message
         * @return True iff valid
         */
        public static bool weakBBVerify(ECP2 pk, ECP sig, BIG m)
        {
            ECP2 p = new ECP2();

            p.Copy(pk);
            p.Add(IdemixUtils.GenG2.Mul(m));
            p.Affine();

            return(PAIR.FExp(PAIR.Ate(p, sig)).Equals(IdemixUtils.GenGT));
        }
コード例 #7
0
ファイル: ECP.cs プロジェクト: ratranqu/milagro-crypto
/* this=P */
    public void copy(ECP P)
    {
        x.copy(P.x);
        if (ROM.CURVETYPE != ROM.MONTGOMERY)
        {
            y.copy(P.y);
        }
        z.copy(P.z);
        INF = P.INF;
    }
コード例 #8
0
/* IEEE ECDSA Signature, C and D are signature on F using private key S */
    public static int ECPSP_DSA(RAND RNG, sbyte[] S, sbyte[] F, sbyte[] C, sbyte[] D)
    {
        sbyte[] T = new sbyte[EFS];
        BIG     gx, gy, r, s, f, c, d, u, vx;
        ECP     G, V;

        HASH H = new HASH();

        H.process_array(F);
        sbyte[] B = H.hash();

        gx = new BIG(ROM.CURVE_Gx);
        gy = new BIG(ROM.CURVE_Gy);

        G = new ECP(gx, gy);
        r = new BIG(ROM.CURVE_Order);

        s = BIG.fromBytes(S);
        f = BIG.fromBytes(B);

        c = new BIG(0);
        d = new BIG(0);
        V = new ECP();

        do
        {
            u = BIG.randomnum(r, RNG);

            V.copy(G);
            V  = V.mul(u);
            vx = V.X;
            c.copy(vx);
            c.mod(r);
            if (c.iszilch())
            {
                continue;
            }
            u.invmodp(r);
            d.copy(BIG.modmul(s, c, r));
            d.add(f);
            d.copy(BIG.modmul(u, d, r));
        } while (d.iszilch());

        c.toBytes(T);
        for (int i = 0; i < EFS; i++)
        {
            C[i] = T[i];
        }
        d.toBytes(T);
        for (int i = 0; i < EFS; i++)
        {
            D[i] = T[i];
        }
        return(0);
    }
コード例 #9
0
/* Optimal R-ate pairing */
    public static FP12 ate(ECP2 P, ECP Q)
    {
        FP2  f = new FP2(new BIG(ROM.CURVE_Fra), new BIG(ROM.CURVE_Frb));
        BIG  x = new BIG(ROM.CURVE_Bnx);
        BIG  n = new BIG(x);
        ECP2 K = new ECP2();
        FP12 lv;

        n.pmul(6);
        n.dec(2);
        n.norm();
        P.affine();
        Q.affine();
        FP Qx = new FP(Q.getx());
        FP Qy = new FP(Q.gety());

        ECP2 A = new ECP2();
        FP12 r = new FP12(1);

        A.copy(P);
        int nb = n.nbits();

        for (int i = nb - 2; i >= 1; i--)
        {
            lv = line(A, A, Qx, Qy);
            r.smul(lv);

            if (n.bit(i) == 1)
            {
                lv = line(A, P, Qx, Qy);

                r.smul(lv);
            }
            r.sqr();
        }

        lv = line(A, A, Qx, Qy);
        r.smul(lv);

/* R-ate fixup */

        r.conj();

        K.copy(P);
        K.frob(f);
        A.neg();
        lv = line(A, K, Qx, Qy);
        r.smul(lv);
        K.frob(f);
        K.neg();
        lv = line(A, K, Qx, Qy);
        r.smul(lv);

        return(r);
    }
コード例 #10
0
/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
/* Note that u and v are indistinguisible from random strings */
    public static int ENCODING(RAND rng, sbyte[] E)
    {
        int rn, m, su, sv;

        sbyte[] T = new sbyte[EFS];

        for (int i = 0; i < EFS; i++)
        {
            T[i] = E[i + 1];
        }
        BIG u = BIG.fromBytes(T);

        for (int i = 0; i < EFS; i++)
        {
            T[i] = E[i + EFS + 1];
        }
        BIG v = BIG.fromBytes(T);

        ECP P = new ECP(u, v);

        if (P.is_infinity())
        {
            return(INVALID_POINT);
        }

        BIG p = new BIG(ROM.Modulus);

        u = BIG.randomnum(p, rng);

        su  = rng.Byte;        //if (su<0) su=-su;
        su %= 2;

        ECP W = map(u, su);

        P.sub(W);
        sv = P.S;
        rn = unmap(v, P);
        m  = rng.Byte;        //if (m<0) m=-m;
        m %= rn;
        v.inc(m + 1);
        E[0] = (sbyte)(su + 2 * sv);
        u.toBytes(T);
        for (int i = 0; i < EFS; i++)
        {
            E[i + 1] = T[i];
        }
        v.toBytes(T);
        for (int i = 0; i < EFS; i++)
        {
            E[i + EFS + 1] = T[i];
        }

        return(0);
    }
コード例 #11
0
        /**
         * Converts an amcl.BN256.ECP into an ECP protobuf object.
         *
         * @param w an ECP to be transformed into a protobuf object
         * @return a protobuf representation of the ECP
         */
        public static Protos.Idemix.ECP ToProto(this ECP w)
        {
            byte[] valueX = new byte[FIELD_BYTES];
            byte[] valueY = new byte[FIELD_BYTES];

            w.X.ToBytes(valueX);
            w.Y.ToBytes(valueY);
            Protos.Idemix.ECP ecp = new Protos.Idemix.ECP();
            ecp.X = ByteString.CopyFrom(valueX);
            ecp.Y = ByteString.CopyFrom(valueY);
            return(ecp);
        }
コード例 #12
0
        public EMHCrypt01(GetMeterDelegate GetMeter,
                          CheckMeterPublicKeySignatureDelegate CheckMeterPublicKeySignature)

            : base("ECC secp192r1",
                   GetMeter,
                   CheckMeterPublicKeySignature)

        {
            this.CurveName = "P-192";
            this.ECP       = ECNamedCurveTable.GetByName(CurveName);
            this.ECSpec    = new ECDomainParameters(ECP.Curve, ECP.G, ECP.N, ECP.H, ECP.GetSeed());
            this.C         = (FpCurve)ECSpec.Curve;
        }
コード例 #13
0
/* R=R1+R2 in group G1 */
    public static int RECOMBINE_G1(sbyte[] R1, sbyte[] R2, sbyte[] R)
    {
        ECP P = ECP.fromBytes(R1);
        ECP Q = ECP.fromBytes(R2);

        if (P.is_infinity() || Q.is_infinity())
        {
            return(INVALID_POINT);
        }

        P.add(Q);

        P.toBytes(R);
        return(0);
    }
コード例 #14
0
        /* Multiply P by e in group G1 */
        public static ECP G1Mul(ECP P, BIG e)
        {
            ECP R;

            if (USE_GLV)
            {
                //P.affine();
                R = new ECP();
                R.Copy(P);
                int np, nn;
                ECP Q = new ECP();
                Q.Copy(P);
                Q.Affine();
                BIG   q   = new BIG(ROM.CURVE_Order);
                FP    cru = new FP(new BIG(ROM.CURVE_Cru));
                BIG   t   = new BIG(0);
                BIG[] u   = Glv(e);
                Q.GetX().Mul(cru);

                np = u[0].NBits();
                t.Copy(BIG.ModNeg(u[0], q));
                nn = t.NBits();
                if (nn < np)
                {
                    u[0].Copy(t);
                    R.Neg();
                }

                np = u[1].NBits();
                t.Copy(BIG.ModNeg(u[1], q));
                nn = t.NBits();
                if (nn < np)
                {
                    u[1].Copy(t);
                    Q.Neg();
                }

                u[0].Norm();
                u[1].Norm();
                R = R.Mul2(u[0], Q, u[1]);
            }
            else
            {
                R = P.Mul(e);
            }

            return(R);
        }
コード例 #15
0
    public static int DECODING(sbyte[] D)
    {
        int su, sv;

        sbyte[] T = new sbyte[EFS];

        if ((D[0] & 0x04) != 0)
        {
            return(INVALID_POINT);
        }

        for (int i = 0; i < EFS; i++)
        {
            T[i] = D[i + 1];
        }
        BIG u = BIG.fromBytes(T);

        for (int i = 0; i < EFS; i++)
        {
            T[i] = D[i + EFS + 1];
        }
        BIG v = BIG.fromBytes(T);

        su = D[0] & 1;
        sv = (D[0] >> 1) & 1;
        ECP W = map(u, su);
        ECP P = map(v, sv);

        P.add(W);
        u    = P.X;
        v    = P.Y;
        D[0] = 0x04;
        u.toBytes(T);
        for (int i = 0; i < EFS; i++)
        {
            D[i + 1] = T[i];
        }
        v.toBytes(T);
        for (int i = 0; i < EFS; i++)
        {
            D[i + EFS + 1] = T[i];
        }

        return(0);
    }
コード例 #16
0
/* Multiply P by e in group G1 */
    public static ECP G1mul(ECP P, BIG e)
    {
        ECP R;

        if (ROM.USE_GLV)
        {
            P.affine();
            R = new ECP();
            R.copy(P);
            int i, np, nn;
            ECP Q = new ECP();
            Q.copy(P);
            BIG   q   = new BIG(ROM.CURVE_Order);
            FP    cru = new FP(new BIG(ROM.CURVE_Cru));
            BIG   t   = new BIG(0);
            BIG[] u   = glv(e);
            Q.getx().mul(cru);

            np = u[0].nbits();
            t.copy(BIG.modneg(u[0], q));
            nn = t.nbits();
            if (nn < np)
            {
                u[0].copy(t);
                R.neg();
            }

            np = u[1].nbits();
            t.copy(BIG.modneg(u[1], q));
            nn = t.nbits();
            if (nn < np)
            {
                u[1].copy(t);
                Q.neg();
            }

            R = R.mul2(u[0], Q, u[1]);
        }
        else
        {
            R = P.mul(e);
        }
        return(R);
    }
コード例 #17
0
ファイル: ECP.cs プロジェクト: ratranqu/milagro-crypto
/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
    public void dadd(ECP Q, ECP W)
    {
        FP A  = new FP(x);
        FP B  = new FP(x);
        FP C  = new FP(Q.x);
        FP D  = new FP(Q.x);
        FP DA = new FP(0);
        FP CB = new FP(0);

        A.add(z);
        B.sub(z);

        C.add(Q.z);
        D.sub(Q.z);

        DA.copy(D);
        DA.mul(A);
        CB.copy(C);
        CB.mul(B);

        A.copy(DA);
        A.add(CB);
        A.sqr();
        B.copy(DA);
        B.sub(CB);
        B.sqr();

        x.copy(A);
        z.copy(W.x);
        z.mul(B);

        if (z.iszilch())
        {
            inf();
        }
        else
        {
            INF = false;
        }

        //		x.reduce();
        x.norm();
    }
コード例 #18
0
/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
/* maps a random u to a point on the curve */
    public static ECP map(BIG u, int cb)
    {
        ECP P;
        BIG x = new BIG(u);
        BIG p = new BIG(ROM.Modulus);

        x.mod(p);
        while (true)
        {
            P = new ECP(x, cb);
            if (!P.is_infinity())
            {
                break;
            }
            x.inc(1);
            x.norm();
        }
        return(P);
    }
コード例 #19
0
/* Implement step 2 on client side of MPin protocol */
    public static int CLIENT_2(sbyte[] X, sbyte[] Y, sbyte[] SEC)
    {
        BIG r = new BIG(ROM.CURVE_Order);
        ECP P = ECP.fromBytes(SEC);

        if (P.is_infinity())
        {
            return(INVALID_POINT);
        }

        BIG px = BIG.fromBytes(X);
        BIG py = BIG.fromBytes(Y);

        px.add(py);
        px.mod(r);
        px.rsub(r);

        PAIR.G1mul(P, px).toBytes(SEC);
        return(0);
    }
コード例 #20
0
    public static ECP mapit(sbyte[] h)
    {
        BIG q = new BIG(ROM.Modulus);
        BIG x = BIG.fromBytes(h);

        x.mod(q);
        ECP P;

        while (true)
        {
            P = new ECP(x, 0);
            if (!P.is_infinity())
            {
                break;
            }
            x.inc(1);
            x.norm();
        }
        return(P);
    }
コード例 #21
0
/* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p),
 * where S is the secret key and W is the public key
 * and G is fixed generator.
 * If RNG is NULL then the private key is provided externally in S
 * otherwise it is generated randomly internally */
    public static int KEY_PAIR_GENERATE(RAND RNG, sbyte[] S, sbyte[] W)
    {
        BIG r, gx, gy, s;
        ECP G, WP;
        int res = 0;

        sbyte[] T = new sbyte[EFS];

        gx = new BIG(ROM.CURVE_Gx);
        if (ROM.CURVETYPE != ROM.MONTGOMERY)
        {
            gy = new BIG(ROM.CURVE_Gy);
            G  = new ECP(gx, gy);
        }
        else
        {
            G = new ECP(gx);
        }

        r = new BIG(ROM.CURVE_Order);

        if (RNG == null)
        {
            s = BIG.fromBytes(S);
        }
        else
        {
            s = BIG.randomnum(r, RNG);

            s.toBytes(T);
            for (int i = 0; i < EGS; i++)
            {
                S[i] = T[i];
            }
        }

        WP = G.mul(s);
        WP.toBytes(W);

        return(res);
    }
コード例 #22
0
/* returns u derived from P. Random value in range 1 to return value should then be added to u */
    public static int unmap(BIG u, ECP P)
    {
        int s = P.S;
        ECP R;
        int r = 0;
        BIG x = P.X;

        u.copy(x);
        while (true)
        {
            u.dec(1);
            u.norm();
            r++;
            R = new ECP(u, s);
            if (!R.is_infinity())
            {
                break;
            }
        }
        return(r);
    }
コード例 #23
0
ファイル: ECP.cs プロジェクト: ratranqu/milagro-crypto
/* Constant time select from pre-computed table */
    private void select(ECP[] W, int b)
    {
        ECP MP   = new ECP();
        int m    = b >> 31;
        int babs = (b ^ m) - m;

        babs = (babs - 1) / 2;

        cmove(W[0], teq(babs, 0));       // conditional move
        cmove(W[1], teq(babs, 1));
        cmove(W[2], teq(babs, 2));
        cmove(W[3], teq(babs, 3));
        cmove(W[4], teq(babs, 4));
        cmove(W[5], teq(babs, 5));
        cmove(W[6], teq(babs, 6));
        cmove(W[7], teq(babs, 7));

        MP.copy(this);
        MP.neg();
        cmove(MP, (int)(m & 1));
    }
コード例 #24
0
/* Extract PIN from TOKEN for identity CID */
    public static int EXTRACT_PIN(sbyte[] CID, int pin, sbyte[] TOKEN)
    {
        ECP P = ECP.fromBytes(TOKEN);

        if (P.is_infinity())
        {
            return(INVALID_POINT);
        }
        sbyte[] h = hashit(0, CID);
        ECP     R = mapit(h);


        pin %= MAXPIN;

        R = R.pinmul(pin, PBLEN);
        P.sub(R);

        P.toBytes(TOKEN);

        return(0);
    }
コード例 #25
0
/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
    public static void SERVER_1(int date, sbyte[] CID, sbyte[] HID, sbyte[] HTID)
    {
        sbyte[] h = hashit(0, CID);
        ECP     R, P = mapit(h);

        if (date != 0)
        {
            if (HID != null)
            {
                P.toBytes(HID);
            }
            h = hashit(date, h);
            R = mapit(h);
            P.add(R);
            P.toBytes(HTID);
        }
        else
        {
            P.toBytes(HID);
        }
    }
コード例 #26
0
ファイル: ECP.cs プロジェクト: ratranqu/milagro-crypto
/* Conditional move of Q to P dependant on d */
    private void cmove(ECP Q, int d)
    {
        x.cmove(Q.x, d);
        if (ROM.CURVETYPE != ROM.MONTGOMERY)
        {
            y.cmove(Q.y, d);
        }
        z.cmove(Q.z, d);
        if (ROM.CURVETYPE != ROM.EDWARDS)
        {
            bool bd;
            if (d == 0)
            {
                bd = false;
            }
            else
            {
                bd = true;
            }
            INF ^= (INF ^ Q.INF) & bd;
        }
    }
コード例 #27
0
/* validate public key. Set full=true for fuller check */
    public static int PUBLIC_KEY_VALIDATE(bool full, sbyte[] W)
    {
        BIG r;
        ECP WP  = ECP.fromBytes(W);
        int res = 0;

        r = new BIG(ROM.CURVE_Order);

        if (WP.is_infinity())
        {
            res = INVALID_PUBLIC_KEY;
        }
        if (res == 0 && full)
        {
            WP = WP.mul(r);
            if (!WP.is_infinity())
            {
                res = INVALID_PUBLIC_KEY;
            }
        }
        return(res);
    }
コード例 #28
0
ファイル: ECP.cs プロジェクト: ratranqu/milagro-crypto
/* Conditional swap of P and Q dependant on d */
    private void cswap(ECP Q, int d)
    {
        x.cswap(Q.x, d);
        if (ROM.CURVETYPE != ROM.MONTGOMERY)
        {
            y.cswap(Q.y, d);
        }
        z.cswap(Q.z, d);
        if (ROM.CURVETYPE != ROM.EDWARDS)
        {
            bool bd;
            if (d == 0)
            {
                bd = false;
            }
            else
            {
                bd = true;
            }
            bd     = bd & (INF ^ Q.INF);
            INF   ^= bd;
            Q.INF ^= bd;
        }
    }
コード例 #29
0
/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
    public static int ECPSVDP_DH(sbyte[] S, sbyte[] WD, sbyte[] Z)
    {
        BIG r, s;
        ECP W;
        int res = 0;

        sbyte[] T = new sbyte[EFS];

        s = BIG.fromBytes(S);

        W = ECP.fromBytes(WD);
        if (W.is_infinity())
        {
            res = ERROR;
        }

        if (res == 0)
        {
            r = new BIG(ROM.CURVE_Order);
            s.mod(r);
            W = W.mul(s);
            if (W.is_infinity())
            {
                res = ERROR;
            }
            else
            {
                W.X.toBytes(T);
                for (int i = 0; i < EFS; i++)
                {
                    Z[i] = T[i];
                }
            }
        }
        return(res);
    }
コード例 #30
0
ファイル: ECP.cs プロジェクト: ratranqu/milagro-crypto
/* return e.this */

    public ECP mul(BIG e)
    {
        if (e.iszilch() || is_infinity())
        {
            return(new ECP());
        }
        ECP P = new ECP();

        if (ROM.CURVETYPE == ROM.MONTGOMERY)
        {
/* use Ladder */
            int nb, i, b;
            ECP D  = new ECP();
            ECP R0 = new ECP();
            R0.copy(this);
            ECP R1 = new ECP();
            R1.copy(this);
            R1.dbl();
            D.copy(this);
            D.affine();
            nb = e.nbits();
            for (i = nb - 2; i >= 0; i--)
            {
                b = e.bit(i);
                P.copy(R1);
                P.dadd(R0, D);
                R0.cswap(R1, b);
                R1.copy(P);
                R0.dbl();
                R0.cswap(R1, b);
            }
            P.copy(R0);
        }
        else
        {
// fixed size windows
            int     i, b, nb, m, s, ns;
            BIG     mt = new BIG();
            BIG     t  = new BIG();
            ECP     Q  = new ECP();
            ECP     C  = new ECP();
            ECP[]   W  = new ECP[8];
            sbyte[] w  = new sbyte[1 + (ROM.NLEN * ROM.BASEBITS + 3) / 4];

            affine();

// precompute table
            Q.copy(this);
            Q.dbl();
            W[0] = new ECP();
            W[0].copy(this);

            for (i = 1; i < 8; i++)
            {
                W[i] = new ECP();
                W[i].copy(W[i - 1]);
                W[i].add(Q);
            }

// convert the table to affine
            if (ROM.CURVETYPE == ROM.WEIERSTRASS)
            {
                multiaffine(8, W);
            }

// make exponent odd - add 2P if even, P if odd
            t.copy(e);
            s = t.parity();
            t.inc(1);
            t.norm();
            ns = t.parity();
            mt.copy(t);
            mt.inc(1);
            mt.norm();
            t.cmove(mt, s);
            Q.cmove(this, ns);
            C.copy(Q);

            nb = 1 + (t.nbits() + 3) / 4;

// convert exponent to signed 4-bit window
            for (i = 0; i < nb; i++)
            {
                w[i] = (sbyte)(t.lastbits(5) - 16);
                t.dec(w[i]);
                t.norm();
                t.fshr(4);
            }
            w[nb] = (sbyte)t.lastbits(5);

            P.copy(W[(w[nb] - 1) / 2]);
            for (i = nb - 1; i >= 0; i--)
            {
                Q.select(W, w[i]);
                P.dbl();
                P.dbl();
                P.dbl();
                P.dbl();
                P.add(Q);
            }
            P.sub(C);             // apply correction
        }
        P.affine();
        return(P);
    }