Files
msa-agent/contracts/test-vectors.json
T
chick 3473816167 feat: 独立成仓库,自己拥有契约文件
agent 从 simadmin-relay 拆出来,作为公开仓库:安装命令要由节点执行,而节点没有凭据,所以
二进制必须放在匿名可读的地方,而 relay 源码与 IPA 继续私有。

拆出来之后有一处必须改,不是搬文件:`contracts/agent-policy.json` 原先由 relay 仓库拥有、
agent 侧是 vendored 副本(Makefile 从 `../contracts/` 拷、policy_test.go 断言两份相等)。
现在反过来——**agent 拥有策略**,因为 agent 是执行它的那一方,让它去拷贝一份住在别处的文件
本来就是别扭的。relay 保留 vendored 副本(它的限额从同一组数字推导),而两侧的一致性不再
靠构建期比较(跨仓库的构建期比较谁也做不了),改由运行时保证:agent 在握手时上报策略摘要,
relay 拿自己那份比对,在 `/v1/status` 里逐节点给出 `policyMatches`。这比原来的测试更强,
因为它比的是**实际部署的那两份**,而不是两个 checkout 里的文件。

`contracts/test-vectors.json` 同理:Go 实现生成它(`make vectors`),relay 的 Node 参考实现
与 app 的 Swift 测试从 vendored 副本重算。实测重新生成的结果与 relay 那份逐字节相同。

systemd 单元的 `Documentation=` 指向本仓库。`VERSION` 提到 1.0.0。

20 个提交的历史完整保留(filter-branch 按子目录抽取)。
2026-09-17 23:24:57 +08:00

130 lines
5.4 KiB
JSON

{
"generatedBy": "agent/cmd/msa-vectors",
"note": "Deterministic cross-language vectors. The Node relay (test/wire.test.js) and the Swift app recompute these from this file; a mismatch means the envelope encoding drifted, not that one side is broken.",
"suite": "X25519-ED25519-AES256GCM-HKDFSHA256",
"version": 1,
"canonical": {
"comment": "Length-prefixed canonical bytes, then the deterministic Ed25519 signature over them. ek and p are raw decoded bytes, never their base64 text.",
"input": {
"v": 1,
"node": "nd_7f3a9c2e",
"req": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
"app": "ap_5b1d",
"ts": 1727289600,
"ek": "hSDwCYkwp1R0i33ctD73Wg2/Og0mOBr066SpjqqbTmo=",
"alg": "X25519-ED25519-AES256GCM-HKDFSHA256",
"p": "AAEC/v8="
},
"bytes": {
"request": "6d73612d72656c61792d76312d726571000000000800000000000000010000000b6e645f37663361396332650000002433663235303465302d346638392d343164332d396130632d3033303565383263333330310000000761705f35623164000000080000000066f45900000000208520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a00000005000102feff",
"response": "6d73612d72656c61792d76312d72657370000000000800000000000000010000000b6e645f37663361396332650000002433663235303465302d346638392d343164332d396130632d303330356538326333333031000000080000000066f45900000000208520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a00000005000102feff"
},
"signature": {
"request": "f91fc4a40c4cb19643515894dba14a1baae602d0b307feafcf441a2d64690b94ac10e20982f8f3e8d2b48c74db11d250344256af92f7c22fa1b2c2fb6975fb0f",
"response": "3977dc4ad544ed83d9921be9929c49f5b38cd618583d99d10d096d3997237c02110da17ffebd45656453d94cd07044d2b1348e0c45381fa193bf5fb08b30b50e"
},
"publicKey": "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
"responsePublicKey": "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c"
},
"hkdf": {
"comment": "RFC 5869 test case 1, pinning the KDF itself rather than only its callers.",
"secret": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
"salt": "000102030405060708090a0b0c",
"info": "f0f1f2f3f4f5f6f7f8f9",
"length": 42,
"output": "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"
},
"x25519": {
"comment": "RFC 7748 section 6.1, then the derived per-direction key and IV for the envelope above. The IV is bound to the request id and the direction, so a key that is ever reused still cannot reuse a nonce.",
"appPrivate": "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
"agentPublic": "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
"sharedSecret": "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742",
"requestKey": "14f4f1823bd68aabccf326e5a4ee69891428491477bfc6949a25dd69bff1b1e9",
"requestIV": "e5be943a5b8ea89e34bbcb02",
"responseKey": "e350511cf24be4045c770180276146ec647283695394dec608f1695b5f07524e",
"responseIV": "44c5c0499337c5da62b463ad"
},
"aead": {
"comment": "AES-256-GCM under the request key above; the 16-byte tag is appended.",
"key": "14f4f1823bd68aabccf326e5a4ee69891428491477bfc6949a25dd69bff1b1e9",
"iv": "e5be943a5b8ea89e34bbcb02",
"plaintext": "7b226d223a22474554222c2270223a222f6170692f6865616c7468227d",
"ciphertext": "6601340c304905a8308894ab9d050d5e2ea083ee7ad43df01b9c3e79daead5321099e7203abd5985cf2ef12114"
},
"intent": {
"comment": "sha256(method | path | canonical body). The agent recomputes it from the request it is about to forward, so a relay cannot edit a confirmed write.",
"method": "POST",
"path": "/api/sms/send",
"body": "{\"phone_number\":\"+8613800000000\",\"content\":\"hello\"}",
"digest": "d0f584d844cf6e25d5b233a65e86fd80b82a396836cb80522e528b0f6ea5ac00"
},
"canonicalJson": [
{
"input": "{\"b\":1,\"a\":2}",
"output": "{\"a\":2,\"b\":1}"
},
{
"input": "[3,1,2]",
"output": "[3,1,2]"
},
{
"input": " null ",
"output": "null"
},
{
"input": "true",
"output": "true"
},
{
"input": "\"\u003c\u0026\u003e\"",
"output": "\"\u003c\u0026\u003e\""
},
{
"input": "{\"x\":100}",
"output": "{\"x\":100}"
},
{
"input": "{\"a\":{\"c\":[1,{\"b\":2,\"a\":1}],\"b\":0},\"A\":9}",
"output": "{\"A\":9,\"a\":{\"b\":0,\"c\":[1,{\"a\":1,\"b\":2}]}}"
},
{
"input": "{\"phone_number\":\"+8613800000000\",\"content\":\"hello\"}",
"output": "{\"content\":\"hello\",\"phone_number\":\"+8613800000000\"}"
},
{
"input": "{\"content\":\"a\u2028b\"}",
"output": "{\"content\":\"a\u2028b\"}"
},
{
"input": "{\"content\":\"a\u2029b\"}",
"output": "{\"content\":\"a\u2029b\"}"
},
{
"input": "{\"content\":\"😀\"}",
"output": "{\"content\":\"😀\"}"
},
{
"input": "{\"content\":\"\\b\\f\\u0001\"}",
"output": "{\"content\":\"\\b\\f\\u0001\"}"
},
{
"input": "{\"content\":\"\\\\u2028\"}",
"output": "{\"content\":\"\\\\u2028\"}"
},
{
"input": "{\"é\":1,\"z\":2}",
"output": "{\"z\":2,\"é\":1}"
},
{
"input": "{\"𐀀\":1,\"�\":2}",
"output": "{\"�\":2,\"𐀀\":1}"
}
],
"canonicalJsonRejected": [
"{\"content\":\"\\ud800\"}",
"{\"content\":\"\\udc00\"}",
"{\"content\":\"\\ud800\\ud800\"}",
"{\"content\":\"\\ud800x\"}"
]
}