blob: 326fbfc6063a3afa0d7d90c1bb382747c56bde51 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
/* MPIN API Functions */
var MPIN256 = function(ctx) {
"use strict";
/**
* Creates an instance of MPIN256
*
* @constructor
* @this {MPIN256}
*/
var MPIN256 = {
BAD_PARAMS: -11,
INVALID_POINT: -14,
WRONG_ORDER: -18,
BAD_PIN: -19,
/* configure PIN here */
MAXPIN: 10000,
/* max PIN */
PBLEN: 14,
/* MAXPIN length in bits */
TS: 12,
/* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
TRAP: 2000,
/* 200 for 4 digit PIN, 2000 for 6-digit PIN - approx 2*sqrt(MAXPIN) */
EFS: ctx.BIG.MODBYTES,
EGS: ctx.BIG.MODBYTES,
SHA256: 32,
SHA384: 48,
SHA512: 64,
/**
* Get epoch time for day
*
* @this {MPIN256}
* @return time in slots since epoch
*/
today: function() {
var now = new Date();
return Math.floor(now.getTime() / (60000 * 1440)); // for daily tokens
},
/**
* Convert byte array to string
*
* @this {MPIN256}
* @param b byte array
* @return s string
*/
bytestostring: function(b) {
var s = "",
len = b.length,
ch, i;
for (i = 0; i < len; i++) {
ch = b[i];
s += ((ch >>> 4) & 15).toString(16);
s += (ch & 15).toString(16);
}
return s;
},
/**
* Convert a string to byte array
*
* @this {MPIN256}
* @param s string
* @return b byte array
*/
stringtobytes: function(s) {
var b = [],
i;
for (i = 0; i < s.length; i++) {
b.push(s.charCodeAt(i));
}
return b;
},
/**
* Convert byte arrays
*
* @this {MPIN256}
* @param a byte array
* @param b byte array
* @return true if equal
*/
comparebytes: function(a, b) {
if (a.length != b.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
},
/**
* Hash values
*
* @this {MPIN256}
* @param c FP8 instance
* @param U ECP unstancebyte array
* @return R hash value
*/
mpin_hash: function(sha, c, U) {
var t = [],
w = [],
h = [],
H, R, i;
c.geta().geta().geta().getA().toBytes(w);
for (i = 0; i < this.EFS; i++) {
t[i] = w[i];
}
c.geta().geta().geta().getB().toBytes(w);
for (i = this.EFS; i < 2 * this.EFS; i++) {
t[i] = w[i - this.EFS];
}
c.geta().geta().getb().getA().toBytes(w);
for (i = 2 * this.EFS; i < 3 * this.EFS; i++) {
t[i] = w[i - 2 * this.EFS];
}
c.geta().geta().getb().getB().toBytes(w);
for (i = 3 * this.EFS; i < 4 * this.EFS; i++) {
t[i] = w[i - 3 * this.EFS];
}
c.geta().getb().geta().getA().toBytes(w);
for (i = 4 * this.EFS; i < 5 * this.EFS; i++) {
t[i] = w[i - 4 * this.EFS];
}
c.geta().getb().geta().getB().toBytes(w);
for (i = 5 * this.EFS; i < 6 * this.EFS; i++) {
t[i] = w[i - 5 * this.EFS];
}
c.geta().getb().getb().getA().toBytes(w);
for (i = 6 * this.EFS; i < 7 * this.EFS; i++) {
t[i] = w[i - 6 * this.EFS];
}
c.geta().getb().getb().getB().toBytes(w);
for (i = 7 * this.EFS; i < 8 * this.EFS; i++) {
t[i] = w[i - 7 * this.EFS];
}
c.getb().geta().geta().getA().toBytes(w);
for (i = 8 * this.EFS; i < 9 * this.EFS; i++) {
t[i] = w[i - 8 * this.EFS];
}
c.getb().geta().geta().getB().toBytes(w);
for (i = 9 *this.EFS; i < 10 * this.EFS; i++) {
t[i] = w[i - 9 * this.EFS];
}
c.getb().geta().getb().getA().toBytes(w);
for (i = 10 * this.EFS; i < 11 * this.EFS; i++) {
t[i] = w[i - 10 * this.EFS];
}
c.getb().geta().getb().getB().toBytes(w);
for (i = 11 * this.EFS; i < 12 * this.EFS; i++) {
t[i] = w[i - 11 * this.EFS];
}
c.getb().getb().geta().getA().toBytes(w);
for (i = 12 * this.EFS; i < 13 * this.EFS; i++) {
t[i] = w[i - 12 * this.EFS];
}
c.getb().getb().geta().getB().toBytes(w);
for (i = 13 * this.EFS; i < 14 * this.EFS; i++) {
t[i] = w[i - 13 * this.EFS];
}
c.getb().getb().getb().getA().toBytes(w);
for (i = 14 * this.EFS; i < 15 * this.EFS; i++) {
t[i] = w[i - 14 * this.EFS];
}
c.getb().getb().getb().getB().toBytes(w);
for (i = 15 * this.EFS; i < 16 * this.EFS; i++) {
t[i] = w[i - 15 * this.EFS];
}
U.getX().toBytes(w);
for (i = 16 * this.EFS; i < 17 * this.EFS; i++) {
t[i] = w[i - 16 * this.EFS];
}
U.getY().toBytes(w);
for (i = 17 * this.EFS; i < 18 * this.EFS; i++) {
t[i] = w[i - 17 * this.EFS];
}
if (sha == this.SHA256) {
H = new ctx.HASH256();
} else if (sha == this.SHA384) {
H = new ctx.HASH384();
} else if (sha == this.SHA512) {
H = new ctx.HASH512();
}
H.process_array(t);
h = H.hash();
if (h.length == 0) {
return null;
}
R = [];
for (i = 0; i < ctx.ECP.AESKEY; i++) {
R[i] = h[i];
}
return R;
},
/**
* General purpose hash function
*
* @this {MPIN256}
* @param sha is the hash type
* @param n Integer
* @param B byte array
* @return R hash value
*/
hashit: function(sha, n, B) {
var R = [],
H, W, i, len;
if (sha == this.SHA256) {
H = new ctx.HASH256();
} else if (sha == this.SHA384) {
H = new ctx.HASH384();
} else if (sha == this.SHA512) {
H = new ctx.HASH512();
}
if (n > 0) {
H.process_num(n);
}
H.process_array(B);
R = H.hash();
if (R.length == 0) {
return null;
}
W = [];
len = ctx.BIG.MODBYTES;
if (sha >= len) {
for (i = 0; i < len; i++) {
W[i] = R[i];
}
} else {
for (i = 0; i < sha; i++) {
W[i + len - sha] = R[i];
}
for (i = 0; i < len - sha; i++) {
W[i] = 0;
}
}
return W;
},
/**
* maps a random u to a point on the curve
*
* @this {MPIN256}
* @param u BIG numberInteger
* @param cb an integer representing the "sign" of y, in fact its least significant bit.
* @return P ECP pointhash value
*/
map: function(u, cb) {
var P = new ctx.ECP(),
x = new ctx.BIG(u),
p = new ctx.BIG(0);
p.rcopy(ctx.ROM_FIELD.Modulus);
x.mod(p);
for (;;) {
P.setxi(x, cb);
if (!P.is_infinity()) {
break;
}
x.inc(1);
x.norm();
}
return P;
},
/**
* returns u derived from P. Random value in range 1 to return value should then be added to u
*
* @this {MPIN256}
* @param u BIG numberInteger
* @param P ECP pointhash value
* @return r Value that should be added to u to derive P
*/
unmap: function(u, P) {
var s = P.getS(),
R = new ctx.ECP(),
r = 0,
x = P.getX();
u.copy(x);
for (;;) {
u.dec(1);
u.norm();
r++;
R.setxi(u, s);
if (!R.is_infinity()) {
break;
}
}
return r;
},
/* 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 indistinguishable from random strings */
ENCODING: function(rng, E) {
var T = [],
i, rn, m, su, sv,
u, v, P, p, W;
for (i = 0; i < this.EFS; i++) {
T[i] = E[i + 1];
}
u = ctx.BIG.fromBytes(T);
for (i = 0; i < this.EFS; i++) {
T[i] = E[i + this.EFS + 1];
}
v = ctx.BIG.fromBytes(T);
P = new ctx.ECP(0);
P.setxy(u, v);
if (P.is_infinity()) {
return this.INVALID_POINT;
}
p = new ctx.BIG(0);
p.rcopy(ctx.ROM_FIELD.Modulus);
u = ctx.BIG.randomnum(p, rng);
su = rng.getByte();
if (su < 0) {
su = -su;
}
su %= 2;
W = this.map(u, su);
P.sub(W);
sv = P.getS();
rn = this.unmap(v, P);
m = rng.getByte();
if (m < 0) {
m = -m;
}
m %= rn;
v.inc(m + 1);
E[0] = (su + 2 * sv);
u.toBytes(T);
for (i = 0; i < this.EFS; i++) {
E[i + 1] = T[i];
}
v.toBytes(T);
for (i = 0; i < this.EFS; i++) {
E[i + this.EFS + 1] = T[i];
}
return 0;
},
DECODING: function(D) {
var T = [],
i, su, sv, u, v, W, P;
if ((D[0] & 0x04) !== 0) {
return this.INVALID_POINT;
}
for (i = 0; i < this.EFS; i++) {
T[i] = D[i + 1];
}
u = ctx.BIG.fromBytes(T);
for (i = 0; i < this.EFS; i++) {
T[i] = D[i + this.EFS + 1];
}
v = ctx.BIG.fromBytes(T);
su = D[0] & 1;
sv = (D[0] >> 1) & 1;
W = this.map(u, su);
P = this.map(v, sv);
P.add(W);
u = P.getX();
v = P.getY();
D[0] = 0x04;
u.toBytes(T);
for (i = 0; i < this.EFS; i++) {
D[i + 1] = T[i];
}
v.toBytes(T);
for (i = 0; i < this.EFS; i++) {
D[i + this.EFS + 1] = T[i];
}
return 0;
},
/**
* Add two members from the group G1
*
* @this {MPIN256}
* @param R1 Input member of G1
* @param R2 Input member of G1
* @param R Output member of G1. R=R1+R2
* @return 0 or an error code
*/
RECOMBINE_G1: function(R1, R2, R) {
var P = ctx.ECP.fromBytes(R1),
Q = ctx.ECP.fromBytes(R2);
if (P.is_infinity() || Q.is_infinity()) {
return this.INVALID_POINT;
}
P.add(Q);
P.toBytes(R,false);
return 0;
},
/**
* Add two members from the group G2
*
* @this {MPIN256}
* @param W1 Input member of G2
* @param W2 Input member of G2
* @param W Output member of G2. W=W1+W2
* @return 0 or an error code
*/
RECOMBINE_G2: function(W1, W2, W) {
var P = ctx.ECP8.fromBytes(W1),
Q = ctx.ECP8.fromBytes(W2);
if (P.is_infinity() || Q.is_infinity()) {
return this.INVALID_POINT;
}
P.add(Q);
P.toBytes(W);
return 0;
},
/**
* Hash the identity
*
* @this {MPIN256}
* @param sha is the hash type
* @param ID Identity as byte array
* @return hash value
*/
HASH_ID: function(sha, ID) {
return this.hashit(sha, 0, ID);
},
/**
* Create random secret
*
* @this {MPIN256}
* @param rng cryptographically secure random number generator
* @param S Random secret value
* @return O for success or else error code
*/
RANDOM_GENERATE: function(rng, S) {
var r = new ctx.BIG(0),
s;
r.rcopy(ctx.ROM_CURVE.CURVE_Order);
s = ctx.BIG.randomnum(r, rng);
s.toBytes(S);
return 0;
},
/**
* Extract a PIN number from a client secret
*
* @this {MPIN256}
* @parameter sha hash type
* @parameter CID Client identity
* @parameter pin PIN value
* @parameter TOKEN Client secret
* @return token
*/
EXTRACT_PIN: function(sha, CID, pin, TOKEN) {
return this.EXTRACT_FACTOR(sha,CID,pin%this.MAXPIN,this.PBLEN,TOKEN);
},
/**
* Extract factor from TOKEN for identity CID
*
* @this {MPIN256}
* @parameter sha hash type
* @parameter CID Client identity
* @parameter factor Value to extract
* @parameter facbits Number of bits in factor
* @parameter TOKEN Token value
* @return token
*/
EXTRACT_FACTOR: function(sha, CID, factor, facbits, TOKEN) {
var P, R, h;
P = ctx.ECP.fromBytes(TOKEN);
if (P.is_infinity()) {
return this.INVALID_POINT;
}
h = this.hashit(sha, 0, CID);
R = ctx.ECP.mapit(h);
R = R.pinmul(factor, facbits);
P.sub(R);
P.toBytes(TOKEN,false);
return 0;
},
/**
* Restore factor to TOKEN for identity CID
*
* @this {MPIN256}
* @parameter sha hash type
* @parameter CID Client identity
* @parameter factor Value to extract
* @parameter facbits Number of bits in factor
* @parameter TOKEN Token value
* @return token
*/
RESTORE_FACTOR: function(sha, CID, factor, facbits, TOKEN) {
var P, R, h;
P = ctx.ECP.fromBytes(TOKEN);
if (P.is_infinity()) {
return this.INVALID_POINT;
}
h = this.hashit(sha, 0, CID),
R = ctx.ECP.mapit(h);
R = R.pinmul(factor, facbits);
P.add(R);
P.toBytes(TOKEN,false);
return 0;
},
/**
* Create a server secret in G2 from a master secret
*
* @this {MPIN256}
* @param S Master secret
* @param SST Server secret = s.Q where Q is a fixed generator of G2
* @return O for success or else error code
*/
GET_SERVER_SECRET: function(S, SST) {
var s,Q;
Q = ctx.ECP8.generator();
s = ctx.BIG.fromBytes(S);
Q = ctx.PAIR256.G2mul(Q, s);
Q.toBytes(SST);
return 0;
},
/**
* Find a random multiple of a point in G1
*
* @this {MPIN256}
* @parameter rng cryptographically secure random number generator
* @param type determines type of action to be taken
* @param x an output internally randomly generated if R!=NULL, otherwise must be provided as an input
* @param G if type=0 a point in G1, else an octet to be mapped to G1
* @param W the output =x.G or x.M(G), where M(.) is a mapping
* @return O for success or else error code
*/
GET_G1_MULTIPLE: function(rng, type, X, G, W) {
var r = new ctx.BIG(0),
x, P;
r.rcopy(ctx.ROM_CURVE.CURVE_Order);
if (rng != null) {
x = ctx.BIG.randomnum(r, rng);
x.toBytes(X);
} else {
x = ctx.BIG.fromBytes(X);
}
if (type == 0) {
P = ctx.ECP.fromBytes(G);
if (P.is_infinity()) {
return this.INVALID_POINT;
}
} else {
P = ctx.ECP.mapit(G);
}
ctx.PAIR256.G1mul(P, x).toBytes(W,false);
return 0;
},
/**
* Create a client secret in G1 from a master secret and the client ID
*
* @this {MPIN256}
* @param S is an input master secret
* @param CID is the input client identity
* @param CST is the full client secret = s.H(ID)
* @return O for success or else error code
*/
GET_CLIENT_SECRET: function(S, CID, CST) {
return this.GET_G1_MULTIPLE(null, 1, S, CID, CST);
},
/**
* Create a Time Permit in G1 from a master secret and the client ID
*
* @this {MPIN256}
* @param sha is the hash type
* @param date is input date, in days since the epoch.
* @param S is an input master secret
* @param CID is the input client identity
* @param CTT is a Time Permit for the given date = s.H(d|H(ID))
* @return O for success or else error code
*/
GET_CLIENT_PERMIT: function(sha, date, S, CID, CTT) {
var h = this.hashit(sha, date, CID),
P = ctx.ECP.mapit(h),
s = ctx.BIG.fromBytes(S);
P = ctx.PAIR256.G1mul(P, s);
P.toBytes(CTT,false);
return 0;
},
/**
* Perform first pass of the client side of the 3-pass version of the M-Pin protocol
*
* @this {MPIN256}
* @param sha is the hash type
* @param date is input date, in days since the epoch. Set to 0 if Time permits disabled
* @param CLIENT_ID is the input client identity
* @param rng is a pointer to a cryptographically secure random number generator
* @param X an output internally randomly generated if R!=NULL, otherwise must be provided as an input
* @param pin is the input PIN number
* @param TOKEN is the input M-Pin token (the client secret with PIN portion removed)
* @param SEC is output = CS+TP, where CS=is the reconstructed client secret, and TP is the time permit
* @param xID is output = x.H(ID)
* @param xCID is output = x.(H(ID)+H(d|H(ID)))
* @param PERMIT is the input time permit
* @return O for success or else error code
*/
CLIENT_1: function(sha, date, CLIENT_ID, rng, X, pin, TOKEN, SEC, xID, xCID, PERMIT) {
var r = new ctx.BIG(0),
x, P, T, W, h;
r.rcopy(ctx.ROM_CURVE.CURVE_Order);
if (rng !== null) {
x = ctx.BIG.randomnum(r, rng);
x.toBytes(X);
} else {
x = ctx.BIG.fromBytes(X);
}
h = this.hashit(sha, 0, CLIENT_ID);
P = ctx.ECP.mapit(h);
T = ctx.ECP.fromBytes(TOKEN);
if (T.is_infinity()) {
return this.INVALID_POINT;
}
pin %= this.MAXPIN;
W = P.pinmul(pin, this.PBLEN);
T.add(W);
if (date != 0) {
W = ctx.ECP.fromBytes(PERMIT);
if (W.is_infinity()) {
return this.INVALID_POINT;
}
T.add(W);
h = this.hashit(sha, date, h);
W = ctx.ECP.mapit(h);
if (xID != null) {
P = ctx.PAIR256.G1mul(P, x);
P.toBytes(xID,false);
W = ctx.PAIR256.G1mul(W, x);
P.add(W);
} else {
P.add(W);
P = ctx.PAIR256.G1mul(P, x);
}
if (xCID != null) {
P.toBytes(xCID,false);
}
} else {
if (xID != null) {
P = ctx.PAIR256.G1mul(P, x);
P.toBytes(xID,false);
}
}
T.toBytes(SEC,false);
return 0;
},
/**
* Perform second pass of the client side of the 3-pass version of the M-Pin protocol
*
* @this {MPIN256}
* @param X an input, a locally generated random number
* @param Y an input random challenge from the server
* @param SEC on output = -(x+y).V
* @return O for success or else error code
*/
CLIENT_2: function(X, Y, SEC) {
var r = new ctx.BIG(0),
P, px, py;
r.rcopy(ctx.ROM_CURVE.CURVE_Order);
P = ctx.ECP.fromBytes(SEC);
if (P.is_infinity()) {
return this.INVALID_POINT;
}
px = ctx.BIG.fromBytes(X);
py = ctx.BIG.fromBytes(Y);
px.add(py);
px.mod(r);
P = ctx.PAIR256.G1mul(P, px);
P.neg();
P.toBytes(SEC,false);
return 0;
},
/**
* Perform first pass of the server side of the 3-pass version of the M-Pin protocol
*
* @this {MPIN256}
* @param sha is the hash type
* @param date is input date, in days since the epoch. Set to 0 if Time permits disabled
* @param CID is the input claimed client identity
* @param HID is output H(ID), a hash of the client ID
* @param HTID is output H(ID)+H(d|H(ID))
* @return O for success or else error code
*/
SERVER_1: function(sha, date, CID, HID, HTID) {
var h = this.hashit(sha, 0, CID),
P = ctx.ECP.mapit(h),
R;
P.toBytes(HID,false);
if (date !== 0) {
h = this.hashit(sha, date, h);
R = ctx.ECP.mapit(h);
P.add(R);
P.toBytes(HTID,false);
}
},
/**
* Perform third pass on the server side of the 3-pass version of the M-Pin protocol
*
* @this {MPIN256}
* @param date is input date, in days since the epoch. Set to 0 if Time permits disabled
* @param HID is input H(ID), a hash of the client ID
* @param HTID is input H(ID)+H(d|H(ID))
* @param Y is the input server's randomly generated challenge
* @param SST is the input server secret
* @param xID is input from the client = x.H(ID)
* @param xCID is input from the client= x.(H(ID)+H(d|H(ID)))
* @param mSEC is an input from the client
* @param E is an output to help the Kangaroos to find the PIN error, or NULL if not required
* @param F is an output to help the Kangaroos to find the PIN error, or NULL if not required
* @param Pa is the input public key from the client, z.Q or NULL if the client uses regular mpin
* @return O for success or else error code
*/
SERVER_2: function(date, HID, HTID, Y, SST, xID, xCID, mSEC, E, F, Pa) {
var Q, sQ, R, y, P, g;
if (typeof Pa === "undefined" || Pa == null) {
Q = ctx.ECP8.generator();
} else {
Q = ctx.ECP8.fromBytes(Pa);
if (Q.is_infinity()) {
return this.INVALID_POINT;
}
}
sQ = ctx.ECP8.fromBytes(SST);
if (sQ.is_infinity()) {
return this.INVALID_POINT;
}
if (date !== 0) {
R = ctx.ECP.fromBytes(xCID);
} else {
if (xID == null) {
return this.BAD_PARAMS;
}
R = ctx.ECP.fromBytes(xID);
}
if (R.is_infinity()) {
return this.INVALID_POINT;
}
y = ctx.BIG.fromBytes(Y);
if (date != 0) {
P = ctx.ECP.fromBytes(HTID);
} else {
if (HID == null) {
return this.BAD_PARAMS;
}
P = ctx.ECP.fromBytes(HID);
}
if (P.is_infinity()) {
return this.INVALID_POINT;
}
P = ctx.PAIR256.G1mul(P, y);
P.add(R);
R = ctx.ECP.fromBytes(mSEC);
if (R.is_infinity()) {
return this.INVALID_POINT;
}
g = ctx.PAIR256.ate2(Q, R, sQ, P);
g = ctx.PAIR256.fexp(g);
if (!g.isunity()) {
if (HID != null && xID != null && E != null && F != null) {
g.toBytes(E);
if (date !== 0) {
P = ctx.ECP.fromBytes(HID);
if (P.is_infinity()) {
return this.INVALID_POINT;
}
R = ctx.ECP.fromBytes(xID);
if (R.is_infinity()) {
return this.INVALID_POINT;
}
P = ctx.PAIR256.G1mul(P, y);
P.add(R);
}
g = ctx.PAIR256.ate(Q, P);
g = ctx.PAIR256.fexp(g);
g.toBytes(F);
}
return this.BAD_PIN;
}
return 0;
},
/**
* Use Kangaroos to find PIN error
*
* @this {MPIN256}
* @param E a member of the group GT
* @param F a member of the group GT = E^e
* @return 0 if Kangaroos failed, or the PIN error e
*/
KANGAROO: function(E, F) {
var ge = ctx.FP48.fromBytes(E),
gf = ctx.FP48.fromBytes(F),
distance = [],
t = new ctx.FP48(gf),
table = [],
i, j, m, s, dn, dm, res, steps;
s = 1;
for (m = 0; m < this.TS; m++) {
distance[m] = s;
table[m] = new ctx.FP48(t);
s *= 2;
t.usqr();
}
t.one();
dn = 0;
for (j = 0; j < this.TRAP; j++) {
i = t.geta().geta().geta().geta().getA().lastbits(20) % this.TS;
t.mul(table[i]);
dn += distance[i];
}
gf.copy(t);
gf.conj();
steps = 0;
dm = 0;
res = 0;
while (dm - dn < this.MAXPIN) {
steps++;
if (steps > 4 * this.TRAP) {
break;
}
i = ge.geta().geta().geta().geta().getA().lastbits(20) % this.TS;
ge.mul(table[i]);
dm += distance[i];
if (ge.equals(t)) {
res = dm - dn;
break;
}
if (ge.equals(gf)) {
res = dn - dm;
break;
}
}
if (steps > 4 * this.TRAP || dm - dn >= this.MAXPIN) {
res = 0;
} // Trap Failed - probable invalid token
return res;
},
/**
* Time since epoch
*
* @this {MPIN256}
* @return time since epoch
*/
GET_TIME: function() {
var now = new Date();
return Math.floor(now.getTime() / (1000));
},
/* y = H(time,xCID) */
GET_Y: function(sha, TimeValue, xCID, Y) {
var q = new ctx.BIG(0),
h = this.hashit(sha, TimeValue, xCID),
y = ctx.BIG.fromBytes(h);
q.rcopy(ctx.ROM_CURVE.CURVE_Order);
y.mod(q);
y.toBytes(Y);
return 0;
},
/**
* Perform client side of the one-pass version of the M-Pin protocol
*
* @this {MPIN256}
* @param sha is the hash type
* @param date is input date, in days since the epoch. Set to 0 if Time permits disabled
* @param CLIENT_ID is the input client identity
* @param rng is a pointer to a cryptographically secure random number generator
* @param X an output internally randomly generated if R!=NULL, otherwise must be provided as an input
* @param pin is the input PIN number
* @param TOKEN is the input M-Pin token (the client secret with PIN portion removed)
* @param SEC is output = -(x+y)(CS+TP), where CS is the reconstructed client secret, and TP is the time permit
* @param xID is output = x.H(ID)
* @param xCID is output = x.(H(ID)+H(d|H(ID)))
* @param PERMIT is the input time permit
* @param TimeValue is input epoch time in seconds - a timestamp
* @param Y is output H(t|U) or H(t|UT) if Time Permits enabled
* @param Message is the message to be signed
* @return O for success or else error code
*/
CLIENT: function(sha, date, CLIENT_ID, rng, X, pin, TOKEN, SEC, xID, xCID, PERMIT, TimeValue, Y, Message) {
var rtn = 0,
M = [],
pID, i;
if (date == 0) {
pID = xID;
} else {
pID = xCID;
xID = null;
}
rtn = this.CLIENT_1(sha, date, CLIENT_ID, rng, X, pin, TOKEN, SEC, xID, xCID, PERMIT);
if (rtn != 0) {
return rtn;
}
M = pID.slice();
if (typeof Message !== "undefined" || Message != null) {
for (i = 0; i < Message.length; i++) {
M.push(Message[i]);
}
}
this.GET_Y(sha, TimeValue, M, Y);
rtn = this.CLIENT_2(X, Y, SEC);
if (rtn != 0) {
return rtn;
}
return 0;
},
/**
* Perform server side of the one-pass version of the M-Pin protocol
*
* @this {MPIN256}
* @param sha is the hash type
* @param date is input date, in days since the epoch. Set to 0 if Time permits disabled
* @param HID is output H(ID), a hash of the client ID
* @param HTID is output H(ID)+H(d|H(ID))
* @param Y is output H(t|U) or H(t|UT) if Time Permits enabled
* @param SST is the input server secret
* @param xID is input from the client = x.H(ID)
* @param xCID is input from the client= x.(H(ID)+H(d|H(ID)))
* @param mSEC is an input from the client
* @param E is an output to help the Kangaroos to find the PIN error, or NULL if not required
* @param F is an output to help the Kangaroos to find the PIN error, or NULL if not required
* @param CID is the input claimed client identity
* @param TimeValue is input epoch time in seconds - a timestamp
* @param MESSAGE is the message to be signed
* @param Pa is input from the client z.Q or NULL if the key-escrow less scheme is not used
* @return O for success or else error code
*/
SERVER: function(sha, date, HID, HTID, Y, SST, xID, xCID, mSEC, E, F, CID, TimeValue, Message, Pa) {
var rtn = 0,
M = [],
pID, i;
if (date == 0) {
pID = xID;
} else {
pID = xCID;
}
this.SERVER_1(sha, date, CID, HID, HTID);
M = pID.slice();
if (typeof Message !== "undefined" || Message != null) {
for (i = 0; i < Message.length; i++) {
M.push(Message[i]);
}
}
this.GET_Y(sha, TimeValue, M, Y);
rtn = this.SERVER_2(date, HID, HTID, Y, SST, xID, xCID, mSEC, E, F, Pa);
if (rtn != 0) {
return rtn;
}
return 0;
},
/**
* Precompute values for use by the client side of M-Pin Full
*
* @this {MPIN256}
* @param TOKEN is the input M-Pin token (the client secret with PIN portion removed)
* @param CID is the input client identity
* @param G1 precomputed output
* @param G2 precomputed output
* @return O for success or else error code
*/
PRECOMPUTE: function(TOKEN, CID, G1, G2) {
var P, T, g, Q;
T = ctx.ECP.fromBytes(TOKEN);
if (T.is_infinity()) {
return this.INVALID_POINT;
}
P = ctx.ECP.mapit(CID);
Q = ctx.ECP8.generator();
g = ctx.PAIR256.ate(Q, T);
g = ctx.PAIR256.fexp(g);
g.toBytes(G1);
g = ctx.PAIR256.ate(Q, P);
g = ctx.PAIR256.fexp(g);
g.toBytes(G2);
return 0;
},
/**
* Hash the session transcript
*
* @this {MPIN256}
* @param sha is the hash type
* @param HID is the hashed input client ID = H(ID)
* @param xID is the client output = x.H(ID)
* @param xCID is the client output = x.(H(ID)+H(T|H(ID)))
* @param SEC is the client part response
* @param Y is the server challenge
* @param R is the client part response
* @param W is the server part response
* @return H the output is the hash of all of the above that apply
*/
HASH_ALL: function(sha, HID, xID, xCID, SEC, Y, R, W) {
var tlen = 0,
T = [],
i;
for (i = 0; i < HID.length; i++) {
T[i] = HID[i];
}
tlen += HID.length;
if (xCID != null) {
for (i = 0; i < xCID.length; i++) {
T[i + tlen] = xCID[i];
}
tlen += xCID.length;
} else {
for (i = 0; i < xID.length; i++) {
T[i + tlen] = xID[i];
}
tlen += xID.length;
}
for (i = 0; i < SEC.length; i++) {
T[i + tlen] = SEC[i];
}
tlen += SEC.length;
for (i = 0; i < Y.length; i++) {
T[i + tlen] = Y[i];
}
tlen += Y.length;
for (i = 0; i < R.length; i++) {
T[i + tlen] = R[i];
}
tlen += R.length;
for (i = 0; i < W.length; i++) {
T[i + tlen] = W[i];
}
tlen += W.length;
return this.hashit(sha, 0, T);
},
/**
* Calculate Key on Client side for M-Pin Full
*
* @this {MPIN256}
* @param sha is the hash type
* @param G1 precomputed input
* @param G2 precomputed input
* @param pin is the input PIN number
* @param R is an input, a locally generated random number
* @param X is an input, a locally generated random number
* @param H is an input, hash of the protocol transcript
* @param wCID is the input Server-side Diffie-Hellman component
* @param CK is the output calculated shared key
* @return 0 or an error code
*/
CLIENT_KEY: function(sha, G1, G2, pin, R, X, H, wCID, CK) {
var t = [],
g1 = ctx.FP48.fromBytes(G1),
g2 = ctx.FP48.fromBytes(G2),
z = ctx.BIG.fromBytes(R),
x = ctx.BIG.fromBytes(X),
h = ctx.BIG.fromBytes(H),
W = ctx.ECP.fromBytes(wCID),
r, c, i;
if (W.is_infinity()) {
return this.INVALID_POINT;
}
W = ctx.PAIR256.G1mul(W, x);
r = new ctx.BIG(0);
r.rcopy(ctx.ROM_CURVE.CURVE_Order);
z.add(h);
z.mod(r);
g2.pinpow(pin, this.PBLEN);
g1.mul(g2);
c = g1.compow(z, r);
t = this.mpin_hash(sha, c, W);
for (i = 0; i < ctx.ECP.AESKEY; i++) {
CK[i] = t[i];
}
return 0;
},
/**
* Calculate Key on Server side for M-Pin Full
*
* @this {MPIN256}
* @param h is the hash type
* @param Z is the input Client-side Diffie-Hellman component
* @param SST is the input server secret
* @param W is an input random number generated by the server
* @param H is an input, hash of the protocol transcript
* @param HID is the hashed input client ID = H(ID)
* @param xID is input from the client = x.H(ID)
* @param xCID is input from the client= x.(H(ID)+H(d|H(ID)))
* @param SK is the output calculated shared key
* @return 0 or an error code
*/
SERVER_KEY: function(sha, Z, SST, W, H, HID, xID, xCID, SK) {
var t = [],
sQ, R, A, U, w, h, g, c, i;
sQ = ctx.ECP8.fromBytes(SST);
if (sQ.is_infinity()) {
return this.INVALID_POINT;
}
R = ctx.ECP.fromBytes(Z);
if (R.is_infinity()) {
return this.INVALID_POINT;
}
A = ctx.ECP.fromBytes(HID);
if (A.is_infinity()) {
return this.INVALID_POINT;
}
if (xCID != null) {
U = ctx.ECP.fromBytes(xCID);
} else {
U = ctx.ECP.fromBytes(xID);
}
if (U.is_infinity()) {
return this.INVALID_POINT;
}
w = ctx.BIG.fromBytes(W);
h = ctx.BIG.fromBytes(H);
A = ctx.PAIR256.G1mul(A, h);
R.add(A);
U = ctx.PAIR256.G1mul(U, w);
g = ctx.PAIR256.ate(sQ, R);
g = ctx.PAIR256.fexp(g);
c = g.trace();
t = this.mpin_hash(sha, c, U);
for (i = 0; i < ctx.ECP.AESKEY; i++) {
SK[i] = t[i];
}
return 0;
},
/**
* Generates a random public key for the client z.Q
*
* @this {MPIN256}
* @param rng cryptographically secure random number generator
* @param Z an output internally randomly generated if R!=NULL, otherwise it must be provided as an input
* @param Pa the output public key for the client
* @return 0 or an error code
*/
GET_DVS_KEYPAIR: function(rng, Z, Pa) {
var r = new ctx.BIG(0),
z, Q;
r.rcopy(ctx.ROM_CURVE.CURVE_Order);
if (rng != null) {
z = ctx.BIG.randomnum(r, rng);
z.toBytes(Z);
} else {
z = ctx.BIG.fromBytes(Z);
}
z.invmodp(r);
Q = ctx.ECP8.generator();
Q = ctx.PAIR256.G2mul(Q, z);
Q.toBytes(Pa);
return 0;
}
};
return MPIN256;
};
if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
module.exports = {
MPIN256: MPIN256
};
}