package org.thoughtcrime.SMP.crypto.SMP;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* Java OTR library
* Copyright (C) 2008-2009 Ian Goldberg, Muhaimeen Ashraf, Andrew Chung,
* Can Tang
* <p/>
* This library is free software; you can redistribute it and/or
* modify it under the terms of version 2.1 of the GNU Lesser General
* Public License as published by the Free Software Foundation
* <p/>
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* <p/>
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Ported from otr4j by ludwig on 17/06/15 */
public class SMP {
private static final String TAG = SMP.class.getSimpleName();
public static final int PROG_OK = 0;
private static final int PROG_CHEATED = -2;
private static final int PROG_FAILED = -1;
private static final int PROG_SUCCEEDED = 1;
private static final BigInteger MODULUS_S = new BigInteger(
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", 16);
private static final BigInteger MODULUS_MINUS_2 = new BigInteger(
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFD", 16);
private static final BigInteger ORDER_S = new BigInteger(
"7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" +
"948127044533E63A0105DF531D89CD9128A5043CC71A026E" +
"F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" +
"F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" +
"F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9E" +
"E1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AF" +
"C1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36" +
"B3861AA7255E4C0278BA36046511B993FFFFFFFFFFFFFFFF", 16);
public static final byte[] GENERATOR_S = Util.hexStringToBytes("02");
private static final int MOD_LEN_BYTES = 192;
/**
* Generate a random exponent
*
* @return the generated random exponent.
*/
private static BigInteger randomExponent() {
SecureRandom sr = new SecureRandom();
byte[] sb = new byte[MOD_LEN_BYTES];
sr.nextBytes(sb);
return new BigInteger(1, sb);
}
/**
* Hash one or two BigIntegers. To hash only one BigInteger, b may be set to
* NULL.
*
* @param version the prefix to use
* @param a The 1st BigInteger to hash.
* @param b The 2nd BigInteger to hash.
* @return the BigInteger for the resulting hash value.
* @throws SMPException when the SHA-256 algorithm
* is missing or when the BigInteger can't be serialized.
*/
private static BigInteger hash(int version, BigInteger a, BigInteger b) throws SMPException {
try {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.update((byte) version);
sha256.update(SerializationUtils.writeMpi(a));
if (b != null)
sha256.update(SerializationUtils.writeMpi(b));
return new BigInteger(1, sha256.digest());
} catch (NoSuchAlgorithmException e) {
throw new SMPException("cannot find SHA-256");
} catch (IOException e) {
throw new SMPException("cannot serialize bigint");
}
}
private static byte[] serialize(BigInteger[] ints) throws SMPException {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
SMPOutputStream sos = new SMPOutputStream(out);
sos.writeInt(ints.length);
for (BigInteger i : ints) {
sos.writeBigInt(i);
}
byte[] b = out.toByteArray();
sos.close();
return b;
} catch (IOException ex) {
throw new SMPException("cannot serialize bigints");
}
}
private static BigInteger[] unserialize(byte[] bytes) throws SMPException {
try {
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
SMPInputStream sis = new SMPInputStream(in);
int len = sis.readInt();
if (len > 100) {
throw new SMPException("Too many ints");
}
BigInteger[] ints = new BigInteger[len];
for (int i = 0; i < len; i++) {
ints[i] = sis.readBigInt();
}
sis.close();
return ints;
} catch (IOException ex) {
throw new SMPException("cannot unserialize bigints");
}
}
/**
* Check that an BigInteger is in the right range to be a (non-unit) group
* element.
*
* @param g the BigInteger to check.
* @return true if the BigInteger is in the right range, false otherwise.
*/
private static boolean checkGroupElement(BigInteger g) {
return !(g.compareTo(BigInteger.valueOf(2)) > 0 &&
g.compareTo(SMP.MODULUS_MINUS_2) < 0);
}
/**
* Check that an BigInteger is in the right range to be a (non-zero)
* exponent.
*
* @param x The BigInteger to check.
* @return true if the BigInteger is in the right range, false otherwise.
*/
private static boolean checkExponent(BigInteger x) {
return !(x.compareTo(BigInteger.ONE) > 0 && x.compareTo(SMP.ORDER_S) <= 0);
}
/**
* Proof of knowledge of a discrete logarithm.
*
* @param g the group generator
* @param x the secret information
* @param version the prefix to use for the hashing function
* @return c and d.
* @throws SMPException when c and d could not be calculated
*/
private static BigInteger[] proofKnowLog(BigInteger g, BigInteger x, int version) throws SMPException {
BigInteger r = randomExponent();
BigInteger temp = g.modPow(r, SMP.MODULUS_S);
BigInteger c = hash(version, temp, null);
temp = x.multiply(c).mod(ORDER_S);
BigInteger d = r.subtract(temp).mod(ORDER_S);
BigInteger[] ret = new BigInteger[2];
ret[0] = c;
ret[1] = d;
return ret;
}
/**
* Verify a proof of knowledge of a discrete logarithm. Checks that c = h(g^d x^c)
*
* @param c c from remote party
* @param d d from remote party
* @param g the group generator
* @param x our secret information
* @param version the prefix to use
* @return -1, 0 or 1 as our locally calculated value of c is numerically
* less than, equal to, or greater than {@code c}.
* @throws SMPException when something goes wrong
*/
private static int checkKnowLog(BigInteger c, BigInteger d, BigInteger g, BigInteger x, int version) throws SMPException {
BigInteger gd = g.modPow(d, MODULUS_S);
BigInteger xc = x.modPow(c, MODULUS_S);
BigInteger gdxc = gd.multiply(xc).mod(MODULUS_S);
BigInteger hgdxc = hash(version, gdxc, null);
return hgdxc.compareTo(c);
}
/**
* Proof of knowledge of coordinates with first components being equal
*
* @param state MVN_PASS_JAVADOC_INSPECTION
* @param r MVN_PASS_JAVADOC_INSPECTION
* @param version MVN_PASS_JAVADOC_INSPECTION
* @return MVN_PASS_JAVADOC_INSPECTION
* @throws SMPException MVN_PASS_JAVADOC_INSPECTION
*/
private static BigInteger[] proofEqualCoords(SMPState state, BigInteger r, int version) throws SMPException {
BigInteger r1 = randomExponent();
BigInteger r2 = randomExponent();
/* Compute the value of c, as c = h(g3^r1, g1^r1 g2^r2) */
BigInteger temp1 = state.g1.modPow(r1, MODULUS_S);
BigInteger temp2 = state.g2.modPow(r2, MODULUS_S);
temp2 = temp1.multiply(temp2).mod(MODULUS_S);
temp1 = state.g3.modPow(r1, MODULUS_S);
BigInteger c = hash(version, temp1, temp2);
/* Compute the d values, as d1 = r1 - r c, d2 = r2 - secret c */
temp1 = r.multiply(c).mod(ORDER_S);
BigInteger d1 = r1.subtract(temp1).mod(ORDER_S);
temp1 = state.secret.multiply(c).mod(ORDER_S);
BigInteger d2 = r2.subtract(temp1).mod(ORDER_S);
BigInteger[] ret = new BigInteger[3];
ret[0] = c;
ret[1] = d1;
ret[2] = d2;
return ret;
}
/**
* Verify a proof of knowledge of coordinates with first components being equal
*
* @param c MVN_PASS_JAVADOC_INSPECTION
* @param d1 MVN_PASS_JAVADOC_INSPECTION
* @param d2 MVN_PASS_JAVADOC_INSPECTION
* @param p MVN_PASS_JAVADOC_INSPECTION
* @param q MVN_PASS_JAVADOC_INSPECTION
* @param state MVN_PASS_JAVADOC_INSPECTION
* @param version MVN_PASS_JAVADOC_INSPECTION
* @return MVN_PASS_JAVADOC_INSPECTION
* @throws SMPException MVN_PASS_JAVADOC_INSPECTION
*/
private static int checkEqualCoords(BigInteger c, BigInteger d1, BigInteger d2, BigInteger p,
BigInteger q, SMPState state, int version) throws SMPException {
/* To verify, we test that hash(g3^d1 * p^c, g1^d1 * g2^d2 * q^c) = c
* If indeed c = hash(g3^r1, g1^r1 g2^r2), d1 = r1 - r*c,
* d2 = r2 - secret*c. And if indeed p = g3^r, q = g1^r * g2^secret
* Then we should have that:
* hash(g3^d1 * p^c, g1^d1 * g2^d2 * q^c)
* = hash(g3^(r1 - r*c + r*c), g1^(r1 - r*c + q*c) *
* g2^(r2 - secret*c + secret*c))
* = hash(g3^r1, g1^r1 g2^r2)
* = c
*/
BigInteger temp2 = state.g3.modPow(d1, MODULUS_S);
BigInteger temp3 = p.modPow(c, MODULUS_S);
BigInteger temp1 = temp2.multiply(temp3).mod(MODULUS_S);
temp2 = state.g1.modPow(d1, MODULUS_S);
temp3 = state.g2.modPow(d2, MODULUS_S);
temp2 = temp2.multiply(temp3).mod(MODULUS_S);
temp3 = q.modPow(c, MODULUS_S);
temp2 = temp3.multiply(temp2).mod(MODULUS_S);
BigInteger c_prime = hash(version, temp1, temp2);
return c.compareTo(c_prime);
}
/**
* Proof of knowledge of logs with exponents being equal
*
* @param state MVN_PASS_JAVADOC_INSPECTION
* @param version MVN_PASS_JAVADOC_INSPECTION
* @return MVN_PASS_JAVADOC_INSPECTION
* @throws SMPException MVN_PASS_JAVADOC_INSPECTION
*/
private static BigInteger[] proofEqualLogs(SMPState state, int version) throws SMPException {
BigInteger r = randomExponent();
/* Compute the value of c, as c = h(g1^r, (Qa/Qb)^r) */
BigInteger temp1 = state.g1.modPow(r, MODULUS_S);
BigInteger temp2 = state.qab.modPow(r, MODULUS_S);
BigInteger c = hash(version, temp1, temp2);
/* Compute the d values, as d = r - x3 c */
temp1 = state.x3.multiply(c).mod(ORDER_S);
BigInteger d = r.subtract(temp1).mod(ORDER_S);
BigInteger[] ret = new BigInteger[2];
ret[0] = c;
ret[1] = d;
return ret;
}
/**
* Verify a proof of knowledge of logs with exponents being equal
*
* @param c MVN_PASS_JAVADOC_INSPECTION
* @param d MVN_PASS_JAVADOC_INSPECTION
* @param r MVN_PASS_JAVADOC_INSPECTION
* @param state MVN_PASS_JAVADOC_INSPECTION
* @param version MVN_PASS_JAVADOC_INSPECTION
* @return MVN_PASS_JAVADOC_INSPECTION
* @throws SMPException MVN_PASS_JAVADOC_INSPECTION
*/
private static int checkEqualLogs(BigInteger c, BigInteger d, BigInteger r, SMPState state, int version) throws SMPException {
/* Here, we recall the exponents used to create g3.
* If we have previously seen g3o = g1^x where x is unknown
* during the DH exchange to produce g3, then we may proceed with:
*
* To verify, we test that hash(g1^d * g3o^c, qab^d * r^c) = c
* If indeed c = hash(g1^r1, qab^r1), d = r1- x * c
* And if indeed r = qab^x
* Then we should have that:
* hash(g1^d * g3o^c, qab^d r^c)
* = hash(g1^(r1 - x*c + x*c), qab^(r1 - x*c + x*c))
* = hash(g1^r1, qab^r1)
* = c
*/
BigInteger temp2 = state.g1.modPow(d, MODULUS_S);
BigInteger temp3 = state.g3o.modPow(c, MODULUS_S);
BigInteger temp1 = temp2.multiply(temp3).mod(MODULUS_S);
temp3 = state.qab.modPow(d, MODULUS_S);
temp2 = r.modPow(c, MODULUS_S);
temp2 = temp3.multiply(temp2).mod(MODULUS_S);
BigInteger c_prime = hash(version, temp1, temp2);
return c.compareTo(c_prime);
}
/**
* Create first message in SMP exchange. Input is Alice's secret value
* which this protocol aims to compare to Bob's. The return value is a serialized
* BigInteger array whose elements correspond to the following:
* [0] = g2a, Alice's half of DH exchange to determine g2
* [1] = c2, [2] = d2, Alice's ZK proof of knowledge of g2a exponent
* [3] = g3a, Alice's half of DH exchange to determine g3
* [4] = c3, [5] = d3, Alice's ZK proof of knowledge of g3a exponent
*
* @param aState MVN_PASS_JAVADOC_INSPECTION
* @param secret MVN_PASS_JAVADOC_INSPECTION
* @return MVN_PASS_JAVADOC_INSPECTION
* @throws SMPException MVN_PASS_JAVADOC_INSPECTION
*/
public static byte[] step1(SMPState aState, byte[] secret) throws SMPException {
/* Initialize the sm state or update the secret */
aState.secret = new BigInteger(1, secret);
aState.receivedQuestion = 0;
aState.x2 = randomExponent();
aState.x3 = randomExponent();
BigInteger[] msg1 = new BigInteger[6];
msg1[0] = aState.g1.modPow(aState.x2, MODULUS_S);
BigInteger[] res = proofKnowLog(aState.g1, aState.x2, 1);
msg1[1] = res[0];
msg1[2] = res[1];
msg1[3] = aState.g1.modPow(aState.x3, MODULUS_S);
res = proofKnowLog(aState.g1, aState.x3, 2);
msg1[4] = res[0];
msg1[5] = res[1];
byte[] ret = serialize(msg1);
aState.smProgState = PROG_OK;
return ret;
}
/**
* Receive the first message in SMP exchange, which was generated by
* step1. Input is saved until the user inputs their secret
* information. No output.
*
* @param bState MVN_PASS_JAVADOC_INSPECTION
* @param input MVN_PASS_JAVADOC_INSPECTION
* @param received_question MVN_PASS_JAVADOC_INSPECTION
* @throws SMPException MVN_PASS_JAVADOC_INSPECTION
*/
public static void step2a(SMPState bState, byte[] input, int received_question)
throws SMPException {
/* Initialize the sm state if needed */
bState.receivedQuestion = received_question;
bState.smProgState = PROG_CHEATED;
/* Read from input to find the mpis */
BigInteger[] msg1 = unserialize(input);
if (checkGroupElement(msg1[0]) || checkExponent(msg1[2]) ||
checkGroupElement(msg1[3]) || checkExponent(msg1[5])) {
throw new SMPException("Invalid parameter");
}
/* Store Alice's g3a value for later in the protocol */
bState.g3o = msg1[3];
/* Verify Alice's proofs */
if (checkKnowLog(msg1[1], msg1[2], bState.g1, msg1[0], 1) != 0
|| checkKnowLog(msg1[4], msg1[5], bState.g1, msg1[3], 2) != 0) {
throw new SMPException("Proof checking failed");
}
/* Create Bob's half of the generators g2 and g3 */
bState.x2 = randomExponent();
bState.x3 = randomExponent();
/* Combine the two halves from Bob and Alice and determine g2 and g3 */
bState.g2 = msg1[0].modPow(bState.x2, MODULUS_S);
bState.g3 = msg1[3].modPow(bState.x3, MODULUS_S);
bState.smProgState = PROG_OK;
}
/**
* Create second message in SMP exchange. Input is Bob's secret value.
* Information from earlier steps in the exchange is taken from Bob's
* state. Output is a serialized mpi array whose elements correspond
* to the following:
* [0] = g2b, Bob's half of DH exchange to determine g2
* [1] = c2, [2] = d2, Bob's ZK proof of knowledge of g2b exponent
* [3] = g3b, Bob's half of DH exchange to determine g3
* [4] = c3, [5] = d3, Bob's ZK proof of knowledge of g3b exponent
* [6] = pb, [7] = qb, Bob's halves of the (Pa/Pb) and (Qa/Qb) values
* [8] = cp, [9] = d5, [10] = d6, Bob's ZK proof that pb, qb formed correctly
*
* @param bState MVN_PASS_JAVADOC_INSPECTION
* @param secret MVN_PASS_JAVADOC_INSPECTION
* @return MVN_PASS_JAVADOC_INSPECTION
* @throws SMPException MVN_PASS_JAVADOC_INSPECTION
*/
public static byte[] step2b(SMPState bState, byte[] secret) throws SMPException {
/* Convert the given secret to the proper form and store it */
bState.secret = new BigInteger(1, secret);
BigInteger[] msg2 = new BigInteger[11];
msg2[0] = bState.g1.modPow(bState.x2, MODULUS_S);
BigInteger[] res = proofKnowLog(bState.g1, bState.x2, 3);
msg2[1] = res[0];
msg2[2] = res[1];
msg2[3] = bState.g1.modPow(bState.x3, MODULUS_S);
res = proofKnowLog(bState.g1, bState.x3, 4);
msg2[4] = res[0];
msg2[5] = res[1];
/* Calculate P and Q values for Bob */
BigInteger r = randomExponent();
bState.p = bState.g3.modPow(r, MODULUS_S);
msg2[6] = bState.p;
BigInteger qb1 = bState.g1.modPow(r, MODULUS_S);
BigInteger qb2 = bState.g2.modPow(bState.secret, MODULUS_S);
bState.q = qb1.multiply(qb2).mod(MODULUS_S);
msg2[7] = bState.q;
res = proofEqualCoords(bState, r, 5);
msg2[8] = res[0];
msg2[9] = res[1];
msg2[10] = res[2];
/* Convert to serialized form */
return serialize(msg2);
}
/**
* Create third message in SMP exchange. Input is a message generated
* by otrl_sm_step2b. Output is a serialized mpi array whose elements
* correspond to the following:
* [0] = pa, [1] = qa, Alice's halves of the (Pa/Pb) and (Qa/Qb) values
* [2] = cp, [3] = d5, [4] = d6, Alice's ZK proof that pa, qa formed correctly
* [5] = ra, calculated as (Qa/Qb)^x3 where x3 is the exponent used in g3a
* [6] = cr, [7] = d7, Alice's ZK proof that ra is formed correctly
*
* @param aState MVN_PASS_JAVADOC_INSPECTION
* @param input MVN_PASS_JAVADOC_INSPECTION
* @return MVN_PASS_JAVADOC_INSPECTION
* @throws SMPException MVN_PASS_JAVADOC_INSPECTION
*/
public static byte[] step3(SMPState aState, byte[] input) throws SMPException {
/* Read from input to find the mpis */
aState.smProgState = PROG_CHEATED;
BigInteger[] msg2 = unserialize(input);
if (checkGroupElement(msg2[0]) || checkGroupElement(msg2[3]) ||
checkGroupElement(msg2[6]) || checkGroupElement(msg2[7]) ||
checkExponent(msg2[2]) || checkExponent(msg2[5]) ||
checkExponent(msg2[9]) || checkExponent(msg2[10])) {
throw new SMPException("Invalid Parameter");
}
BigInteger[] msg3 = new BigInteger[8];
/* Store Bob's g3a value for later in the protocol */
aState.g3o = msg2[3];
/* Verify Bob's knowledge of discreet log proofs */
if (checkKnowLog(msg2[1], msg2[2], aState.g1, msg2[0], 3) != 0 ||
checkKnowLog(msg2[4], msg2[5], aState.g1, msg2[3], 4) != 0) {
throw new SMPException("Proof checking failed");
}
/* Combine the two halves from Bob and Alice and determine g2 and g3 */
aState.g2 = msg2[0].modPow(aState.x2, MODULUS_S);
aState.g3 = msg2[3].modPow(aState.x3, MODULUS_S);
/* Verify Bob's coordinate equality proof */
if (checkEqualCoords(msg2[8], msg2[9], msg2[10], msg2[6], msg2[7], aState, 5) != 0) {
throw new SMPException("Invalid Parameter_2");
}
/* Calculate P and Q values for Alice */
BigInteger r = randomExponent();
aState.p = aState.g3.modPow(r, MODULUS_S);
msg3[0] = aState.p;
BigInteger qa1 = aState.g1.modPow(r, MODULUS_S);
BigInteger qa2 = aState.g2.modPow(aState.secret, MODULUS_S);
aState.q = qa1.multiply(qa2).mod(MODULUS_S);
msg3[1] = aState.q;
BigInteger[] res = proofEqualCoords(aState, r, 6);
msg3[2] = res[0];
msg3[3] = res[1];
msg3[4] = res[2];
/* Calculate Ra and proof */
BigInteger inv = msg2[6].modInverse(MODULUS_S);
aState.pab = aState.p.multiply(inv).mod(MODULUS_S);
inv = msg2[7].modInverse(MODULUS_S);
aState.qab = aState.q.multiply(inv).mod(MODULUS_S);
msg3[5] = aState.qab.modPow(aState.x3, MODULUS_S);
res = proofEqualLogs(aState, 7);
msg3[6] = res[0];
msg3[7] = res[1];
byte[] output = serialize(msg3);
aState.smProgState = PROG_OK;
return output;
}
/**
* Create final message in SMP exchange. Input is a message generated
* by otrl_sm_step3. Output is a serialized mpi array whose elements
* correspond to the following:
* [0] = rb, calculated as (Qa/Qb)^x3 where x3 is the exponent used in g3b
* [1] = cr, [2] = d7, Bob's ZK proof that rb is formed correctly
* This method also checks if Alice and Bob's secrets were the same. If
* so, it returns NO_ERROR. If the secrets differ, an INV_VALUE error is
* returned instead.
*
* @param bState MVN_PASS_JAVADOC_INSPECTION
* @param input MVN_PASS_JAVADOC_INSPECTION
* @return MVN_PASS_JAVADOC_INSPECTION
* @throws SMPException MVN_PASS_JAVADOC_INSPECTION
*/
public static byte[] step4(SMPState bState, byte[] input) throws SMPException {
/* Read from input to find the mpis */
BigInteger[] msg3 = unserialize(input);
bState.smProgState = PROG_CHEATED;
BigInteger[] msg4 = new BigInteger[3];
if (checkGroupElement(msg3[0]) || checkGroupElement(msg3[1]) ||
checkGroupElement(msg3[5]) || checkExponent(msg3[3]) ||
checkExponent(msg3[4]) || checkExponent(msg3[7])) {
throw new SMPException("Invalid Parameter");
}
/* Verify Alice's coordinate equality proof */
if (checkEqualCoords(msg3[2], msg3[3], msg3[4], msg3[0], msg3[1], bState, 6) != 0)
throw new SMPException("Invalid Parameter");
/* Find Pa/Pb and Qa/Qb */
BigInteger inv = bState.p.modInverse(MODULUS_S);
bState.pab = msg3[0].multiply(inv).mod(MODULUS_S);
inv = bState.q.modInverse(MODULUS_S);
bState.qab = msg3[1].multiply(inv).mod(MODULUS_S);
/* Verify Alice's log equality proof */
if (checkEqualLogs(msg3[6], msg3[7], msg3[5], bState, 7) != 0) {
throw new SMPException("Proof checking failed");
}
/* Calculate Rb and proof */
msg4[0] = bState.qab.modPow(bState.x3, MODULUS_S);
BigInteger[] res = proofEqualLogs(bState, 8);
msg4[1] = res[0];
msg4[2] = res[1];
byte[] output = serialize(msg4);
/* Calculate Rab and verify that secrets match */
BigInteger rab = msg3[5].modPow(bState.x3, MODULUS_S);
int comp = rab.compareTo(bState.pab);
bState.smProgState = (comp != 0) ? PROG_FAILED : PROG_SUCCEEDED;
return output;
}
/**
* Receives the final SMP message, which was generated in otrl_sm_step.
* This method checks if Alice and Bob's secrets were the same. If
* so, it returns NO_ERROR. If the secrets differ, an INV_VALUE error is
* returned instead.
*
* @param aState MVN_PASS_JAVADOC_INSPECTION
* @param input MVN_PASS_JAVADOC_INSPECTION
* @throws SMPException MVN_PASS_JAVADOC_INSPECTION
*/
public static void step5(SMPState aState, byte[] input) throws SMPException {
/* Read from input to find the mpis */
BigInteger[] msg4 = unserialize(input);
aState.smProgState = PROG_CHEATED;
if (checkGroupElement(msg4[0]) || checkExponent(msg4[2])) {
throw new SMPException("Invalid Parameter");
}
/* Verify Bob's log equality proof */
if (checkEqualLogs(msg4[1], msg4[2], msg4[0], aState, 8) != 0)
throw new SMPException("Invalid Parameter");
/* Calculate Rab and verify that secrets match */
BigInteger rab = msg4[0].modPow(aState.x3, MODULUS_S);
int comp = rab.compareTo(aState.pab);
aState.smProgState = (comp != 0) ? PROG_FAILED : PROG_SUCCEEDED;
}
}