"""签名金标向量与 HMAC/SHA256 守卫(任务书 P1-H)。 金标向量由独立实现(Node.js crypto)生成,与 C# 端 InboundSignatureHandler 同一算法: message = METHOD&path&accessKey×tamp&nonce&bodySha256&idempotencyKey signature = Base64(HMAC-SHA256(secret, message)) """ from __future__ import annotations from clients.inbound_client import InboundClient, build_message, sha256_hex, sign EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" GOLDEN = { "secret": "sim-secret-golden", "method": "POST", "path": "/api/mdp/inbound/S1_SALES_ORDER_ENTRY", "accessKey": "SIMKEY01", "timestamp": "1750000000", "nonce": "nonce-fixed-0001", "bodySha256": EMPTY_SHA256, "idempotencyKey": "IDEM-GOLDEN-0001", "signature": "caR7gxdSziU4IXI/kX4u8X11tOq0jGRmzzNHn8rEEAU=", } def test_sha256_empty_body(): assert sha256_hex(b"") == EMPTY_SHA256 def test_signature_golden_vector(): message = build_message( GOLDEN["method"], GOLDEN["path"], GOLDEN["accessKey"], GOLDEN["timestamp"], GOLDEN["nonce"], GOLDEN["bodySha256"], GOLDEN["idempotencyKey"]) assert message == ( "POST&/api/mdp/inbound/S1_SALES_ORDER_ENTRY&SIMKEY01&1750000000" "&nonce-fixed-0001&" + EMPTY_SHA256 + "&IDEM-GOLDEN-0001") assert sign(GOLDEN["secret"], message) == GOLDEN["signature"] def test_signature_changes_with_secret(): message = build_message("POST", "/api/mdp/inbound/MDM_ITEM", "K", "1", "n", EMPTY_SHA256, "I") assert sign("secret-a", message) != sign("secret-b", message) def test_request_headers_do_not_leak_signature(): """sent_headers 中签名必须遮蔽;Secret 不出现在任何返回头。""" client = InboundClient("http://127.0.0.1:1", "SIMKEY01", GOLDEN["secret"]) # 目标端口 1 必然连接失败,但 headers 在异常前已构造并返回 status, body, headers = client.request( "GET", "/api/mdp/inbound/MDM_ITEM/schema", b"", idem=GOLDEN["idempotencyKey"], ts=int(GOLDEN["timestamp"]), nonce=GOLDEN["nonce"], contract_version="v2") assert status == 0 assert headers["X-Signature"] == "***REDACTED***" assert headers["X-Access-Key"] == "SIMKEY01" assert headers["X-Content-SHA256"] == EMPTY_SHA256 assert headers["X-Mdp-Contract-Version"] == "v2" assert GOLDEN["signature"] not in str(headers) for value in headers.values(): assert GOLDEN["secret"] not in str(value) assert "SOURCE_UNREACHABLE" in str(body) def test_missing_idempotency_header_when_skipped(): client = InboundClient("http://127.0.0.1:1", "SIMKEY01", "s") _, _, headers = client.request("POST", "/api/mdp/inbound/MDM_ITEM", b"{}", skip_idem=True) assert "Idempotency-Key" not in headers