forked from DecentralizedClimateFoundation/DCIPs
302 lines
15 KiB
Markdown
302 lines
15 KiB
Markdown
|
---
|
|||
|
eip: 2335
|
|||
|
title: BLS12-381 Keystore
|
|||
|
author: Carl Beekhuizen <carl@ethereum.org>
|
|||
|
discussions-to: https://github.com/ethereum/EIPs/issues/2339
|
|||
|
status: Stagnant
|
|||
|
type: Standards Track
|
|||
|
category: ERC
|
|||
|
created: 2019-09-30
|
|||
|
requires: 2333, 2334
|
|||
|
---
|
|||
|
|
|||
|
## Simple Summary
|
|||
|
|
|||
|
A JSON format for the storage and interchange of BLS12-381 private keys.
|
|||
|
|
|||
|
## Abstract
|
|||
|
|
|||
|
A keystore is a mechanism for storing private keys. It is a JSON file that encrypts a private key and is the standard for interchanging keys between devices as until a user provides their password, their key is safe.
|
|||
|
|
|||
|
## A note on purpose
|
|||
|
|
|||
|
This specification is designed not only to be an Ethereum 2.0 standard, but one that is adopted by the wider community who have adopted the BLS12-381 signature standard. It is therefore important also to consider the needs of the wider industry along with those specific to Ethereum. As a part of these considerations, it is the intention of the author that this standard eventually migrate to a more neutral repository in the future.
|
|||
|
|
|||
|
## Motivation
|
|||
|
|
|||
|
The secure storage and exchange of keys is a vital component of the user experience as people are expected to hold their own keys. It allows users to control access to individual keys and their use by applications.
|
|||
|
|
|||
|
In Ethereum 1, [the Web3 Secret Storage Definition](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) fulfills these requirements, however it is not perfectly suitable for these purposes moving forward. Specifically the problems with the existing standard are:
|
|||
|
|
|||
|
* __The use of Keccak256.__ Eth1 keystores use Keccak for their checksum, a sensible choice considering its usage within Ethereum 1. BLS12-381 [signatures](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-00), [keys (EIP-2333)](./eip-2333.md), and key-storage are inter-chain standards, the establishment and proliferation of which hinges on them being neutral to all chains, something which Keccak is not.
|
|||
|
|
|||
|
* __A lack of abstraction.__ Eth1 keystores are a result of an iterative design process whereby functionality was added and modified as needed without considering how abstractions could simplify the notion of different properties.
|
|||
|
|
|||
|
## Specification
|
|||
|
|
|||
|
The process of decrypting the secret held within a keystore can be broken down into 3 sub-processes: obtaining the decryption key, verifying the password and decrypting the secret. Each process has its own functions which can be selected from as well as parameters required for the function all of which are specified within the keystore file itself.
|
|||
|
|
|||
|
### Password requirements
|
|||
|
|
|||
|
The password is a string of arbitrary unicode characters. The password is first converted to its NFKD representation, then the control codes (specified below) are stripped from the password and finally it is UTF-8 encoded.
|
|||
|
|
|||
|
#### Control codes removal
|
|||
|
|
|||
|
The C0, C1, and `Delete` control codes are not valid characters in the password and should therefore be stripped from the password. C0 are the control codes between `0x00` - `0x1F` (inclusive) and C1 codes lie between `0x80` and `0x9F` (inclusive). `Delete`, commonly known as "backspace", is the UTF-8 character `7F` which must also be stripped. Note that space (`Sp` UTF-8 `0x20`) is a valid character in passwords despite it being a pseudo-control character.
|
|||
|
|
|||
|
### Modules
|
|||
|
|
|||
|
This standard makes use of the notion of a _module_ which serves to represent, in an abstract sense, the different cryptographic constructions and corresponding parameters for each component of the keystore. The idea being that components can be swapped out without affecting the rest of the specification should the need arise.
|
|||
|
|
|||
|
A module is comprised of a `function`, which defines which cryptographic construct is being used, `params`, the parameters required by the function, and `message` the primary input to the function.
|
|||
|
|
|||
|
### Decryption key
|
|||
|
|
|||
|
The decryption key is an intermediate key which is used both to verify the user-supplied password is correct, as well as for the final secret decryption. This key is simply derived from the password, the `function`, and the `params` specified by the`kdf` module as per the keystore file.
|
|||
|
|
|||
|
| KDF | `"function"` | `"params"` | `"message"` | Definition |
|
|||
|
|----------------|--------------|------------------------------------------------------------------------------------------|-------------|--------------------------------------------------|
|
|||
|
| PBKDF2-SHA-256 | `"pbkdf2"` | <ul><li>`"c"`</li><li>`"dklen"`</li><li>`"prf: "hmac-sha256"`</li><li>`"salt"`</li></ul> | | [RFC 2898](https://www.ietf.org/rfc/rfc2898.txt) |
|
|||
|
| scrypt | `"scrypt"` | <ul><li>`"dklen"`</li><li>`"n"`</li><li>`"p"`</li><li>`"r"`</li><li>`"salt"`</li></ul> | | [RFC 7914](https://tools.ietf.org/html/rfc7914) |
|
|||
|
|
|||
|
### Password verification
|
|||
|
|
|||
|
The password verification step verifies that the password is correct with respect to the `checksum.message`, `cipher.message`, and `kdf`. This is done by appending the `cipher.message` to the 2nd 16 bytes of the decryption key, obtaining its SHA256 hash and verifying whether it matches the `checksum.message`.
|
|||
|
|
|||
|
#### Inputs
|
|||
|
|
|||
|
* `decryption_key`, the octet string obtained from decryption key process
|
|||
|
* `cipher_message`, the octet string obtained from keystore file from `crypto.cipher.message`
|
|||
|
* `checksum_message`, the octet string obtained from keystore file from `crypto.checksum.message`
|
|||
|
|
|||
|
#### Outputs
|
|||
|
|
|||
|
* `valid_password`, a boolean value indicating whether the password is valid
|
|||
|
|
|||
|
#### Definitions
|
|||
|
|
|||
|
* `a[0:3]` returns a slice of `a` including octets 0, 1, 2
|
|||
|
* `a | b` is the concatenation of `a` with `b`
|
|||
|
|
|||
|
#### Procedure
|
|||
|
|
|||
|
```text
|
|||
|
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
|
|||
|
```
|
|||
|
|
|||
|
| Hash | `"function"` | `"params"` | `"message"` | Definition |
|
|||
|
|------------|-----------------|------------|-------------|-------------------------------------------------|
|
|||
|
| SHA-256 | `"sha256"` | | | [RFC 6234](https://tools.ietf.org/html/rfc6234) |
|
|||
|
|
|||
|
### Secret decryption
|
|||
|
|
|||
|
The `cipher.function` encrypts the secret using the decryption key, thus to decrypt it, the decryption key along with the `cipher.function` and `cipher.params` must be used. If the `decryption_key` is longer than the key size required by the cipher, it is truncated to the correct number of bits. In the case of aes-128-ctr, only the first 16 bytes of the `decryption_key` are used as the AES key.
|
|||
|
|
|||
|
| Cipher | `"function"` | `"params"` | `"message"` | Definition |
|
|||
|
|----------------------|-----------------|--------------------------|-------------|-------------------------------------------------|
|
|||
|
| AES-128 Counter Mode | `"aes-128-ctr"` | <ul><li>`"iv"`</li></ul> | | [RFC 3686](https://tools.ietf.org/html/rfc3686) |
|
|||
|
|
|||
|
## Description
|
|||
|
|
|||
|
This field is an optional field to help explain the purpose and identify a particular keystores in a user-friendly manner. While this field can, and should, be used to help distinguish keystores from one-another, the `description` **is not necessarily unique**.
|
|||
|
|
|||
|
## PubKey
|
|||
|
|
|||
|
The `pubkey` is the public key associated with the the private key secured within the keystore. It is stored here to improve user experience and security which is achieved by not requiring users to enter their password just to obtain their public keys. This field is required if the secret being stored within the keystore is a private key. The encoding of the `pubkey` is specified in the in the appropriate signature standard (eg. [BLS12-381 signature standard](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-00)), but can be seen as a byte-string in the abstract and should be directly compatible with the appropriate signature library.
|
|||
|
|
|||
|
## Path
|
|||
|
|
|||
|
The `path` indicates where in the key-tree a key originates from. It is a string defined by [EIP-2334](./eip-2334.md), if no path is known or the path is not relevant, the empty string, `""` indicates this. The `path` can specify an arbitrary depth within the tree and the deepest node within the tree indicates the depth of the key stored within this file.
|
|||
|
|
|||
|
## UUID
|
|||
|
|
|||
|
The `uuid` provided in the keystore is a randomly generated UUID as specified by [RFC 4122](https://tools.ietf.org/html/rfc4122). It is used as a 128-bit proxy for referring to a particular set of keys or account.
|
|||
|
|
|||
|
## Version
|
|||
|
|
|||
|
The `version` is set to `4`.
|
|||
|
|
|||
|
## JSON schema
|
|||
|
|
|||
|
The keystore, at its core, is constructed with modules which allow for the configuration of the cryptographic constructions used password hashing, password verification and secret decryption. Each module is composed of: `function`, `params`, and `message` which corresponds with which construction is to be used, what the configuration for the construction is, and what the input is.
|
|||
|
|
|||
|
```json
|
|||
|
{
|
|||
|
"$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"
|
|||
|
]
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
## Rationale
|
|||
|
|
|||
|
The rationale behind the design of this specification is largely the same as that behind the [Ethereum 1 keystore definition](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) except for the lack of support for Keccak (explained in [motivation above](#motivation)) and the notion of modules.
|
|||
|
|
|||
|
Modules provide a very useful level of abstraction which allow the Key-Derivation-Function, Checksum, and Cipher to be thought of as instances of the same thing allowing for their substitution with minimal effort.
|
|||
|
|
|||
|
The `version` is set to 4 to prevent collisions with the existing Ethereum keystore standard.
|
|||
|
|
|||
|
## Backwards Compatibility
|
|||
|
|
|||
|
This specification is not backwards compatible with the [existing keystore standard](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) due to the lack of Keccak256 checksums as explained above. While this format is capable of supporting Keccak checksums via the Checksum module, it would defeat the purpose of this standard to include it as this standard could no longer be considered neutral with respect to other projects in the industry.
|
|||
|
|
|||
|
## Test Cases
|
|||
|
|
|||
|
### Scrypt Test Vector
|
|||
|
|
|||
|
Password `"𝔱𝔢𝔰𝔱𝔭𝔞𝔰𝔰𝔴𝔬𝔯𝔡🔑"`
|
|||
|
Encoded Password: `0x7465737470617373776f7264f09f9491`
|
|||
|
Secret `0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f`
|
|||
|
|
|||
|
```json
|
|||
|
{
|
|||
|
"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 Test Vector
|
|||
|
|
|||
|
Password `"𝔱𝔢𝔰𝔱𝔭𝔞𝔰𝔰𝔴𝔬𝔯𝔡🔑"`
|
|||
|
Encoded Password: `0x7465737470617373776f7264f09f9491`
|
|||
|
Secret `0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f`
|
|||
|
|
|||
|
```json
|
|||
|
{
|
|||
|
"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
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
## Implementation
|
|||
|
|
|||
|
Implementations exist in the following languages:
|
|||
|
|
|||
|
* [Python3](https://github.com/ethereum/eth2.0-deposit-cli)
|
|||
|
* [TypeScript](https://github.com/nodefactoryio/bls-keystore)
|
|||
|
* [Go](https://github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4/)
|
|||
|
|
|||
|
## Copyright
|
|||
|
|
|||
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|