Bug 1197720 - NativeCrypto (SHA-256): Let caller define number of bytes in 'str' to update from. r=snorp
authorSebastian Kaspari <s.kaspari@gmail.com>
Mon, 30 Nov 2015 15:59:56 +0100
changeset 308807 49811ce73e27ef75cf48b3bf2a69e652595e46bc
parent 308800 fb7bdd4da8d9294e8ff6cc5abc78de0511059ee9
child 308808 dbf3576dd3483052c6f6bd3599b994b62f273a1f
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1197720
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1197720 - NativeCrypto (SHA-256): Let caller define number of bytes in 'str' to update from. r=snorp In Java we will call this method with byte array buffers we use to read from a stream. Java does not guarantee to completely fill the buffer (and can't at the end of a stream). Passing the number of bytes to read from the array to NativeCrypto avoids copying bytes between arrays of various lengths.
mobile/android/base/background/nativecode/NativeCrypto.java
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testNativeCrypto.java
mozglue/android/NativeCrypto.cpp
mozglue/android/NativeCrypto.h
--- a/mobile/android/base/background/nativecode/NativeCrypto.java
+++ b/mobile/android/base/background/nativecode/NativeCrypto.java
@@ -46,15 +46,15 @@ public class NativeCrypto {
   /**
    * Wrapper to perform SHA-256 init in native code. Returns a SHA-256 context.
    */
   public native static byte[] sha256init();
 
   /**
    * Wrapper to update a SHA-256 context in native code.
    */
-  public native static void sha256update(byte[] ctx, byte[] str);
+  public native static void sha256update(byte[] ctx, byte[] str, int len);
 
   /**
    * Wrapper to finalize a SHA-256 context in native code. Returns digest.
    */
   public native static byte[] sha256finalize(byte[] ctx);
 }
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testNativeCrypto.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testNativeCrypto.java
@@ -4,16 +4,18 @@
 
 package org.mozilla.gecko.tests;
 
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertArrayEquals;
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertEquals;
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertNotNull;
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fFail;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.security.GeneralSecurityException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 
 import org.mozilla.gecko.background.nativecode.NativeCrypto;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.tests.helpers.GeckoHelper;
@@ -55,16 +57,17 @@ public class testNativeCrypto extends UI
     _testPBKDF2SHA256InvalidLenArg();
 
     _testSHA1();
     _testSHA1AgainstMessageDigest();
 
     _testSHA256();
     _testSHA256MultiPart();
     _testSHA256AgainstMessageDigest();
+    _testSHA256WithMultipleUpdatesFromStream();
   }
 
   public void _testPBKDF2SHA256A() throws UnsupportedEncodingException, GeneralSecurityException {
     final String  p = "password";
     final String  s = "salt";
     final int dkLen = 32;
 
     checkPBKDF2SHA256(p, s, 1, dkLen, "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b");
@@ -187,32 +190,32 @@ public class testNativeCrypto extends UI
       "594847328451bdfa85056225462cc1d867d877fb388df0ce35f25ab5562bfbb5"
     };
 
     for (int i = 0; i < inputs.length; ++i) {
       final byte[] input = inputs[i].getBytes("US-ASCII");
       final String expected = expecteds[i];
 
       final byte[] ctx = NativeCrypto.sha256init();
-      NativeCrypto.sha256update(ctx, input);
+      NativeCrypto.sha256update(ctx, input, input.length);
       final byte[] actual = NativeCrypto.sha256finalize(ctx);
       fAssertNotNull("Hashed value is non-null", actual);
       assertExpectedBytes(expected, actual);
     }
   }
 
   private void _testSHA256MultiPart() throws UnsupportedEncodingException {
     final String input = "01234567";
     final int repetitions = 80;
     final String expected = "594847328451bdfa85056225462cc1d867d877fb388df0ce35f25ab5562bfbb5";
 
     final byte[] inputBytes = input.getBytes("US-ASCII");
     final byte[] ctx = NativeCrypto.sha256init();
     for (int i = 0; i < repetitions; ++i) {
-      NativeCrypto.sha256update(ctx, inputBytes);
+      NativeCrypto.sha256update(ctx, inputBytes, inputBytes.length);
     }
     final byte[] actual = NativeCrypto.sha256finalize(ctx);
     fAssertNotNull("Hashed value is non-null", actual);
     assertExpectedBytes(expected, actual);
   }
 
   private void _testSHA256AgainstMessageDigest() throws UnsupportedEncodingException,
       NoSuchAlgorithmException {
@@ -224,22 +227,43 @@ public class testNativeCrypto extends UI
 
     final MessageDigest digest = MessageDigest.getInstance("SHA-256");
     for (final String input : inputs) {
       final byte[] inputBytes = input.getBytes("US-ASCII");
 
       final byte[] mdBytes = digest.digest(inputBytes);
 
       final byte[] ctx = NativeCrypto.sha256init();
-      NativeCrypto.sha256update(ctx, inputBytes);
+      NativeCrypto.sha256update(ctx, inputBytes, inputBytes.length);
       final byte[] ourBytes = NativeCrypto.sha256finalize(ctx);
       fAssertArrayEquals("MessageDigest hash is the same as NativeCrypto SHA-256 hash", mdBytes, ourBytes);
     }
   }
 
+  private void _testSHA256WithMultipleUpdatesFromStream() throws UnsupportedEncodingException {
+    final String input = "HelloWorldThisIsASuperLongStringThatIsReadAsAStreamOfBytes";
+    final ByteArrayInputStream stream = new ByteArrayInputStream(input.getBytes("UTF-8"));
+    final String expected = "8b5cb76b80f7eb6fb83ee138bfd31e2922e71dd245daa21a8d9876e8dee9eef5";
+
+    byte[] buffer = new byte[10];
+    final byte[] ctx = NativeCrypto.sha256init();
+    int c;
+
+    try {
+      while ((c = stream.read(buffer)) != -1) {
+        NativeCrypto.sha256update(ctx, buffer, c);
+      }
+      final byte[] actual = NativeCrypto.sha256finalize(ctx);
+      fAssertNotNull("Hashed value is non-null", actual);
+      assertExpectedBytes(expected, actual);
+    } catch (IOException e) {
+      fFail("IOException while reading stream");
+    }
+  }
+
   private void checkPBKDF2SHA256(String p, String s, int c, int dkLen, final String expectedStr)
       throws GeneralSecurityException, UnsupportedEncodingException {
     final long start = SystemClock.elapsedRealtime();
 
     final byte[] key = NativeCrypto.pbkdf2SHA256(p.getBytes("US-ASCII"), s.getBytes("US-ASCII"), c, dkLen);
     fAssertNotNull("Hash result is non-null", key);
 
     final long end = SystemClock.elapsedRealtime();
--- a/mozglue/android/NativeCrypto.cpp
+++ b/mozglue/android/NativeCrypto.cpp
@@ -91,23 +91,22 @@ extern "C" JNIEXPORT jbyteArray MOZ_JNIC
 
   return out;
 }
 
 /**
  * Helper function to invoke native SHA-256 update with JNI arguments.
  */
 extern "C" JNIEXPORT void MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256update
-    (JNIEnv *env, jclass jc, jbyteArray jctx, jbyteArray jstr) {
+    (JNIEnv *env, jclass jc, jbyteArray jctx, jbyteArray jstr, jint len) {
   jbyte *str = env->GetByteArrayElements(jstr, nullptr);
-  size_t strLen = env->GetArrayLength(jstr);
 
   SHA256_CTX *shaContext = (SHA256_CTX*)env->GetByteArrayElements(jctx, nullptr);
 
-  SHA256_Update(shaContext, (void*)str, strLen);
+  SHA256_Update(shaContext, (void*)str, (size_t) len);
 
   env->ReleaseByteArrayElements(jstr, str, JNI_ABORT);
   env->ReleaseByteArrayElements(jctx, (jbyte*)shaContext, 0);
 
   return;
 }
 
 /**
--- a/mozglue/android/NativeCrypto.h
+++ b/mozglue/android/NativeCrypto.h
@@ -32,17 +32,17 @@ JNIEXPORT jbyteArray JNICALL Java_org_mo
   (JNIEnv *, jclass);
 
 /*
  * Class:     org_mozilla_gecko_background_nativecode_NativeCrypto
  * Method:    sha256update
  * Signature: ([B[B)V
  */
 JNIEXPORT void JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256update
-  (JNIEnv *, jclass, jbyteArray, jbyteArray);
+  (JNIEnv *, jclass, jbyteArray, jbyteArray, jint);
 
 /*
  * Class:     org_mozilla_gecko_background_nativecode_NativeCrypto
  * Method:    sha256finalize
  * Signature: ([B)[B
  */
 JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256finalize
   (JNIEnv *, jclass, jbyteArray);