Search Docs…

Search Docs…

Reference

Message Integrity (Digest) v.2 HMAC

Message Integrity (Digest) v.2 - HMAC

v2 is the hardened signing scheme that the v1 page itself calls for in its Hardening (recommended) note: HMAC-SHA256 over the raw body, with an explicit timestamp and nonce. It coexists with v1;

Wire format
POST /opentrade HTTP/1.1
Content-Type: application/json
X-Sig-Version: 2
X-Timestamp:   1715630400
X-Nonce:       3a7c9e1b4f2d8a5e0c1b9d6f3a8e5c2b
X-Signature:   8f3a4d…a91d

{"token":"…","amount":"10","currency":"USD","externalTradeType":"options","externalTradeId":"8461378","data":[]}

Headers

  • X-Sig-Version: 2 — opaque version tag. Future signing changes bump this; verifiers can switch behaviour by version.

  • X-Timestamp: <unix seconds> — when the request was signed. Server rejects requests outside ±60 s from its own clock.

  • X-Nonce: <32 hex chars> — bin2hex(random_bytes(16)), a unique per-request token. Server stores it for ≥ 180 s (= 2 × skew) and rejects any repeat within that window.

  • X-Signature: <hex> — hex( HMAC-SHA256(secret, canonical) ). Lowercase hex.

The body itself is unchanged from v1 with one exception: no "hash" field. In v2-dual transition mode the body keeps the legacy "hash" field so the partner can verify either signature; the v2 HMAC signs over those exact bytes (including the legacy field).

Canonical string

METHOD \n PATH \n TIMESTAMP \n NONCE \n SHA256_hex(BODY)

Concretely for the example above:

POST
/opentrade
1715630400
3a7c9e1b4f2d8a5e0c1b9d6f3a8e5c2b
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Five lines, \n-separated. BODY is the raw bytes the partner received (php://input), not a re-encoding. SHA256_hex(BODY) is the lowercase hex SHA-256 of those bytes.

Reference (PHP, signer)


Reference (PHP, verifier — partner side)


Critical: verify against file_get_contents('php://input') — the exact bytes the wire delivered. Never decode + re-encode the body before hashing; PHP's json_encode defaults differ between major versions (float precision, unicode escape flags, key order under sorting helpers) and any drift breaks signatures silently.

Replay-protection parameters

Parameter

Value

Notes

Timestamp skew tolerance

±60 s

Server rejects abs(now − X-Timestamp) > 60. NTP-sync both sides.

Nonce store TTL

≥ 180 s

Must be ≥ 2 × skew so the nonce cannot expire while a replayed timestamp is still considered fresh.

Nonce bit-width

128 bits

bin2hex(random_bytes(16)) — 32 hex chars.

Nonce store

Redis (recommended) or equivalent

Per-partner; isolated per-shard if the partner is sharded.