Skip to content

Commit

Permalink
[SRI Message Signatures] Enforce signature matching.
Browse files Browse the repository at this point in the history
This patch teaches the network service's `URLLoader` how to evaluate the
SRI-valid subset of HTTP Message Signatures, blocking mismatched
responses once headers are received and processed.

This check is implemented behind a new feature flag, which is disabled
by default. End-to-end tests live in web platform tests under
//web_tests/virtual/sri-message-signatures that enables the flag.

This is part of a chain of CLs implementing this feature (#2 from
https://wicg.github.io/signature-based-sri/#overview):

1.  [Parsing] https://crrev.com/c/6020612
2.  [Validation 1] https://crrev.com/c/6030571
3.  [Validation 2] https://crrev.com/c/6032589
4.  [Enforcement] https://crrev.com/c/6038714 [You are here]

    `url_loader.cc` are the only meaningful changes in behavior
    reported as undercovered. These are tested through the WPT
    included in this CL.

Bug: 379534943
Low-Coverage-Reason: COVERAGE_UNDERREPORTED The changes to
Change-Id: I6ece80da25ed4329a6f976c2c74c639c2799b856
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6038714
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
Reviewed-by: Camille Lamy <clamy@chromium.org>
Reviewed-by: Kent Tamura <tkent@chromium.org>
Commit-Queue: Mike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1389294}
  • Loading branch information
mikewest authored and chromium-wpt-export-bot committed Nov 28, 2024
1 parent 217a094 commit 5d7e670
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 0 deletions.
51 changes: 51 additions & 0 deletions subresource-integrity/signatures/tentative/fetch.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// META: global=window,dedicatedworker,sharedworker

// Given `{ digest: "...", body: "...", cors: true, type: "..." }`:
function resourceURL(data) {
let params = new URLSearchParams(data);
return "./resource.py?" + params.toString();
}

// HTTP/1.1 200 OK
// Date: Tue, 20 Apr 2021 02:07:56 GMT
// Content-Type: application/json
// Identity-Digest: sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
// Content-Length: 18
// Signature-Input:
// signature=("identity-digest";sf);alg="ed25519";keyid="JrQLj5P/89iXES9+vFgrI \
// y29clF9CC/oPPsw3c5D0bs=";tag="sri"
// Signature: signature=:H7AqWWgo1DJ7VdyF9DKotG/4hvatKDfRTq2mpuY/hvJupSn+EYzus \
// 5p24qPK7DtVQcxJFhzSYDj4RBq9grZTAQ==:
//
// {"hello": "world"}
//
promise_test(test => {
const data = {
body: `{"hello": "world"}`
};
return fetch(resourceURL(data)).then(r => {
assert_equals(r.status, 200, "Response status is 200.");
});
}, "No signature: loads.");

promise_test(test => {
const data = {
body: `{"hello": "world"}`,
digest: `sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:`,
signature: `signature=:H7AqWWgo1DJ7VdyF9DKotG/4hvatKDfRTq2mpuY/hvJupSn+EYzus5p24qPK7DtVQcxJFhzSYDj4RBq9grZTAQ==:`,
signatureInput: `signature=("identity-digest";sf);alg="ed25519";keyid="JrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs=";tag="sri"`
};
return fetch(resourceURL(data)).then(r => {
assert_equals(r.status, 200, "Response status is 200.");
});
}, "Valid signature: loads.");

promise_test(test => {
const data = {
body: `{"hello": "world"}`,
digest: `sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:`,
signature: `signature=:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==:`,
signatureInput: `signature=("identity-digest";sf);alg="ed25519";keyid="JrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs=";tag="sri"`
};
return promise_rejects_js(test, TypeError, fetch(resourceURL(data)));
}, "Non-matching signature: blocked.");
38 changes: 38 additions & 0 deletions subresource-integrity/signatures/tentative/resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'''
SRI Message Signature helper, generating responses that:
* Include or exclude an `Integrity-Digest` header depending on the request's
`digest` parameter.
* Include or exclude an `Signature` header depending on the request's
`signature` parameter.
* Include or exclude an `Signature-Input` header depending on the request's
`signatureInput` parameter.
* Include or exclude `Access-Control-Allow-Origin: *` depending on the
request's `cors` parameter.
* Sets a `Content-Type` header from the request's `type` parameter.
* Echos the `body` parameter into the response body.
'''
def main(request, response):
digest = request.GET.first(b'digest', b'')
signature = request.GET.first(b'signature', b'')
signatureInput = request.GET.first(b'signatureInput', b'')
if digest:
response.headers.set(b'identity-digest', digest)
if signature:
response.headers.set(b'signature', signature)
if signatureInput:
response.headers.set(b'signature-input', signatureInput)


cors = request.GET.first(b'cors', '')
if cors:
response.headers.set(b'access-control-allow-origin', b'*')

response.headers.set(b'content-type',
request.GET.first(b'type', b'text/plain'))

response.status_code = 200
response.content = request.GET.first(b'body', '')

0 comments on commit 5d7e670

Please sign in to comment.