changeset 11216:fe567d56155a 13+3

8219917: [WebView] Sub-resource integrity check fails on Windows and Linux Reviewed-by: shadzic, kcr
author arajkumar
date Thu, 21 Mar 2019 19:18:23 +0530
parents d39ab27d9c87
children dd580ab38ad7
files modules/javafx.web/src/main/java/com/sun/javafx/webkit/WCMessageDigestImpl.java modules/javafx.web/src/main/java/com/sun/webkit/perf/WCMessageDigestPerfLogger.java modules/javafx.web/src/main/java/com/sun/webkit/security/WCMessageDigest.java modules/javafx.web/src/main/native/Source/WebCore/PAL/pal/PlatformJava.cmake modules/javafx.web/src/main/native/Source/WebCore/PAL/pal/crypto/java/CryptoDigestJava.cpp modules/javafx.web/src/test/java/test/javafx/scene/web/SubresourceIntegrityTest.java modules/javafx.web/src/test/resources/test/html/subresource-integrity-test.js
diffstat 7 files changed, 394 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.web/src/main/java/com/sun/javafx/webkit/WCMessageDigestImpl.java	Thu Mar 21 19:18:23 2019 +0530
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.webkit;
+
+import com.sun.webkit.security.WCMessageDigest;
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class WCMessageDigestImpl extends WCMessageDigest {
+    private final MessageDigest digest;
+
+    public WCMessageDigestImpl(String algorithm) throws NoSuchAlgorithmException {
+        digest = MessageDigest.getInstance(algorithm);
+    }
+
+    @Override
+    public void addBytes(ByteBuffer input) {
+        digest.update(input);
+    }
+
+    @Override
+    public byte[] computeHash() {
+        return digest.digest();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.web/src/main/java/com/sun/webkit/perf/WCMessageDigestPerfLogger.java	Thu Mar 21 19:18:23 2019 +0530
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.webkit.perf;
+
+import com.sun.javafx.logging.PlatformLogger;
+import com.sun.webkit.security.WCMessageDigest;
+import java.nio.ByteBuffer;
+
+public class WCMessageDigestPerfLogger extends WCMessageDigest {
+    private static final PlatformLogger log =
+            PlatformLogger.getLogger(WCMessageDigestPerfLogger.class.getName());
+
+    private static final PerfLogger logger = PerfLogger.getLogger(log);
+
+    final private WCMessageDigest digest;
+
+    public WCMessageDigestPerfLogger(WCMessageDigest digest) {
+        this.digest = digest;
+    }
+
+    public synchronized static boolean isEnabled() {
+        return logger.isEnabled();
+    }
+
+    @Override
+    public void addBytes(ByteBuffer input) {
+        logger.resumeCount("ADDBYTES");
+        digest.addBytes(input);
+        logger.suspendCount("ADDBYTES");
+    }
+
+    @Override
+    public byte[] computeHash() {
+        logger.resumeCount("COMPUTEHASH");
+        byte[] result = digest.computeHash();
+        logger.suspendCount("COMPUTEHASH");
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.web/src/main/java/com/sun/webkit/security/WCMessageDigest.java	Thu Mar 21 19:18:23 2019 +0530
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.webkit.security;
+
+import com.sun.javafx.webkit.WCMessageDigestImpl;
+import com.sun.webkit.perf.WCMessageDigestPerfLogger;
+import java.nio.ByteBuffer;
+
+public abstract class WCMessageDigest {
+    /**
+     * Creates the instance of WCMessageDigest for the given algorithm.
+     * @param algorithm the name of the algorithm like SHA-1, SHA-256.
+     */
+    protected static WCMessageDigest getInstance(String algorithm) {
+        try {
+            WCMessageDigest digest = new WCMessageDigestImpl(algorithm);
+            return WCMessageDigestPerfLogger.isEnabled() ? new WCMessageDigestPerfLogger(digest) : digest;
+        } catch (Exception ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Update the digest using the specified ByteBuffer.
+     */
+    public abstract void addBytes(ByteBuffer input);
+
+    /**
+     * Returns the computed hash value.
+     */
+    public abstract byte[] computeHash();
+}
--- a/modules/javafx.web/src/main/native/Source/WebCore/PAL/pal/PlatformJava.cmake	Thu Mar 21 06:12:55 2019 -0700
+++ b/modules/javafx.web/src/main/native/Source/WebCore/PAL/pal/PlatformJava.cmake	Thu Mar 21 19:18:23 2019 +0530
@@ -9,15 +9,9 @@
     "${ICU_INCLUDE_DIRS}"
 )
 
-if (APPLE)
-    list(APPEND PAL_SOURCES
-        crypto/commoncrypto/CryptoDigestCommonCrypto.cpp
-    )
-else ()
-    list(APPEND PAL_SOURCES
-        crypto/java/CryptoDigestJava.cpp
-    )
-endif ()
+list(APPEND PAL_SOURCES
+    crypto/java/CryptoDigestJava.cpp
+)
 
 add_definitions(-DSTATICALLY_LINKED_WITH_JavaScriptCore)
 add_definitions(-DSTATICALLY_LINKED_WITH_WTF)
--- a/modules/javafx.web/src/main/native/Source/WebCore/PAL/pal/crypto/java/CryptoDigestJava.cpp	Thu Mar 21 06:12:55 2019 -0700
+++ b/modules/javafx.web/src/main/native/Source/WebCore/PAL/pal/crypto/java/CryptoDigestJava.cpp	Thu Mar 21 19:18:23 2019 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,39 +26,131 @@
 #include "config.h"
 
 #include "CryptoDigest.h"
-#include "Logging.h"
-
-#define notImplemented()
+#include <jni.h>
+#include <wtf/java/JavaEnv.h>
+#include <wtf/java/JavaRef.h>
 
 namespace PAL {
 
-struct CryptoDigestContext { };
+namespace CryptoDigestInternal {
+
+inline jclass GetMessageDigestClass(JNIEnv* env)
+{
+    static JGClass messageDigestCls(
+        env->FindClass("com/sun/webkit/security/WCMessageDigest"));
+    ASSERT(messageDigestCls);
+    return messageDigestCls;
+}
+
+inline JLObject GetMessageDigestInstance(jstring algorithm)
+{
+    JNIEnv* env = WebCore_GetJavaEnv();
+    if (!env) {
+        return { };
+    }
+
+    static jmethodID midGetInstance = env->GetStaticMethodID(
+        GetMessageDigestClass(env),
+        "getInstance",
+        "(Ljava/lang/String;)Lcom/sun/webkit/security/WCMessageDigest;");
+    ASSERT(midGetInstance);
+    JLObject jDigest = env->CallStaticObjectMethod(GetMessageDigestClass(env), midGetInstance, algorithm);
+    if (CheckAndClearException(env)) {
+        return { };
+    }
+    return jDigest;
+}
+
+jstring toJavaMessageDigestAlgorithm(CryptoDigest::Algorithm algorithm)
+{
+    JNIEnv* env = WebCore_GetJavaEnv();
+
+    const char* algorithmStr = "";
+    switch (algorithm) {
+        case CryptoDigest::Algorithm::SHA_1:
+            algorithmStr = "SHA-1";
+            break;
+        case CryptoDigest::Algorithm::SHA_224:
+            algorithmStr = "SHA-224";
+            break;
+        case CryptoDigest::Algorithm::SHA_256:
+            algorithmStr = "SHA-256";
+            break;
+        case CryptoDigest::Algorithm::SHA_384:
+            algorithmStr = "SHA-384";
+            break;
+        case CryptoDigest::Algorithm::SHA_512:
+            algorithmStr = "SHA-512";
+            break;
+    }
+    return env->NewStringUTF(algorithmStr);
+}
+
+} // namespace CryptoDigestInternal
+
+struct CryptoDigestContext {
+    JGObject jDigest { };
+};
 
 CryptoDigest::CryptoDigest()
+    : m_context(new CryptoDigestContext)
 {
-    notImplemented();
 }
 
 CryptoDigest::~CryptoDigest()
 {
-    notImplemented();
 }
 
-std::unique_ptr<CryptoDigest> CryptoDigest::create(CryptoDigest::Algorithm)
+std::unique_ptr<CryptoDigest> CryptoDigest::create(CryptoDigest::Algorithm algorithm)
 {
-    notImplemented();
-    return std::unique_ptr<CryptoDigest>(new CryptoDigest);
+    using namespace CryptoDigestInternal;
+    auto digest = std::unique_ptr<CryptoDigest>(new CryptoDigest);
+    digest->m_context->jDigest = GetMessageDigestInstance(toJavaMessageDigestAlgorithm(algorithm));
+    return digest;
 }
 
-void CryptoDigest::addBytes(const void*, size_t)
+void CryptoDigest::addBytes(const void* input, size_t length)
 {
-    notImplemented();
+    using namespace CryptoDigestInternal;
+
+    JNIEnv* env = WebCore_GetJavaEnv();
+    if (!m_context->jDigest || !env) {
+        return;
+    }
+
+    static jmethodID midUpdate = env->GetMethodID(
+        GetMessageDigestClass(env),
+        "addBytes",
+        "(Ljava/nio/ByteBuffer;)V");
+    ASSERT(midUpdate);
+    env->CallVoidMethod(jobject(m_context->jDigest), midUpdate, env->NewDirectByteBuffer(const_cast<void*>(input), length));
 }
 
 Vector<uint8_t> CryptoDigest::computeHash()
 {
-    notImplemented();
-    return {};
+    using namespace CryptoDigestInternal;
+
+    JNIEnv* env = WebCore_GetJavaEnv();
+    if (!m_context->jDigest || !env) {
+        return { };
+    }
+
+    static jmethodID midDigest = env->GetMethodID(
+        GetMessageDigestClass(env),
+        "computeHash",
+        "()[B");
+    ASSERT(midUpdate);
+
+    JLocalRef<jbyteArray> jDigestBytes = static_cast<jbyteArray>(env->CallObjectMethod(jobject(m_context->jDigest), midDigest));
+    void* digest = env->GetPrimitiveArrayCritical(static_cast<jbyteArray>(jDigestBytes), 0);
+    if (!digest) {
+        return { };
+    }
+
+    Vector<uint8_t> result;
+    result.append(static_cast<uint8_t*>(digest), env->GetArrayLength(jDigestBytes));
+    env->ReleasePrimitiveArrayCritical(jDigestBytes, digest, 0);
+    return result;
 }
 
 } // namespace PAL
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.web/src/test/java/test/javafx/scene/web/SubresourceIntegrityTest.java	Thu Mar 21 19:18:23 2019 +0530
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package test.javafx.scene.web;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(Parameterized.class)
+public final class SubresourceIntegrityTest extends TestBase {
+
+    // To arguments from junit data provider.
+    private final String hashValue;
+    private final String expected;
+    private File htmlFile;
+    // Expectations
+    private final static String LOADED = "hello";
+    private final static String NOT_LOADED = "not loaded";
+    // TODO: junit 4.11 provides an option to label arguments.
+    @Parameters
+    public static Collection<String[]> data() {
+        return Arrays.asList(new String[][] {
+            // shasum -b -a 1 subresource-integrity-test.js | awk '{ print $1 }' | xxd -r -p | base64
+            {"sha1-/kpzvnGzRkcE9OFn5j8qRE61nZY=", LOADED},
+            // shasum -b -a 224 subresource-integrity-test.js | awk '{ print $1 }' | xxd -r -p | base64
+            {"sha224-zgiBbbuKJixMVEkaOXnvpSYZGsx7SbSZ0QOckg==", LOADED},
+            // shasum -b -a 256 subresource-integrity-test.js | awk '{ print $1 }' | xxd -r -p | base64
+            {"sha256-vcl3cFaIDAtcQBkUZFdY+tW/bjrg6vX1R+hQ8uB5tHc=", LOADED},
+            // shasum -b -a 384 subresource-integrity-test.js | awk '{ print $1 }' | xxd -r -p | base64
+            {"sha384-+GrI+cacF05VlQitRghQhs1by9CSIyc8XgZTbymUg2oA0EYdLiPMtilnFP3LDbkY", LOADED},
+            // shasum -b -a 512 subresource-integrity-test.js | awk '{ print $1 }' | xxd -r -p | base64
+            {"sha512-V8m3j61x5soaVcO83NuHavY7Yn4MQYoUgrqJe38f6QYG9QzzgWbVDB1SrZsZ2CVR1IsOnV2MLhnDaZhWOwHDsw==", LOADED},
+            // Only sha256, sha384, sha512 are validated, rest will be ignored and loaded
+            // Ref. https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity#Using_Subresource_Integrity
+            {"sha1-0000000000000000000000000000", LOADED},
+            {"sha224-0000000000000000000000000000000000000000", LOADED},
+            // negative tests, change the hash value and ensure it fails
+            {"sha256-Vcl3cFaIDAtcQBkUZFdY+tW/bjrg6vX1R+hQ8uB5tHc=", NOT_LOADED},
+            {"sha384-+grI+cacF05VlQitRghQhs1by9CSIyc8XgZTbymUg2oA0EYdLiPMtilnFP3LDbkY", NOT_LOADED},
+            {"sha512-v8m3j61x5soaVcO83NuHavY7Yn4MQYoUgrqJe38f6QYG9QzzgWbVDB1SrZsZ2CVR1IsOnV2MLhnDaZhWOwHDsw==", NOT_LOADED},
+            // should load for invalid hash algorithm
+            {"unknown-0000", LOADED},
+            {"", LOADED},
+        });
+    }
+
+    public SubresourceIntegrityTest(final String hashValue, final String expected) {
+        this.hashValue = hashValue;
+        this.expected = expected;
+    }
+
+    @Before
+    public void setup() throws Exception {
+        // loadContent won't work with CORS, use file:// for main resource.
+        htmlFile = new File("subresource-integrity-test.html");
+        final FileOutputStream out = new FileOutputStream(htmlFile);
+        final String scriptUrl =
+                new File("src/test/resources/test/html/subresource-integrity-test.js").toURI().toASCIIString();
+        final String html =
+                String.format("<html>\n" +
+                "<head><script src='%s' integrity='%s' crossorigin='anonymous'></script></head>\n" +
+                "<body>%s</body>\n" +
+                "</html>", scriptUrl, hashValue, NOT_LOADED);
+        out.write(html.getBytes());
+        out.close();
+    }
+
+    @Test
+    public void testScriptTagWithCorrectHashValue() {
+        load(htmlFile);
+        final String bodyText = (String) executeScript("document.body.innerText");
+        assertNotNull("document.body.innerText must be non null for " + hashValue, bodyText);
+        assertEquals(hashValue, expected, bodyText);
+    }
+
+    @After
+    public void tearDown() {
+        if (!htmlFile.delete()) {
+            htmlFile.deleteOnExit();
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.web/src/test/resources/test/html/subresource-integrity-test.js	Thu Mar 21 19:18:23 2019 +0530
@@ -0,0 +1,3 @@
+window.onload = () => {
+    document.body.innerText = "hello";
+}