I would be surprised if this were ever used by anyone, but we should
either make it work, or add code to turn the feature off when
configured. All the things that prevented it from working we just bugs,
so it was easier to just fix this and enable all the tests.
The main nuisance is how our in-memory representation reflects the old
DTLS 1.2 header, conflicting ECH's many uses of the transcript. (We
really should switch the in-memory representation.) Then some bits of
ClientHello processing needed to not lose track of the cookie.
(This test already covers HelloVerifyRequest interactions because we
run through HelloVerifyRequest in DTLS 1.2.)
Bug: 42290594
Change-Id: Ic09b78ddc0c5524ffc6c5b965eafab881eff0a0f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/73729
Reviewed-by: Nick Harper <nharper@chromium.org>
Commit-Queue: David Benjamin <davidben@google.com>
This change has DTLS 1.3 clients reject connections from servers that
negotiate 1.3 after sending HVR. It also enables HRR tests in DTLS 1.3.
Bug: 42290594
Change-Id: I7bd5ab0e968e6b4c301a5697b1166703c28d9ebc
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/71348
Commit-Queue: Nick Harper <nharper@chromium.org>
Commit-Queue: David Benjamin <davidben@google.com>
Auto-Submit: Nick Harper <nharper@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
This is in preparation for adding code to parse the tls_flags extension.
Change-Id: Ia82e7cd6a5d76663cfc817eb16271dd51bbfa26d
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/70487
Commit-Queue: Bob Beck <bbe@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
https://boringssl-review.googlesource.com/c/34884 tried to update to the
newer DC draft, but didn't quite do so. In that update, DCs
overcomplicated the signature algorithm negotiation so that there are
two different signature algorithm lists, used in different contexts.
The existing signature_algorithms extension is used to verify the
signature *on* the DC, made by the end-entity certificate. On the server
side, we should be using that to decide whether to use the DC, and we
weren't.
The new delegated_credentials extension contains another sigalg list.
That is used to verify the signature *by* the DC, in the
CertificateVerify message. (This means DC changes the operative sigalg
list for the CertificateVerify message, which is quite a mess.) On the
server side, the above CL mixed things up. When deciding whether to use
DCs, it checked the correct list. When actually using DCs, it checked
the wrong one. As a result, any time the DC list wasn't a subset of the
main list, the connection would just break!
Fix both of these, in preparation for redoing DCs over the upcoming
SSL_CREDENTIAL mechanism.
Thankfully we don't support one direction of DC usage (authenticating in
C++ and verifying in Go), so there are fewer places to worry about
mixing this up. Given this overcomplication, I'm now much, much less
inclined to ever support DCs as a client, without an rfc9345bis to redo
this.
Bug: 249
Change-Id: Id5257e89a6c8daf1635757be473c45029492d420
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66550
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
Although the comments say draft-03, we're currently on draft-06.
dcd6e447eb forgot to update all the
comments.
The final RFC is identical to draft-06, except
expected_cert_verify_algorithm was renamed to dc_cert_verify_algorithm,
so this is just changing comment and renaming something.
While I'm here, write the codepoint in decimal instead of hex, to match
the document and how the other IANA codepoints are written out.
Change-Id: I6d1f362a21eecafeef5bba5879f4158e31c8def4
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66367
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Old version Chrome with the existing ALPS codepoint can potentially cause network error due to an arithmetic overflow bug in Chrome ALPS decoder (We already fixed the issues starting from M100 in Chrome).
This CL add a new codepoint for ALPS extension in a way that can be enabled on individual connections., To support multiple versions of Chrome, we need to support both codepoints in BoringSSL.
For details: https://docs.google.com/document/d/16pysbV_ym_qAau_DBYnrw2A4h5ve2212wfcoYASt52U
Change-Id: Iea7822e757d23009648febc8eaff1c91b0f06e18
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/61125
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
While I'm here, update x/crypto and x/net to their latest versions.
byteReader is a straightforward port, except there doesn't seem to be a
convenient way to read length-prefixed bytes without manually casting
from cryptobyte.String to []byte, so I've done that.
byteBuilder is a bit more involved because it's based on closures, but
still a mechanical change.
As part of this, I switched runner's ticket format to use u24 length
prefixes instead of u32, because cryptobyte.String doesn't have u32
length prefixes. (Although, oddly, cryptobyte.Builder does.)
Fixed: 374
Change-Id: If9bea0b41fe2b8bc48f040a667753b160da469bb
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/61186
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
It should not be possible to make BoringSSL request unknown signature
algorithms, or the special SSL_SIGN_RSA_PKCS1_MD5_SHA1 value, in the
ClientHello or CertificateRequest.
Update-Note: This CL makes unknown values fail
SSL_set_verify_algorithm_prefs, etc. SSL_SIGN_RSA_PKCS1_MD5_SHA1 is
silently dropped from the list, rather than an error because, although
documented as incorrect, this hole in the abstraction seems to be
confusing. I think there's some code in Chromium which accidentally puts
it in the signing prefs (wrong but harmless) and I often need to explain
to folks that it doesn't belowing in verify prefs (puts it in the
ClientHello). This makes us tolerate the value by ignoring it.
This makes the previous pkey_supports_algorithm change moot because we'd
never get that far with SSL_SIGN_RSA_PKCS1_MD5_SHA1, but I think the
check, but I think the check belongs in that function too.
The test also reveals that some of our tests have been accidentally
passing zero into the preference list all this time.
Change-Id: I76d4eb98682515c3b819e0ed8d44f2d708a98975
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/55446
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
The cookie is only needed in SSL_HANDSHAKE, so there's no need to retain
it for the lifetime of the connection. (SSL_HANDSHAKE is released after
the handshake completes.)
Back when DTLS1_COOKIE_LENGTH was 32, storing it inline made some sense.
Now that RFC 6347 increased the maximum to 255 bytes, just indirect it
with an Array<uint8_t>. Along the way, remove the DTLS1_COOKIE_LENGTH
checks. The new limit is the largest that fits in the length prefix, so
it's always redundant. In fact, the constant was one higher was allowed
anyway. Add some tests for the maximum length, as well as zero-length
cookies.
I considered just repurposing the plain cookie field, used in
HelloRetryRequest (as opposed to HelloVerifyRequest), as they're
mutually exclusive, even in DTLS 1.3. But, when we get to DTLS 1.3,
that'll get a little hairy because ssl_write_client_hello will need
extra checks to know whether hs->cookie is meant to go in the
ClientHello directly or in extensions.
Change-Id: I1afedc7ce31414879545701bf8fe4658657ba66f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/54466
Reviewed-by: Bob Beck <bbe@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
Later CLs will clean up the ClientHello construction a bit (draft-12
avoids computing ClientHelloOuter twice). I suspect the transcript
handling on the client can also be simpler, but I'll see what's
convenient after I've changed how ClientHelloOuter is constructed.
Changes of note between draft-10 and draft-13:
- There is now an ECH confirmation signal in both HRR and SH. We don't
actually make much use of this in our client right now, but it
resolves a bunch of weird issues around HRR, including edge cases if
HRR applies to one ClientHello but not the other.
- The confirmation signal no longer depends on key_share and PSK, so we
don't have to work around a weird ordering issue.
- ech_is_inner is now folded into the main encrypted_client_hello code
point. This works better with some stuff around HRR.
- Padding is moved from the padding extension, computed with
ClientHelloInner, to something we fill in afterwards. This makes it
easier to pad up the whole thing to a multiple of 32. I've accordingly
updated to the latest recommended padding construction, and updated
the GREASE logic to match.
- ech_outer_extensions is much easier to process because the order is
required to be consistent. We were doing that anyway, and now a simple
linear scan works.
- ClientHelloOuterAAD now uses an all zero placeholder payload of the
same length. This lets us simplify the server code, but, for now, I've
kept the client code the same. I'll follow this up with a CL to avoid
computing ClientHelloOuter twice.
- ClientHelloOuterAAD is allowed to contain a placeholder PSK. I haven't
filled that in and will do it in a follow-up CL.
Bug: 275
Change-Id: I7464345125c53968b2fe692f9268e392120fc2eb
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48912
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Based on an initial implementation by Dan McArdle at
https://boringssl-review.googlesource.com/c/boringssl/+/46784
This CL contains most of a client implementation for
draft-ietf-tls-esni-10. The pieces missing so far, which will be done in
follow-up CLs are:
1. While the ClientHelloInner is padded, the server Certificate message
is not. I'll add that once we resolve the spec discussions on how to
do that. (We were originally going to use TLS record-level padding,
but that doesn't work well with QUIC.)
2. The client should check the public name is a valid DNS name before
copying it into ClientHelloOuter.server_name.
3. The ClientHelloOuter handshake flow is not yet implemented. This CL
can detect when the server selects ClientHelloOuter, but for now the
handshake immediately fails. A follow-up CL will remove that logic
and instead add the APIs and extra checks needed.
Otherwise, this should be complete, including padding and compression.
The main interesting point design-wise is that we run through
ClientHello construction multiple times. We need to construct
ClientHelloInner and ClientHelloOuter. Then each of those has slight
variants: EncodedClientHelloInner is the compressed form, and
ClientHelloOuterAAD just has the ECH extension erased to avoid a
circular dependency.
I've computed ClientHelloInner and EncodedClientHelloInner concurrently
because the compression scheme requires shifting the extensions around
to be contiguous. However, I've computed ClientHelloOuterAAD and
ClientHelloOuter by running through the logic twice. This probably can
be done better, but the next draft revises the construction anyway, so
I'm thinking I'll rework it then. (In the next draft, we use a
placeholder payload of the same length, so we can construct the
ClientHello once and fill in the payload.)
Additionally, now that we have a client available in ssl_test, this adds
a threading test to confirm that SSL_CTX_set1_ech_keys is properly
synchronized. (Confirmed that, if I drop the lock in
SSL_CTX_set1_ech_keys, TSan notices.)
Change-Id: Icaff68b595035bdcc73c468ff638e67c84239ef4
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48004
Reviewed-by: Adam Langley <agl@google.com>
This implements draft-ietf-tls-esni-10.
This will be used to test the client implementation. While I'm here,
I've switched the setup logic in the server tests to use the new
ServerECHConfig type. I'll probably need to patch in various features
later for testing, but this should be a usable starting point.
Based on an initial implementation by Dan McArdle in
https://boringssl-review.googlesource.com/c/boringssl/+/46786
Bug: 275
Change-Id: I69523cda70c3da2ae505bcab837fd358195fb9e9
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47967
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Noticed this while I was in the area. We currently use an extremely lax
parse that even tolerates syntax errors. Instead use a strict parse that
ensures our client only sends what we expect.
Change-Id: Ifb0e1e1698489ff217db0c7a0317caa885e20759
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47966
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
Having the nil vs. non-nil []byte for the sake of a couple tests with
invalid payloads is tedious. Use separate fields instead.
Bug: 275
Change-Id: I557d914d60ce94d68796c05162ff3dd2ab7684db
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47965
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
An ECHConfig is like a certificate in that knowing the fields isn't
sufficient. The exact byte representation is significant. (The ECHConfig
is bound into the encryption.) But the ECHConfig type only has fields,
so runner can only represent ECHConfigs that are the output of our
serialization function.
This matters less as a client testing a server because the server can
only parse ECHConfigs with fields we support. But as a server testing a
client, we need to see how the client reacts to extra extensions, etc.
Just using []byte to represent ECHConfigs is inconvenient, so instead
pattern this after x509.Certificate: you can parse one from a byte
string (not currently included since we don't need it yet), or you can
construct a new one from a template with the fields you want.
Bug: 275
Change-Id: I6602d0780b1cef12b6c4b442999bdff7b3d7dd70
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47964
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
We misread (or maybe it changed?) the draft padding scheme. The current
text does not round the whole payload to a multiple of 32, just the
server name as a fallback. Switch the GREASE size selection to match.
Although, we may want to change the draft here. See also
https://github.com/tlswg/draft-ietf-tls-esni/issues/433
While I'm here, update some references from draft-09 to draft-10. Also
make the comment less verbose.
Bug: 275
Change-Id: I3c9f34159890bc3b7d71f6877f34b895bc7f9b17
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47644
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
We didn't end up deploying this. We also never implemented the final
RFC, so what we do have isn't useful for someone who wishes to deploy
it anyway.
Update-Note: Token binding APIs are removed.
Change-Id: Iecea7c3dcf9d3e2644a3b7afaf61511310b45d5f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47584
Reviewed-by: Adam Langley <agl@google.com>
Bug: 275
Change-Id: I8096070386af7d2b5020875ea09bcc0c04ebc8cd
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47245
Reviewed-by: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Steven Valdez <svaldez@google.com>
This CL adds an initial implementation of the ECH server, with pieces of
the client in BoGo as necessary for testing. In particular, the server
supports ClientHelloInner compression with ech_outer_extensions. When
ECH decryption fails, it can send retry_configs back to the client.
This server passes the "ech-accept" and "ech-reject" test cases in
tls-interop-runner[0] when tested against both the cloudflare-go and nss
clients. For reproducibility, I started with the main branch at commit
707604c262d8bcf3e944ed1d5a675077304732ce and updated the endpoint's
script to pass the server's ECHConfig and private key to the boringssl
tool.
Follow-up CLs will update HPKE to the latest draft and catch us up to
draft-10.
[0]: https://github.com/xvzcf/tls-interop-runner
Bug: 275
Change-Id: I49be35af46d1fd5dd9c62252f07d0bae179381ab
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/45285
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
The client handshake currently defers creating the finishedHash and
writing things into the transcript, which is a little annoying for ECH.
In preparation for simplifying that, one nuisance is that we retain both
hello and helloBytes, across a long span of code. helloBytes is *almost*
the same as hello.marshal() except:
- When we send a V2ClientHello, helloBytes records that we serialized
the ClientHello completely differently.
- For the JDK11 workaround tests, helloBytes records that we swapped out
the ClientHello entirely.
- By the time we finally write helloBytes into the transcript, hello may
have been updated to the second ClientHello.
This CL resolves the first two issues. It replaces the v2ClientHelloMsg
with an option when serializing the clientHelloMsg, and it has the
ClientHello replacement function return a clientHelloMsg instead of a
[]byte. (This is a little weird because we're conflating parsed and
constructed ClientHellos, but ah well.)
A follow-up CL will remove the differed transcript bits and we'll
actually be able to drop helloBytes.
Change-Id: Ib82ac216604e2c4bf421277e57aa5fd3b4cef161
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/46629
Reviewed-by: Adam Langley <agl@google.com>
Re-encoding a message does not necessarily give back the same value.
Bug: 275
Change-Id: I52cddd6152445b70579cbe03525898383bee211d
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/46644
Reviewed-by: David Benjamin <davidben@google.com>
Reviewed-by: Dan McArdle <dmcardle@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
We only need to implement enough of SSL 3.0 to test that the shim does
not.
Change-Id: I25cb48e407f1bc458bbdb3544b9df9fdfbc3d9c9
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/46625
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
In early TLS 1.3 drafts, HelloRetryRequest was a dedicated message type.
Our HelloRetryRequest handling in runner is still based on this. Along
the way, remove the SendServerHelloAsHelloRetryRequest test, since
that's just a generic unexpected message type now.
Change-Id: Idd9c54d0ab66d962657af9a53849c3928f78ce5c
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/46585
Reviewed-by: Adam Langley <agl@google.com>
A linter was complaining about some instance, so just fix the lot of it.
Change-Id: I7e23cbada6e42da887d740b84a05de9f104a86ab
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/45284
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
IETF QUIC draft 33 is replacing the TLS extension
codepoint for QUIC transport parameters from 0xffa5
to 57. To support multiple versions of Chrome, we
need to support both codepoints in BoringSSL. This
CL adds support for the new codepoint in a way that
can be enabled on individual connections.
Note that when BoringSSL is not in QUIC mode, it
will error if it sees the new codepoint as a server
but it will ignore the legacy codepoint as that could
be a different private usage of that codepoint.
Change-Id: I314f8f0b169cedd96eeccc42b44153e97044388c
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/44704
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
This CL implements the backend server behavior described in Section 7.2
of draft-ietf-tls-esni-09.
Bug: 275
Change-Id: I2e162673ce564db0cb75fc9b71ef11ed15037f4b
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43924
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
(Original CL by svaldez, reworked by davidben.)
Change-Id: I8570808fa5e96a1c9e6e03c4877039a22e73254f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/42404
Reviewed-by: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
The Go TLS implementation, at the time runner forked, had custom
testing-only equal methods on all the handshake messages. We've since
removed all of them except for ClientHello, where we repurposed the
function to check ClientHello consistency on HelloVerifyRequest and
HelloRetryRequest.
These are tedious to update. Upstream has since replaced them with
reflect.DeepEqual, but the comparison we want is even tighter. Even
unknown extensions aren't allowed to change. Replace the check with a
custom one that works on the byte serialization and remove
clientHelloMsg.equal.
Along the way, I've fixed the HRR PSK identity logic to match the spec a
bit more and check binders more consistently.
Change-Id: Ib39e8791201c42d37e304ae5110c7aeed62c8b3f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43364
Reviewed-by: Adam Langley <agl@google.com>
Reviewed-by: Steven Valdez <svaldez@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
This CL replaces clientHelloMsg's npnAfterAlpn and pskBinderFirst fields
with a new field: prefixExtensions. The extensions in prefixExtensions
are tried first when marshalling clientHelloMsg.
The ability to control extensions' marshalling order will make it
simpler to implement the "outer_extensions" behavior defined in
draft-ietf-tls-esni-07.
Bug: 275
Change-Id: Ib6dcc1e6fa0281f312cb65a9e204415c3f3ef2c6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43064
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
This version adds signature algorithms to the extension
Change-Id: I91dc78d33ee81cb7a6221c7bdeefc8ea460a2d6c
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/42424
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>