Metadata-Version: 2.4
Name: chrome-fp
Version: 0.2.2
Summary: 与真 Chrome 152 逐字节同指纹(JA4/HTTP2/头顺序)的纯 Python HTTP 请求库
Author: DreamXiaoJing
License: MIT
Project-URL: Homepage, https://github.com/DreamXiaoJing
Project-URL: Source, https://github.com/DreamXiaoJing/chrome-fp
Project-URL: Issues, https://github.com/DreamXiaoJing/chrome-fp/issues
Keywords: chrome,tls,fingerprint,ja4,http2,http3,client-hello
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: License :: OSI Approved :: MIT License
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: cryptography>=42
Requires-Dist: hpack>=4
Requires-Dist: brotli>=1.1
Requires-Dist: zstandard>=0.22
Requires-Dist: certifi
Provides-Extra: dev
Requires-Dist: pqcrypto>=0.7; extra == "dev"
Requires-Dist: kyber-py; extra == "dev"

# chrome-fp — 与真 Chrome 逐字节同指纹的纯 Python 请求库

用 Python 发 HTTP 请求，但 TLS/HTTP2 指纹跟本机真 Chrome（**152.0.7977.64**）完全一致：
JA4、HTTP/2 Akamai 指纹、请求头顺序、扩展集合、GREASE、ALPS、trust_anchors、PQ 混合密钥共享（X25519MLKEM768）全都一致。

不依赖 curl/Go/浏览器，握手是自己实现的（`ssl` 模块无法自定义 ClientHello，所以只借用 `cryptography` 的密码原语）。

## 实测结果（第三方服务器 tls.peet.ws 判定）

| 项目 | 真 Chrome 152 | 本库 |
|---|---|---|
| JA4 | `t13d1517h2_8daaf6152771_cb7bf5808d99` | **完全一致** |
| HTTP/2 Akamai | `1:65536;2:0;4:6291456;6:262144\|15663105\|0\|m,a,s,p` | **完全一致** |
| 请求头顺序 | `:method :authority :scheme :path sec-ch-ua … priority` | **完全一致** |
| 扩展数 | 19（17 真实 + 2 GREASE） | **一致** |

离线自检（`tools/verify_hello.py`）用 26 条真机抓包做基准，**43/43 通过**：
密码套件列表与顺序、17 个扩展内容逐字节、GREASE 首空尾 1 字节、扩展随机置换、trust_anchors 32 个 ID 集合、
key_share 三项（GREASE 1B / X25519MLKEM768 1216B / x25519 32B）、ECH GREASE 长度集合 {186,218,250,282}、SNI、ALPS、压缩证书、签名算法。

## 快速开始

```bash
cd ~/PycharmProjects/chrome-fp
python3 -m venv .venv && ./.venv/bin/pip install cryptography hpack brotli zstandard certifi
```

```python
from chrome_fp import Session

s = Session(proxy="http://127.0.0.1:7892")          # 可选代理
r = s.get("https://tls.peet.ws/api/all")
d = r.json()
print(d["tls"]["ja4"])                             # 与真 Chrome 一致
print(d["http2"]["akamai_fingerprint"])

r = s.post("https://httpbin.org/post", json={"a": 1})
print(r.status_code, r.json())
s.close()
```

`Session` 提供 requests 风格 API：`get/post/put/delete/head/patch/options`、
`params/data/json/headers/timeout/allow_redirects`、cookie jar、gzip/deflate/br/zstd 自动解压、
HTTP/2 与 HTTP/1.1 自适应（按 ALPN）、代理 CONNECT、证书链校验（浏览器语义：EKU 缺失放行、主机名通配符）。

## 关键参数

```python
Session(
    proxy="http://127.0.0.1:7892",  # 代理
    verify=True,                    # 证书链 + 主机名校验
    timeout=30,                     # 秒
    mode="navigate",                # navigate | cors | no-cors（决定 sec-fetch-*/accept）
    headers={"referer": "..."},     # 追加请求头
    user_agent=None,                # 覆盖 UA
    send_priority_tree=True,        # 发 Chrome 的 PRIORITY 树(3/5/7/9)，Akamai 指纹需要
)
```

底层构造单条 ClientHello（不含网络）：

```python
from chrome_fp import build_client_hello, ja4_from_hello
ch = build_client_hello("example.com")
print(ja4_from_hello(ch.record))
ch.record          # 可直接写 socket 的原始字节
```

- `include_mlkem=False`：不发 X25519MLKEM768（老服务器/只想用 x25519 时）
- `grease_parts=()`：完全关闭 GREASE；`("cipher","ext")` 之类可细粒度控制
- `permute_extensions=False`：关闭扩展随机置换（调试用）

## 目录结构

```
chrome_fp/
  spec.py         Chrome 152 指纹常量(全部标注源码出处)
  hello.py        按 BoringSSL ssl_add_clienthello_tlsext 规则拼 ClientHello
  fingerprint.py  解析 + JA3/JA4 计算
  tls13.py        纯 Python TLS 1.3 客户端(record 层/密钥调度/CV 校验/证书链校验/KeyUpdate)
  tls12.py        TLS 1.2 回落(ECDHE/RSA + GCM/ChaCha20, EMS, ServerKeyExchange 验签)
  client.py       发同一个 ClientHello, 按 ServerHello 自动分派 1.3 / 1.2
  mlkem.py        ML-KEM-768 (FIPS 203) 纯 Python，供 X25519MLKEM768 混合密钥共享
  http2.py        HTTP/2 客户端(Chrome 帧序/SETTINGS/优先级/头顺序)
  http1.py        HTTP/1.1 客户端(ALPN=http/1.1 时)
  session.py      requests 风格 API
tools/            抓包/自检/诊断脚本
tests/            单元与端到端测试
data/             真 Chrome 152 的 26 条 ClientHello 抓包 + peet 指纹真值
specs/            源码提取的规格文档(含出处)
```

## 测试

```bash
./.venv/bin/python tools/verify_hello.py data/chrome152_clienthello   # 43/43
./.venv/bin/python tests/test_mlkem.py                                # 9/9  (对 pqcrypto/kyber-py 双向互操作)
./.venv/bin/python tools/verify_live.py                               # 11/11 (实网指纹: JA4/Akamai/头顺序)
./.venv/bin/python tests/test_endtoend.py                             # 28/28 (本地端到端: TLS1.3 与 TLS1.2 各 14 项)
```

真实站点实测（20 个）：19 个成功（15 个走 TLS 1.3、4 个回落 TLS 1.2），唯一失败是 google.com（见"已知问题"）。
证书链校验支持**交叉签名根**（Google GTS／DigiCert G2 交叉签那类），按 DER 级名称匹配 + 逐级验签 + BFS 建路 + 有效期筛选。

抓真值（需要本地 Chrome）：
```bash
python3 tools/hello_sniff.py 8899 /tmp/fp_hello     # 起 CONNECT 代理并 dump ClientHello
google-chrome --headless=new --proxy-server=http://127.0.0.1:8899 https://example.com/
```

## 已知边界与说明

- **TLS 1.2 回落已实现**（ECDHE + AES-GCM / ChaCha20-Poly1305，含 RSA 密钥交换，支持 EMS/ALPN/证书校验）。
  用**同一个 ClientHello** 回落 —— 真 Chrome 就是这样，所以 JA4 等指纹不变。
  实测：nginx.org / bing.com（h2 over TLS1.2）/ sohu.com / qq.com 等只支持 1.2 的站点都能正常请求。
  不支持：CBC 类套件（极少见，会明确报错）、会话恢复、0-RTT、客户端证书。
- **已知问题**：www.google.com / www.googleapis.com 握手能完成（JA4 一致），但 GFE 随后会用
  `unexpected_message` 断链 —— 它在等某个特定形态的客户端消息，本库还没对齐。其余 19/20 个实测站点正常。
  （curl / Python ssl 走同一个代理访问 google 正常，所以不是代理问题。）
- **JA3 哈希每次连接都不同**，这是真 Chrome 的行为（扩展顺序随机置换），不是 bug；稳定的标识是 **JA4**。
- 扩展顺序、GREASE 取值、ECH GREASE 长度、trust_anchors 顺序都是**每次连接随机**的（与 Chrome 相同分布），所以「逐字节一致」指的是：同一连接内所有非随机字段逐字节相同 + 结构/取值集合与 Chrome 完全一致。
- 会话恢复（PSK / session ticket）、0-RTT、客户端证书、ECH 真加密未实现。
- QUIC/HTTP3 未实现（Chrome 走 TCP 时就是这套指纹）。

## 真值来源

1. 本机真 Chrome 152.0.7977.64 的原始 ClientHello 抓包 26 条（`data/chrome152_clienthello/`）+ tls.peet.ws 判定结果（`data/chrome152_peet.json`）
2. 本地 Chromium/BoringSSL 源码：
   - 扩展表顺序与置换：`boringssl/src/ssl/extensions.cc:4067-4295, 4306-4328`
   - GREASE 首尾与 padding 规则：`extensions.cc:4489-4560`
   - ECH GREASE 长度：`boringssl/src/ssl/encrypted_client_hello.cc:732-784`
   - trust_anchors(0xca34)：`include/openssl/tls1.h:141` + `extensions.cc:2948-2965`
   - 混合密钥共享拼接顺序：`boringssl/src/ssl/ssl_key_share.cc:308-346`（ML-KEM ss ‖ X25519 ss）
   - TLS 客户端配置：`/home/wqw/chromium/src/net/socket/ssl_client_socket_impl.cc`
