1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/tests/testNativeCrypto.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,192 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +package org.mozilla.gecko.tests; 1.9 + 1.10 +import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertArrayEquals; 1.11 +import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertEquals; 1.12 +import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertNotNull; 1.13 +import static org.mozilla.gecko.tests.helpers.AssertionHelper.fFail; 1.14 + 1.15 +import java.io.UnsupportedEncodingException; 1.16 +import java.security.GeneralSecurityException; 1.17 +import java.security.MessageDigest; 1.18 +import java.security.NoSuchAlgorithmException; 1.19 + 1.20 +import org.mozilla.gecko.background.nativecode.NativeCrypto; 1.21 +import org.mozilla.gecko.sync.Utils; 1.22 +import org.mozilla.gecko.tests.helpers.GeckoHelper; 1.23 + 1.24 +import android.os.SystemClock; 1.25 + 1.26 +/** 1.27 + * Tests the Java wrapper over native implementations of crypto code. Test vectors from: 1.28 + * * PBKDF2SHA256: 1.29 + * - <https://github.com/ircmaxell/PHP-PasswordLib/blob/master/test/Data/Vectors/pbkdf2-draft-josefsson-sha256.test-vectors> 1.30 + - <https://gitorious.org/scrypt/nettle-scrypt/blobs/37c0d5288e991604fe33dba2f1724986a8dddf56/testsuite/pbkdf2-test.c> 1.31 + * SHA-1: 1.32 + - <http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c> 1.33 + */ 1.34 +public class testNativeCrypto extends UITest { 1.35 + private final static String LOGTAG = "testNativeCrypto"; 1.36 + 1.37 + /** 1.38 + * Robocop supports only a single test function per test class. Therefore, we 1.39 + * have a single top-level test function that dispatches to sub-tests, 1.40 + * accepting that we might fail part way through the cycle. Proper JUnit 3 1.41 + * testing can't land soon enough! 1.42 + * 1.43 + * @throws Exception 1.44 + */ 1.45 + public void test() throws Exception { 1.46 + // This test could complete very quickly. If it completes too soon, the 1.47 + // minidumps directory may not be created before the process is 1.48 + // taken down, causing bug 722166. But we can't run the test and then block 1.49 + // for Gecko:Ready, since it may have arrived before we block. So we wait. 1.50 + // Again, JUnit 3 can't land soon enough! 1.51 + GeckoHelper.blockForReady(); 1.52 + 1.53 + _testPBKDF2SHA256A(); 1.54 + _testPBKDF2SHA256B(); 1.55 + _testPBKDF2SHA256C(); 1.56 + _testPBKDF2SHA256scryptA(); 1.57 + _testPBKDF2SHA256scryptB(); 1.58 + _testPBKDF2SHA256InvalidLenArg(); 1.59 + 1.60 + _testSHA1(); 1.61 + _testSHA1AgainstMessageDigest(); 1.62 + } 1.63 + 1.64 + public void _testPBKDF2SHA256A() throws UnsupportedEncodingException, GeneralSecurityException { 1.65 + final String p = "password"; 1.66 + final String s = "salt"; 1.67 + final int dkLen = 32; 1.68 + 1.69 + checkPBKDF2SHA256(p, s, 1, dkLen, "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b"); 1.70 + checkPBKDF2SHA256(p, s, 4096, dkLen, "c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a"); 1.71 + } 1.72 + 1.73 + public void _testPBKDF2SHA256B() throws UnsupportedEncodingException, GeneralSecurityException { 1.74 + final String p = "passwordPASSWORDpassword"; 1.75 + final String s = "saltSALTsaltSALTsaltSALTsaltSALTsalt"; 1.76 + final int dkLen = 40; 1.77 + 1.78 + checkPBKDF2SHA256(p, s, 4096, dkLen, "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c635518c7dac47e9"); 1.79 + } 1.80 + 1.81 + public void _testPBKDF2SHA256scryptA() throws UnsupportedEncodingException, GeneralSecurityException { 1.82 + final String p = "passwd"; 1.83 + final String s = "salt"; 1.84 + final int dkLen = 64; 1.85 + 1.86 + checkPBKDF2SHA256(p, s, 1, dkLen, "55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc49ca9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783"); 1.87 + } 1.88 + 1.89 + public void _testPBKDF2SHA256scryptB() throws UnsupportedEncodingException, GeneralSecurityException { 1.90 + final String p = "Password"; 1.91 + final String s = "NaCl"; 1.92 + final int dkLen = 64; 1.93 + 1.94 + checkPBKDF2SHA256(p, s, 80000, dkLen, "4ddcd8f60b98be21830cee5ef22701f9641a4418d04c0414aeff08876b34ab56a1d425a1225833549adb841b51c9b3176a272bdebba1d078478f62b397f33c8d"); 1.95 + } 1.96 + 1.97 + public void _testPBKDF2SHA256C() throws UnsupportedEncodingException, GeneralSecurityException { 1.98 + final String p = "pass\0word"; 1.99 + final String s = "sa\0lt"; 1.100 + final int dkLen = 16; 1.101 + 1.102 + checkPBKDF2SHA256(p, s, 4096, dkLen, "89b69d0516f829893c696226650a8687"); 1.103 + } 1.104 + 1.105 + public void _testPBKDF2SHA256InvalidLenArg() throws UnsupportedEncodingException, GeneralSecurityException { 1.106 + final String p = "password"; 1.107 + final String s = "salt"; 1.108 + final int c = 1; 1.109 + final int dkLen = -1; // Should always be positive. 1.110 + 1.111 + try { 1.112 + final byte[] key = NativeCrypto.pbkdf2SHA256(p.getBytes("US-ASCII"), s.getBytes("US-ASCII"), c, dkLen); 1.113 + fFail("Expected sha256 to throw with negative dkLen argument."); 1.114 + } catch (IllegalArgumentException e) { } // Expected. 1.115 + } 1.116 + 1.117 + private void _testSHA1() throws UnsupportedEncodingException { 1.118 + final String[] inputs = new String[] { 1.119 + "abc", 1.120 + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 1.121 + "" // To be filled in below. 1.122 + }; 1.123 + final String baseStr = "01234567"; 1.124 + final int repetitions = 80; 1.125 + final StringBuilder builder = new StringBuilder(baseStr.length() * repetitions); 1.126 + for (int i = 0; i < 80; ++i) { 1.127 + builder.append(baseStr); 1.128 + } 1.129 + inputs[2] = builder.toString(); 1.130 + 1.131 + final String[] expecteds = new String[] { 1.132 + "a9993e364706816aba3e25717850c26c9cd0d89d", 1.133 + "84983e441c3bd26ebaae4aa1f95129e5e54670f1", 1.134 + "dea356a2cddd90c7a7ecedc5ebb563934f460452" 1.135 + }; 1.136 + 1.137 + for (int i = 0; i < inputs.length; ++i) { 1.138 + final byte[] input = inputs[i].getBytes("US-ASCII"); 1.139 + final String expected = expecteds[i]; 1.140 + 1.141 + final byte[] actual = NativeCrypto.sha1(input); 1.142 + fAssertNotNull("Hashed value is non-null", actual); 1.143 + assertExpectedBytes(expected, actual); 1.144 + } 1.145 + } 1.146 + 1.147 + /** 1.148 + * Test to ensure the output of our SHA1 algo is the same as MessageDigest's. This is important 1.149 + * because we intend to replace MessageDigest in FHR with this SHA-1 algo (bug 959652). 1.150 + */ 1.151 + private void _testSHA1AgainstMessageDigest() throws UnsupportedEncodingException, 1.152 + NoSuchAlgorithmException { 1.153 + final String[] inputs = { 1.154 + "password", 1.155 + "saranghae", 1.156 + "aoeusnthaoeusnthaoeusnth \0 12345098765432109876_!" 1.157 + }; 1.158 + 1.159 + final MessageDigest digest = MessageDigest.getInstance("SHA-1"); 1.160 + for (final String input : inputs) { 1.161 + final byte[] inputBytes = input.getBytes("US-ASCII"); 1.162 + 1.163 + final byte[] mdBytes = digest.digest(inputBytes); 1.164 + final byte[] ourBytes = NativeCrypto.sha1(inputBytes); 1.165 + fAssertArrayEquals("MessageDigest hash is the same as NativeCrypto SHA-1 hash", mdBytes, ourBytes); 1.166 + } 1.167 + } 1.168 + 1.169 + private void checkPBKDF2SHA256(String p, String s, int c, int dkLen, final String expectedStr) 1.170 + throws GeneralSecurityException, UnsupportedEncodingException { 1.171 + final long start = SystemClock.elapsedRealtime(); 1.172 + 1.173 + final byte[] key = NativeCrypto.pbkdf2SHA256(p.getBytes("US-ASCII"), s.getBytes("US-ASCII"), c, dkLen); 1.174 + fAssertNotNull("Hash result is non-null", key); 1.175 + 1.176 + final long end = SystemClock.elapsedRealtime(); 1.177 + dumpLog(LOGTAG, "SHA-256 " + c + " took " + (end - start) + "ms"); 1.178 + 1.179 + if (expectedStr == null) { 1.180 + return; 1.181 + } 1.182 + 1.183 + fAssertEquals("Hash result is the appropriate length", dkLen, 1.184 + Utils.hex2Byte(expectedStr).length); 1.185 + assertExpectedBytes(expectedStr, key); 1.186 + } 1.187 + 1.188 + private void assertExpectedBytes(final String expectedStr, byte[] key) { 1.189 + fAssertEquals("Expected string matches hash result", expectedStr, Utils.byte2Hex(key)); 1.190 + final byte[] expected = Utils.hex2Byte(expectedStr); 1.191 + 1.192 + fAssertEquals("Expected byte array length matches key length", expected.length, key.length); 1.193 + fAssertArrayEquals("Expected byte array matches key byte array", expected, key); 1.194 + } 1.195 +}