// NewClient:返回标准 *http.Client
if cfg.DialTimeout == 0 {
cfg.DialTimeout = 30 * time.Second
}
return &http.Client{
Transport: &http.Transport{
Proxy: nil, // 连接由 SDK 直接建立
ForceAttemptHTTP2: false,
DialTLSContext: func(ctx context.Context,
network, addr string) (net.Conn, error) {
ctx, cancel := context.WithTimeout(
ctx, cfg.DialTimeout)
defer cancel()
return dialAttested(ctx, network, addr, cfg)
},
},
}
raw, err := (&net.Dialer{}).DialContext(ctx, network, addr)
if err != nil { return nil, err }
conn := tls.Client(raw, &tls.Config{
ServerName: host, MinVersion: tls.VersionTLS13,
NextProtos: []string{"http/1.1"}, RootCAs: cfg.RootCAs,
})
if err := conn.HandshakeContext(ctx); err != nil {
raw.Close(); return nil, err
}
cs := conn.ConnectionState()
raw32, err := cs.ExportKeyingMaterial(attest.EKMLabel, nil, 32)
if err != nil { conn.Close(); return nil, err }
var ekm [32]byte; copy(ekm[:], raw32)
resp, err := fetchAttestation(ctx, conn, host)
if err != nil { conn.Close(); return nil, err }
// 同一条 conn 上收取 quote 与 collateral
// 验证通过前,conn 不交给 HTTP Transport
// Verify:已检查响应、协议版本和非空 Quote
opts := verify.DefaultOptions()
opts.GetCollateral = true
opts.CheckRevocations = true
opts.Getter = bundleGetter{b: resp.Collateral}
err := verify.RawTdxQuoteContext(ctx, resp.Quote, opts)
if err != nil { return err }
body, err := tdBody(resp.Quote)
if err != nil { return err }
if err := checkNotDebug(body); err != nil { return err }
if err := checkMeasurements(body, exp); err != nil {
return err
}
return checkEKMBinding(body.GetReportData(), ekm)
// MRTD + RTMR0–3 必须与期望度量值一致
// report_data = SHA512(ekm || 32 个零字节)
// 验证失败则关连接,通过才交给 Transport