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.io.UnsupportedEncodingException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.HashMap; 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.Base32;
import org.mozilla.apache.commons.codec.binary.Base64; import org.mozilla.apache.commons.codec.binary.Base64;
@ -56,6 +57,8 @@ public class Utils {
private static final String LOG_TAG = "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> // See <http://developer.android.com/reference/android/content/Context.html#getSharedPreferences%28java.lang.String,%20int%29>
public static final int SHARED_PREFERENCES_MODE = 0; public static final int SHARED_PREFERENCES_MODE = 0;
@ -104,13 +107,35 @@ public class Utils {
return new String(encodedBytes).replace("+", "-").replace("/", "_"); 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]; byte[] bytes = new byte[length];
Random random = new Random(System.nanoTime()); sharedSecureRandom.nextBytes(bytes);
random.nextBytes(bytes);
return 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 * Helper to convert Byte Array to a Hex String
* Input: byte[] array * Input: byte[] array

View File

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

View File

@ -101,8 +101,9 @@ public class JPakeCrypto {
* *
* @param mySignerId * @param mySignerId
* @param valuesOut * @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). // Randomly select x1 from [0,q), x2 from [1,q).
BigInteger x1 = gen.generateFromRange(Q); // [0, q) BigInteger x1 = gen.generateFromRange(Q); // [0, q)
BigInteger x2 = jp.x2 = BigInteger.ONE.add(gen.generateFromRange(Q BigInteger x2 = jp.x2 = BigInteger.ONE.add(gen.generateFromRange(Q
@ -132,9 +133,11 @@ public class JPakeCrypto {
* @param zkp3 * @param zkp3
* @param zkp4 * @param zkp4
* @throws IncorrectZkpException * @throws IncorrectZkpException
* @throws NoSuchAlgorithmException
*/ */
public static void round2(String secret, JPakeParty jp, public static void round2(BigInteger secretValue, JPakeParty jp, JPakeNumGenerator gen)
JPakeNumGenerator gen) throws IncorrectZkpException, Gx3OrGx4IsZeroOrOneException { throws IncorrectZkpException, NoSuchAlgorithmException,
Gx3OrGx4IsZeroOrOneException, UnsupportedEncodingException {
Log.d(LOG_TAG, "round2 started."); Log.d(LOG_TAG, "round2 started.");
@ -150,13 +153,7 @@ public class JPakeCrypto {
// Compute a = g^[(x1+x3+x4)*(x2*secret)]. // Compute a = g^[(x1+x3+x4)*(x2*secret)].
BigInteger y1 = jp.gx3.multiply(jp.gx4).mod(P).multiply(jp.gx1).mod(P); BigInteger y1 = jp.gx3.multiply(jp.gx4).mod(P).multiply(jp.gx1).mod(P);
BigInteger y2 = null; BigInteger y2 = jp.x2.multiply(secretValue).mod(P);
try {
y2 = jp.x2.multiply(new BigInteger(secret.getBytes("US-ASCII"))).mod(P);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BigInteger a = y1.modPow(y2, P); BigInteger a = y1.modPow(y2, P);
jp.thisZkpA = createZkp(y1, y2, a, jp.signerId, gen); jp.thisZkpA = createZkp(y1, y2, a, jp.signerId, gen);
@ -175,8 +172,8 @@ public class JPakeCrypto {
* @return KeyBundle * @return KeyBundle
* @throws IncorrectZkpException * @throws IncorrectZkpException
*/ */
public static KeyBundle finalRound(String secret, JPakeParty jp) public static KeyBundle finalRound(BigInteger secretValue, JPakeParty jp)
throws IncorrectZkpException, NoSuchAlgorithmException, InvalidKeyException { throws IncorrectZkpException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
Log.d(LOG_TAG, "Final round started."); Log.d(LOG_TAG, "Final round started.");
BigInteger gb = jp.gx1.multiply(jp.gx2).mod(P).multiply(jp.gx3) BigInteger gb = jp.gx1.multiply(jp.gx2).mod(P).multiply(jp.gx3)
.mod(P); .mod(P);
@ -184,7 +181,7 @@ public class JPakeCrypto {
// Calculate shared key g^(x1+x3)*x2*x4*secret, which is equivalent to // 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. // (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); .modPow(jp.x2, P);
byte[] enc = new byte[32]; byte[] enc = new byte[32];
@ -217,7 +214,7 @@ public class JPakeCrypto {
* pass in gx to save on an exponentiation of g^x) * pass in gx to save on an exponentiation of g^x)
*/ */
private static Zkp createZkp(BigInteger g, BigInteger x, BigInteger gx, 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. // Generate random r for exponent.
BigInteger r = gen.generateFromRange(Q); BigInteger r = gen.generateFromRange(Q);
@ -238,7 +235,7 @@ public class JPakeCrypto {
* Verify ZKP. * Verify ZKP.
*/ */
private static void checkZkp(BigInteger g, BigInteger gx, Zkp 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); BigInteger h = computeBHash(g, zkp.gr, gx, zkp.id);
@ -278,10 +275,8 @@ public class JPakeCrypto {
* form hash. * form hash.
*/ */
private static BigInteger computeBHash(BigInteger g, BigInteger gr, BigInteger gx, private static BigInteger computeBHash(BigInteger g, BigInteger gr, BigInteger gx,
String id) { String id) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest sha = null; MessageDigest sha = MessageDigest.getInstance("SHA-256");
try {
sha = MessageDigest.getInstance("SHA-256");
sha.reset(); sha.reset();
/* /*
@ -291,20 +286,11 @@ public class JPakeCrypto {
* item with its byte length, but I've omitted that here. * item with its byte length, but I've omitted that here.
*/ */
hashByteArrayWithLength(sha, hashByteArrayWithLength(sha, BigIntegerHelper.BigIntegerToByteArrayWithoutSign(g));
BigIntegerHelper.BigIntegerToByteArrayWithoutSign(g)); hashByteArrayWithLength(sha, BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gr));
hashByteArrayWithLength(sha, hashByteArrayWithLength(sha, BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gx));
BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gr)); hashByteArrayWithLength(sha, id.getBytes("UTF-8"));
hashByteArrayWithLength(sha,
BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gx));
hashByteArrayWithLength(sha, id.getBytes("US-ASCII"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte[] hash = sha.digest(); byte[] hash = sha.digest();
return BigIntegerHelper.ByteArrayToBigIntegerWithoutSign(hash); return BigIntegerHelper.ByteArrayToBigIntegerWithoutSign(hash);

View File

@ -38,7 +38,8 @@
package org.mozilla.gecko.sync.jpake; package org.mozilla.gecko.sync.jpake;
import java.math.BigInteger; 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). * Helper Function to generate a uniformly random value in [0, r).
@ -47,14 +48,6 @@ public class JPakeNumGeneratorRandom implements JPakeNumGenerator {
@Override @Override
public BigInteger generateFromRange(BigInteger r) { public BigInteger generateFromRange(BigInteger r) {
int maxBytes = (int) Math.ceil(((double) r.bitLength()) / 8); return Utils.generateBigIntegerLessThan(r);
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);
} }
} }

View File

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

File diff suppressed because one or more lines are too long