Split cryptographic digest computation and parsing out of CSP code so it can be reused
authorweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Apr 2017 22:13:58 +0000 (22:13 +0000)
committerweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Apr 2017 22:13:58 +0000 (22:13 +0000)
https://bugs.webkit.org/show_bug.cgi?id=171076

Reviewed by Chris Dumez.

Source/WebCore:

Factor out cryptographic digest parsing from Content Security Policy code
so that it can be reused for the Subresource Integrity implementation.

* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj:
Add new files.

* html/parser/ParsingUtilities.h:
(WebCore::skipExactlyIgnoringASCIICase):
Add parsing helper to match / skip over a constant string, using IgnoringASCIICase semantics.

* loader/ResourceCryptographicDigest.cpp: Added.
(WebCore::parseHashAlgorithmAdvancingPosition):
(WebCore::parseCryptographicDigestImpl):
(WebCore::parseCryptographicDigest):
Move parsing of cryptographic-digest strings from ContentSecurityPolicySourceList.cpp
and optimize it a little by avoiding String allocations and generalizing it so that it
can parse either UChars or LChars.

* loader/ResourceCryptographicDigest.h: Added.
(WebCore::ResourceCryptographicDigest::operator==):
(WebCore::ResourceCryptographicDigest::operator!=):
(WTF::DefaultHash<WebCore::ResourceCryptographicDigest>::Hash::hash):
(WTF::DefaultHash<WebCore::ResourceCryptographicDigest>::Hash::equal):
(WTF::HashTraits<WebCore::ResourceCryptographicDigest>::emptyValue):
(WTF::HashTraits<WebCore::ResourceCryptographicDigest>::constructDeletedValue):
(WTF::HashTraits<WebCore::ResourceCryptographicDigest>::isDeletedValue):
Add a struct (rather than using a std::pair) to represent the digest + algorithm. And add
HashTraits so it can be used as HashMap.

* page/csp/ContentSecurityPolicy.cpp:
(WebCore::ContentSecurityPolicy::findHashOfContentInPolicies):
(WebCore::toCryptoDigestAlgorithm): Deleted.
Move algorithm conversion to ResourceCryptographicDigest.cpp. Make use of new
cryptographicDigestForBytes function to do hashing.

* page/csp/ContentSecurityPolicy.h:
* page/csp/ContentSecurityPolicyHash.h:
(WTF::DefaultHash<WebCore::ContentSecurityPolicyDigest>::Hash::hash): Deleted.
(WTF::DefaultHash<WebCore::ContentSecurityPolicyDigest>::Hash::equal): Deleted.
Remove HashTraits for the digest, this is now handled by ResourceCryptographicDigest.
To keep things relatively straight-forward, redefine ContentSecurityPolicyHashAlgorithm
and ContentSecurityPolicyHash in terms of ResourceCryptographicDigest, so that less code
has to be changed all at once. In a later pass, if wanted, we can remove these using
declarations.

* page/csp/ContentSecurityPolicySourceList.cpp:
(WebCore::isNonceCharacter):
Use renamed isBase64OrBase64URLCharacter predicate.

(WebCore::ContentSecurityPolicySourceList::parseHashSource):
Rework using ResourceCryptographicDigest parsing. Quotation and maximum digest
length have been kept here, as they are not applicable to other uses of
the digest, specifically Subresource Integrity.

Source/WTF:

* wtf/text/Base64.cpp:
(WTF::base64Decode):
(WTF::base64URLDecode):
Add overloads for base64Decode and base64URLDecode that take a StringView, to avoid allocations
of Strings.

* wtf/text/Base64.h:
(WTF::isBase64OrBase64URLCharacter):
Move helper predicate used for parsing either type of Base64 encoded string from WebCore.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215646 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/WTF/ChangeLog
Source/WTF/wtf/text/Base64.cpp
Source/WTF/wtf/text/Base64.h
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/html/parser/ParsingUtilities.h
Source/WebCore/loader/ResourceCryptographicDigest.cpp [new file with mode: 0644]
Source/WebCore/loader/ResourceCryptographicDigest.h [new file with mode: 0644]
Source/WebCore/page/csp/ContentSecurityPolicy.cpp
Source/WebCore/page/csp/ContentSecurityPolicy.h
Source/WebCore/page/csp/ContentSecurityPolicyHash.h
Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp

index 5d2c6b1..c5e42a6 100644 (file)
@@ -1,3 +1,20 @@
+2017-04-20  Sam Weinig  <sam@webkit.org>
+
+        Split cryptographic digest computation and parsing out of CSP code so it can be reused
+        https://bugs.webkit.org/show_bug.cgi?id=171076
+
+        Reviewed by Chris Dumez.
+
+        * wtf/text/Base64.cpp:
+        (WTF::base64Decode):
+        (WTF::base64URLDecode):
+        Add overloads for base64Decode and base64URLDecode that take a StringView, to avoid allocations
+        of Strings.
+
+        * wtf/text/Base64.h:
+        (WTF::isBase64OrBase64URLCharacter):
+        Move helper predicate used for parsing either type of Base64 encoded string from WebCore.
+
 2017-04-21  Keith Miller  <keith_miller@apple.com>
 
         Unreviewed, rolling out r215634.
index 714a7ea..2ae5de2 100644 (file)
@@ -274,6 +274,14 @@ bool base64Decode(const String& in, SignedOrUnsignedCharVectorAdapter out, unsig
     return base64DecodeInternal(in.characters16(), length, out, options, base64DecMap);
 }
 
+bool base64Decode(StringView in, SignedOrUnsignedCharVectorAdapter out, unsigned options)
+{
+    unsigned length = in.length();
+    if (!length || in.is8Bit())
+        return base64DecodeInternal(in.characters8(), length, out, options, base64DecMap);
+    return base64DecodeInternal(in.characters16(), length, out, options, base64DecMap);
+}
+
 bool base64Decode(const Vector<char>& in, SignedOrUnsignedCharVectorAdapter out, unsigned options)
 {
     out.clear();
@@ -298,6 +306,14 @@ bool base64URLDecode(const String& in, SignedOrUnsignedCharVectorAdapter out)
     return base64DecodeInternal(in.characters16(), length, out, Base64Default, base64URLDecMap);
 }
 
+bool base64URLDecode(StringView in, SignedOrUnsignedCharVectorAdapter out)
+{
+    unsigned length = in.length();
+    if (!length || in.is8Bit())
+        return base64DecodeInternal(in.characters8(), length, out, Base64Default, base64URLDecMap);
+    return base64DecodeInternal(in.characters16(), length, out, Base64Default, base64URLDecMap);
+}
+
 bool base64URLDecode(const Vector<char>& in, SignedOrUnsignedCharVectorAdapter out)
 {
     out.clear();
index 8205575..041483a 100644 (file)
@@ -25,8 +25,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#ifndef Base64_h
-#define Base64_h
+#pragma once
 
 #include <wtf/Vector.h>
 #include <wtf/text/CString.h>
@@ -152,6 +151,7 @@ String base64Encode(ConstSignedOrUnsignedCharVectorAdapter, Base64EncodePolicy =
 String base64Encode(const CString&, Base64EncodePolicy = Base64DoNotInsertLFs);
 
 WTF_EXPORT_PRIVATE bool base64Decode(const String&, SignedOrUnsignedCharVectorAdapter, unsigned options = Base64Default);
+WTF_EXPORT_PRIVATE bool base64Decode(StringView, SignedOrUnsignedCharVectorAdapter, unsigned options = Base64Default);
 WTF_EXPORT_PRIVATE bool base64Decode(const Vector<char>&, SignedOrUnsignedCharVectorAdapter, unsigned options = Base64Default);
 WTF_EXPORT_PRIVATE bool base64Decode(const char*, unsigned, SignedOrUnsignedCharVectorAdapter, unsigned options = Base64Default);
 
@@ -189,6 +189,7 @@ String base64URLEncode(ConstSignedOrUnsignedCharVectorAdapter);
 String base64URLEncode(const CString&);
 
 WTF_EXPORT_PRIVATE bool base64URLDecode(const String&, SignedOrUnsignedCharVectorAdapter);
+WTF_EXPORT_PRIVATE bool base64URLDecode(StringView, SignedOrUnsignedCharVectorAdapter);
 WTF_EXPORT_PRIVATE bool base64URLDecode(const Vector<char>&, SignedOrUnsignedCharVectorAdapter);
 WTF_EXPORT_PRIVATE bool base64URLDecode(const char*, unsigned, SignedOrUnsignedCharVectorAdapter);
 
@@ -212,6 +213,11 @@ inline String base64URLEncode(const CString& in)
     return base64URLEncode(in.data(), in.length());
 }
 
+template<typename CharacterType> static inline bool isBase64OrBase64URLCharacter(CharacterType c)
+{
+    return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '-' || c == '_';
+}
+
 } // namespace WTF
 
 using WTF::Base64EncodePolicy;
@@ -222,5 +228,4 @@ using WTF::Base64IgnoreSpacesAndNewLines;
 using WTF::base64Encode;
 using WTF::base64Decode;
 using WTF::base64URLDecode;
-
-#endif // Base64_h
+using WTF::isBase64OrBase64URLCharacter;
index 45f12cb..a960d27 100644 (file)
@@ -1965,6 +1965,7 @@ set(WebCore_SOURCES
     loader/PolicyCallback.cpp
     loader/PolicyChecker.cpp
     loader/ProgressTracker.cpp
+    loader/ResourceCryptographicDigest.cpp
     loader/ResourceLoadNotifier.cpp
     loader/ResourceLoadObserver.cpp
     loader/ResourceLoadStatistics.cpp
index dcf2a5b..be2f33d 100644 (file)
@@ -1,3 +1,65 @@
+2017-04-20  Sam Weinig  <sam@webkit.org>
+
+        Split cryptographic digest computation and parsing out of CSP code so it can be reused
+        https://bugs.webkit.org/show_bug.cgi?id=171076
+
+        Reviewed by Chris Dumez.
+
+        Factor out cryptographic digest parsing from Content Security Policy code
+        so that it can be reused for the Subresource Integrity implementation.
+
+        * CMakeLists.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        Add new files.
+
+        * html/parser/ParsingUtilities.h:
+        (WebCore::skipExactlyIgnoringASCIICase):
+        Add parsing helper to match / skip over a constant string, using IgnoringASCIICase semantics.
+
+        * loader/ResourceCryptographicDigest.cpp: Added.
+        (WebCore::parseHashAlgorithmAdvancingPosition):
+        (WebCore::parseCryptographicDigestImpl):
+        (WebCore::parseCryptographicDigest):
+        Move parsing of cryptographic-digest strings from ContentSecurityPolicySourceList.cpp
+        and optimize it a little by avoiding String allocations and generalizing it so that it
+        can parse either UChars or LChars.
+
+        * loader/ResourceCryptographicDigest.h: Added.
+        (WebCore::ResourceCryptographicDigest::operator==):
+        (WebCore::ResourceCryptographicDigest::operator!=):
+        (WTF::DefaultHash<WebCore::ResourceCryptographicDigest>::Hash::hash):
+        (WTF::DefaultHash<WebCore::ResourceCryptographicDigest>::Hash::equal):
+        (WTF::HashTraits<WebCore::ResourceCryptographicDigest>::emptyValue):
+        (WTF::HashTraits<WebCore::ResourceCryptographicDigest>::constructDeletedValue):
+        (WTF::HashTraits<WebCore::ResourceCryptographicDigest>::isDeletedValue):
+        Add a struct (rather than using a std::pair) to represent the digest + algorithm. And add
+        HashTraits so it can be used as HashMap.
+        
+        * page/csp/ContentSecurityPolicy.cpp:
+        (WebCore::ContentSecurityPolicy::findHashOfContentInPolicies):
+        (WebCore::toCryptoDigestAlgorithm): Deleted.
+        Move algorithm conversion to ResourceCryptographicDigest.cpp. Make use of new 
+        cryptographicDigestForBytes function to do hashing.
+
+        * page/csp/ContentSecurityPolicy.h:
+        * page/csp/ContentSecurityPolicyHash.h:
+        (WTF::DefaultHash<WebCore::ContentSecurityPolicyDigest>::Hash::hash): Deleted.
+        (WTF::DefaultHash<WebCore::ContentSecurityPolicyDigest>::Hash::equal): Deleted.
+        Remove HashTraits for the digest, this is now handled by ResourceCryptographicDigest. 
+        To keep things relatively straight-forward, redefine ContentSecurityPolicyHashAlgorithm
+        and ContentSecurityPolicyHash in terms of ResourceCryptographicDigest, so that less code
+        has to be changed all at once. In a later pass, if wanted, we can remove these using 
+        declarations.
+
+        * page/csp/ContentSecurityPolicySourceList.cpp:
+        (WebCore::isNonceCharacter):
+        Use renamed isBase64OrBase64URLCharacter predicate.
+
+        (WebCore::ContentSecurityPolicySourceList::parseHashSource):
+        Rework using ResourceCryptographicDigest parsing. Quotation and maximum digest
+        length have been kept here, as they are not applicable to other uses of 
+        the digest, specifically Subresource Integrity.
+
 2017-04-21  Jer Noble  <jer.noble@apple.com>
 
         Unreviewed fix after r215624; null-deref crash.
index 45c3ecd..9a9055b 100644 (file)
                7C1E97281A9F9834007BF0FB /* AutoFillButtonElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C1E97261A9F9834007BF0FB /* AutoFillButtonElement.h */; };
                7C2BDD3D17C7F98C0038FF15 /* JSDOMGlobalObjectTask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C2BDD3B17C7F98B0038FF15 /* JSDOMGlobalObjectTask.cpp */; };
                7C2BDD3E17C7F98C0038FF15 /* JSDOMGlobalObjectTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C2BDD3C17C7F98B0038FF15 /* JSDOMGlobalObjectTask.h */; };
+               7C2FA6111EA95A3900A03108 /* ResourceCryptographicDigest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C2FA60F1EA95A3200A03108 /* ResourceCryptographicDigest.cpp */; };
+               7C2FA6121EA95A3C00A03108 /* ResourceCryptographicDigest.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C2FA6101EA95A3200A03108 /* ResourceCryptographicDigest.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7C330A021DF8FAC600D3395C /* GraphicsContext3DAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C330A011DF8FAC600D3395C /* GraphicsContext3DAttributes.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7C330A071DF9F95100D3395C /* JSPositionOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C330A051DF9F95100D3395C /* JSPositionOptions.cpp */; };
                7C330A081DF9F95100D3395C /* JSPositionOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C330A061DF9F95100D3395C /* JSPositionOptions.h */; };
                CE7B2DB41586ABAD0098B3FA /* AlternativeTextUIController.mm in Sources */ = {isa = PBXBuildFile; fileRef = CE7B2DB01586ABAD0098B3FA /* AlternativeTextUIController.mm */; };
                CE7B2DB51586ABAD0098B3FA /* TextAlternativeWithRange.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7B2DB11586ABAD0098B3FA /* TextAlternativeWithRange.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CE7B2DB61586ABAD0098B3FA /* TextAlternativeWithRange.mm in Sources */ = {isa = PBXBuildFile; fileRef = CE7B2DB21586ABAD0098B3FA /* TextAlternativeWithRange.mm */; };
-               CE7E17831C83A49100AD06AF /* ContentSecurityPolicyHash.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7E17821C83A49100AD06AF /* ContentSecurityPolicyHash.h */; };
+               CE7E17831C83A49100AD06AF /* ContentSecurityPolicyHash.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7E17821C83A49100AD06AF /* ContentSecurityPolicyHash.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CEC337AD1A46071F009B8523 /* ServersSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = CEC337AC1A46071F009B8523 /* ServersSPI.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CEC337AF1A46086D009B8523 /* GraphicsServicesSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = CEC337AE1A46086D009B8523 /* GraphicsServicesSPI.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CECADFC6153778FF00E37068 /* DictationAlternative.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CECADFC2153778FF00E37068 /* DictationAlternative.cpp */; };
                7C1E97261A9F9834007BF0FB /* AutoFillButtonElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoFillButtonElement.h; sourceTree = "<group>"; };
                7C2BDD3B17C7F98B0038FF15 /* JSDOMGlobalObjectTask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDOMGlobalObjectTask.cpp; sourceTree = "<group>"; };
                7C2BDD3C17C7F98B0038FF15 /* JSDOMGlobalObjectTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDOMGlobalObjectTask.h; sourceTree = "<group>"; };
+               7C2FA60F1EA95A3200A03108 /* ResourceCryptographicDigest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResourceCryptographicDigest.cpp; sourceTree = "<group>"; };
+               7C2FA6101EA95A3200A03108 /* ResourceCryptographicDigest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResourceCryptographicDigest.h; sourceTree = "<group>"; };
                7C330A011DF8FAC600D3395C /* GraphicsContext3DAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GraphicsContext3DAttributes.h; sourceTree = "<group>"; };
                7C330A031DF9E95B00D3395C /* PositionOptions.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = PositionOptions.idl; sourceTree = "<group>"; };
                7C330A051DF9F95100D3395C /* JSPositionOptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPositionOptions.cpp; sourceTree = "<group>"; };
                                1A2A68210B5BEDE70002A480 /* ProgressTracker.cpp */,
                                1A2A68220B5BEDE70002A480 /* ProgressTracker.h */,
                                1ACADD781880D91C00D8B71D /* ProgressTrackerClient.h */,
+                               7C2FA60F1EA95A3200A03108 /* ResourceCryptographicDigest.cpp */,
+                               7C2FA6101EA95A3200A03108 /* ResourceCryptographicDigest.h */,
                                93E227DE0AF589AD00D48324 /* ResourceLoader.cpp */,
                                656D37270ADBA5DE00A4554D /* ResourceLoader.h */,
                                D0A3A7301405A39800FB8ED3 /* ResourceLoaderOptions.h */,
                                A8EA7CAF0A192B9C00A8EF5F /* HTMLHRElement.h in Headers */,
                                A871DE270A152AC800B12A68 /* HTMLHtmlElement.h in Headers */,
                                A871DE2A0A152AC800B12A68 /* HTMLIFrameElement.h in Headers */,
+                               7C2FA6121EA95A3C00A03108 /* ResourceCryptographicDigest.h in Headers */,
                                A8EA7D2D0A19385500A8EF5F /* HTMLImageElement.h in Headers */,
                                A8EA7D2B0A19385500A8EF5F /* HTMLImageLoader.h in Headers */,
                                A81369CC097374F600D74463 /* HTMLInputElement.h in Headers */,
                                B2A1F2B00CEF0ABF00442F6A /* SVGMissingGlyphElement.cpp in Sources */,
                                B2227A4D0D00BF220071B782 /* SVGMPathElement.cpp in Sources */,
                                A833C7CA0A2CF06B00D57664 /* SVGNames.cpp in Sources */,
+                               7C2FA6111EA95A3900A03108 /* ResourceCryptographicDigest.cpp in Sources */,
                                7C39C3731DDBB8C100FEFB29 /* SVGNumberListValues.cpp in Sources */,
                                B2227A560D00BF220071B782 /* SVGParserUtilities.cpp in Sources */,
                                84C6784C1214814700A92902 /* SVGPathBlender.cpp in Sources */,
index a70d312..a56cc25 100644 (file)
@@ -30,6 +30,8 @@
 
 #pragma once
 
+#include <wtf/text/StringCommon.h>
+
 namespace WebCore {
 
 template<typename CharType>
@@ -80,4 +82,16 @@ void reverseSkipWhile(const CharType*& position, const CharType* start)
         --position;
 }
 
+template<typename CharacterType, unsigned lowercaseLettersLength>
+bool skipExactlyIgnoringASCIICase(const CharacterType*& position, const CharacterType* end, const char (&lowercaseLetters)[lowercaseLettersLength])
+{
+    if (position + lowercaseLettersLength > end)
+        return false;
+
+    bool result = WTF::equalLettersIgnoringASCIICase(position, lowercaseLettersLength - 1, lowercaseLetters);
+    if (result)
+        position += (lowercaseLettersLength - 1);
+    return result;
+}
+
 } // namespace WebCore
diff --git a/Source/WebCore/loader/ResourceCryptographicDigest.cpp b/Source/WebCore/loader/ResourceCryptographicDigest.cpp
new file mode 100644 (file)
index 0000000..e0d73b8
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ResourceCryptographicDigest.h"
+
+#include "CachedResource.h"
+#include "HTMLParserIdioms.h"
+#include "ParsingUtilities.h"
+#include "SharedBuffer.h"
+#include <pal/crypto/CryptoDigest.h>
+#include <wtf/text/Base64.h>
+
+namespace WebCore {
+
+template<typename CharacterType>
+static bool parseHashAlgorithmAdvancingPosition(const CharacterType*& position, const CharacterType* end, ResourceCryptographicDigest::Algorithm& algorithm)
+{
+    // FIXME: This would be much cleaner with a lookup table of pairs of label / algorithm enum values, but I can't
+    // figure out how to keep the labels compiletime strings for skipExactlyIgnoringASCIICase.
+
+    if (skipExactlyIgnoringASCIICase(position, end, "sha256")) {
+        algorithm = ResourceCryptographicDigest::Algorithm::SHA256;
+        return true;
+    }
+    if (skipExactlyIgnoringASCIICase(position, end, "sha384")) {
+        algorithm = ResourceCryptographicDigest::Algorithm::SHA384;
+        return true;
+    }
+    if (skipExactlyIgnoringASCIICase(position, end, "sha512")) {
+        algorithm = ResourceCryptographicDigest::Algorithm::SHA512;
+        return true;
+    }
+
+    return false;
+}
+
+template<typename CharacterType>
+std::optional<ResourceCryptographicDigest> parseCryptographicDigestImpl(const CharacterType*& position, const CharacterType* end)
+{
+    if (position == end)
+        return std::nullopt;
+
+    ResourceCryptographicDigest::Algorithm algorithm;
+    if (!parseHashAlgorithmAdvancingPosition(position, end, algorithm))
+        return std::nullopt;
+
+    if (!skipExactly<CharacterType>(position, end, '-'))
+        return std::nullopt;
+
+    const CharacterType* beginHashValue = position;
+    skipWhile<CharacterType, isBase64OrBase64URLCharacter>(position, end);
+    skipExactly<CharacterType>(position, end, '=');
+    skipExactly<CharacterType>(position, end, '=');
+
+    if (position == beginHashValue)
+        return std::nullopt;
+
+    Vector<uint8_t> digest;
+    StringView hashValue(beginHashValue, position - beginHashValue);
+    if (!base64Decode(hashValue, digest, Base64ValidatePadding)) {
+        if (!base64URLDecode(hashValue, digest))
+            return std::nullopt;
+    }
+
+    return ResourceCryptographicDigest { algorithm, WTFMove(digest) };
+}
+
+std::optional<ResourceCryptographicDigest> parseCryptographicDigest(const UChar*& begin, const UChar* end)
+{
+    return parseCryptographicDigestImpl(begin, end);
+}
+
+std::optional<ResourceCryptographicDigest> parseCryptographicDigest(const LChar*& begin, const LChar* end)
+{
+    return parseCryptographicDigestImpl(begin, end);
+}
+
+static PAL::CryptoDigest::Algorithm toCryptoDigestAlgorithm(ResourceCryptographicDigest::Algorithm algorithm)
+{
+    switch (algorithm) {
+    case ResourceCryptographicDigest::Algorithm::SHA256:
+        return PAL::CryptoDigest::Algorithm::SHA_256;
+    case ResourceCryptographicDigest::Algorithm::SHA384:
+        return PAL::CryptoDigest::Algorithm::SHA_384;
+    case ResourceCryptographicDigest::Algorithm::SHA512:
+        return PAL::CryptoDigest::Algorithm::SHA_512;
+    }
+    ASSERT_NOT_REACHED();
+    return PAL::CryptoDigest::Algorithm::SHA_512;
+}
+
+ResourceCryptographicDigest cryptographicDigestForBytes(ResourceCryptographicDigest::Algorithm algorithm, const char* bytes, size_t length)
+{
+    auto cryptoDigest = PAL::CryptoDigest::create(toCryptoDigestAlgorithm(algorithm));
+    cryptoDigest->addBytes(bytes, length);
+    return { algorithm, cryptoDigest->computeHash() };
+}
+
+}
diff --git a/Source/WebCore/loader/ResourceCryptographicDigest.h b/Source/WebCore/loader/ResourceCryptographicDigest.h
new file mode 100644 (file)
index 0000000..59f5566
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <type_traits>
+#include <wtf/HashFunctions.h>
+#include <wtf/HashTraits.h>
+#include <wtf/Hasher.h>
+#include <wtf/Optional.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CachedResource;
+
+struct ResourceCryptographicDigest {
+    enum class Algorithm {
+        SHA256 = 1 << 0,
+        SHA384 = 1 << 1,
+        SHA512 = 1 << 2,
+    };
+
+    // Number of bytes to hold SHA-512 digest
+    static constexpr size_t maximumDigestLength = 64;
+
+    Algorithm algorithm;
+    Vector<uint8_t> value;
+
+    bool operator==(const ResourceCryptographicDigest& other) const
+    {
+        return algorithm == other.algorithm && value == other.value;
+    }
+
+    bool operator!=(const ResourceCryptographicDigest& other) const
+    {
+        return !(*this == other);
+    }
+};
+
+std::optional<ResourceCryptographicDigest> parseCryptographicDigest(const UChar*& begin, const UChar* end);
+std::optional<ResourceCryptographicDigest> parseCryptographicDigest(const LChar*& begin, const LChar* end);
+
+ResourceCryptographicDigest cryptographicDigestForBytes(ResourceCryptographicDigest::Algorithm, const char* bytes, size_t length);
+
+}
+
+namespace WTF {
+
+template<> struct DefaultHash<WebCore::ResourceCryptographicDigest> {
+    struct Hash {
+        static unsigned hash(const WebCore::ResourceCryptographicDigest& digest)
+        {
+            return pairIntHash(intHash(static_cast<unsigned>(digest.algorithm)), StringHasher::computeHash(digest.value.data(), digest.value.size()));
+        }
+        static bool equal(const WebCore::ResourceCryptographicDigest& a, const WebCore::ResourceCryptographicDigest& b)
+        {
+            return a == b;
+        }
+        static const bool safeToCompareToEmptyOrDeleted = true;
+    };
+};
+
+template<> struct HashTraits<WebCore::ResourceCryptographicDigest> : GenericHashTraits<WebCore::ResourceCryptographicDigest> {
+    using Algorithm = WebCore::ResourceCryptographicDigest::Algorithm;
+    using AlgorithmUnderlyingType = typename std::underlying_type<Algorithm>::type;
+    static constexpr auto emptyAlgorithmValue = static_cast<Algorithm>(std::numeric_limits<AlgorithmUnderlyingType>::max());
+    static constexpr auto deletedAlgorithmValue = static_cast<Algorithm>(std::numeric_limits<AlgorithmUnderlyingType>::max() - 1);
+
+    static const bool emptyValueIsZero = false;
+
+    static WebCore::ResourceCryptographicDigest emptyValue()
+    {
+        return { emptyAlgorithmValue, { } };
+    }
+
+    static void constructDeletedValue(WebCore::ResourceCryptographicDigest& slot)
+    {
+        slot.algorithm = deletedAlgorithmValue;
+    }
+
+    static bool isDeletedValue(const WebCore::ResourceCryptographicDigest& slot)
+    {
+        return slot.algorithm == deletedAlgorithmValue;
+    }
+};
+
+}
index 475087f..f1a03f6 100644 (file)
@@ -311,20 +311,6 @@ bool ContentSecurityPolicy::allPoliciesAllow(ViolatedDirectiveCallback&& callbac
     return isAllowed;
 }
 
-static PAL::CryptoDigest::Algorithm toCryptoDigestAlgorithm(ContentSecurityPolicyHashAlgorithm algorithm)
-{
-    switch (algorithm) {
-    case ContentSecurityPolicyHashAlgorithm::SHA_256:
-        return PAL::CryptoDigest::Algorithm::SHA_256;
-    case ContentSecurityPolicyHashAlgorithm::SHA_384:
-        return PAL::CryptoDigest::Algorithm::SHA_384;
-    case ContentSecurityPolicyHashAlgorithm::SHA_512:
-        return PAL::CryptoDigest::Algorithm::SHA_512;
-    }
-    ASSERT_NOT_REACHED();
-    return PAL::CryptoDigest::Algorithm::SHA_512;
-}
-
 template<typename Predicate>
 ContentSecurityPolicy::HashInEnforcedAndReportOnlyPoliciesPair ContentSecurityPolicy::findHashOfContentInPolicies(Predicate&& predicate, const String& content, OptionSet<ContentSecurityPolicyHashAlgorithm> algorithms) const
 {
@@ -343,9 +329,7 @@ ContentSecurityPolicy::HashInEnforcedAndReportOnlyPoliciesPair ContentSecurityPo
     bool foundHashInEnforcedPolicies = false;
     bool foundHashInReportOnlyPolicies = false;
     for (auto algorithm : algorithms) {
-        auto cryptoDigest = PAL::CryptoDigest::create(toCryptoDigestAlgorithm(algorithm));
-        cryptoDigest->addBytes(contentCString.data(), contentCString.length());
-        ContentSecurityPolicyHash hash = { algorithm, cryptoDigest->computeHash() };
+        ContentSecurityPolicyHash hash = cryptographicDigestForBytes(algorithm, contentCString.data(), contentCString.length());
         if (!foundHashInEnforcedPolicies && allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, std::forward<Predicate>(predicate), hash))
             foundHashInEnforcedPolicies = true;
         if (!foundHashInReportOnlyPolicies && allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, std::forward<Predicate>(predicate), hash))
index 5286a86..68974a2 100644 (file)
@@ -26,6 +26,7 @@
 
 #pragma once
 
+#include "ContentSecurityPolicyHash.h"
 #include "ContentSecurityPolicyResponseHeaders.h"
 #include "SecurityOrigin.h"
 #include "SecurityOriginHash.h"
@@ -56,8 +57,6 @@ class SecurityOrigin;
 class TextEncoding;
 class URL;
 
-enum class ContentSecurityPolicyHashAlgorithm;
-
 typedef Vector<std::unique_ptr<ContentSecurityPolicyDirectiveList>> CSPDirectiveListVector;
 typedef int SandboxFlags;
 
index 83c1ec2..7c64372 100644 (file)
 
 #pragma once
 
-#include <wtf/HashTraits.h>
-#include <wtf/Hasher.h>
-#include <wtf/Vector.h>
+#include "ResourceCryptographicDigest.h"
 
 namespace WebCore {
 
-// Keep this synchronized with the constant maximumContentSecurityPolicyDigestLength below.
-enum class ContentSecurityPolicyHashAlgorithm {
-    SHA_256 = 1 << 0,
-    SHA_384 = 1 << 1,
-    SHA_512 = 1 << 2,
-};
-
-const size_t maximumContentSecurityPolicyDigestLength = 64; // bytes to hold SHA-512 digest
-
-typedef Vector<uint8_t> ContentSecurityPolicyDigest;
-typedef std::pair<ContentSecurityPolicyHashAlgorithm, ContentSecurityPolicyDigest> ContentSecurityPolicyHash;
+using ContentSecurityPolicyHashAlgorithm = ResourceCryptographicDigest::Algorithm;
+using ContentSecurityPolicyHash = ResourceCryptographicDigest;
 
 } // namespace WebCore
-
-namespace WTF {
-
-template<> struct DefaultHash<WebCore::ContentSecurityPolicyHashAlgorithm> { typedef IntHash<WebCore::ContentSecurityPolicyHashAlgorithm> Hash; };
-template<> struct HashTraits<WebCore::ContentSecurityPolicyHashAlgorithm> : StrongEnumHashTraits<WebCore::ContentSecurityPolicyHashAlgorithm> { };
-template<> struct DefaultHash<WebCore::ContentSecurityPolicyDigest> {
-    struct Hash {
-        static unsigned hash(const WebCore::ContentSecurityPolicyDigest& digest)
-        {
-            return StringHasher::computeHashAndMaskTop8Bits(digest.data(), digest.size());
-        }
-        static bool equal(const WebCore::ContentSecurityPolicyDigest& a, const WebCore::ContentSecurityPolicyDigest& b)
-        {
-            return a == b;
-        }
-        static const bool safeToCompareToEmptyOrDeleted = true;
-    };
-};
-
-} // namespace WTF
index b9e8c11..920ad12 100644 (file)
@@ -425,17 +425,12 @@ bool ContentSecurityPolicySourceList::parsePort(const UChar* begin, const UChar*
     return ok;
 }
 
-static bool isBase64Character(UChar c)
-{
-    return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '-' || c == '_';
-}
-
 // Match Blink's behavior of allowing an equal sign to appear anywhere in the value of the nonce
 // even though this does not match the behavior of Content Security Policy Level 3 spec.,
 // <https://w3c.github.io/webappsec-csp/> (29 February 2016).
 static bool isNonceCharacter(UChar c)
 {
-    return isBase64Character(c) || c == '=';
+    return isBase64OrBase64URLCharacter(c) || c == '=';
 }
 
 // nonce-source    = "'nonce-" nonce-value "'"
@@ -454,29 +449,6 @@ bool ContentSecurityPolicySourceList::parseNonceSource(const UChar* begin, const
     return true;
 }
 
-static bool parseHashAlgorithmAdvancingPosition(const UChar*& position, size_t length, ContentSecurityPolicyHashAlgorithm& algorithm)
-{
-    static struct {
-        NeverDestroyed<String> label;
-        ContentSecurityPolicyHashAlgorithm algorithm;
-    } labelToHashAlgorithmTable[] {
-        { ASCIILiteral("sha256"), ContentSecurityPolicyHashAlgorithm::SHA_256 },
-        { ASCIILiteral("sha384"), ContentSecurityPolicyHashAlgorithm::SHA_384 },
-        { ASCIILiteral("sha512"), ContentSecurityPolicyHashAlgorithm::SHA_512 },
-    };
-
-    StringView stringView(position, length);
-    for (auto& entry : labelToHashAlgorithmTable) {
-        String& label = entry.label.get();
-        if (!stringView.startsWithIgnoringASCIICase(label))
-            continue;
-        position += label.length();
-        algorithm = entry.algorithm;
-        return true;
-    }
-    return false;
-}
-
 // hash-source    = "'" hash-algorithm "-" base64-value "'"
 // hash-algorithm = "sha256" / "sha384" / "sha512"
 // base64-value  = 1*( ALPHA / DIGIT / "+" / "/" / "-" / "_" )*2( "=" )
@@ -489,31 +461,18 @@ bool ContentSecurityPolicySourceList::parseHashSource(const UChar* begin, const
     if (!skipExactly<UChar>(position, end, '\''))
         return false;
 
-    ContentSecurityPolicyHashAlgorithm algorithm;
-    if (!parseHashAlgorithmAdvancingPosition(position, end - position, algorithm))
+    auto digest = parseCryptographicDigest(position, end);
+    if (!digest)
         return false;
 
-    if (!skipExactly<UChar>(position, end, '-'))
+    if (position >= end || *position != '\'')
         return false;
 
-    const UChar* beginHashValue = position;
-    skipWhile<UChar, isBase64Character>(position, end);
-    skipExactly<UChar>(position, end, '=');
-    skipExactly<UChar>(position, end, '=');
-    if (position >= end || position == beginHashValue || *position != '\'')
-        return false;
-    Vector<uint8_t> digest;
-    StringView hashValue(beginHashValue, position - beginHashValue); // base64url or base64 encoded
-    // FIXME: Normalize Base64URL to Base64 instead of decoding twice. See <https://bugs.webkit.org/show_bug.cgi?id=155186>.
-    if (!base64Decode(hashValue.toStringWithoutCopying(), digest, Base64ValidatePadding)) {
-        if (!base64URLDecode(hashValue.toStringWithoutCopying(), digest))
-            return false;
-    }
-    if (digest.size() > maximumContentSecurityPolicyDigestLength)
+    if (digest->value.size() > ContentSecurityPolicyHash::maximumDigestLength)
         return false;
 
-    m_hashes.add(std::make_pair(algorithm, digest));
-    m_hashAlgorithmsUsed |= algorithm;
+    m_hashAlgorithmsUsed |= digest->algorithm;
+    m_hashes.add(WTFMove(*digest));
     return true;
 }