Bug 915312 - Part 3: Add NativeCrypto Java interface. r=nalexander

This commit is contained in:
Michael Comella 2014-03-06 12:10:54 -08:00
parent e2a9e2ac3b
commit a531628bad
5 changed files with 145 additions and 23 deletions

View File

@ -534,6 +534,7 @@ sync_java_files = [
'background/healthreport/upload/ObsoleteDocumentTracker.java',
'background/healthreport/upload/SubmissionClient.java',
'background/healthreport/upload/SubmissionPolicy.java',
'background/nativecode/NativeCrypto.java',
'background/preferences/PreferenceFragment.java',
'background/preferences/PreferenceManagerCompat.java',
'browserid/ASNUtils.java',

View File

@ -0,0 +1,27 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.background.nativecode;
import org.mozilla.gecko.mozglue.RobocopTarget;
import java.security.GeneralSecurityException;
@RobocopTarget
public class NativeCrypto {
static {
System.loadLibrary("mozglue");
}
/**
* Wrapper to perform PBKDF2-HMAC-SHA-256 in native code.
*/
public native static byte[] pbkdf2SHA256(byte[] password, byte[] salt, int c, int dkLen)
throws GeneralSecurityException;
/**
* Wrapper to perform SHA-1 in native code.
*/
public native static byte[] sha1(byte[] str);
}

View File

@ -4,30 +4,14 @@
package org.mozilla.gecko.sync.crypto;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.SecretKeySpec;
public class PBKDF2 {
public static byte[] pbkdf2SHA1(byte[] password, byte[] salt, int c, int dkLen)
throws GeneralSecurityException {
// Won't work on API level 8, but this is trivial.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec keySpec;
try {
keySpec = new PBEKeySpec(new String(password, "UTF-8").toCharArray(), salt, c, dkLen * 8);
} catch (UnsupportedEncodingException e) {
throw new GeneralSecurityException(e);
}
SecretKey key = factory.generateSecret(keySpec);
return key.getEncoded();
}
public static byte[] pbkdf2SHA256(byte[] password, byte[] salt, int c, int dkLen)
throws GeneralSecurityException {
final String algorithm = "HmacSHA256";
@ -36,12 +20,18 @@ public class PBKDF2 {
prf.init(keyspec);
int hLen = prf.getMacLength();
byte U_r[] = new byte[hLen];
byte U_i[] = new byte[salt.length + 4];
byte scratch[] = new byte[hLen];
int l = Math.max(dkLen, hLen);
int r = dkLen - (l - 1) * hLen;
byte T[] = new byte[l * hLen];
int ti_offset = 0;
for (int i = 1; i <= l; i++) {
F(T, ti_offset, prf, salt, c, i);
Arrays.fill(U_r, (byte) 0);
F(T, ti_offset, prf, salt, c, i, U_r, U_i, scratch);
ti_offset += hLen;
}
@ -55,17 +45,18 @@ public class PBKDF2 {
return T;
}
private static void F(byte[] dest, int offset, Mac prf, byte[] S, int c, int blockIndex) {
private static void F(byte[] dest, int offset, Mac prf, byte[] S, int c, int blockIndex, byte U_r[], byte U_i[], byte[] scratch)
throws ShortBufferException, IllegalStateException {
final int hLen = prf.getMacLength();
byte U_r[] = new byte[hLen];
// U0 = S || INT (i);
byte U_i[] = new byte[S.length + 4];
System.arraycopy(S, 0, U_i, 0, S.length);
INT(U_i, S.length, blockIndex);
for (int i = 0; i < c; i++) {
U_i = prf.doFinal(U_i);
prf.update(U_i);
prf.doFinal(scratch, 0);
U_i = scratch;
xor(U_r, U_i);
}

View File

@ -43,6 +43,7 @@ BACKGROUND_TESTS_JAVA_FILES := \
src/helpers/DBHelpers.java \
src/helpers/DBProviderTestCase.java \
src/helpers/FakeProfileTestCase.java \
src/nativecode/test/TestNativeCrypto.java \
src/sync/helpers/BookmarkHelpers.java \
src/sync/helpers/DefaultBeginDelegate.java \
src/sync/helpers/DefaultCleanDelegate.java \

View File

@ -0,0 +1,102 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.nativecode.test;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import junit.framework.TestCase;
import org.mozilla.gecko.background.nativecode.NativeCrypto;
import org.mozilla.gecko.sync.Utils;
// Test vectors from
// SHA-256: <https://github.com/ircmaxell/PHP-PasswordLib/blob/master/test/Data/Vectors/pbkdf2-draft-josefsson-sha256.test-vectors>
// <https://gitorious.org/scrypt/nettle-scrypt/blobs/37c0d5288e991604fe33dba2f1724986a8dddf56/testsuite/pbkdf2-test.c>
public class TestNativeCrypto extends TestCase {
public final void testPBKDF2SHA256A() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "password";
String s = "salt";
int dkLen = 32;
checkPBKDF2SHA256(p, s, 1, dkLen, "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b");
checkPBKDF2SHA256(p, s, 4096, dkLen, "c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a");
}
public final void testPBKDF2SHA256B() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "passwordPASSWORDpassword";
String s = "saltSALTsaltSALTsaltSALTsaltSALTsalt";
int dkLen = 40;
checkPBKDF2SHA256(p, s, 4096, dkLen, "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c635518c7dac47e9");
}
public final void testPBKDF2SHA256scryptA() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "passwd";
String s = "salt";
int dkLen = 64;
checkPBKDF2SHA256(p, s, 1, dkLen, "55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc49ca9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783");
}
public final void testPBKDF2SHA256scryptB() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "Password";
String s = "NaCl";
int dkLen = 64;
checkPBKDF2SHA256(p, s, 80000, dkLen, "4ddcd8f60b98be21830cee5ef22701f9641a4418d04c0414aeff08876b34ab56a1d425a1225833549adb841b51c9b3176a272bdebba1d078478f62b397f33c8d");
}
public final void testPBKDF2SHA256C() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "pass\0word";
String s = "sa\0lt";
int dkLen = 16;
checkPBKDF2SHA256(p, s, 4096, dkLen, "89b69d0516f829893c696226650a8687");
}
/*
// This test takes two or three minutes to run, so we don't.
public final void testPBKDF2SHA256D() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "password";
String s = "salt";
int dkLen = 32;
checkPBKDF2SHA256(p, s, 16777216, dkLen, "cf81c66fe8cfc04d1f31ecb65dab4089f7f179e89b3b0bcb17ad10e3ac6eba46");
}
*/
public final void testTimePBKDF2SHA256() throws UnsupportedEncodingException, GeneralSecurityException {
checkPBKDF2SHA256("password", "salt", 80000, 32, null);
}
private void checkPBKDF2SHA256(String p, String s, int c, int dkLen,
final String expectedStr)
throws GeneralSecurityException, UnsupportedEncodingException {
long start = System.currentTimeMillis();
byte[] key = NativeCrypto.pbkdf2SHA256(p.getBytes("US-ASCII"), s.getBytes("US-ASCII"), c, dkLen);
assertNotNull(key);
long end = System.currentTimeMillis();
System.err.println("SHA-256 " + c + " took " + (end - start) + "ms");
if (expectedStr == null) {
return;
}
assertEquals(dkLen, Utils.hex2Byte(expectedStr).length);
assertExpectedBytes(expectedStr, key);
}
private void assertExpectedBytes(final String expectedStr, byte[] key) {
assertEquals(expectedStr, Utils.byte2Hex(key));
byte[] expected = Utils.hex2Byte(expectedStr);
assertEquals(expected.length, key.length);
for (int i = 0; i < key.length; i++) {
assertEquals(expected[i], key[i]);
}
}
}