Bug 722496, Bug 722579, Bug 722482, Bug 722541. r=rnewman

This commit is contained in:
Nick Alexander 2012-02-15 22:05:52 -08:00
parent 26e9ab663b
commit aacf3c37de
6 changed files with 197 additions and 171 deletions

View File

@ -40,9 +40,10 @@ package org.mozilla.gecko.sync;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Random;
import java.security.SecureRandom;
import org.mozilla.apache.commons.codec.binary.Base32;
import org.mozilla.apache.commons.codec.binary.Base64;
@ -56,6 +57,8 @@ public class Utils {
private static final String LOG_TAG = "Utils";
private static SecureRandom sharedSecureRandom = new SecureRandom();
// See <http://developer.android.com/reference/android/content/Context.html#getSharedPreferences%28java.lang.String,%20int%29>
public static final int SHARED_PREFERENCES_MODE = 0;
@ -104,13 +107,35 @@ public class Utils {
return new String(encodedBytes).replace("+", "-").replace("/", "_");
}
private static byte[] generateRandomBytes(int length) {
/*
* Helper to generate secure random bytes.
*
* @param length Number of bytes to generate.
*/
public static byte[] generateRandomBytes(int length) {
byte[] bytes = new byte[length];
Random random = new Random(System.nanoTime());
random.nextBytes(bytes);
sharedSecureRandom.nextBytes(bytes);
return bytes;
}
/*
* Helper to generate a random integer in a specified range.
*
* @param r Generate an integer between 0 and r-1 inclusive.
*/
public static BigInteger generateBigIntegerLessThan(BigInteger r) {
int maxBytes = (int) Math.ceil(((double) r.bitLength()) / 8);
BigInteger randInt = new BigInteger(generateRandomBytes(maxBytes));
return randInt.mod(r);
}
/*
* Helper to reseed the shared secure random number generator.
*/
public static void reseedSharedRandom() {
sharedSecureRandom.setSeed(sharedSecureRandom.generateSeed(8));
}
/*
* Helper to convert Byte Array to a Hex String
* Input: byte[] array

View File

@ -245,7 +245,7 @@ public class JPakeClient implements JPakeRequestDelegate {
channelRequest = new JPakeRequest(jpakeServer + "new_channel",
makeRequestResourceDelegate());
} catch (URISyntaxException e) {
e.printStackTrace();
Log.e(LOG_TAG, "URISyntaxException", e);
abort(Constants.JPAKE_ERROR_CHANNEL);
return;
}
@ -266,7 +266,7 @@ public class JPakeClient implements JPakeRequestDelegate {
putRequest = new JPakeRequest(channelUrl,
makeRequestResourceDelegate());
} catch (URISyntaxException e) {
e.printStackTrace();
Log.e(LOG_TAG, "URISyntaxException", e);
abort(Constants.JPAKE_ERROR_CHANNEL);
return;
}
@ -283,7 +283,7 @@ public class JPakeClient implements JPakeRequestDelegate {
/*
* Step One of J-PAKE protocol.
*/
private void computeStepOne() {
private void computeStepOne() throws NoSuchAlgorithmException, UnsupportedEncodingException {
Log.d(LOG_TAG, "Computing round 1.");
JPakeCrypto.round1(jParty, numGen);
@ -321,7 +321,7 @@ public class JPakeClient implements JPakeRequestDelegate {
* Verifies message computed by other party in their Step One. Creates Step
* Two message to be sent.
*/
private void computeStepTwo() {
private void computeStepTwo() throws NonObjectJSONException {
Log.d(LOG_TAG, "Computing round 2.");
// Check incoming message sender.
@ -332,61 +332,47 @@ public class JPakeClient implements JPakeRequestDelegate {
}
// Check incoming message fields.
ExtendedJSONObject iPayload = null;
try {
iPayload = jIncoming.getObject(Constants.JSON_KEY_PAYLOAD);
if (iPayload == null
|| iPayload.getObject(Constants.ZKP_KEY_ZKP_X1) == null
|| !theirSignerId.equals(iPayload.getObject(Constants.ZKP_KEY_ZKP_X1)
.get(Constants.ZKP_KEY_ID))
|| iPayload.getObject(Constants.ZKP_KEY_ZKP_X2) == null
|| !theirSignerId.equals(iPayload.getObject(Constants.ZKP_KEY_ZKP_X2)
.get(Constants.ZKP_KEY_ID))) {
Log.e(LOG_TAG, "Invalid round 1 message: " + jIncoming.toJSONString());
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return;
}
} catch (NonObjectJSONException e) {
e.printStackTrace();
ExtendedJSONObject iPayload = jIncoming.getObject(Constants.JSON_KEY_PAYLOAD);
if (iPayload == null) {
Log.e(LOG_TAG, "Invalid round 1 message: " + jIncoming.toJSONString());
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return;
}
ExtendedJSONObject zkpPayload3 = iPayload.getObject(Constants.ZKP_KEY_ZKP_X1);
ExtendedJSONObject zkpPayload4 = iPayload.getObject(Constants.ZKP_KEY_ZKP_X2);
if (zkpPayload3 == null || zkpPayload4 == null) {
Log.e(LOG_TAG, "Invalid round 1 zkpPayload message");
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return;
}
if (!theirSignerId.equals(zkpPayload3.get(Constants.ZKP_KEY_ID)) ||
!theirSignerId.equals(zkpPayload4.get(Constants.ZKP_KEY_ID))) {
Log.e(LOG_TAG, "Invalid round 1 zkpPayload message");
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return;
}
// Extract message fields.
jParty.gx3 = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_GX1),
16);
jParty.gx4 = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_GX2),
16);
ExtendedJSONObject zkpPayload3 = null;
ExtendedJSONObject zkpPayload4 = null;
try {
zkpPayload3 = iPayload.getObject(Constants.ZKP_KEY_ZKP_X1);
zkpPayload4 = iPayload.getObject(Constants.ZKP_KEY_ZKP_X2);
if (zkpPayload3 == null || zkpPayload4 == null) {
Log.e(LOG_TAG, "Invalid round 1 zkpPayload message");
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return;
}
} catch (NonObjectJSONException e) {
e.printStackTrace();
}
jParty.gx3 = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_GX1), 16);
jParty.gx4 = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_GX2), 16);
// Extract ZKPs.
String zkp3_gr = (String) zkpPayload3.get(Constants.ZKP_KEY_GR);
String zkp3_b = (String) zkpPayload3.get(Constants.ZKP_KEY_B);
String zkp3_b = (String) zkpPayload3.get(Constants.ZKP_KEY_B);
String zkp3_id = (String) zkpPayload3.get(Constants.ZKP_KEY_ID);
String zkp4_gr = (String) zkpPayload4.get(Constants.ZKP_KEY_GR);
String zkp4_b = (String) zkpPayload4.get(Constants.ZKP_KEY_B);
String zkp4_b = (String) zkpPayload4.get(Constants.ZKP_KEY_B);
String zkp4_id = (String) zkpPayload4.get(Constants.ZKP_KEY_ID);
jParty.zkp3 = new Zkp(new BigInteger(zkp3_gr, 16), new BigInteger(zkp3_b,
16), zkp3_id);
jParty.zkp4 = new Zkp(new BigInteger(zkp4_gr, 16), new BigInteger(zkp4_b,
16), zkp4_id);
jParty.zkp3 = new Zkp(new BigInteger(zkp3_gr, 16), new BigInteger(zkp3_b, 16), zkp3_id);
jParty.zkp4 = new Zkp(new BigInteger(zkp4_gr, 16), new BigInteger(zkp4_b, 16), zkp4_id);
// Jpake round 2
// J-PAKE round 2.
try {
JPakeCrypto.round2(secret, jParty, numGen);
JPakeCrypto.round2(JPakeClient.secretAsBigInteger(secret), jParty, numGen);
} catch (Gx3OrGx4IsZeroOrOneException e) {
Log.e(LOG_TAG, "gx3 and gx4 cannot equal 0 or 1.");
abort(Constants.JPAKE_ERROR_INTERNAL);
@ -395,14 +381,21 @@ public class JPakeClient implements JPakeRequestDelegate {
Log.e(LOG_TAG, "ZKP mismatch");
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return;
} catch (NoSuchAlgorithmException e) {
Log.e(LOG_TAG, "NoSuchAlgorithmException", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
return;
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "UnsupportedEncodingException", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
return;
}
// Make outgoing payload.
Zkp zkpA = jParty.thisZkpA;
ExtendedJSONObject oPayload = new ExtendedJSONObject();
ExtendedJSONObject jZkpA = makeJZkp(zkpA.gr, zkpA.b, zkpA.id);
oPayload.put(Constants.ZKP_KEY_A,
BigIntegerHelper.toEvenLengthHex(jParty.thisA));
oPayload.put(Constants.ZKP_KEY_A, BigIntegerHelper.toEvenLengthHex(jParty.thisA));
oPayload.put(Constants.ZKP_KEY_ZKP_A, jZkpA);
// Make outgoing message.
@ -429,7 +422,7 @@ public class JPakeClient implements JPakeRequestDelegate {
* Verifies message computed by other party in Step Two. Creates or fetches
* encrypted message for verification of successful key exchange.
*/
private void computeFinal() {
private void computeFinal() throws NonObjectJSONException {
Log.d(LOG_TAG, "Computing final round.");
// Check incoming message type.
if (!jIncoming.get(Constants.JSON_KEY_TYPE).equals(theirSignerId + "2")) {
@ -439,53 +432,49 @@ public class JPakeClient implements JPakeRequestDelegate {
}
// Check incoming message fields.
ExtendedJSONObject iPayload = null;
try {
iPayload = jIncoming.getObject(Constants.JSON_KEY_PAYLOAD);
if (iPayload == null
|| iPayload.getObject(Constants.ZKP_KEY_ZKP_A) == null
|| !theirSignerId.equals(iPayload.getObject(Constants.ZKP_KEY_ZKP_A)
.get(Constants.ZKP_KEY_ID))) {
Log.e(LOG_TAG, "Invalid round 2 message: " + jIncoming.toJSONString());
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return;
}
} catch (NonObjectJSONException e) {
e.printStackTrace();
ExtendedJSONObject iPayload = jIncoming.getObject(Constants.JSON_KEY_PAYLOAD);
if (iPayload == null ||
iPayload.getObject(Constants.ZKP_KEY_ZKP_A) == null) {
Log.e(LOG_TAG, "Invalid round 2 message: " + jIncoming.toJSONString());
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return;
}
ExtendedJSONObject zkpPayload = iPayload.getObject(Constants.ZKP_KEY_ZKP_A);
if (!theirSignerId.equals(zkpPayload.get(Constants.ZKP_KEY_ID))) {
Log.e(LOG_TAG, "Invalid round 2 message: " + jIncoming.toJSONString());
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return;
}
// Extract fields.
jParty.otherA = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_A),
16);
ExtendedJSONObject zkpPayload = null;
try {
zkpPayload = iPayload.getObject(Constants.ZKP_KEY_ZKP_A);
} catch (NonObjectJSONException e) {
e.printStackTrace();
}
// Extract fields.
jParty.otherA = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_A), 16);
// Extract ZKP.
String gr = (String) zkpPayload.get(Constants.ZKP_KEY_GR);
String b = (String) zkpPayload.get(Constants.ZKP_KEY_B);
String b = (String) zkpPayload.get(Constants.ZKP_KEY_B);
String id = (String) zkpPayload.get(Constants.ZKP_KEY_ID);
jParty.otherZkpA = new Zkp(new BigInteger(gr, 16), new BigInteger(b, 16),
id);
jParty.otherZkpA = new Zkp(new BigInteger(gr, 16), new BigInteger(b, 16), id);
myKeyBundle = null;
try {
myKeyBundle = JPakeCrypto.finalRound(secret, jParty);
myKeyBundle = JPakeCrypto.finalRound(JPakeClient.secretAsBigInteger(secret), jParty);
} catch (IncorrectZkpException e) {
Log.e(LOG_TAG, "ZKP mismatch");
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
e.printStackTrace();
return;
} catch (NoSuchAlgorithmException e) {
Log.e(LOG_TAG, "NoSuchAlgorithmException", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
e.printStackTrace();
return;
} catch (InvalidKeyException e) {
Log.e(LOG_TAG, "InvalidKeyException", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
e.printStackTrace();
return;
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "UnsupportedEncodingException", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
return;
}
if (pairWithPin) { // Wait for other device to send verification of keys.
@ -498,12 +487,10 @@ public class JPakeClient implements JPakeRequestDelegate {
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "Failed to encrypt key verification value.", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
e.printStackTrace();
return;
} catch (CryptoException e) {
Log.e(LOG_TAG, "Failed to encrypt key verification value.", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
e.printStackTrace();
return;
}
@ -523,13 +510,6 @@ public class JPakeClient implements JPakeRequestDelegate {
Log.d(LOG_TAG, "Encrypting key verification value.");
// KeyBundle not null
ExtendedJSONObject jPayload = encryptPayload(JPAKE_VERIFY_VALUE, keyBundle);
Log.d(
LOG_TAG,
"enc key64: "
+ new String(Base64.encodeBase64(keyBundle.getEncryptionKey())));
Log.e(LOG_TAG,
"hmac64: " + new String(Base64.encodeBase64(keyBundle.getHMACKey())));
ExtendedJSONObject result = new ExtendedJSONObject();
result.put(Constants.JSON_KEY_TYPE, mySignerId + "3");
result.put(Constants.JSON_KEY_VERSION, KEYEXCHANGE_VERSION);
@ -546,8 +526,7 @@ public class JPakeClient implements JPakeRequestDelegate {
NonObjectJSONException {
if (!verificationObject.get(Constants.JSON_KEY_TYPE).equals(
theirSignerId + "3")) {
Log.e(LOG_TAG,
"Invalid round 3 message: " + verificationObject.toJSONString());
Log.e(LOG_TAG, "Invalid round 3 message: " + verificationObject.toJSONString());
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return false;
}
@ -609,9 +588,11 @@ public class JPakeClient implements JPakeRequestDelegate {
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "Failed to encrypt data.", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
return;
} catch (CryptoException e) {
Log.e(LOG_TAG, "Failed to encrypt data.", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
return;
}
jOutgoing = new ExtendedJSONObject();
jOutgoing.put(Constants.JSON_KEY_TYPE, mySignerId + "3");
@ -636,6 +617,7 @@ public class JPakeClient implements JPakeRequestDelegate {
e.printStackTrace();
}
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return;
}
// Decrypt payload and verify HMAC.
@ -645,6 +627,7 @@ public class JPakeClient implements JPakeRequestDelegate {
} catch (NonObjectJSONException e1) {
Log.e(LOG_TAG, "Invalid round 3 data.", e1);
abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
return;
}
Log.d(LOG_TAG, "Decrypting data.");
String cleartext = null;
@ -653,9 +636,11 @@ public class JPakeClient implements JPakeRequestDelegate {
} catch (UnsupportedEncodingException e1) {
Log.e(LOG_TAG, "Failed to decrypt data.", e1);
abort(Constants.JPAKE_ERROR_INTERNAL);
return;
} catch (CryptoException e1) {
Log.e(LOG_TAG, "Failed to decrypt data.", e1);
abort(Constants.JPAKE_ERROR_KEYMISMATCH);
return;
}
JSONObject jCreds = null;
try {
@ -679,7 +664,7 @@ public class JPakeClient implements JPakeRequestDelegate {
ssActivity.onComplete(jCreds);
}
/* JpakeRequestDelegate methods */
// JPakeRequestDelegate methods.
@Override
public void onRequestFailure(HttpResponse res) {
JPakeResponse response = new JPakeResponse(res);
@ -718,16 +703,14 @@ public class JPakeClient implements JPakeRequestDelegate {
onRequestSuccess(res);
break;
default:
Log.e(LOG_TAG, "Could not retrieve data. Server responded with HTTP "
+ statusCode);
Log.e(LOG_TAG, "Could not retrieve data. Server responded with HTTP " + statusCode);
abort(Constants.JPAKE_ERROR_SERVER);
return;
}
pollTries = 0;
break;
case PUT:
Log.e(LOG_TAG, "Could not upload data. Server responded with HTTP "
+ response.getStatusCode());
Log.e(LOG_TAG, "Could not upload data. Server responded with HTTP " + response.getStatusCode());
abort(Constants.JPAKE_ERROR_SERVER);
break;
case ABORT:
@ -777,7 +760,17 @@ public class JPakeClient implements JPakeRequestDelegate {
// Set up next step.
this.state = State.RCVR_STEP_ONE;
computeStepOne();
try {
computeStepOne();
} catch (NoSuchAlgorithmException e) {
Log.e(LOG_TAG, "NoSuchAlgorithmException", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
return;
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "UnsupportedEncodingException", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
return;
}
break;
// Results from GET request. Continue flow depending on case.
@ -794,8 +787,7 @@ public class JPakeClient implements JPakeRequestDelegate {
etagHeaders = response.httpResponse().getHeaders("etag");
if (etagHeaders == null) {
try {
Log.e(LOG_TAG,
"Server did not supply ETag for message: " + response.body());
Log.e(LOG_TAG, "Server did not supply ETag for message: " + response.body());
abort(Constants.JPAKE_ERROR_SERVER);
} catch (IllegalStateException e) {
e.printStackTrace();
@ -822,16 +814,37 @@ public class JPakeClient implements JPakeRequestDelegate {
Log.d(LOG_TAG, "incoming message: " + jIncoming.toJSONString());
if (this.state == State.SNDR_STEP_ZERO) {
computeStepOne();
try {
computeStepOne();
} catch (NoSuchAlgorithmException e) {
Log.e(LOG_TAG, "NoSuchAlgorithmException", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
return;
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "UnsupportedEncodingException", e);
abort(Constants.JPAKE_ERROR_INTERNAL);
return;
}
} else if (this.state == State.RCVR_STEP_ONE
|| this.state == State.SNDR_STEP_ONE) {
computeStepTwo();
try {
computeStepTwo();
} catch (NonObjectJSONException e) {
Log.e(LOG_TAG, "NonObjectJSONException", e);
abort(Constants.JPAKE_ERROR_INVALID);
return;
}
} else if (this.state == State.SNDR_STEP_TWO) {
stateContext = state;
state = State.PUT;
putStep();
} else if (this.state == State.RCVR_STEP_TWO) {
computeFinal();
try {
computeFinal();
} catch (NonObjectJSONException e) {
abort(Constants.JPAKE_ERROR_INVALID);
return;
}
} else if (this.state == State.VERIFY_KEY) {
decryptData(myKeyBundle);
} else if (this.state == State.VERIFY_PAIRING) {
@ -864,11 +877,23 @@ public class JPakeClient implements JPakeRequestDelegate {
ssActivity.onPaired();
}
if (state == State.SNDR_STEP_ONE) {
computeStepTwo();
try {
computeStepTwo();
} catch (NonObjectJSONException e) {
Log.e(LOG_TAG, "NonObjectJSONException", e);
abort(Constants.JPAKE_ERROR_INVALID);
return;
}
return; // No need to wait for response from PUT request.
}
if (state == State.SNDR_STEP_TWO) {
computeFinal();
try {
computeFinal();
} catch (NonObjectJSONException e) {
Log.e(LOG_TAG, "NonObjectJSONException", e);
abort(Constants.JPAKE_ERROR_INVALID);
return;
}
return; // No need to wait for response from PUT request.
}
@ -892,20 +917,20 @@ public class JPakeClient implements JPakeRequestDelegate {
/* ResourceDelegate that handles Resource responses */
public ResourceDelegate makeRequestResourceDelegate() {
return new JpakeRequestResourceDelegate(this);
return new JPakeRequestResourceDelegate(this);
}
public class JpakeRequestResourceDelegate implements ResourceDelegate {
public class JPakeRequestResourceDelegate implements ResourceDelegate {
private JPakeRequestDelegate requestDelegate;
public JpakeRequestResourceDelegate(JPakeRequestDelegate delegate) {
public JPakeRequestResourceDelegate(JPakeRequestDelegate delegate) {
this.requestDelegate = delegate;
}
@Override
public String getCredentials() {
// Jpake setup has no credentials
// J-PAKE setup has no credentials
return null;
}
@ -1063,7 +1088,7 @@ public class JPakeClient implements JPakeRequestDelegate {
* Generates and sets a clientId for communications with JPAKE setup server.
*/
private void setClientId() {
byte[] rBytes = generateRandomBytes(JPAKE_LENGTH_CLIENTID / 2);
byte[] rBytes = Utils.generateRandomBytes(JPAKE_LENGTH_CLIENTID / 2);
StringBuilder id = new StringBuilder();
for (byte b : rBytes) {
@ -1085,7 +1110,7 @@ public class JPakeClient implements JPakeRequestDelegate {
String key = "23456789abcdefghijkmnpqrstuvwxyz";
int keylen = key.length();
byte[] rBytes = generateRandomBytes(JPAKE_LENGTH_SECRET);
byte[] rBytes = Utils.generateRandomBytes(JPAKE_LENGTH_SECRET);
StringBuilder secret = new StringBuilder();
for (byte b : rBytes) {
secret.append(key.charAt(Math.abs(b) * keylen / 256));
@ -1133,7 +1158,7 @@ public class JPakeClient implements JPakeRequestDelegate {
try {
getRequest = new JPakeRequest(channelUrl, makeRequestResourceDelegate());
} catch (URISyntaxException e) {
e.printStackTrace();
Log.e(LOG_TAG, "URISyntaxException", e);
abort(Constants.JPAKE_ERROR_CHANNEL);
return;
}
@ -1156,17 +1181,13 @@ public class JPakeClient implements JPakeRequestDelegate {
}
/*
* Helper to generate random bytes
*
* @param length Number of bytes to generate
* Helper for turning a string secret into a numeric secret.
*/
private static byte[] generateRandomBytes(int length) {
byte[] bytes = new byte[length];
Random random = new Random(System.nanoTime());
random.nextBytes(bytes);
return bytes;
public static BigInteger secretAsBigInteger(String secretString) throws UnsupportedEncodingException {
return new BigInteger(secretString.getBytes("UTF-8"));
}
/*
* Helper function to generate a JSON encoded ZKP.
*/

View File

@ -101,8 +101,9 @@ public class JPakeCrypto {
*
* @param mySignerId
* @param valuesOut
* @throws NoSuchAlgorithmException
*/
public static void round1(JPakeParty jp, JPakeNumGenerator gen) {
public static void round1(JPakeParty jp, JPakeNumGenerator gen) throws NoSuchAlgorithmException, UnsupportedEncodingException {
// Randomly select x1 from [0,q), x2 from [1,q).
BigInteger x1 = gen.generateFromRange(Q); // [0, q)
BigInteger x2 = jp.x2 = BigInteger.ONE.add(gen.generateFromRange(Q
@ -132,9 +133,11 @@ public class JPakeCrypto {
* @param zkp3
* @param zkp4
* @throws IncorrectZkpException
* @throws NoSuchAlgorithmException
*/
public static void round2(String secret, JPakeParty jp,
JPakeNumGenerator gen) throws IncorrectZkpException, Gx3OrGx4IsZeroOrOneException {
public static void round2(BigInteger secretValue, JPakeParty jp, JPakeNumGenerator gen)
throws IncorrectZkpException, NoSuchAlgorithmException,
Gx3OrGx4IsZeroOrOneException, UnsupportedEncodingException {
Log.d(LOG_TAG, "round2 started.");
@ -150,13 +153,7 @@ public class JPakeCrypto {
// Compute a = g^[(x1+x3+x4)*(x2*secret)].
BigInteger y1 = jp.gx3.multiply(jp.gx4).mod(P).multiply(jp.gx1).mod(P);
BigInteger y2 = null;
try {
y2 = jp.x2.multiply(new BigInteger(secret.getBytes("US-ASCII"))).mod(P);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BigInteger y2 = jp.x2.multiply(secretValue).mod(P);
BigInteger a = y1.modPow(y2, P);
jp.thisZkpA = createZkp(y1, y2, a, jp.signerId, gen);
@ -175,8 +172,8 @@ public class JPakeCrypto {
* @return KeyBundle
* @throws IncorrectZkpException
*/
public static KeyBundle finalRound(String secret, JPakeParty jp)
throws IncorrectZkpException, NoSuchAlgorithmException, InvalidKeyException {
public static KeyBundle finalRound(BigInteger secretValue, JPakeParty jp)
throws IncorrectZkpException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
Log.d(LOG_TAG, "Final round started.");
BigInteger gb = jp.gx1.multiply(jp.gx2).mod(P).multiply(jp.gx3)
.mod(P);
@ -184,7 +181,7 @@ public class JPakeCrypto {
// Calculate shared key g^(x1+x3)*x2*x4*secret, which is equivalent to
// (B/g^(x2*x4*s))^x2 = (B*(g^x4)^x2^s^-1)^2.
BigInteger k = jp.gx4.modPow(jp.x2.multiply(new BigInteger(secret.getBytes())).negate().mod(Q), P).multiply(jp.otherA)
BigInteger k = jp.gx4.modPow(jp.x2.multiply(secretValue).negate().mod(Q), P).multiply(jp.otherA)
.modPow(jp.x2, P);
byte[] enc = new byte[32];
@ -217,7 +214,7 @@ public class JPakeCrypto {
* pass in gx to save on an exponentiation of g^x)
*/
private static Zkp createZkp(BigInteger g, BigInteger x, BigInteger gx,
String id, JPakeNumGenerator gen) {
String id, JPakeNumGenerator gen) throws NoSuchAlgorithmException, UnsupportedEncodingException {
// Generate random r for exponent.
BigInteger r = gen.generateFromRange(Q);
@ -238,7 +235,7 @@ public class JPakeCrypto {
* Verify ZKP.
*/
private static void checkZkp(BigInteger g, BigInteger gx, Zkp zkp)
throws IncorrectZkpException {
throws IncorrectZkpException, NoSuchAlgorithmException, UnsupportedEncodingException {
BigInteger h = computeBHash(g, zkp.gr, gx, zkp.id);
@ -278,33 +275,22 @@ public class JPakeCrypto {
* form hash.
*/
private static BigInteger computeBHash(BigInteger g, BigInteger gr, BigInteger gx,
String id) {
MessageDigest sha = null;
try {
sha = MessageDigest.getInstance("SHA-256");
sha.reset();
String id) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
sha.reset();
/*
* Note: you should ensure the items in H(...) have clear boundaries. It
* is simple if the other party knows sizes of g, gr, gx and signerID and
* hence the boundary is unambiguous. If not, you'd better prepend each
* item with its byte length, but I've omitted that here.
*/
/*
* Note: you should ensure the items in H(...) have clear boundaries. It
* is simple if the other party knows sizes of g, gr, gx and signerID and
* hence the boundary is unambiguous. If not, you'd better prepend each
* item with its byte length, but I've omitted that here.
*/
hashByteArrayWithLength(sha,
BigIntegerHelper.BigIntegerToByteArrayWithoutSign(g));
hashByteArrayWithLength(sha,
BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gr));
hashByteArrayWithLength(sha,
BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gx));
hashByteArrayWithLength(sha, id.getBytes("US-ASCII"));
hashByteArrayWithLength(sha, BigIntegerHelper.BigIntegerToByteArrayWithoutSign(g));
hashByteArrayWithLength(sha, BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gr));
hashByteArrayWithLength(sha, BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gx));
hashByteArrayWithLength(sha, id.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte[] hash = sha.digest();
return BigIntegerHelper.ByteArrayToBigIntegerWithoutSign(hash);

View File

@ -38,7 +38,8 @@
package org.mozilla.gecko.sync.jpake;
import java.math.BigInteger;
import java.security.SecureRandom;
import org.mozilla.gecko.sync.Utils;
/**
* Helper Function to generate a uniformly random value in [0, r).
@ -47,14 +48,6 @@ public class JPakeNumGeneratorRandom implements JPakeNumGenerator {
@Override
public BigInteger generateFromRange(BigInteger r) {
int maxBytes = (int) Math.ceil(((double) r.bitLength()) / 8);
byte[] bytes = new byte[maxBytes];
new SecureRandom().nextBytes(bytes);
BigInteger randInt = new BigInteger(bytes);
// TODO: is this going to be very slow?
// bit shifting/masking to decrease mod computation
return randInt.mod(r);
return Utils.generateBigIntegerLessThan(r);
}
}

View File

@ -222,6 +222,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
final String authority,
final ContentProviderClient provider,
final SyncResult syncResult) {
Utils.reseedSharedRandom(); // Make sure we don't work with the same random seed for too long.
long delay = delayMilliseconds();
if (delay > 0) {

File diff suppressed because one or more lines are too long