diff --git a/mobile/android/base/sync/Utils.java b/mobile/android/base/sync/Utils.java index 0f5be6122bf..002e46a6fe3 100644 --- a/mobile/android/base/sync/Utils.java +++ b/mobile/android/base/sync/Utils.java @@ -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 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 diff --git a/mobile/android/base/sync/jpake/JPakeClient.java b/mobile/android/base/sync/jpake/JPakeClient.java index aefadebff41..fdfa1e96567 100644 --- a/mobile/android/base/sync/jpake/JPakeClient.java +++ b/mobile/android/base/sync/jpake/JPakeClient.java @@ -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. */ diff --git a/mobile/android/base/sync/jpake/JPakeCrypto.java b/mobile/android/base/sync/jpake/JPakeCrypto.java index 5b4215b17a5..d3f5104b71e 100644 --- a/mobile/android/base/sync/jpake/JPakeCrypto.java +++ b/mobile/android/base/sync/jpake/JPakeCrypto.java @@ -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); diff --git a/mobile/android/base/sync/jpake/JPakeNumGeneratorRandom.java b/mobile/android/base/sync/jpake/JPakeNumGeneratorRandom.java index 6e47df798b0..c2077f75819 100644 --- a/mobile/android/base/sync/jpake/JPakeNumGeneratorRandom.java +++ b/mobile/android/base/sync/jpake/JPakeNumGeneratorRandom.java @@ -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); } - } diff --git a/mobile/android/base/sync/syncadapter/SyncAdapter.java b/mobile/android/base/sync/syncadapter/SyncAdapter.java index 023039ff4ec..9b7c393e3ec 100644 --- a/mobile/android/base/sync/syncadapter/SyncAdapter.java +++ b/mobile/android/base/sync/syncadapter/SyncAdapter.java @@ -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) { diff --git a/mobile/android/sync/java-sources.mn b/mobile/android/sync/java-sources.mn index 64b4efee849..a8934571c2d 100644 --- a/mobile/android/sync/java-sources.mn +++ b/mobile/android/sync/java-sources.mn @@ -1 +1 @@ -sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/Cryptographer.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/cryptographer/CryptoStatusBundle.java sync/cryptographer/SyncCryptographer.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/JPakeUtils.java sync/jpake/Zkp.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/HandleProgressException.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/NoCollectionKeysSetException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/PrefsSource.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java sync/repositories/android/AndroidBrowserPasswordsRepository.java sync/repositories/android/AndroidBrowserPasswordsRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BrowserContract.java sync/repositories/android/PasswordColumns.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/SyncAuthenticatorService.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureKeysStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/ServerSyncStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/SynchronizerConfigurations.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java +sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/Cryptographer.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/cryptographer/CryptoStatusBundle.java sync/cryptographer/SyncCryptographer.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/Zkp.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/HandleProgressException.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/NoCollectionKeysSetException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/PrefsSource.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java sync/repositories/android/AndroidBrowserPasswordsRepository.java sync/repositories/android/AndroidBrowserPasswordsRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BrowserContract.java sync/repositories/android/PasswordColumns.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/SyncAuthenticatorService.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureKeysStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/ServerSyncStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/SynchronizerConfigurations.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java