static bool DigestsEqual(const DigestValue& digest1, const DigestValue& digest2) { if (digest1.size() != digest2.size()) return false; for (size_t i = 0; i < digest1.size(); i++) { if (digest1[i] != digest2[i]) return false; } return true; }
String WebSocketHandshake::getExpectedWebSocketAccept(const String& secWebSocketKey) { static const char webSocketKeyGUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; CString keyData = secWebSocketKey.ascii(); StringBuilder digestable; digestable.append(secWebSocketKey); digestable.append(webSocketKeyGUID, strlen(webSocketKeyGUID)); CString digestableCString = digestable.toString().utf8(); DigestValue digest; bool digestSuccess = computeDigest(HashAlgorithmSha1, digestableCString.data(), digestableCString.length(), digest); RELEASE_ASSERT(digestSuccess); return base64Encode(reinterpret_cast<const char*>(digest.data()), sha1HashSize); }
// hash-source = "'" hash-algorithm "-" hash-value "'" // hash-algorithm = "sha1" / "sha256" / "sha384" / "sha512" // hash-value = 1*( ALPHA / DIGIT / "+" / "/" / "=" ) // bool CSPSourceList::parseHash(const UChar* begin, const UChar* end, DigestValue& hash, ContentSecurityPolicyHashAlgorithm& hashAlgorithm) { // Any additions or subtractions from this struct should also modify the // respective entries in the kAlgorithmMap array in checkDigest(). static const struct { const char* prefix; ContentSecurityPolicyHashAlgorithm type; } kSupportedPrefixes[] = { // FIXME: Drop support for SHA-1. It's not in the spec. { "'sha1-", ContentSecurityPolicyHashAlgorithmSha1 }, { "'sha256-", ContentSecurityPolicyHashAlgorithmSha256 }, { "'sha384-", ContentSecurityPolicyHashAlgorithmSha384 }, { "'sha512-", ContentSecurityPolicyHashAlgorithmSha512 }, { "'sha-256-", ContentSecurityPolicyHashAlgorithmSha256 }, { "'sha-384-", ContentSecurityPolicyHashAlgorithmSha384 }, { "'sha-512-", ContentSecurityPolicyHashAlgorithmSha512 } }; String prefix; hashAlgorithm = ContentSecurityPolicyHashAlgorithmNone; size_t hashLength = end - begin; for (const auto& algorithm : kSupportedPrefixes) { if (hashLength > strlen(algorithm.prefix) && equalIgnoringCase(algorithm.prefix, begin, strlen(algorithm.prefix))) { prefix = algorithm.prefix; hashAlgorithm = algorithm.type; break; } } if (hashAlgorithm == ContentSecurityPolicyHashAlgorithmNone) return true; const UChar* position = begin + prefix.length(); const UChar* hashBegin = position; ASSERT(position < end); skipWhile<UChar, isBase64EncodedCharacter>(position, end); ASSERT(hashBegin <= position); // Base64 encodings may end with exactly one or two '=' characters if (position < end) skipExactly<UChar>(position, position + 1, '='); if (position < end) skipExactly<UChar>(position, position + 1, '='); if (position + 1 != end || *position != '\'' || position == hashBegin) return false; Vector<char> hashVector; // We accept base64url-encoded data here by normalizing it to base64. base64Decode(normalizeToBase64(String(hashBegin, position - hashBegin)), hashVector); if (hashVector.size() > kMaxDigestSize) return false; hash.append(reinterpret_cast<uint8_t*>(hashVector.data()), hashVector.size()); return true; }
bool SubresourceIntegrity::CheckSubresourceIntegrity(const IntegrityMetadataSet& metadataSet, const char* content, size_t size, const KURL& resourceUrl, Document& document, String& errorMessage) { if (!metadataSet.size()) return true; HashAlgorithm strongestAlgorithm = HashAlgorithmSha256; for (const IntegrityMetadata& metadata : metadataSet) strongestAlgorithm = getPrioritizedHashFunction(metadata.algorithm(), strongestAlgorithm); DigestValue digest; for (const IntegrityMetadata& metadata : metadataSet) { if (metadata.algorithm() != strongestAlgorithm) continue; digest.clear(); bool digestSuccess = computeDigest(metadata.algorithm(), content, size, digest); if (digestSuccess) { Vector<char> hashVector; base64Decode(metadata.digest(), hashVector); DigestValue convertedHashVector; convertedHashVector.append(reinterpret_cast<uint8_t*>(hashVector.data()), hashVector.size()); if (DigestsEqual(digest, convertedHashVector)) { UseCounter::count(document, UseCounter::SRIElementWithMatchingIntegrityAttribute); return true; } } } digest.clear(); if (computeDigest(HashAlgorithmSha256, content, size, digest)) { // This message exposes the digest of the resource to the console. // Because this is only to the console, that's okay for now, but we // need to be very careful not to expose this in exceptions or // JavaScript, otherwise it risks exposing information about the // resource cross-origin. errorMessage = "Failed to find a valid digest in the 'integrity' attribute for resource '" + resourceUrl.elidedString() + "' with computed SHA-256 integrity '" + digestToString(digest) + "'. The resource has been blocked."; } else { errorMessage = "There was an error computing an integrity value for resource '" + resourceUrl.elidedString() + "'. The resource has been blocked."; } UseCounter::count(document, UseCounter::SRIElementWithNonMatchingIntegrityAttribute); return false; }
PassOwnPtr<DOMPatchSupport::Digest> DOMPatchSupport::createDigest(Node* node, UnusedNodesMap* unusedNodesMap) { Digest* digest = new Digest(node); OwnPtr<blink::WebCryptoDigestor> digestor = createDigestor(HashAlgorithmSha1); DigestValue digestResult; Node::NodeType nodeType = node->nodeType(); digestor->consume(reinterpret_cast<const unsigned char*>(&nodeType), sizeof(nodeType)); addStringToDigestor(digestor.get(), node->nodeName()); addStringToDigestor(digestor.get(), node->nodeValue()); if (node->isElementNode()) { Element& element = toElement(*node); Node* child = element.firstChild(); while (child) { OwnPtr<Digest> childInfo = createDigest(child, unusedNodesMap); addStringToDigestor(digestor.get(), childInfo->m_sha1); child = child->nextSibling(); digest->m_children.append(childInfo.release()); } if (element.hasAttributesWithoutUpdate()) { OwnPtr<blink::WebCryptoDigestor> attrsDigestor = createDigestor(HashAlgorithmSha1); AttributeCollection attributes = element.attributes(); AttributeCollection::const_iterator end = attributes.end(); for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) { addStringToDigestor(attrsDigestor.get(), it->name().toString()); addStringToDigestor(attrsDigestor.get(), it->value().string()); } finishDigestor(attrsDigestor.get(), digestResult); digest->m_attrsSHA1 = base64Encode(reinterpret_cast<const char*>(digestResult.data()), 10); addStringToDigestor(digestor.get(), digest->m_attrsSHA1); digestResult.clear(); } } finishDigestor(digestor.get(), digestResult); digest->m_sha1 = base64Encode(reinterpret_cast<const char*>(digestResult.data()), 10); if (unusedNodesMap) unusedNodesMap->add(digest->m_sha1, digest); return adoptPtr(digest); }
// hash-source = "'" hash-algorithm "-" hash-value "'" // hash-algorithm = "sha1" / "sha256" / "sha384" / "sha512" // hash-value = 1*( ALPHA / DIGIT / "+" / "/" / "=" ) // bool CSPSourceList::parseHash(const UChar* begin, const UChar* end, DigestValue& hash, ContentSecurityPolicyHashAlgorithm& hashAlgorithm) { // Any additions or subtractions from this struct should also modify the // respective entries in the kAlgorithmMap array in checkDigest(). static const struct { const char* prefix; ContentSecurityPolicyHashAlgorithm algorithm; } kSupportedPrefixes[] = { { "'sha1-", ContentSecurityPolicyHashAlgorithmSha1 }, { "'sha256-", ContentSecurityPolicyHashAlgorithmSha256 }, { "'sha384-", ContentSecurityPolicyHashAlgorithmSha384 }, { "'sha512-", ContentSecurityPolicyHashAlgorithmSha512 } }; String prefix; hashAlgorithm = ContentSecurityPolicyHashAlgorithmNone; // Instead of this sizeof() calculation to get the length of this array, // it would be preferable to use WTF_ARRAY_LENGTH for simplicity and to // guarantee a compile time calculation. Unfortunately, on some // compliers, the call to WTF_ARRAY_LENGTH fails on arrays of anonymous // stucts, so, for now, it is necessary to resort to this sizeof // calculation. for (size_t i = 0; i < (sizeof(kSupportedPrefixes) / sizeof(kSupportedPrefixes[0])); i++) { if (equalIgnoringCase(kSupportedPrefixes[i].prefix, begin, strlen(kSupportedPrefixes[i].prefix))) { prefix = kSupportedPrefixes[i].prefix; hashAlgorithm = kSupportedPrefixes[i].algorithm; break; } } if (hashAlgorithm == ContentSecurityPolicyHashAlgorithmNone) return true; const UChar* position = begin + prefix.length(); const UChar* hashBegin = position; skipWhile<UChar, isBase64EncodedCharacter>(position, end); ASSERT(hashBegin <= position); // Base64 encodings may end with exactly one or two '=' characters skipExactly<UChar>(position, position + 1, '='); skipExactly<UChar>(position, position + 1, '='); if ((position + 1) != end || *position != '\'' || !(position - hashBegin)) return false; Vector<char> hashVector; base64Decode(hashBegin, position - hashBegin, hashVector); if (hashVector.size() > kMaxDigestSize) return false; hash.append(reinterpret_cast<uint8_t*>(hashVector.data()), hashVector.size()); return true; }
void finishDigestor(WebCryptoDigestor* digestor, DigestValue& digestResult) { unsigned char* result = 0; unsigned resultSize = 0; if (!digestor->finish(result, resultSize)) return; ASSERT(result); digestResult.append(static_cast<uint8_t*>(result), resultSize); }
String InspectorAnimationAgent::createCSSId(blink::Animation& animation) { String type = m_idToAnimationType.get(String::number(animation.sequenceNumber())); ASSERT(type != AnimationType::WebAnimation); KeyframeEffect* effect = toKeyframeEffect(animation.effect()); Vector<CSSPropertyID> cssProperties; if (type == AnimationType::CSSAnimation) { for (CSSPropertyID property : animationProperties) cssProperties.append(property); } else { for (CSSPropertyID property : transitionProperties) cssProperties.append(property); cssProperties.append(cssPropertyID(animation.id())); } Element* element = effect->target(); HeapVector<Member<CSSStyleDeclaration>> styles = m_cssAgent->matchingStyles(element); std::unique_ptr<WebCryptoDigestor> digestor = createDigestor(HashAlgorithmSha1); addStringToDigestor(digestor.get(), type); addStringToDigestor(digestor.get(), animation.id()); for (CSSPropertyID property : cssProperties) { CSSStyleDeclaration* style = m_cssAgent->findEffectiveDeclaration(property, styles); // Ignore inline styles. if (!style || !style->parentStyleSheet() || !style->parentRule() || style->parentRule()->type() != CSSRule::kStyleRule) continue; addStringToDigestor(digestor.get(), getPropertyNameString(property)); addStringToDigestor(digestor.get(), m_cssAgent->styleSheetId(style->parentStyleSheet())); addStringToDigestor(digestor.get(), toCSSStyleRule(style->parentRule())->selectorText()); } DigestValue digestResult; finishDigestor(digestor.get(), digestResult); return base64Encode(reinterpret_cast<const char*>(digestResult.data()), 10); }
bool computeDigest(HashAlgorithm algorithm, const char* digestable, size_t length, DigestValue& digestResult) { WebCryptoAlgorithmId algorithmId = toWebCryptoAlgorithmId(algorithm); WebCrypto* crypto = Platform::current()->crypto(); unsigned char* result; unsigned resultSize; ASSERT(crypto); OwnPtr<WebCryptoDigestor> digestor = adoptPtr(crypto->createDigestor(algorithmId)); if (!digestor.get() || !digestor->consume(reinterpret_cast<const unsigned char*>(digestable), length) || !digestor->finish(result, resultSize)) return false; digestResult.append(static_cast<uint8_t*>(result), resultSize); return true; }
static String digestToString(const DigestValue& digest) { return base64Encode(reinterpret_cast<const char*>(digest.data()), digest.size(), Base64DoNotInsertLFs); }
// source = scheme ":" // / ( [ scheme "://" ] host [ port ] [ path ] ) // / "'self'" bool CSPSourceList::parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, int& port, String& path, CSPSource::WildcardDisposition& hostWildcard, CSPSource::WildcardDisposition& portWildcard) { if (begin == end) return false; if (equalIgnoringCase("'none'", begin, end - begin)) return false; if (end - begin == 1 && *begin == '*') { addSourceStar(); return true; } if (equalIgnoringCase("'self'", begin, end - begin)) { addSourceSelf(); return true; } if (equalIgnoringCase("'unsafe-inline'", begin, end - begin)) { addSourceUnsafeInline(); return true; } if (equalIgnoringCase("'unsafe-eval'", begin, end - begin)) { addSourceUnsafeEval(); return true; } String nonce; if (!parseNonce(begin, end, nonce)) return false; if (!nonce.isNull()) { addSourceNonce(nonce); return true; } DigestValue hash; ContentSecurityPolicyHashAlgorithm algorithm = ContentSecurityPolicyHashAlgorithmNone; if (!parseHash(begin, end, hash, algorithm)) return false; if (hash.size() > 0) { addSourceHash(algorithm, hash); return true; } const UChar* position = begin; const UChar* beginHost = begin; const UChar* beginPath = end; const UChar* beginPort = 0; skipWhile<UChar, isNotColonOrSlash>(position, end); if (position == end) { // host // ^ return parseHost(beginHost, position, host, hostWildcard); } if (position < end && *position == '/') { // host/path || host/ || / // ^ ^ ^ return parseHost(beginHost, position, host, hostWildcard) && parsePath(position, end, path); } if (position < end && *position == ':') { if (end - position == 1) { // scheme: // ^ return parseScheme(begin, position, scheme); } if (position[1] == '/') { // scheme://host || scheme:// // ^ ^ if (!parseScheme(begin, position, scheme) || !skipExactly<UChar>(position, end, ':') || !skipExactly<UChar>(position, end, '/') || !skipExactly<UChar>(position, end, '/')) return false; if (position == end) return false; beginHost = position; skipWhile<UChar, isNotColonOrSlash>(position, end); } if (position < end && *position == ':') { // host:port || scheme://host:port // ^ ^ beginPort = position; skipUntil<UChar>(position, end, '/'); } } if (position < end && *position == '/') { // scheme://host/path || scheme://host:port/path // ^ ^ if (position == beginHost) return false; beginPath = position; } if (!parseHost(beginHost, beginPort ? beginPort : beginPath, host, hostWildcard)) return false; if (beginPort) { if (!parsePort(beginPort, beginPath, port, portWildcard)) return false; } else { port = 0; } if (beginPath != end) { if (!parsePath(beginPath, end, path)) return false; } return true; }