Alert Source Discuss
🚧 Stagnant Standards Track: Interface

EIP-3030: BLS 远程签名器 HTTP API

Authors Herman Junge (@hermanjunge)
Created 2020-09-30
Discussion Link https://ethereum-magicians.org/t/eip-3030-bls-remote-signer-http-api-standard/4810

简单总结

本 EIP 定义了一个 BLS 远程签名器的 HTTP API 标准,供验证者客户端在 Ethereum 2.0 (eth2) 的上下文中签名区块提案和证明。

摘要

验证者客户端通过使用 BLS 私钥签署区块的提案和证明来为 Eth2 区块链的共识做出贡献,该私钥必须始终可供该客户端使用。

BLS 远程签名器 API 旨在供验证者客户端使用,这些客户端寻求更安全的途径来存储其 BLS12-381 私钥,从而使它们能够在更宽松和可扩展的环境中运行。

动机

Eth2 使用 BLS12-381 签名。

eth2 区块链上的共识是通过验证者客户端对区块的提案和证明来实现的,使用 BLS 私钥(签名 密钥),该私钥必须在每次消息签名时都可用:也就是说,至少每个 epoch(6.4 分钟)一次,在这个 epoch 内的一个很小的时间窗口(一个 slot,即 12 秒),因为预计每个验证者在每个 epoch 中恰好证明一次。

eth2 规范 没有明确提供关于 BLS 私钥必须/应该存储在哪里的指令,而是将这个实现细节留给客户端团队,他们假设这个加密秘密与验证者客户端存储在同一主机上。

在验证者客户端在物理安全的网络中运行的用例中,这种假设是足够的(即,除了操作员之外,没有人有机会登录到托管验证者客户端的机器),因为这种配置只允许来自验证者客户端的 出站 调用。在这种情况下,只有物理安全漏洞或远程代码执行 (RCE) 漏洞才能允许攻击者任意访问设备的存储或内存。

但是,在某些用例中,操作员需要以不太受限制的安全环境中运行验证者客户端节点,例如云提供商提供的环境。尽管有任何安全期望,但没有什么可以阻止恶意操作员任意访问在节点内运行的资产。

当需要通过利用容器编排解决方案(例如 Kubernetes)来执行验证者时,情况并没有好转。从运营和安全角度来看,跨节点处理密钥可能会成为一种负担。

所提出的解决方案包括运行一个专门的节点,该节点具有对密钥的独占访问权限,监听一个简单的 API(在规范部分中定义),并返回所请求的签名。在此模式下工作的操作员必须使用具有支持使用此 API 的足够功能的客户端。

本规范的重点是 按需 提供 BLS 签名。身份验证、密钥管理(创建、更新和删除)和传输加密等方面将在本文档的原理部分进行讨论。此外,本文档的威胁模型部分提供了威胁和攻击向量的(非详尽的)列表,以及建议的相关缓解策略。

规范

GET /upcheck

响应

成功
代码 200
内容 {"status": "OK"}

GET /keys

返回签名者可用的密钥的标识符。

响应

成功
代码 200
内容 {"keys": "[identifier]"}

POST /sign/:identifier

URL 参数
:identifier public_key_hex_string_without_0x

请求

JSON Body

bls_domain 必需 BLS 签名域。
规范中所定义,使用小写字母,省略 domain 前缀。
支持 beacon_proposerbeacon_attesterrandao
data 必需 要签名的数据。
blockattestationepoch 规范中所定义。
fork 必需 包含先前和当前版本的 Fork 对象。
规范 中所定义。
genesis_validators_root 必需 用于域分离和链版本控制的 Hash256

可选 签名者将忽略任何其他字段。

响应

成功
代码 200
内容 {"signature": "<signature_hex_string>"}

其中 signature 是一个编码为十六进制字符串的 BLS 签名 字节数组。

错误
代码 400
内容 {"error": "<错误的请求错误消息>"}

错误
代码 404
内容 {"error": "未找到密钥:<identifier>"}

原理

UNIX 哲学:简单的 API

此 API 规范仅包含三个方法:一个用于 状态,一个用于 列出可用密钥,一个用于 生成签名。没有用于身份验证、密钥管理或传输加密的方法。

以下小节讨论了客户端实施人员相对于这些主题需要考虑的方面。

附加功能的实现

从 API 管道视图来看,我们有两个节点:验证者客户端 (1) 向远程签名者 (2) 发出请求。可以通过在这两个节点之间引入元素来构建更复杂的链。可以通过设置反向代理服务,也可以通过向远程签名器实现添加插件。

身份验证

可以通过使用 HTTP 请求头来完成。有几种方法可以协商和颁发有效的令牌,以验证验证者客户端和远程签名者之间的通信,每种方法都有潜在的缺点(例如重放攻击、将令牌分发给验证者客户端的挑战等)。通常,任何身份验证方法都必须与传输加密相结合才能有效。

操作员还可以在验证者客户端的网络和远程签名者的网络之间实施网络访问控制列表 (ACL),从而通过要求潜在的攻击者与验证者客户端位于同一网络中来减少攻击面。

密钥管理

有几种存储密钥的方法,即硬件安全模块 (HSM)、密钥管理应用程序(例如 Hashicorp Vault)、具有严格专用网络 ACL 规则的云存储,甚至目录中的原始文件。通常,远程签名器实现者将从 HTTP API 中抽象出存储介质。

从这个角度来看,任何创建、更新或删除密钥的过程都应与客户端实现分开构建。

传输加密

如果操作员正在使用自签名证书,则需要使用远程签名器的客户端增强功能允许此选项。

测试用例

测试数据

  • BLS 对
    • 公钥:0xb7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a
    • 私钥:0x68081afeb7ad3e8d469f87010804c3e8d53ef77d393059a55132637206cc59ec
  • 签名根:0xb6bb8f3765f93f4f1e7c7348479289c9261399a3c6906685e320071a1a13955c
  • 预期签名:0xb5d0c01cef3b028e2c5f357c2d4b886f8e374d09dd660cd7dd14680d4f956778808b4d3b2ab743e890fc1a77ae62c3c90d613561b23c6adaeb5b0e288832304fddc08c7415080be73e556e8862a1b4d0f6aa8084e34a901544d5bb6aeed3a612

GET /upcheck

# 成功

## 请求
curl -v localhost:9000/upcheck

## 响应
*   Trying 127.0.0.1:9000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9000 (#0)
> GET /upcheck HTTP/1.1
> Host: localhost:9000
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: application/json
< content-length: 15
< date: Wed, 30 Sep 2020 02:25:08 GMT
<
* Connection #0 to host localhost left intact
{"status":"OK"}

GET /keys

# 成功

## 请求
curl -v localhost:9000/keys

## 响应
*   Trying 127.0.0.1:9000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9000 (#0)
> GET /publicKeys HTTP/1.1
> Host: localhost:9000
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: application/json
< content-length: 116
< date: Wed, 30 Sep 2020 02:25:36 GMT
<
* Connection #0 to host localhost left intact
{"keys":["b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a"]}

# 服务器错误

## 准备
## `chmod` 密钥目录为八进制 311 (-wx--x--x)。

## 请求
curl -v localhost:9000/keys

## 响应
*   Trying 127.0.0.1:9000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9000 (#0)
> GET /publicKeys HTTP/1.1
> Host: localhost:9000
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 500 Internal Server Error
< content-length: 43
< date: Wed, 30 Sep 2020 02:26:09 GMT
<
* Connection #0 to host localhost left intact
{"error":"Storage error: PermissionDenied"}

POST /sign/:identifier

# 成功

## 请求
curl -v -X POST -d @payload.json -H 'Content-Type: application/json' localhost:9000/sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a

## 响应
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1:9000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9000 (#0)
> POST /sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a HTTP/1.1
> Host: localhost:9000
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 84
>
* upload completely sent off: 84 out of 84 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: application/json
< content-length: 210
< date: Wed, 30 Sep 2020 02:16:02 GMT
<
* Connection #0 to host localhost left intact
{"signature":"0xb5d0c01cef3b028e2c5f357c2d4b886f8e374d09dd660cd7dd14680d4f956778808b4d3b2ab743e890fc1a77ae62c3c90d613561b23c6adaeb5b0e288832304fddc08c7415080be73e556e8862a1b4d0f6aa8084e34a901544d5bb6aeed3a612"}

# 错误的请求错误

## 请求
curl -v -X POST -d 'foobar' -H 'Content-Type: application/json' localhost:9000/sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a

## 响应
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1:9000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9000 (#0)
> POST /sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a HTTP/1.1
> Host: localhost:9000
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 23
>
* upload completely sent off: 23 out of 23 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 400 Bad Request
< content-length: 38
< date: Wed, 30 Sep 2020 02:15:05 GMT
<
* Connection #0 to host localhost left intact
{"error":"Unable to parse body message from JSON: Error(\"expected ident\", line: 1, column: 2)"}

# 没有可用的密钥

## 请求
curl -v -X POST -d @payload.json -H 'Content-Type: application/json' localhost:9000/sign/000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

## 响应
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1:9000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9000 (#0)
> POST /sign/000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 HTTP/1.1
> Host: localhost:9000
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 84
>
* upload completely sent off: 84 out of 84 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< content-length: 123
< date: Wed, 30 Sep 2020 02:18:53 GMT
<
* Connection #0 to host localhost left intact
{"error":"Key not found: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}

# 服务器错误

## 准备
## `chmod` 密钥目录和文件为八进制 311 (-wx--x--x)。
## `chmod` 返回到 755 以在之后删除它们。

## 请求
curl -v -X POST -d @payload.json -H 'Content-Type: application/json' localhost:9000/sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a

## 响应
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1:9000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9000 (#0)
> POST /sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a HTTP/1.1
> Host: localhost:9000
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 84
>
* upload completely sent off: 84 out of 84 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 500 Internal Server Error
< content-length: 43
< date: Wed, 30 Sep 2020 02:21:08 GMT
<
* Connection #0 to host localhost left intact
{"error":"Storage error: PermissionDenied"}

实现

存储库 URL 语言 组织 评论
BLS Remote Signer Rust Sigma Prime 支持提议的规范。
Web3signer Java PegaSys 支持提议的规范,但方法略有不同
{/sign => /api/v1/eth2/sign, /publicKeys => /api/v1/eth2/publicKeys}。
Remote Signing Wallet Golang Prysmatics Labs 支持 gRPC 和 JSON over HTTP。

安全注意事项

威胁模型

让我们考虑以下威胁及其缓解措施:

威胁 缓解措施
攻击者可以欺骗验证者客户端。 请参阅身份验证中的讨论。
攻击者可以向签名者发送精心制作的消息,从而导致削减冒犯。 远程签名器的操作员有责任添加验证模块,如附加功能的实现中所述。
攻击者可以创建、更新或删除密钥。 密钥不能通过远程签名器的任何界面写入。
攻击者可以拒绝发送的消息。 在签名者中实施日志记录。通过将日志发送到系统日志框来增强它。
攻击者可以通过从存储中检索密钥来泄露私钥的内容。 在硬件安全模块 (HSM) 中存储。

在密钥管理应用程序中存储(例如 Hashicorp Vault)。
攻击者可以窃听密钥的上传。 根据每个存储规范,使用安全通道上传密钥。
攻击者可以窃听从远程签名器检索密钥。 始终使用安全通道在存储和远程签名器节点之间传递数据。
攻击者可以转储远程签名器中的内存以泄露密钥。 阻止物理访问运行远程签名器的节点。

阻止访问运行远程签名器的节点的终端:日志被发送到系统日志框。由一个简单的、非参数化的 API 触发的部署。

在内存中实现密钥归零。

探索在可信执行环境 (TEE) 中编译和运行远程签名器。
攻击者可以对远程签名器进行 DoS 攻击。 实施 IP 过滤。

实施速率限制。

版权

CC0 下放弃版权及相关权利。

Citation

Please cite this document as:

Herman Junge (@hermanjunge), "EIP-3030: BLS 远程签名器 HTTP API [DRAFT]," Ethereum Improvement Proposals, no. 3030, September 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-3030.