Alert Source Discuss
🚧 Stagnant Standards Track: ERC

ERC-2335: BLS12-381 密钥库

Authors Carl Beekhuizen (@CarlBeek) <carl@ethereum.org>
Created 2019-09-30
Discussion Link https://github.com/ethereum/EIPs/issues/2339
Requires EIP-2333, EIP-2334

简述

用于存储和交换 BLS12-381 私钥的 JSON 格式。

摘要

密钥库是一种存储私钥的机制。它是一个 JSON 文件,用于加密私钥,并且是设备之间交换密钥的标准,因为在用户提供密码之前,他们的密钥是安全的。

关于目的的说明

此规范不仅旨在成为以太坊 2.0 标准,而且旨在被更广泛的采用 BLS12-381 签名标准的社区所采用。因此,同样重要的是要考虑更广泛行业的需求以及以太坊的特定需求。作为这些考虑的一部分,作者的意图是该标准最终将在将来迁移到更中立的存储库。

动机

密钥的安全存储和交换是用户体验的重要组成部分,因为人们希望持有自己的密钥。它允许用户控制对单个密钥的访问以及应用程序对密钥的使用。

在以太坊 1 中,Web3 密钥存储定义 满足了这些要求,但是,它并不完全适合未来使用。现有标准的具体问题是:

  • 使用 Keccak256. Eth1 密钥库使用 Keccak 作为其校验和,考虑到它在以太坊 1 中的使用,这是一个明智的选择。BLS12-381 签名密钥 (EIP-2333) 和密钥存储是跨链标准,其建立和普及取决于它们对所有链都是中立的,而 Keccak 并非如此。

  • 缺乏抽象. Eth1 密钥库是迭代设计过程的结果,在该过程中,根据需要添加和修改功能,而没有考虑抽象如何简化不同属性的概念。

规范

解密密钥库中保存的密钥的过程可以分为 3 个子过程:获取解密密钥、验证密码和解密密钥。每个过程都有自己的函数,可以从中进行选择,以及函数所需的所有参数,所有这些都在密钥库文件本身中指定。

密码要求

密码是任意 Unicode 字符的字符串。首先将密码转换为其 NFKD 表示形式,然后从密码中删除控制代码(如下指定),最后对其进行 UTF-8 编码。

删除控制代码

C0、C1 和 Delete 控制代码在密码中不是有效的字符,因此应从密码中删除。C0 是 0x00 - 0x1F(含)之间的控制代码,C1 代码位于 0x800x9F(含)之间。Delete 通常被称为“退格键”,是 UTF-8 字符 7F,也必须将其删除。请注意,空格(Sp UTF-8 0x20)是密码中的有效字符,尽管它是一种伪控制字符。

模块

此标准使用了 模块 的概念,该模块用于以抽象的方式表示密钥库的每个组件的不同加密结构和相应参数。其思想是,如果需要,可以替换组件,而不会影响规范的其余部分。

一个模块由一个 function 组成,该 function 定义了正在使用的加密结构,params,该函数所需的参数,以及 message,该函数的主要输入。

解密密钥

解密密钥是一个中间密钥,用于验证用户提供的密码是否正确,以及用于最终的密钥解密。该密钥只是根据密钥库文件,从密码、functionkdf 模块指定的 params 派生的。

KDF "function" "params" "message" 定义
PBKDF2-SHA-256 "pbkdf2" <ul><li>"c"</li><li>"dklen"</li><li>"prf: "hmac-sha256"</li><li>"salt"</li></ul>   RFC 2898
scrypt "scrypt" <ul><li>"dklen"</li><li>"n"</li><li>"p"</li><li>"r"</li><li>"salt"</li></ul>   RFC 7914

密码验证

密码验证步骤验证密码相对于 checksum.messagecipher.messagekdf 是否正确。这是通过将 cipher.message 附加到解密密钥的第二个 16 字节,获取其 SHA256 哈希值,然后验证它是否与 checksum.message 匹配来完成的。

输入

  • decryption_key,从解密密钥过程获得的八位字节字符串
  • cipher_message,从 crypto.cipher.message 的密钥库文件获得的八位字节字符串
  • checksum_message,从 crypto.checksum.message 的密钥库文件获得的八位字节字符串

输出

  • valid_password,一个布尔值,指示密码是否有效

定义

  • a[0:3] 返回 a 的切片,包括八位字节 0、1、2
  • a | bab 的串联

流程

0. DK_slice = decryption_key[16:32]
1. pre_image = DK_slice | cipher_message
2. checksum = SHA256(pre_image)
3. valid_password = checksum == checksum_message
4. return valid_password
哈希算法 "function" "params" "message" 定义
SHA-256 "sha256"     RFC 6234

密钥解密

cipher.function 使用解密密钥加密密钥,因此要解密它,必须使用解密密钥以及 cipher.functioncipher.params。如果 decryption_key 的长度大于密码所需的密钥大小,则将其截断为正确的位数。对于 aes-128-ctr,只有 decryption_key 的前 16 个字节用作 AES 密钥。

密码 "function" "params" "message" 定义
AES-128 计数器模式 "aes-128-ctr" <ul><li>"iv"</li></ul>   RFC 3686

描述

此字段是一个可选字段,有助于解释密钥库的用途并以用户友好的方式识别特定的密钥库。虽然此字段可以并且应该用于帮助区分密钥库,但 description 不一定是唯一的

PubKey

pubkey 是与密钥库中保护的私钥关联的公钥。存储在此处是为了改善用户体验和安全性,这是通过不要求用户输入密码即可获得其公钥来实现的。如果存储在密钥库中的密钥是私钥,则此字段是必需的。pubkey 的编码在相应的签名标准中指定(例如 BLS12-381 签名标准),但可以抽象地将其视为字节字符串,并且应与相应的签名库直接兼容。

路径

path 指示密钥在密钥树中的来源位置。它是 EIP-2334 定义的字符串,如果不知道路径或路径不相关,则空字符串 "" 表示这一点。path 可以在树中指定任意深度,并且树中最深的节点指示存储在此文件中的密钥的深度。

UUID

密钥库中提供的 uuidRFC 4122 指定的随机生成的 UUID。它用作引用特定密钥集或帐户的 128 位代理。

版本

version 设置为 4

JSON 模式

密钥库的核心是使用模块构建的,这些模块允许配置用于密码哈希、密码验证和密钥解密的加密结构。每个模块由:functionparamsmessage 组成,它们分别与要使用的构造、构造的配置以及输入的内容相对应。

{
    "$ref": "#/definitions/Keystore",
    "definitions": {
        "Keystore": {
            "type": "object",
            "properties": {
                "crypto": {
                    "type": "object",
                    "properties": {
                        "kdf": {
                            "$ref": "#/definitions/Module"
                        },
                        "checksum": {
                            "$ref": "#/definitions/Module"
                        },
                        "cipher": {
                            "$ref": "#/definitions/Module"
                        }
                    }
                },
                "description": {
                    "type": "string"
                },
                "pubkey": {
                    "type": "string"
                },
                "path": {
                    "type": "string"
                },
                "uuid": {
                    "type": "string",
                    "format": "uuid"
                },
                "version": {
                    "type": "integer"
                }
            },
            "required": [
                "crypto",
                "path",
                "uuid",
                "version"
            ],
            "title": "Keystore"
        },
        "Module": {
            "type": "object",
            "properties": {
                "function": {
                    "type": "string"
                },
                "params": {
                    "type": "object"
                },
                "message": {
                    "type": "string"
                }
            },
            "required": [
                "function",
                "message",
                "params"
            ]
        }
    }
}

理论依据

此规范设计背后的基本原理与 以太坊 1 密钥库定义 背后的基本原理基本相同,除了缺少对 Keccak 的支持(在上面的 动机 中解释)以及模块的概念。

模块提供了一个非常有用的抽象级别,该级别允许将密钥派生函数、校验和和密码视为同一事物的实例,从而可以以最小的努力进行替换。

version 设置为 4,以防止与现有的以太坊密钥库标准发生冲突。

向后兼容性

由于缺少如上所述的 Keccak256 校验和,因此本规范与 现有密钥库标准 不向后兼容。虽然此格式能够通过校验和模块支持 Keccak 校验和,但将其包含在内会破坏此标准的目的,因为此标准将不再被认为是行业中其他项目的中立标准。

测试用例

Scrypt 测试向量

密码 "𝔱𝔢𝔰𝔱𝔭𝔞𝔰𝔰𝔴𝔬𝔯𝔡🔑" 编码密码:0x7465737470617373776f7264f09f9491 密钥 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f

{
    "crypto": {
        "kdf": {
            "function": "scrypt",
            "params": {
                "dklen": 32,
                "n": 262144,
                "p": 1,
                "r": 8,
                "salt": "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
            },
            "message": ""
        },
        "checksum": {
            "function": "sha256",
            "params": {},
            "message": "d2217fe5f3e9a1e34581ef8a78f7c9928e436d36dacc5e846690a5581e8ea484"
        },
        "cipher": {
            "function": "aes-128-ctr",
            "params": {
                "iv": "264daa3f303d7259501c93d997d84fe6"
            },
            "message": "06ae90d55fe0a6e9c5c3bc5b170827b2e5cce3929ed3f116c2811e6366dfe20f"
        }
    },
    "description": "This is a test keystore that uses scrypt to secure the secret.",
    "pubkey": "9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07",
    "path": "m/12381/60/3141592653/589793238",
    "uuid": "1d85ae20-35c5-4611-98e8-aa14a633906f",
    "version": 4
}

PBKDF2 测试向量

密码 "𝔱𝔢𝔰𝔱𝔭𝔞𝔰𝔰𝔴𝔬𝔯𝔡🔑" 编码密码:0x7465737470617373776f7264f09f9491 密钥 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f

{
    "crypto": {
        "kdf": {
            "function": "pbkdf2",
            "params": {
                "dklen": 32,
                "c": 262144,
                "prf": "hmac-sha256",
                "salt": "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
            },
            "message": ""
        },
        "checksum": {
            "function": "sha256",
            "params": {},
            "message": "8a9f5d9912ed7e75ea794bc5a89bca5f193721d30868ade6f73043c6ea6febf1"
        },
        "cipher": {
            "function": "aes-128-ctr",
            "params": {
                "iv": "264daa3f303d7259501c93d997d84fe6"
            },
            "message": "cee03fde2af33149775b7223e7845e4fb2c8ae1792e5f99fe9ecf474cc8c16ad"
        }
    },
    "description": "This is a test keystore that uses PBKDF2 to secure the secret.",
    "pubkey": "9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07",
    "path": "m/12381/60/0/0",
    "uuid": "64625def-3331-4eea-ab6f-782f3ed16a83",
    "version": 4
}

实现

以下语言中存在实现:

版权

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

Citation

Please cite this document as:

Carl Beekhuizen (@CarlBeek) <carl@ethereum.org>, "ERC-2335: BLS12-381 密钥库 [DRAFT]," Ethereum Improvement Proposals, no. 2335, September 2019. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2335.