LibertyNetLibertyNet 开发者

身份

自证 DID:怎么推导、为什么有两种编码、以及那个不能重排的验证顺序。

LibertyNet 的身份是一个 DID——由公钥推导而来,不是任何人分配的。

did:svrp:n:268d4fe0
│   │    │ └── 标识符:SHA-256(公钥) 截断到 4 字节,十六进制
│   │    └──── 角色标签:n=节点,o=运营者,h=主机
│   └───────── method:svrp
└───────────── scheme

没人签发它。它是算出来的。所以你不问任何人就能核对。

两种编码,同一个身份

这是最容易让人白花一下午的细节。

同一个密钥,在不同地方会以两种拼写出现:

形式样子推导出现在哪
did:svrp:n:268d4fe0SHA-256(密钥)[0:4]绑定、运营者记录、/peers
全 hexdid:svrp:df9d4b9f…02d32 字节密钥本身/nodes、运行中的守护进程

还有一个 10 位 hex 回退形式SHA-256(密钥)[0:5]),保留给碰撞处理。 任何只硬编码 4 字节公式的实现,在第一个 10 位 hex 身份签发的那天就会拒绝一个合法身份—— 而且报的错看起来像是伪造。

警告

在这里做字符串比较会骗你。

javascript
"did:svrp:n:8545027b" === "did:svrp:df9d4b9f...02d"   // false

是同一个节点。比较 DID 字符串会悄悄把一个节点拆成两个——在你的数据库里、 仪表盘里、计费里。而且这个 bug 在有人注意到重复之前是看不见的。

请比较底层身份:

javascript
sameIdentity(shortDid, fullDid, publicKey)   // → true

公钥也有两种编码

GET /nodes 返回 hex,GET /peers 返回 base58。是同样的 32 字节。

把 base58 的密钥当 hex 解析不会报错——它会产出垃圾,导致每一次 id-binding 检查都失败, 看起来就像整个网络都是伪造的。如果你的验证突然全部拒绝,几乎总是这个原因。

SDK 两种编码都接受,并在哈希前归一化。

验证顺序

警告

这个顺序是固定的。它不是代码风格偏好,不允许重排或短路

  1. 验 id-binding —— 声称的身份是否由这个公钥推导而来?不是就立刻拒绝,不要进入第 2 步。
  2. 验签名 —— Ed25519,对规范字节。
  3. 验其余全部规则 —— 有效期、DID 状态、防重放、权限范围。
  4. 接受 —— 以上全部通过之后才接受。

省略第 1 步是验证绕过,不是优化。 为什么

身份 ≠ 认证

值得说清楚:

  • id-binding 证明这个标识符对这个密钥是良构的。这是一个关于算术的陈述。
  • 认证证明调用方持有私钥。它要求对方签一个选定的东西——一个新鲜的挑战—— 这样重放的签名就没用了。

一个出示了 DID 和匹配公钥的调用方,并没有证明任何关于持有的事:这两样他从公开目录里 抄就行了。完整的挑战—应答见运营者登录

根密钥与设备密钥

运营者有一个根密钥和一个或多个设备密钥

根密钥只签一样东西:一份 DeviceCredential,指明某个设备密钥及其权限。然后它就回到 保险箱里,离线保存。之后的每一次操作——登录、授权绑定——都由设备密钥完成。

根密钥  ──签署──▶  DeviceCredential  ──其密钥签署──▶  AuthorizationCredential
(离线、冷存)      (该设备可以行动)                 (这个节点、这些限额)

要点:笔记本被偷,代价是一台设备——你吊销它。代价不是你的身份,那是不可恢复的。

自己验证一遍

快速开始里有十行不依赖任何包的代码,对着真实网络验证全部身份。