diff --git a/.github/workflows/build-and-test-make.yml b/.github/workflows/build-and-test-make.yml index 5ef87dfd..fd8510b7 100644 --- a/.github/workflows/build-and-test-make.yml +++ b/.github/workflows/build-and-test-make.yml @@ -178,7 +178,7 @@ jobs: cp ${{ runner.temp }}/zstd/lib/lib*.a ${{ runner.temp }}/install/lib cp nghttp2*/installed/lib/lib*.a ${{ runner.temp }}/install/lib cp brotli*/out/installed/lib/lib*.a ${{ runner.temp }}/install/lib - cp boringssl/build/lib/lib*.a ${{ runner.temp }}/install/lib + cp boringssl*/lib/lib*.a ${{ runner.temp }}/install/lib cd ${{ runner.temp }}/install/lib ls -lah . diff --git a/Makefile.in b/Makefile.in index 17d4f973..1ed44b3f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -11,8 +11,7 @@ SUBJOBS := 4 BROTLI_VERSION := 1.1.0 # In case this is changed, update build-and-test-make.yml as well - # In case this is changed, update build-and-test-make.yml as well -BORING_SSL_COMMIT := d24a38200fef19150eef00cad35b138936c08767 +BORING_SSL_COMMIT := cd95210465496ac2337b313cf49f607762abe286 NGHTTP2_VERSION := nghttp2-1.63.0 NGHTTP2_URL := https://github.com/nghttp2/nghttp2/releases/download/v1.63.0/nghttp2-1.63.0.tar.bz2 CURL_VERSION := curl-8_7_1 @@ -20,8 +19,9 @@ CURL_VERSION := curl-8_7_1 # https://github.com/google/brotli/commit/641bec0e30bea648b3da1cd90fc6b44deb429f71 brotli_install_dir := $(abspath brotli-$(BROTLI_VERSION)/out/installed) brotli_static_libs := $(brotli_install_dir)/lib/libbrotlicommon.a $(brotli_install_dir)/lib/libbrotlidec.a -boringssl_install_dir := $(abspath boringssl/build) -boringssl_static_libs := $(boringssl_install_dir)/lib/libssl.a $(boringssl_install_dir)/lib/libcrypto.a +boringssl_dir := $(abspath boringssl-$(BORING_SSL_COMMIT)) +boringssl_install_dir := $(boringssl_dir)/build +boringssl_static_libs := $(boringssl_dir)/lib/libssl.a $(boringssl_dir)/lib/libcrypto.a nghttp2_install_dir := $(abspath $(NGHTTP2_VERSION)/installed) nghttp2_static_libs := $(nghttp2_install_dir)/lib/libnghttp2.a @@ -110,7 +110,7 @@ chrome-clean: ## Clean build artifacts of the Chrome version. Use after re-runni clean: ## Remove all build artifacts, including dependencies rm -Rf brotli-$(BROTLI_VERSION).tar.gz brotli-$(BROTLI_VERSION) - rm -Rf boringssl.zip boringssl + rm -Rf boringssl-$(BORING_SSL_COMMIT).zip boringssl-$(BORING_SSL_COMMIT) rm -Rf $(NGHTTP2_VERSION).tar.bz2 $(NGHTTP2_VERSION) rm -Rf $(CURL_VERSION).tar.gz $(CURL_VERSION) @@ -151,19 +151,19 @@ $(brotli_static_libs): brotli-$(BROTLI_VERSION).tar.gz @cmake@ --build . --config Release --target install --parallel $(SUBJOBS) -boringssl.zip: +boringssl-$(BORING_SSL_COMMIT).zip: curl -L https://github.com/google/boringssl/archive/$(BORING_SSL_COMMIT).zip \ - -o boringssl.zip + -o boringssl-$(BORING_SSL_COMMIT).zip # Patch boringssl and use a dummy '.patched' file to mark it patched -boringssl/.patched: $(srcdir)/chrome/patches/boringssl.patch - unzip -q -o boringssl.zip - mv boringssl-$(BORING_SSL_COMMIT) boringssl - cd boringssl/ +boringssl-$(BORING_SSL_COMMIT)/.patched: $(srcdir)/chrome/patches/boringssl.patch + unzip -q -o boringssl-$(BORING_SSL_COMMIT).zip + # mv boringssl boringssl-$(BORING_SSL_COMMIT) + cd boringssl-$(BORING_SSL_COMMIT)/ for p in $^; do patch -p1 < $$p; done touch .patched -$(boringssl_static_libs): boringssl.zip boringssl/.patched +$(boringssl_static_libs): boringssl-$(BORING_SSL_COMMIT).zip boringssl-$(BORING_SSL_COMMIT)/.patched mkdir -p $(boringssl_install_dir) cd $(boringssl_install_dir) @@ -194,12 +194,12 @@ $(boringssl_static_libs): boringssl.zip boringssl/.patched -GNinja \ .. @ninja@ -j$(SUBJOBS) + # Fix the directory structure so that curl can compile against it. - # See https://everything.curl.dev/source/build/tls/boringssl - mkdir -p lib - ln -sf ../crypto/libcrypto.a lib/libcrypto.a - ln -sf ../ssl/libssl.a lib/libssl.a - cp -Rf ../include . + # See: https://everything.curl.dev/build/boringssl.html + mkdir -p $(boringssl_dir)/lib + cp ssl/libssl.a $(boringssl_dir)/lib + cp crypto/libcrypto.a $(boringssl_dir)/lib $(NGHTTP2_VERSION).tar.bz2: @@ -251,7 +251,7 @@ $(CURL_VERSION)/.chrome: $(chrome_libs) $(CURL_VERSION).tar.gz $(CURL_VERSION)/. config_flags="--prefix=@prefix@"; \ config_flags="$$config_flags --with-nghttp2=$(nghttp2_install_dir)"; \ config_flags="$$config_flags --with-brotli=$(brotli_install_dir)"; \ - config_flags="$$config_flags --with-openssl=$(boringssl_install_dir)"; \ + config_flags="$$config_flags --with-openssl=$(boringssl_dir)"; \ config_flags="$$config_flags --enable-websockets"; \ config_flags="$$config_flags --enable-ech"; \ config_flags="$$config_flags --enable-ipv6"; \ @@ -278,12 +278,12 @@ $(CURL_VERSION)/.chrome: $(chrome_libs) $(CURL_VERSION).tar.gz $(CURL_VERSION)/. if test -n "$(with_ca_path)"; then \ config_flags="$$config_flags --with-ca-path=$(with_ca_path)"; \ fi; \ - add_libs="-pthread"; \ + add_libs="-pthread -lc++"; \ } echo "Configuring curl with: $$config_flags" - ./configure $$config_flags LIBS="$$add_libs" + CXXFLAGS="-stdlib=libc++" LDFLAGS="-lc++" ./configure $$config_flags LIBS="$$add_libs" # Remove possible leftovers from a previous compilation $(MAKE) clean MAKEFLAGS= diff --git a/README.md b/README.md index b3fcc074..d72d8df0 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ The following browsers can be impersonated. | ![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome_24x24.png "Chrome") | 120 | 120.0.6099.109 | macOS Sonoma | `chrome120` | [curl_chrome120](chrome/curl_chrome120) | | ![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome_24x24.png "Chrome") | 123 | 123.0.6312.124 | macOS Sonoma | `chrome123` | [curl_chrome123](chrome/curl_chrome123) | | ![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome_24x24.png "Chrome") | 124 | 124.0.6367.60 | macOS Sonoma | `chrome124` | [curl_chrome124](chrome/curl_chrome124) | +| ![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome_24x24.png "Chrome") | 131 | 131.0.6778.86 | macOS Sonoma | `chrome131` | [curl_chrome131](chrome/curl_chrome131) | | ![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome_24x24.png "Chrome") | 99 | 99.0.4844.73 | Android 12 | `chrome99_android` | [curl_chrome99_android](chrome/curl_chrome99_android) | | ![Edge](https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge_24x24.png "Edge") | 99 | 99.0.1150.30 | Windows 10 | `edge99` | [curl_edge99](chrome/curl_edge99) | | ![Edge](https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge_24x24.png "Edge") | 101 | 101.0.1210.47 | Windows 10 | `edge101` | [curl_edge101](chrome/curl_edge101) | diff --git a/chrome/curl_chrome131 b/chrome/curl_chrome131 new file mode 100755 index 00000000..1f2a99f0 --- /dev/null +++ b/chrome/curl_chrome131 @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# Updates in this version: +# 1. Added MLKEM to replace the X25519Kyber768 + +# Find the directory of this script +dir=${0%/*} + +# The list of ciphers can be obtained by looking at the Client Hello message in +# Wireshark, then converting it using this reference +# https://wiki.mozilla.org/Security/Cipher_Suites +"$dir/curl-impersonate-chrome" \ + --ciphers TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-CHACHA20-POLY1305,ECDHE-RSA-CHACHA20-POLY1305,ECDHE-RSA-AES128-SHA,ECDHE-RSA-AES256-SHA,AES128-GCM-SHA256,AES256-GCM-SHA384,AES128-SHA,AES256-SHA \ + --curves X25519MLKEM768:X25519:P-256:P-384 \ + -H 'sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"' \ + -H 'sec-ch-ua-mobile: ?0' \ + -H 'sec-ch-ua-platform: "macOS"' \ + -H 'Upgrade-Insecure-Requests: 1' \ + -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36' \ + -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \ + -H 'Sec-Fetch-Site: none' \ + -H 'Sec-Fetch-Mode: navigate' \ + -H 'Sec-Fetch-User: ?1' \ + -H 'Sec-Fetch-Dest: document' \ + -H 'Accept-Encoding: gzip, deflate, br, zstd' \ + -H 'Accept-Language: en-US,en;q=0.9' \ + -H 'Priority: u=0, i' \ + --http2 \ + --http2-settings '1:65536;2:0;4:6291456;6:262144' \ + --http2-window-update 15663105 \ + --http2-stream-weight 256 \ + --http2-stream-exclusive 1 \ + --compressed \ + --ech GREASE \ + --tlsv1.2 --alps --tls-permute-extensions \ + --cert-compression brotli \ + --tls-grease \ + "$@" diff --git a/chrome/patches/boringssl.patch b/chrome/patches/boringssl.patch index d3adf2de..75ab7329 100644 --- a/chrome/patches/boringssl.patch +++ b/chrome/patches/boringssl.patch @@ -1,18 +1,22 @@ diff --git a/export.sh b/export.sh new file mode 100755 -index 000000000..678d1ca41 +index 000000000..7053c8cde --- /dev/null +++ b/export.sh -@@ -0,0 +1,4 @@ +@@ -0,0 +1,8 @@ +#!/bin/bash + -+git diff d24a382 > boringssl.patch ++# From here: https://chromium.googlesource.com/chromium/src.git/+/refs/tags/131.0.6778.86/DEPS ++ ++BASE_COMMIT=cd95210465496ac2337b313cf49f607762abe286 ++ ++git diff $BASE_COMMIT > boringssl.patch +mv boringssl.patch ../curl-impersonate/chrome/patches/boringssl.patch diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h -index e500dd76e..e75bca26b 100644 +index 7f733ac93..f4db5e346 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h -@@ -1560,6 +1560,12 @@ OPENSSL_EXPORT int SSL_CTX_set_strict_cipher_list(SSL_CTX *ctx, +@@ -1767,6 +1767,12 @@ OPENSSL_EXPORT int SSL_CTX_set_strict_cipher_list(SSL_CTX *ctx, // garbage inputs, unless an empty cipher list results. OPENSSL_EXPORT int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str); @@ -25,7 +29,7 @@ index e500dd76e..e75bca26b 100644 // SSL_set_strict_cipher_list configures the cipher list for |ssl|, evaluating // |str| as a cipher string and returning error if |str| contains anything // meaningless. It returns one on success and zero on failure. -@@ -4583,6 +4589,12 @@ OPENSSL_EXPORT void SSL_CTX_set_grease_enabled(SSL_CTX *ctx, int enabled); +@@ -4865,6 +4871,12 @@ OPENSSL_EXPORT void SSL_CTX_set_grease_enabled(SSL_CTX *ctx, int enabled); // permute extensions. For now, this is only implemented for the ClientHello. OPENSSL_EXPORT void SSL_CTX_set_permute_extensions(SSL_CTX *ctx, int enabled); @@ -39,10 +43,10 @@ index e500dd76e..e75bca26b 100644 // permute extensions. For now, this is only implemented for the ClientHello. OPENSSL_EXPORT void SSL_set_permute_extensions(SSL *ssl, int enabled); diff --git a/ssl/extensions.cc b/ssl/extensions.cc -index b13400097..8b457b873 100644 +index c9424c98b..ee757ea72 100644 --- a/ssl/extensions.cc +++ b/ssl/extensions.cc -@@ -3313,6 +3313,7 @@ bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs) { +@@ -3355,6 +3355,7 @@ bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs) { !permutation.Init(kNumExtensions)) { return false; } @@ -50,7 +54,7 @@ index b13400097..8b457b873 100644 for (size_t i = 0; i < kNumExtensions; i++) { permutation[i] = i; } -@@ -3320,6 +3321,11 @@ bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs) { +@@ -3362,6 +3363,11 @@ bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs) { // Set element |i| to a randomly-selected element 0 <= j <= i. std::swap(permutation[i], permutation[seeds[i - 1] % (i + 1)]); } @@ -62,7 +66,7 @@ index b13400097..8b457b873 100644 hs->extension_permutation = std::move(permutation); return true; } -@@ -3337,6 +3343,50 @@ static const struct tls_extension *tls_extension_find(uint32_t *out_index, +@@ -3379,6 +3385,50 @@ static const struct tls_extension *tls_extension_find(uint32_t *out_index, return NULL; } @@ -113,7 +117,7 @@ index b13400097..8b457b873 100644 static bool add_padding_extension(CBB *cbb, uint16_t ext, size_t len) { CBB child; if (!CBB_add_u16(cbb, ext) || // -@@ -3383,6 +3433,9 @@ static bool ssl_add_clienthello_tlsext_inner(SSL_HANDSHAKE *hs, CBB *out, +@@ -3425,6 +3475,9 @@ static bool ssl_add_clienthello_tlsext_inner(SSL_HANDSHAKE *hs, CBB *out, size_t i = hs->extension_permutation.empty() ? unpermuted : hs->extension_permutation[unpermuted]; @@ -123,7 +127,7 @@ index b13400097..8b457b873 100644 const size_t len_before = CBB_len(&extensions); const size_t len_compressed_before = CBB_len(compressed.get()); if (!kExtensions[i].add_clienthello(hs, &extensions, compressed.get(), -@@ -3492,6 +3545,7 @@ bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, CBB *out_encoded, +@@ -3534,6 +3587,7 @@ bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, CBB *out_encoded, size_t i = hs->extension_permutation.empty() ? unpermuted : hs->extension_permutation[unpermuted]; @@ -132,7 +136,7 @@ index b13400097..8b457b873 100644 if (!kExtensions[i].add_clienthello(hs, &extensions, &extensions, type)) { OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION); diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc -index 971ebd0b1..effe5c920 100644 +index f87e00002..daa805266 100644 --- a/ssl/handshake_client.cc +++ b/ssl/handshake_client.cc @@ -215,14 +215,6 @@ static void ssl_get_client_disabled(const SSL_HANDSHAKE *hs, @@ -150,59 +154,157 @@ index 971ebd0b1..effe5c920 100644 static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out, ssl_client_hello_type_t type) { const SSL *const ssl = hs->ssl; -@@ -240,28 +232,7 @@ static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out, +@@ -240,41 +232,9 @@ static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out, return false; } - // Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on - // hardware support. - if (hs->max_version >= TLS1_3_VERSION) { +- static const uint16_t kCiphersNoAESHardware[] = { +- TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, +- TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff, +- TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff, +- }; +- static const uint16_t kCiphersAESHardware[] = { +- TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff, +- TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff, +- TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, +- }; +- static const uint16_t kCiphersCNSA[] = { +- TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff, +- TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff, +- TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, +- }; +- - const bool has_aes_hw = ssl->config->aes_hw_override - ? ssl->config->aes_hw_override_value - : EVP_has_aes_hardware(); +- const bssl::Span ciphers = +- ssl->config->tls13_cipher_policy == ssl_compliance_policy_cnsa_202407 +- ? bssl::Span(kCiphersCNSA) +- : (has_aes_hw ? bssl::Span(kCiphersAESHardware) +- : bssl::Span(kCiphersNoAESHardware)); - -- if ((!has_aes_hw && // -- !ssl_add_tls13_cipher(&child, -- TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, -- ssl->config->tls13_cipher_policy)) || -- !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff, -- ssl->config->tls13_cipher_policy) || -- !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff, -- ssl->config->tls13_cipher_policy) || -- (has_aes_hw && // -- !ssl_add_tls13_cipher(&child, -- TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, -- ssl->config->tls13_cipher_policy))) { -- return false; +- for (auto cipher : ciphers) { +- if (!ssl_add_tls13_cipher(&child, cipher, +- ssl->config->tls13_cipher_policy)) { +- return false; +- } - } - } + // curl-impersonate: TLS 1.3 suites are added uniformly with other suites. ++ // TODO: in new versions, perhaps it's OK to keep the original code here. ++ if (hs->min_version < TLS1_3_VERSION && type != ssl_client_hello_inner) { bool any_enabled = false; -@@ -539,6 +510,7 @@ static enum ssl_hs_wait_t do_start_connect(SSL_HANDSHAKE *hs) { +@@ -326,8 +286,7 @@ bool ssl_write_client_hello_without_extensions(const SSL_HANDSHAKE *hs, + } + + // Do not send a session ID on renegotiation. +- if (!ssl->s3->initial_handshake_complete && +- !empty_session_id && ++ if (!ssl->s3->initial_handshake_complete && !empty_session_id && + !CBB_add_bytes(&child, hs->session_id.data(), hs->session_id.size())) { + return false; + } +@@ -408,7 +367,7 @@ static bool parse_server_version(const SSL_HANDSHAKE *hs, uint16_t *out_version, + } + + if (!CBS_get_u16(&supported_versions.data, out_version) || +- CBS_len(&supported_versions.data) != 0) { ++ CBS_len(&supported_versions.data) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return false; + } +@@ -555,8 +514,9 @@ static enum ssl_hs_wait_t do_start_connect(SSL_HANDSHAKE *hs) { + hs->early_data_offered = true; + } ++ // curl-impersonate: set extension orders if (!ssl_setup_key_shares(hs, /*override_group_id=*/0) || - !ssl_setup_extension_permutation(hs) || -+ !ssl_set_extension_order(hs) || +- !ssl_setup_extension_permutation(hs) || ++ !ssl_setup_extension_permutation(hs) || !ssl_set_extension_order(hs) || !ssl_encrypt_client_hello(hs, MakeConstSpan(ech_enc, ech_enc_len)) || !ssl_add_client_hello(hs)) { return ssl_hs_error; -@@ -1402,7 +1374,8 @@ static enum ssl_hs_wait_t do_send_client_key_exchange(SSL_HANDSHAKE *hs) { +@@ -583,7 +543,8 @@ static enum ssl_hs_wait_t do_enter_early_data(SSL_HANDSHAKE *hs) { + return ssl_hs_ok; + } + +-static enum ssl_hs_wait_t do_early_reverify_server_certificate(SSL_HANDSHAKE *hs) { ++static enum ssl_hs_wait_t do_early_reverify_server_certificate( ++ SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (ssl->ctx->reverify_on_resume) { + // Don't send an alert on error. The alert would be in the clear, which the +@@ -815,8 +776,7 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { + const SSL_CIPHER *cipher = SSL_get_cipher_by_value(server_hello.cipher_suite); + uint32_t mask_a, mask_k; + ssl_get_client_disabled(hs, &mask_a, &mask_k); +- if (cipher == nullptr || +- (cipher->algorithm_mkey & mask_k) || ++ if (cipher == nullptr || (cipher->algorithm_mkey & mask_k) || + (cipher->algorithm_auth & mask_a) || + SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) || + SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl) || +@@ -1018,8 +978,7 @@ static enum ssl_hs_wait_t do_read_certificate_status(SSL_HANDSHAKE *hs) { + if (!CBS_get_u8(&certificate_status, &status_type) || + status_type != TLSEXT_STATUSTYPE_ocsp || + !CBS_get_u24_length_prefixed(&certificate_status, &ocsp_response) || +- CBS_len(&ocsp_response) == 0 || +- CBS_len(&certificate_status) != 0) { ++ CBS_len(&ocsp_response) == 0 || CBS_len(&certificate_status) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return ssl_hs_error; +@@ -1480,7 +1439,9 @@ static enum ssl_hs_wait_t do_send_client_key_exchange(SSL_HANDSHAKE *hs) { ssl_key_usage_t intended_use = (alg_k & SSL_kRSA) ? key_usage_encipherment : key_usage_digital_signature; - if (!ssl_cert_check_key_usage(&leaf_cbs, intended_use)) { ++ // curl-impersonate: optionally disable ssl key usage check + if (hs->config->key_usage_check_enabled && + !ssl_cert_check_key_usage(&leaf_cbs, intended_use)) { if (hs->config->enforce_rsa_key_usage || EVP_PKEY_id(hs->peer_pubkey.get()) != EVP_PKEY_RSA) { return ssl_hs_error; +@@ -1551,8 +1512,7 @@ static enum ssl_hs_wait_t do_send_client_key_exchange(SSL_HANDSHAKE *hs) { + !CBB_reserve(&enc_pms, &ptr, RSA_size(rsa)) || + !RSA_encrypt(rsa, &enc_pms_len, ptr, RSA_size(rsa), pms.data(), + pms.size(), RSA_PKCS1_PADDING) || +- !CBB_did_write(&enc_pms, enc_pms_len) || +- !CBB_flush(&body)) { ++ !CBB_did_write(&enc_pms, enc_pms_len) || !CBB_flush(&body)) { + return ssl_hs_error; + } + } else if (alg_k & SSL_kECDHE) { +@@ -1665,8 +1625,7 @@ static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs) { + return ssl_hs_private_key_operation; + } + +- if (!CBB_did_write(&child, sig_len) || +- !ssl_add_message_cbb(ssl, cbb.get())) { ++ if (!CBB_did_write(&child, sig_len) || !ssl_add_message_cbb(ssl, cbb.get())) { + return ssl_hs_error; + } + +@@ -1735,8 +1694,7 @@ static bool can_false_start(const SSL_HANDSHAKE *hs) { + // TLS 1.2 and TLS 1.3, but there are too many TLS 1.2 deployments to + // sacrifice False Start on them. Instead, we rely on the ServerHello.random + // downgrade signal, which we unconditionally enforce. +- if (SSL_is_dtls(ssl) || +- SSL_version(ssl) != TLS1_2_VERSION || ++ if (SSL_is_dtls(ssl) || SSL_version(ssl) != TLS1_2_VERSION || + hs->new_cipher->algorithm_mkey != SSL_kECDHE || + hs->new_cipher->algorithm_mac != SSL_AEAD) { + return false; diff --git a/ssl/internal.h b/ssl/internal.h -index c9facb699..a32e9b4ba 100644 +index 092b2987e..5ace601f6 100644 --- a/ssl/internal.h +++ b/ssl/internal.h -@@ -574,9 +574,14 @@ BSSL_NAMESPACE_BEGIN +@@ -807,9 +807,14 @@ BSSL_NAMESPACE_BEGIN // Bits for |algorithm_mac| (symmetric authentication). #define SSL_SHA1 0x00000001u @@ -218,7 +320,7 @@ index c9facb699..a32e9b4ba 100644 // Bits for |algorithm_prf| (handshake digest). #define SSL_HANDSHAKE_MAC_DEFAULT 0x1 -@@ -2161,6 +2166,12 @@ bssl::UniquePtr tls13_create_session_with_ticket(SSL *ssl, +@@ -2514,6 +2519,12 @@ bssl::UniquePtr tls13_create_session_with_ticket(SSL *ssl, // for |hs|, if applicable. It returns true on success and false on error. bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs); @@ -231,7 +333,7 @@ index c9facb699..a32e9b4ba 100644 // ssl_setup_key_shares computes client key shares and saves them in |hs|. It // returns true on success and false on failure. If |override_group_id| is zero, // it offers the default groups, including GREASE. If it is non-zero, it offers -@@ -3033,6 +3044,12 @@ struct SSL_CONFIG { +@@ -3382,6 +3393,12 @@ struct SSL_CONFIG { // crypto UniquePtr cipher_list; @@ -244,7 +346,7 @@ index c9facb699..a32e9b4ba 100644 // This is used to hold the local certificate used (i.e. the server // certificate for a server or the client certificate for a client). UniquePtr cert; -@@ -3490,6 +3507,12 @@ struct ssl_ctx_st { +@@ -3856,6 +3873,12 @@ struct ssl_ctx_st : public bssl::RefCounted { bssl::UniquePtr cipher_list; @@ -258,7 +360,7 @@ index c9facb699..a32e9b4ba 100644 LHASH_OF(SSL_SESSION) *sessions = nullptr; // Most session-ids that will be cached, default is diff --git a/ssl/ssl_cipher.cc b/ssl/ssl_cipher.cc -index fd8cef95d..3d2c8ff6d 100644 +index 97e69ff90..dc3ca1526 100644 --- a/ssl/ssl_cipher.cc +++ b/ssl/ssl_cipher.cc @@ -197,6 +197,37 @@ static constexpr SSL_CIPHER kCiphers[] = { @@ -279,7 +381,7 @@ index fd8cef95d..3d2c8ff6d 100644 + "TLS_RSA_WITH_AES_128_CBC_SHA256", + TLS1_CK_RSA_WITH_AES_128_SHA256, + SSL_kRSA, -+ SSL_aRSA, ++ SSL_aRSA_SIGN, + SSL_AES128, + SSL_SHA256, + SSL_HANDSHAKE_MAC_SHA256, @@ -290,7 +392,7 @@ index fd8cef95d..3d2c8ff6d 100644 + "TLS_RSA_WITH_AES_256_CBC_SHA256", + TLS1_CK_RSA_WITH_AES_256_SHA256, + SSL_kRSA, -+ SSL_aRSA, ++ SSL_aRSA_SIGN, + SSL_AES256, + SSL_SHA256, + SSL_HANDSHAKE_MAC_SHA256, @@ -336,7 +438,7 @@ index fd8cef95d..3d2c8ff6d 100644 + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + 0x0300C012, + SSL_kECDHE, -+ SSL_aRSA, ++ SSL_aRSA_SIGN, + SSL_3DES, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, @@ -392,7 +494,7 @@ index fd8cef95d..3d2c8ff6d 100644 + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", + TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384, + SSL_kECDHE, -+ SSL_aRSA, ++ SSL_aRSA_SIGN, + SSL_AES256, + SSL_SHA384, + SSL_HANDSHAKE_MAC_SHA384, @@ -429,7 +531,7 @@ index fd8cef95d..3d2c8ff6d 100644 // Legacy protocol minimum version aliases. "TLSv1" is intentionally the // same as "SSLv3". -@@ -1154,13 +1253,22 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, +@@ -1147,13 +1246,22 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, TLS1_CK_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 & 0xffff, }; static const uint16_t kLegacyCiphers[] = { @@ -452,7 +554,7 @@ index fd8cef95d..3d2c8ff6d 100644 TLS1_CK_RSA_WITH_AES_128_GCM_SHA256 & 0xffff, TLS1_CK_RSA_WITH_AES_256_GCM_SHA384 & 0xffff, TLS1_CK_RSA_WITH_AES_128_SHA & 0xffff, -@@ -1169,11 +1277,16 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, +@@ -1162,11 +1270,16 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, TLS1_CK_PSK_WITH_AES_256_CBC_SHA & 0xffff, SSL3_CK_RSA_DES_192_CBC3_SHA & 0xffff, }; @@ -472,7 +574,7 @@ index fd8cef95d..3d2c8ff6d 100644 for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(co_list); i++) { co_list[i].next = i + 1 < OPENSSL_ARRAY_SIZE(co_list) ? &co_list[i + 1] : nullptr; -@@ -1209,8 +1322,13 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, +@@ -1202,8 +1315,13 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, co_list[num++].cipher = SSL_get_cipher_by_value(id); assert(co_list[num - 1].cipher != nullptr); } @@ -488,10 +590,10 @@ index fd8cef95d..3d2c8ff6d 100644 "Not all ciphers are included in the cipher order"); diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc -index 58b68e675..455ee4dd0 100644 +index f0b3872e9..e7e23a14d 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc -@@ -657,6 +657,8 @@ SSL *SSL_new(SSL_CTX *ctx) { +@@ -658,6 +658,8 @@ SSL *SSL_new(SSL_CTX *ctx) { ssl->config->retain_only_sha256_of_client_certs = ctx->retain_only_sha256_of_client_certs; ssl->config->permute_extensions = ctx->permute_extensions; @@ -500,7 +602,7 @@ index 58b68e675..455ee4dd0 100644 ssl->config->aes_hw_override = ctx->aes_hw_override; ssl->config->aes_hw_override_value = ctx->aes_hw_override_value; ssl->config->tls13_cipher_policy = ctx->tls13_cipher_policy; -@@ -3015,6 +3017,17 @@ void SSL_CTX_set_permute_extensions(SSL_CTX *ctx, int enabled) { +@@ -3029,6 +3031,17 @@ void SSL_CTX_set_permute_extensions(SSL_CTX *ctx, int enabled) { ctx->permute_extensions = !!enabled; } @@ -519,10 +621,10 @@ index 58b68e675..455ee4dd0 100644 if (!ssl->config) { return; diff --git a/ssl/ssl_privkey.cc b/ssl/ssl_privkey.cc -index 57116cd6c..fa1652832 100644 +index 76ba0849d..819cc53ba 100644 --- a/ssl/ssl_privkey.cc +++ b/ssl/ssl_privkey.cc -@@ -594,7 +594,7 @@ static bool sigalgs_unique(Span in_sigalgs) { +@@ -598,7 +598,7 @@ static bool sigalgs_unique(Span in_sigalgs) { static bool set_sigalg_prefs(Array *out, Span prefs) { if (!sigalgs_unique(prefs)) { diff --git a/chrome/patches/curl-impersonate.patch b/chrome/patches/curl-impersonate.patch index 77dc199e..d6f2fc2d 100644 --- a/chrome/patches/curl-impersonate.patch +++ b/chrome/patches/curl-impersonate.patch @@ -817,7 +817,7 @@ index dc4870608..4746133e9 100644 /* diff --git a/lib/easyoptions.c b/lib/easyoptions.c -index 9c4438a10..56eabf082 100644 +index 9c4438a10..86068cf12 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -86,6 +86,7 @@ struct curl_easyoption Curl_easyopts[] = { @@ -843,7 +843,7 @@ index 9c4438a10..56eabf082 100644 {"HTTPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, 0}, {"HTTPPOST", CURLOPT_HTTPPOST, CURLOT_OBJECT, 0}, {"HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CURLOT_LONG, 0}, -@@ -307,22 +314,28 @@ struct curl_easyoption Curl_easyopts[] = { +@@ -307,21 +314,27 @@ struct curl_easyoption Curl_easyopts[] = { {"SSLKEYTYPE", CURLOPT_SSLKEYTYPE, CURLOT_STRING, 0}, {"SSLKEY_BLOB", CURLOPT_SSLKEY_BLOB, CURLOT_BLOB, 0}, {"SSLVERSION", CURLOPT_SSLVERSION, CURLOT_VALUES, 0}, @@ -858,20 +858,19 @@ index 9c4438a10..56eabf082 100644 + {"SSL_ENABLE_TICKET", CURLOPT_SSL_ENABLE_TICKET, CURLOT_LONG, 0}, {"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0}, {"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0}, ++ {"SSL_PERMUTE_EXTENSIONS", CURLOPT_SSL_PERMUTE_EXTENSIONS, CURLOT_LONG, 0}, {"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0}, + {"SSL_SIG_HASH_ALGS", CURLOPT_SSL_SIG_HASH_ALGS, CURLOT_STRING, 0}, {"SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CURLOT_LONG, 0}, {"SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CURLOT_LONG, 0}, {"SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CURLOT_LONG, 0}, -+ {"SSL_PERMUTE_EXTENSIONS", CURLOPT_SSL_PERMUTE_EXTENSIONS, CURLOT_LONG, 0}, {"STDERR", CURLOPT_STDERR, CURLOT_OBJECT, 0}, {"STREAM_DEPENDS", CURLOPT_STREAM_DEPENDS, CURLOT_OBJECT, 0}, {"STREAM_DEPENDS_E", CURLOPT_STREAM_DEPENDS_E, CURLOT_OBJECT, 0}, - {"STREAM_WEIGHT", CURLOPT_STREAM_WEIGHT, CURLOT_LONG, 0}, + {"STREAM_EXCLUSIVE", CURLOPT_STREAM_EXCLUSIVE, CURLOT_LONG, 0}, + {"STREAM_WEIGHT", CURLOPT_STREAM_WEIGHT, CURLOT_LONG, 0}, {"SUPPRESS_CONNECT_HEADERS", CURLOPT_SUPPRESS_CONNECT_HEADERS, CURLOT_LONG, 0}, - {"TCP_FASTOPEN", CURLOPT_TCP_FASTOPEN, CURLOT_LONG, 0}, @@ -342,6 +355,11 @@ struct curl_easyoption Curl_easyopts[] = { {"TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD, CURLOT_STRING, 0}, {"TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE, CURLOT_STRING, 0}, @@ -1522,10 +1521,10 @@ index 80e183480..8ee390b7e 100644 * Store nghttp2 version info in this buffer. diff --git a/lib/impersonate.c b/lib/impersonate.c new file mode 100644 -index 000000000..3054870de +index 000000000..0a0cce2a4 --- /dev/null +++ b/lib/impersonate.c -@@ -0,0 +1,1127 @@ +@@ -0,0 +1,1177 @@ +#include "curl_setup.h" + +#include @@ -2052,6 +2051,56 @@ index 000000000..3054870de + .tls_grease = true + }, + { ++ .target = "chrome131", ++ .httpversion = CURL_HTTP_VERSION_2_0, ++ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, ++ .ciphers = ++ "TLS_AES_128_GCM_SHA256," ++ "TLS_AES_256_GCM_SHA384," ++ "TLS_CHACHA20_POLY1305_SHA256," ++ "ECDHE-ECDSA-AES128-GCM-SHA256," ++ "ECDHE-RSA-AES128-GCM-SHA256," ++ "ECDHE-ECDSA-AES256-GCM-SHA384," ++ "ECDHE-RSA-AES256-GCM-SHA384," ++ "ECDHE-ECDSA-CHACHA20-POLY1305," ++ "ECDHE-RSA-CHACHA20-POLY1305," ++ "ECDHE-RSA-AES128-SHA," ++ "ECDHE-RSA-AES256-SHA," ++ "AES128-GCM-SHA256," ++ "AES256-GCM-SHA384," ++ "AES128-SHA," ++ "AES256-SHA", ++ .curves = "X25519MLKEM768:X25519:P-256:P-384", ++ .npn = false, ++ .alpn = true, ++ .alps = true, ++ .tls_permute_extensions = true, ++ .tls_session_ticket = true, ++ .cert_compression = "brotli", ++ .http_headers = { ++ "sec-ch-ua: \"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"", ++ "sec-ch-ua-mobile: ?0", ++ "sec-ch-ua-platform: \"macOS\"", ++ "Upgrade-Insecure-Requests: 1", ++ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", ++ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", ++ "Sec-Fetch-Site: none", ++ "Sec-Fetch-Mode: navigate", ++ "Sec-Fetch-User: ?1", ++ "Sec-Fetch-Dest: document", ++ "Accept-Encoding: gzip, deflate, br, zstd", ++ "Accept-Language: en-US,en;q=0.9", ++ "Priority: u=0, i" ++ }, ++ .http2_settings = "1:65536;2:0;4:6291456;6:262144", ++ .http2_window_update = 15663105, ++ .http2_stream_weight = 256, ++ .http2_stream_exclusive = 1, ++ .ech = "GREASE", ++ .tls_extension_order = NULL, ++ .tls_grease = true ++ }, ++ { + .target = "chrome99_android", + .httpversion = CURL_HTTP_VERSION_2_0, + .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, diff --git a/tests/signatures/chrome_131.0.6778.86.yaml b/tests/signatures/chrome_131.0.6778.86.yaml new file mode 100644 index 00000000..da17d7fa --- /dev/null +++ b/tests/signatures/chrome_131.0.6778.86.yaml @@ -0,0 +1,152 @@ +# NOTE: this file is not generated by dpkt, due to a bug: https://github.com/kbandla/dpkt/issues/665 +# The only difference from Chrome 131 is: X25519Kyber768 was replaced by MLKEM +browser: + name: chrome + os: macOS + version: 131.0.6778.86 +signature: + options: + tls_permute_extensions: true + http2: + frames: + - frame_type: SETTINGS + settings: + - key: 1 + value: 65536 + - key: 2 + value: 0 + - key: 4 + value: 6291456 + - key: 6 + value: 262144 + stream_id: 0 + - frame_type: WINDOW_UPDATE + stream_id: 0 + window_size_increment: 15663105 + - frame_type: HEADERS + headers: + - 'sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"' + - 'sec-ch-ua-mobile: ?0' + - 'sec-ch-ua-platform: "macOS"' + - 'upgrade-insecure-requests: 1' + - 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36' + - 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' + - 'sec-fetch-site: none' + - 'sec-fetch-mode: navigate' + - 'sec-fetch-user: ?1' + - 'sec-fetch-dest: document' + - 'accept-encoding: gzip, deflate, br, zstd' + - 'accept-language: en-US,en;q=0.9' + - 'priority: u=0, i' + pseudo_headers: + - :method + - :authority + - :scheme + - :path + stream_id: 1 + tls_client_hello: + ciphersuites: + - GREASE + - 4865 + - 4866 + - 4867 + - 49195 + - 49199 + - 49196 + - 49200 + - 52393 + - 52392 + - 49171 + - 49172 + - 156 + - 157 + - 47 + - 53 + comp_methods: + - 0 + extensions: + - length: 0 + type: GREASE + - alps_alpn_list: + - h2 + length: 5 + type: application_settings + - length: 2 + psk_ke_mode: 1 + type: psk_key_exchange_modes + - algorithms: + - 2 + length: 3 + type: compress_certificate + - key_shares: + - group: GREASE + length: 1 + - group: 4588 + length: 1216 + - group: 29 + length: 32 + length: 1263 + type: keyshare + - length: 7 + supported_versions: + - GREASE + - TLS_VERSION_1_3 + - TLS_VERSION_1_2 + type: supported_versions + - length: 1 + type: renegotiation_info + - length: 0 + type: encrypted_client_hello + - length: 0 + type: extended_master_secret + - ec_point_formats: + - 0 + length: 2 + type: ec_point_formats + - length: 5 + status_request_type: 1 + type: status_request + - length: 0 + type: session_ticket + - length: 12 + supported_groups: + - GREASE + - 4588 + - 29 + - 23 + - 24 + type: supported_groups + - length: 0 + type: signed_certificate_timestamp + - alpn_list: + - h2 + - http/1.1 + length: 14 + type: application_layer_protocol_negotiation + - length: 18 + sig_hash_algs: + - 1027 + - 2052 + - 1025 + - 1283 + - 2053 + - 1281 + - 2054 + - 1537 + type: signature_algorithms + - type: server_name + - data: !!binary | + AA== + length: 1 + type: GREASE + handshake_version: TLS_VERSION_1_2 + record_version: TLS_VERSION_1_0 + session_id_length: 32 +third_party: + akamai_hash: 52d84b11737d980aef856699f885ca86 + akamai_text: 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p + ja3_hash: b41f2b186c3c82a6fc1bb88dad6eb562 + ja3_text: 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,27-13-65281-18-43-0-35-10-5-51-11-16-17513-65037-23-45,4588-29-23-24,0 + ja3n_hash: dee19b855b658c6aa0f575eda2525e19 + ja3n_text: 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-5-10-11-13-16-18-23-27-35-43-45-51-17513-65037-65281,4588-29-23-24,0 + user_agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 diff --git a/tests/targets.yaml b/tests/targets.yaml index b48ce37f..04ef0141 100644 --- a/tests/targets.yaml +++ b/tests/targets.yaml @@ -43,6 +43,10 @@ - null - null - chrome_124.0.6367.60_macOS +- - curl_chrome131 + - null + - null + - chrome_131.0.6778.86_macOS - - curl_chrome99_android - null - null @@ -123,6 +127,10 @@ - CURL_IMPERSONATE: chrome124 - libcurl-impersonate-chrome - chrome_124.0.6367.60_macOS +- - minicurl + - CURL_IMPERSONATE: chrome131 + - libcurl-impersonate-chrome + - chrome_131.0.6778.86_macOS - - minicurl - CURL_IMPERSONATE: chrome99_android - libcurl-impersonate-chrome diff --git a/win/build.sh b/win/build.sh index 880f7f1c..a4572b4b 100644 --- a/win/build.sh +++ b/win/build.sh @@ -7,7 +7,7 @@ cd build/ # Download and patch boringssl -BORING_SSL_COMMIT=d24a38200fef19150eef00cad35b138936c08767 +BORING_SSL_COMMIT=cd95210465496ac2337b313cf49f607762abe286 curl -L https://github.com/google/boringssl/archive/${BORING_SSL_COMMIT}.zip -o boringssl.zip unzip -q -o boringssl.zip mv boringssl-${BORING_SSL_COMMIT} boringssl