From e1dc982243a66206077a96583fb95595c88055df Mon Sep 17 00:00:00 2001 From: weiji-cryptonatty <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Mon, 21 Nov 2022 18:17:35 +0800 Subject: [PATCH 01/38] added EIP draft for private key encapsulation --- EIPS/eip-kem.md | 211 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 EIPS/eip-kem.md diff --git a/EIPS/eip-kem.md b/EIPS/eip-kem.md new file mode 100644 index 00000000000000..2c6cff4009e3a0 --- /dev/null +++ b/EIPS/eip-kem.md @@ -0,0 +1,211 @@ +--- +eip: KEM +title: Private Key Encapsulation +description: defines a specification for encapsulating private keys. +author: Base Labs (@Base-Labs) +discussions-to: https://ethereum-magicians.org/t/private-key-encapsulation-to-move-around-securely-without-entering-seed/11604 +status: Draft +type: Standards Track +category: ERC +created: 2022-11-21 +--- + + +## Abstract + +This EIP proposes a mechanism to encapsulate a private key so that it could be securely relocated to another application without providing the seed. This EIP combines `ECIES` and optional signature verification under various choices to ensure that the private key is encapsulated to a known or trusted party. + +## Motivation + +There are various cases that we might want to export one of many private keys from a much securer but less convenient wallet, which is controlled with a seed or pass phrase. + +1. We might dedicate one of many private keys for messaging purpose, and that private key is probably managed in a not-so-secure manner; +2. We might want to export one of many private keys from a hardware wallet, and split it with MPC technology so that a 3rd party service could help us identify potential frauds or known bad addresses, enforce 2FA, etc., meanwhile we can initiate transactions from a mobile device with much better UX and without carrying a hardware wallet. + +In both cases, it is safer not to provide the seed which controls the whole wallet and might contains many addresses in multiple chains. + +This EIP aims to enable such use cases. + +## Specification + +The basic idea is to encapsulate the private key with ECIES. To ensure that the ephemeral public key to encapsulate the private key to is indeed generated from a trusted party and has not been tampered with, an option is provided to sign that ephemeral public key. + +There should be a mandatory `version` parameter. This allows various kinds of Key Encapsulation Mechanisms to be adopted depending on the security considerations or preference. The list shall be short to minimize compatibility issues among different vendors. + +### Core Algorithms + +In addition to `version` string, parameters, involved keys and functions include: + +1. Sender private key `sk`, to be encapsulated to recipient. +2. Ephemeral recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes the scalar multiplication. Optionally, `R` could be signed and `signerPubKey` and `signature` are then provided for sender to verify if `R` could be trusted or not. +3. Ephemeral sender key pair `(s, S)` such that `S = [s]G`. +4. Share secret `ss := [s]R = [r]S` according to ECDH. +5. `oob`, out of band data, optional. This could be digits or an alpha-numeric string entered by user. +6. Let `derivedKey := HKDF(hash=SHA256, ikm=ss, info=oob, salt, length)`. HKDF is defined in RFC5869. The `length` should be determined by `skey` and `IV` requirements such that the symmetric key `skey = derivedKey[0:keySize]`, and `IV = derivedKey[keySize:length]`. `keySize` denotes the key size of underlying symmetric algorithm, for example, 16 (bytes) for AES-128, and 32 (bytes) for Chacha20. See **Security Considerations** for the use of `salt`. +7. `cipher := authenticated_cncryption(symAlg, skey, IV, data=sk)`. The symmetric cipher algorithm (`symAlg`) and authentication scheme are decided by the version parameter. + +A much simplified example flow without signature and verification is: + +1. Recipient application generates `(r, R)`. +2. User inputs `R` to Sender application, along with a six-digit code “123456” as `oob`. +3. Sender application generates `(s, S)`, and computes `cipher`. +4. Recipient application scans to read `S` and `cipher`. User enters “123456” as `oob` to Recipient application. +5. Recipient application decrypts `cipher` to get `sk`. +6. Optionally Recipient application could derive the address corresponding to `sk` so that user can confirm the correctness. + +### Requests +#### R1. Request for Recipient to generate ephemeral key pair + +``` +request({ + method: 'eth_generateEphemeralKeyPair', + params: [version, signerPubKey], +}) +``` + +`signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and EdDSA for Ed25519. And in this situation, it should be the case that the sender trusts `signerPubKey`, no matter how this trust is maintained. If not, next request WILL be rejected by Sender application. Also see **Security Considerations**. + +Implementation then MUST generate random private key `r` with cryptographic secure random number generator (CSRNG), and derives ephemeral public key `R = [r]G`. Implementation SHOULD safe keep the generated key pair `(r, R)` in a secure manner in accordance with the circumstances, and SHOULD keep it only for limited duration, but the specific duration is left to individual implementations. Implementation SHOULD be able to retrieve `r` when given back the corresponding public key `R` if within said duration. + +Returned value is `R`, compressed if applicable. Also see ****Encoding of data and messages****. If `signerPubKey` is provided, then `R` is followed by the `signature`, also hex-encoded. + +Alternatively, `signature` could be calculated separately, and then appended to the returned data. + +#### R2. Request for Sender to encapsulate the private key + +``` +request({ + method: 'eth_encapsulatePrivateKey', + params: [ + version, + recipient, // public key, may be followed by its signature, see signerPubKey + signerPubKey, + oob, + salt, + account + ], +}) +``` + +`recipient` is the return value from the call to generate ephemeral key pair, with optional `signature` appended either as returned or separately. + +`oob` and `salt` are just byte arrays. + +`account` is used to identify which private key to be encapsulated. With Ethereum it is an address. Also see ****Encoding of data and messages****. + +If `signerPubKey` is provided or `recipient` contains signature, the implementation MUST perform signature verification. Missing data or incorrect format etc. SHALL result in empty return and optional error logs. + +The implementation shall then proceed to retrieve the private key corresponding to `account`, and follow the ****Core Algorithms**** to encrypt it. + +The return data is a byte array which contains first the ephemeral sender public key (compressed if applicable), then cipher including any authentication tag. + +#### R3. Request for Recipient to unwrap and intake the private key + +``` +request({ + method: 'eth_intakePrivateKey', + params: [ + version, + recipientPublicKey, // no signature this time + oob, + salt, + data + ], +}) +``` + +This time `recipientPublicKey` is only the ephemeral public key `R` generated earlier in the recipient side, just for the implementation to retrieve the corresponding private key `r`. `data` is the return value from the call to encapsulate private key, which includes `S` and `cipher`. + +When the encapsulated private key `sk` is decrypted successfully, the implementation can process it further according to the designated purposes. Some general security guidelines SHALL be followed, for example, do *not* log the value, do securely wipe it after use, etc. The implementation COULD derive the corresponding public key or address for user’s verification. + +The return value of this function SHOULD be empty if success, or any error message. NEVER return the decrypted private key. + +### Options and Parameters + +Available elliptic curves are: + +- secp256k1 (mandatory) +- Ed25519 + +Available authenticated encryption schemes are: + +- AES-128-GCM (mandatory) +- AES-256-GCM +- Chacha20-Poly1305 + +Version string is simply concatenation of elliptic curve and AE scheme, for example, secp256k1-AES-128-GCM. + +Signature algorithms for each curve is: + +- secp256k1 --> ECDSA +- Ed25519 --> EdDSA + +### Encoding of data and messages + +- Raw bytes are encoded in hex and prefixed with '0x'. +- `cipher` is encoded into single byte buffer as: (`IV || encrypted_sk || tag`). +- `R`, `S` and `signerPubKey` are compressed if applicable. +- `R` or `signerPubKey` could be followed by a signature to it. + +## Rationale + +A crucial difference of this proposal with [EIP-5630](eip-5630.md) is that, with key encapsulation in order to transport private key securely, the public key from the key-recipient should be ephemeral, and mostly used only one-time. While in EIP-5630 settings, the public key of message recipient shall be stable for a while so that message senders can encrypt messages without key discovery every time. + +There is security implication to this difference, including perfect forward secrecy. We aim to achieve perfect forward secrecy by generating ephemeral key pairs in both sides every time: 1) first the recipient shall generate an ephemeral key pair, retain the private key securely, and export the public key; 2) then the key sender securely wrap the private key in ECIES, with another ephemeral key pair, then destroy the ephemeral key securely; 3) finally the recipient can unwrap the private key, then destroy its ephemeral key pair securely. After these steps, the cipher text in transport intercepted by a malicious 3rd party, is no longer decrypt-able. + +## Backward Compatibility + +No backward compatibility issues for this new proposal. + +To minimize potential compatibility issues among applications (including hardware wallets), this EIP requires that version secp256k1-AES-128-GCM MUST be supported. + +Version could be decided by user or negotiated by both sides. When there is no user input or negotiation, secp256k1-AES-128-GCM is assumed. + +### UX Recommendations +`salt` and/or `oob` data: both are inputs to the HKDF function (`oob` as “info” parameter). For better UX we suggest to require from users only one of them but this is up to the implementation. + +Recipient application is assumed to be powerful enough. Sender application could have very limited computing power and user interaction capabilities. + +## Test Cases +TODO + +## Reference Implementation +TODO + +## Security Considerations + +**Perfect Forward Secrecy**: PFS is achieved by using ephemeral key pairs in both sides. + +**Optional Signature and Trusted Public Keys** + +`R` could be signed so that Sender application can verify if `R` could be trusted or not. This involves both signature verification and if the signer could be trusted or not. While signature verification is quite straightforward in itself, the latter should be managed with care. To facilitate this trust management issue, `signerPubKey` could be further signed, creating a dual-layer trust structure: + +``` +R <-- signerPubKey <-- trusted public key +``` + +This allows various strategies to mange the trust. For example: + +- A hardware wallet vendor which takes it very serious about the brand reputation and the fund safety for its customers, could choose to trust only its own public key(s). These public keys only sign `signerPubKey` from selected partners. +- A MPC service could publish its `signerPubKey` online so that users won't verify the signature against a wrong or fake public key. + +**Security Level**: + +1. We are not considering post-quantum security. If quantum computer becomes a materialized threat, the underlying cipher of Ethereum and other L1 chains would have been replaced, and this EIP will be outdated then (as the EC part of ECIES is also broken). +2. The security level shall match that of the elliptic curve used by the underlying chains. It does not make much sense to use AES-256 to safeguard a secp256k1 private key but implementation could choose freely. +3. That being said, a key might be used in multiple chains. So the security level shall cover the most demanding requirement and potential future developments. + +AES-128, AES-256 and ChaCha20 are provided. + +**Randomness**. `r` and `s` must be generated with a cryptographic secure random number generator (CSRNG). + +`salt` could be random bytes generated the same way as `r` or `s`. `salt` could be in any length but the general suggestion is 12 or 16, which could be displayed as QR code by the screen of some hardware wallet (so that another application could scan to read). If `salt` is not provided, this EIP uses default value as “EIP-xxxx” (to be determined). + +**Out of Band Data**: `oob` data is optional. When non-empty, its content is digits or an alpha-numeric string from user. Sender application may mandate `oob` from user. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + +## Citation +Please cite this document as: +TODO From 1900829a39aaac89530c7ccfa02cc9d542eb6755 Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Wed, 23 Nov 2022 17:23:04 +0800 Subject: [PATCH 02/38] minor updates to spec: intake function shall return the Ethereum address of the private key --- EIPS/eip-kem.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/EIPS/eip-kem.md b/EIPS/eip-kem.md index 2c6cff4009e3a0..2f25ac35125e8b 100644 --- a/EIPS/eip-kem.md +++ b/EIPS/eip-kem.md @@ -2,7 +2,7 @@ eip: KEM title: Private Key Encapsulation description: defines a specification for encapsulating private keys. -author: Base Labs (@Base-Labs) +author: Base Labs (@Base-Labs), Weiji Guo (Base Labs) discussions-to: https://ethereum-magicians.org/t/private-key-encapsulation-to-move-around-securely-without-entering-seed/11604 status: Draft type: Standards Track @@ -118,7 +118,7 @@ This time `recipientPublicKey` is only the ephemeral public key `R` generated When the encapsulated private key `sk` is decrypted successfully, the implementation can process it further according to the designated purposes. Some general security guidelines SHALL be followed, for example, do *not* log the value, do securely wipe it after use, etc. The implementation COULD derive the corresponding public key or address for user’s verification. -The return value of this function SHOULD be empty if success, or any error message. NEVER return the decrypted private key. +The return value is the corresponding Ethereum address for `sk`, or empty if any error. ### Options and Parameters @@ -170,7 +170,7 @@ Recipient application is assumed to be powerful enough. Sender application could TODO ## Reference Implementation -TODO +Contributions are welcome. ## Security Considerations @@ -186,9 +186,11 @@ R <-- signerPubKey <-- trusted public key This allows various strategies to mange the trust. For example: -- A hardware wallet vendor which takes it very serious about the brand reputation and the fund safety for its customers, could choose to trust only its own public key(s). These public keys only sign `signerPubKey` from selected partners. +- A hardware wallet vendor which takes it very serious about the brand reputation and the fund safety for its customers, could choose to trust only its own public keys. These public keys only sign `signerPubKey` from selected partners. - A MPC service could publish its `signerPubKey` online so that users won't verify the signature against a wrong or fake public key. +Note that it is advised that a separate key pair should be used for signing on each curve. + **Security Level**: 1. We are not considering post-quantum security. If quantum computer becomes a materialized threat, the underlying cipher of Ethereum and other L1 chains would have been replaced, and this EIP will be outdated then (as the EC part of ECIES is also broken). From 08f0ac44557856d845365a10ee583b55d9eba8a2 Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Wed, 23 Nov 2022 18:11:41 +0800 Subject: [PATCH 03/38] added test vector #1 --- EIPS/eip-kem.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/EIPS/eip-kem.md b/EIPS/eip-kem.md index 2f25ac35125e8b..4e475c747578ac 100644 --- a/EIPS/eip-kem.md +++ b/EIPS/eip-kem.md @@ -1,3 +1,5 @@ + + --- eip: KEM title: Private Key Encapsulation @@ -167,7 +169,70 @@ Version could be decided by user or negotiated by both sides. When there is no u Recipient application is assumed to be powerful enough. Sender application could have very limited computing power and user interaction capabilities. ## Test Cases -TODO + +Through out the test cases, we fix values for below data: + +- `sk`, the private key to be encapsulated, fixed to: `0xf8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315`. The corresponding address is `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. Note that these values come from [Mastering Ethereum](https://github.com/ethereumbook/ethereumbook/blob/develop/04keys-addresses.asciidoc) +- `oob`, fixed to `0x313233343536` (string value "123456") +- `salt`, fixed to `0x6569703a2070726976617465206b657920656e63617073756c6174696f6e` (string value "eip: private key encapsulation") + +### Case 1 + +Use `version` as: `secp256k1-AES-128-GCM` + +**R1** is provided as: +``` +request({ + method: 'eth_generateEphemeralKeyPair', + params: [ + version: 'secp256k1-AES-128-GCM', + signerPubKey: '' + ], +}) +``` +Suppose the implementation generates an ephemeral key pair `(r, R)` as: + +`r`: `0x83816953554b4c8e33f340cc4d0f5995229ec2c88a2c1aaf5a9f52606ea2de13` +`R`: `0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac` + +Then `0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac` should be returned. Note that `R` is compressed. + +Therefore **R2** is provided as: +``` +request({ + method: 'eth_encapsulatePrivateKey', + params: [ + version: 'secp256k1-AES-128-GCM', + recipient: '0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac', + signerPubKey: '', + oob: '0x313233343536', + salt: '0x6569703a2070726976617465206b657920656e63617073756c6174696f6e', + account: '0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9' + ], +}) +``` +Suppose the implementation generates an ephemeral key pair `(s, S)` as: + +`s`: `0xea1843f0e8b498be263f358cf8ceaeef9eac28cf0a6be8f5f774a3d9a39b8439` +`S`: `0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915f` + +Then the return value should be: `0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915fdd00e421f6b62bcb78b772669d937ac2edbf378d8534d39fec43cf9bab7fd437b4a890da1faa734c8fba0663b3840b7a` + +Then **R3** is provided as: +``` +request({ + method: 'eth_intakePrivateKey', + params: [ + version: 'secp256k1-AES-128-GCM', + recipientPublicKey: '0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac', + oob: '0x313233343536', + salt: '0x6569703a2070726976617465206b657920656e63617073756c6174696f6e', + data: '0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915fdd00e421f6b62bcb78b772669d937ac2edbf378d8534d39fec43cf9bab7fd437b4a890da1faa734c8fba0663b3840b7a' + ], +}) +``` + +The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This matches the `account` parameter in **R2**. ## Reference Implementation Contributions are welcome. From f1d81f3aca7104bc4695ac4e59484f573f743ba7 Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Wed, 23 Nov 2022 18:19:07 +0800 Subject: [PATCH 04/38] minor formatting --- EIPS/eip-kem.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/EIPS/eip-kem.md b/EIPS/eip-kem.md index 4e475c747578ac..7be97a66a73922 100644 --- a/EIPS/eip-kem.md +++ b/EIPS/eip-kem.md @@ -191,10 +191,10 @@ request({ }) ``` Suppose the implementation generates an ephemeral key pair `(r, R)` as: - -`r`: `0x83816953554b4c8e33f340cc4d0f5995229ec2c88a2c1aaf5a9f52606ea2de13` -`R`: `0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac` - +``` +r: 0x83816953554b4c8e33f340cc4d0f5995229ec2c88a2c1aaf5a9f52606ea2de13 +R: 0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac +``` Then `0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac` should be returned. Note that `R` is compressed. Therefore **R2** is provided as: @@ -212,10 +212,10 @@ request({ }) ``` Suppose the implementation generates an ephemeral key pair `(s, S)` as: - -`s`: `0xea1843f0e8b498be263f358cf8ceaeef9eac28cf0a6be8f5f774a3d9a39b8439` -`S`: `0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915f` - +``` +s: 0xea1843f0e8b498be263f358cf8ceaeef9eac28cf0a6be8f5f774a3d9a39b8439 +S: 0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915f +``` Then the return value should be: `0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915fdd00e421f6b62bcb78b772669d937ac2edbf378d8534d39fec43cf9bab7fd437b4a890da1faa734c8fba0663b3840b7a` Then **R3** is provided as: From 5ad1ee7dffb13547e728f8ce4fa27bc176a27871 Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Wed, 23 Nov 2022 18:57:00 +0800 Subject: [PATCH 05/38] minor edits --- EIPS/eip-kem.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/EIPS/eip-kem.md b/EIPS/eip-kem.md index 7be97a66a73922..a5411ddb5ff999 100644 --- a/EIPS/eip-kem.md +++ b/EIPS/eip-kem.md @@ -1,5 +1,3 @@ - - --- eip: KEM title: Private Key Encapsulation @@ -178,7 +176,7 @@ Through out the test cases, we fix values for below data: ### Case 1 -Use `version` as: `secp256k1-AES-128-GCM` +Use `version` as: `secp256k1-AES-128-GCM`. No signature verification. **R1** is provided as: ``` @@ -191,10 +189,10 @@ request({ }) ``` Suppose the implementation generates an ephemeral key pair `(r, R)` as: -``` -r: 0x83816953554b4c8e33f340cc4d0f5995229ec2c88a2c1aaf5a9f52606ea2de13 -R: 0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac -``` + +`r`: `0x83816953554b4c8e33f340cc4d0f5995229ec2c88a2c1aaf5a9f52606ea2de13` +`R`: `0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac` + Then `0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac` should be returned. Note that `R` is compressed. Therefore **R2** is provided as: @@ -212,10 +210,10 @@ request({ }) ``` Suppose the implementation generates an ephemeral key pair `(s, S)` as: -``` -s: 0xea1843f0e8b498be263f358cf8ceaeef9eac28cf0a6be8f5f774a3d9a39b8439 -S: 0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915f -``` + +`s`: `0xea1843f0e8b498be263f358cf8ceaeef9eac28cf0a6be8f5f774a3d9a39b8439` +`S`: `0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915f` + Then the return value should be: `0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915fdd00e421f6b62bcb78b772669d937ac2edbf378d8534d39fec43cf9bab7fd437b4a890da1faa734c8fba0663b3840b7a` Then **R3** is provided as: From cd35885465cf9935ed77674747928816028a54af Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Fri, 25 Nov 2022 18:44:10 +0800 Subject: [PATCH 06/38] added test vector #2 and #3, added signature verification data to #1 --- EIPS/eip-kem.md | 293 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 237 insertions(+), 56 deletions(-) diff --git a/EIPS/eip-kem.md b/EIPS/eip-kem.md index a5411ddb5ff999..cf50bfab63206e 100644 --- a/EIPS/eip-kem.md +++ b/EIPS/eip-kem.md @@ -2,7 +2,7 @@ eip: KEM title: Private Key Encapsulation description: defines a specification for encapsulating private keys. -author: Base Labs (@Base-Labs), Weiji Guo (Base Labs) +author: Base Labs (@Base-Labs), Weiji Guo (@weijiguo) discussions-to: https://ethereum-magicians.org/t/private-key-encapsulation-to-move-around-securely-without-entering-seed/11604 status: Draft type: Standards Track @@ -28,52 +28,60 @@ This EIP aims to enable such use cases. ## Specification -The basic idea is to encapsulate the private key with ECIES. To ensure that the ephemeral public key to encapsulate the private key to is indeed generated from a trusted party and has not been tampered with, an option is provided to sign that ephemeral public key. +### Sender and Recipient -There should be a mandatory `version` parameter. This allows various kinds of Key Encapsulation Mechanisms to be adopted depending on the security considerations or preference. The list shall be short to minimize compatibility issues among different vendors. +Sender is the party who has the private key to be encapsulated. Sender application is what Sender uses. + +Recipient is the party who accepts the encapsulated private key, unwrap and then use it. Recipient application is what Recipient uses. ### Core Algorithms +The basic idea is to encapsulate the private key with ECIES. To ensure that the ephemeral public key to encapsulate the private key to is indeed generated from a trusted party and has not been tampered with, an option is provided to sign that ephemeral public key. + +There should be a mandatory `version` parameter. This allows various kinds of Key Encapsulation Mechanisms to be adopted depending on the security considerations or preference. The list shall be short to minimize compatibility issues among different vendors. + In addition to `version` string, parameters, involved keys and functions include: 1. Sender private key `sk`, to be encapsulated to recipient. -2. Ephemeral recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes the scalar multiplication. Optionally, `R` could be signed and `signerPubKey` and `signature` are then provided for sender to verify if `R` could be trusted or not. +2. Ephemeral recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes the scalar multiplication. Optionally, `R` could be signed, and `signerPubKey` and `signature` are then provided for sender to verify if `R` could be trusted or not. 3. Ephemeral sender key pair `(s, S)` such that `S = [s]G`. -4. Share secret `ss := [s]R = [r]S` according to ECDH. +4. Share secret `ss := [s]R = [r]S` according to ECDH. Note that for secp256k1 this EIP follows RFC5903 and uses compact representation, which means to use *only* the `x` coordinate as the shared secret. For Curve25519 this EIP follows RFC7748. 5. `oob`, out of band data, optional. This could be digits or an alpha-numeric string entered by user. 6. Let `derivedKey := HKDF(hash=SHA256, ikm=ss, info=oob, salt, length)`. HKDF is defined in RFC5869. The `length` should be determined by `skey` and `IV` requirements such that the symmetric key `skey = derivedKey[0:keySize]`, and `IV = derivedKey[keySize:length]`. `keySize` denotes the key size of underlying symmetric algorithm, for example, 16 (bytes) for AES-128, and 32 (bytes) for Chacha20. See **Security Considerations** for the use of `salt`. -7. `cipher := authenticated_cncryption(symAlg, skey, IV, data=sk)`. The symmetric cipher algorithm (`symAlg`) and authentication scheme are decided by the version parameter. +7. `cipher := authenticated_cncryption(symAlg, skey, IV, data=sk)`. The symmetric cipher algorithm `symAlg` and authentication scheme are decided by the version parameter. No additional authentication data `aad` is used. A much simplified example flow without signature and verification is: 1. Recipient application generates `(r, R)`. 2. User inputs `R` to Sender application, along with a six-digit code “123456” as `oob`. -3. Sender application generates `(s, S)`, and computes `cipher`. +3. Sender application generates `(s, S)`, and computes `cipher`, then returns `S || cipher` 4. Recipient application scans to read `S` and `cipher`. User enters “123456” as `oob` to Recipient application. 5. Recipient application decrypts `cipher` to get `sk`. -6. Optionally Recipient application could derive the address corresponding to `sk` so that user can confirm the correctness. +6. Recipient application derives the address corresponding to `sk` so that user can confirm the correctness. + +With signature and verification, the signature to `R` by `singerPubKey` is appended to `R`. `signerPubKey` itself could have been already signed by `trustedPubKey`, and that signature is appended to `signerPubKey`. See **Requests** and **Test Cases** for further clarification and examples. ### Requests #### R1. Request for Recipient to generate ephemeral key pair -``` +```javascript request({ method: 'eth_generateEphemeralKeyPair', params: [version, signerPubKey], }) ``` -`signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and EdDSA for Ed25519. And in this situation, it should be the case that the sender trusts `signerPubKey`, no matter how this trust is maintained. If not, next request WILL be rejected by Sender application. Also see **Security Considerations**. +`signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and Ed25519 for Curve25519. And in this situation, it should be the case that the sender trusts `signerPubKey`, no matter how this trust is maintained. If not, next request WILL be rejected by Sender application. Also see **Security Considerations**. Implementation then MUST generate random private key `r` with cryptographic secure random number generator (CSRNG), and derives ephemeral public key `R = [r]G`. Implementation SHOULD safe keep the generated key pair `(r, R)` in a secure manner in accordance with the circumstances, and SHOULD keep it only for limited duration, but the specific duration is left to individual implementations. Implementation SHOULD be able to retrieve `r` when given back the corresponding public key `R` if within said duration. -Returned value is `R`, compressed if applicable. Also see ****Encoding of data and messages****. If `signerPubKey` is provided, then `R` is followed by the `signature`, also hex-encoded. +Returned value is `R`, compressed if applicable. Also see **Encoding of data and messages**. If `signerPubKey` is provided, then the `signature` is appended to `R`, also hex-encoded. Alternatively, `signature` could be calculated separately, and then appended to the returned data. #### R2. Request for Sender to encapsulate the private key -``` +```javascript request({ method: 'eth_encapsulatePrivateKey', params: [ @@ -91,17 +99,19 @@ request({ `oob` and `salt` are just byte arrays. -`account` is used to identify which private key to be encapsulated. With Ethereum it is an address. Also see ****Encoding of data and messages****. +`account` is used to identify which private key to be encapsulated. With Ethereum it is an address. Also see **Encoding of data and messages**. -If `signerPubKey` is provided or `recipient` contains signature, the implementation MUST perform signature verification. Missing data or incorrect format etc. SHALL result in empty return and optional error logs. +If `signerPubKey` is provided or `recipient` contains `signature` data, the implementation MUST perform signature verification. Missing data or incorrect format etc. SHALL fail the call and result in empty return and optional error logs. -The implementation shall then proceed to retrieve the private key corresponding to `account`, and follow the ****Core Algorithms**** to encrypt it. +`signerPubKey` could have been further signed by another key pair `(trusted, trustedPubKey)`, which is trusted by the Sender application. In that case, `signerPubKey` is appended with the corresponding signature data, which SHOULD be verified against `trustedPubKey`. See **Test Cases** for further clarification. -The return data is a byte array which contains first the ephemeral sender public key (compressed if applicable), then cipher including any authentication tag. +The implementation shall then proceed to retrieve the private key `sk` corresponding to `account`, and follow the **Core Algorithms** to encrypt it. + +The return data is a byte array which contains first the ephemeral sender public key `S` (compressed if applicable), then `cipher` including any authentication tag, that is, `S || cipher`. #### R3. Request for Recipient to unwrap and intake the private key -``` +```javascript request({ method: 'eth_intakePrivateKey', params: [ @@ -114,9 +124,9 @@ request({ }) ``` -This time `recipientPublicKey` is only the ephemeral public key `R` generated earlier in the recipient side, just for the implementation to retrieve the corresponding private key `r`. `data` is the return value from the call to encapsulate private key, which includes `S` and `cipher`. +This time `recipientPublicKey` is only the ephemeral public key `R` generated earlier in the recipient side, just for the implementation to retrieve the corresponding private key `r`. `data` is the return value from the call to encapsulate private key, which is `S || cipher`. -When the encapsulated private key `sk` is decrypted successfully, the implementation can process it further according to the designated purposes. Some general security guidelines SHALL be followed, for example, do *not* log the value, do securely wipe it after use, etc. The implementation COULD derive the corresponding public key or address for user’s verification. +When the encapsulated private key `sk` is decrypted successfully, the implementation can process it further according to the designated purposes. Some general security guidelines SHALL be followed, for example, do *not* log the value, do securely wipe it after use, etc. The return value is the corresponding Ethereum address for `sk`, or empty if any error. @@ -125,7 +135,7 @@ The return value is the corresponding Ethereum address for `sk`, or empty if any Available elliptic curves are: - secp256k1 (mandatory) -- Ed25519 +- Curve25519 Available authenticated encryption schemes are: @@ -133,19 +143,19 @@ Available authenticated encryption schemes are: - AES-256-GCM - Chacha20-Poly1305 -Version string is simply concatenation of elliptic curve and AE scheme, for example, secp256k1-AES-128-GCM. +Version string is simply concatenation of elliptic curve and AE scheme, for example, secp256k1-AES-128-GCM. Above lists allows a combination of six different concrete schemes. Implementations are encouraged to implement curve related logic separately from authenticated encryption scheme to avoid duplication and to promote interoperability. Signature algorithms for each curve is: - secp256k1 --> ECDSA -- Ed25519 --> EdDSA +- Curve25519 --> Ed25519 ### Encoding of data and messages - Raw bytes are encoded in hex and prefixed with '0x'. -- `cipher` is encoded into single byte buffer as: (`IV || encrypted_sk || tag`). -- `R`, `S` and `signerPubKey` are compressed if applicable. -- `R` or `signerPubKey` could be followed by a signature to it. +- `cipher` is encoded into single byte buffer as: `[IV || encrypted_sk || tag]`. +- `R`, `S`, `signerPubKey` and `trustedPubKey` are compressed if applicable. +- `R` or `signerPubKey` could be followed by a signature to it: `[pub || sig]`. Note that for the secp256k1 curve, the signature is just 64 bytes without the `v` indicator as found in a typical Ethereum signature. ## Rationale @@ -157,81 +167,256 @@ There is security implication to this difference, including perfect forward secr No backward compatibility issues for this new proposal. +### Interoperability + To minimize potential compatibility issues among applications (including hardware wallets), this EIP requires that version secp256k1-AES-128-GCM MUST be supported. Version could be decided by user or negotiated by both sides. When there is no user input or negotiation, secp256k1-AES-128-GCM is assumed. +It is expected that implementations cover curve supports separately from encryption support, that is, all the versions that could be derived from supported curve and supported encryption scheme, should work. + ### UX Recommendations -`salt` and/or `oob` data: both are inputs to the HKDF function (`oob` as “info” parameter). For better UX we suggest to require from users only one of them but this is up to the implementation. +`salt` and/or `oob` data: both are inputs to the HKDF function (`oob` as “info” parameter). For better user experiences we suggest to require from users only one of them but this is up to the implementation. Recipient application is assumed to be powerful enough. Sender application could have very limited computing power and user interaction capabilities. ## Test Cases +### Data Fixation + Through out the test cases, we fix values for below data: -- `sk`, the private key to be encapsulated, fixed to: `0xf8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315`. The corresponding address is `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. Note that these values come from [Mastering Ethereum](https://github.com/ethereumbook/ethereumbook/blob/develop/04keys-addresses.asciidoc) +- `sk`, the private key to be encapsulated, fixed to: `0xf8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315`. The corresponding address is `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`, called `account`. Note that these values come from [Mastering Ethereum](https://github.com/ethereumbook/ethereumbook/blob/develop/04keys-addresses.asciidoc) +- `r`, the recipient private key, fixed to `0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79` +- `s`, the sender private key, fixed to `0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8` +- `signer`, the private key to sign the ephemeral public key, fixed to `0xac304db075d1685284ba5e10c343f2324ee32df3394fc093c98932517d36e344`. When used for Ed25519 signing, however, this values acts as `seed`, while the actual private key is calculated as `SHA512(seed)[:32]`. Or put it another way, the public key is the scalar multiplication of hashed private key to the base point. Same for `trusted`. +- `trusted`, the private key to sign `signerPubKey`, fixed to `0xda6649d68fc03b807e444e0034b3b59ec60716212007d72c9ddbfd33e25d38d1` - `oob`, fixed to `0x313233343536` (string value "123456") - `salt`, fixed to `0x6569703a2070726976617465206b657920656e63617073756c6174696f6e` (string value "eip: private key encapsulation") ### Case 1 -Use `version` as: `secp256k1-AES-128-GCM`. No signature verification. +Use `version` as: `secp256k1-AES-128-GCM`. **R1** is provided as: +```javascript +request({ + method: 'eth_generateEphemeralKeyPair', + params: [ + version: 'secp256k1-AES-128-GCM', + signerPubKey: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b' + ], +}) +``` +Suppose the implementation generates an ephemeral key pair `(r, R)`: +```javascript +r: '0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79', +R: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09' +``` +The return value could be: +```javascript +'0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d0927778652f08952d93014db52375bddc5a687724fff339e4ed908e640b54ffa1c6f893666a34a06b36eaf4a811661741a43587dd458858b75c582ca7db82fa77b' +``` +Note that `R` is compressed and R leads the return value. -**R1** is provided as: +Therefore **R2** could be provided as: +```javascript +request({ + method: 'eth_encapsulatePrivateKey', + params: [ + version: 'secp256k1-AES-128-GCM', + recipient: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d0927778652f08952d93014db52375bddc5a687724fff339e4ed908e640b54ffa1c6f893666a34a06b36eaf4a811661741a43587dd458858b75c582ca7db82fa77b', + signerPubKey: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0bae1b131cfb4c4ad3b7117285480cf8cf7964c7099b0f5d91a0a61bd403447dfb35b2b0e979b7d8eefe4df5415b09aa4ffcdb591c72868fff475460526a353f1b', + oob: '0x313233343536', + salt: '0x6569703a2070726976617465206b657920656e63617073756c6174696f6e', + account: '0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9' + ], +}) +``` +The Sender implementation first verifies first layer signature as ECDSA over secp256k1: +```javascript +//string representation of R as message +msg: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09', +sig: '27778652f08952d93014db52375bddc5a687724fff339e4ed908e640b54ffa1c6f893666a34a06b36eaf4a811661741a43587dd458858b75c582ca7db82fa77b', +//signerPubKey +pub: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b' +``` +Then it proceeds to verify second layer signature, also as ECDSA over secp256k1: +```javascript +//string representation of signerPubKey as message +msg: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b', +sig: '0xae1b131cfb4c4ad3b7117285480cf8cf7964c7099b0f5d91a0a61bd403447dfb35b2b0e979b7d8eefe4df5415b09aa4ffcdb591c72868fff475460526a353f1b', +//trustedPubKey +pub: '0x027fb72176f1f9852ce7dd9dc3aa4711675d3d8dc5102b86d758d853002137e839' +``` +Since Sender application trusts `trustedPubKey`, the signature verification succeeds. + +Suppose the implementation generates an ephemeral key pair `(s, S)` as: +```javascript +s: '0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8', +S: '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c02' +``` +The shared secret, symmetric key and IV should be: +```javascript +ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', +skey: '0x6ccc02a61aa16d6c66a1277e5e2434b8', +IV: '0x9c7a0f870d17ced2d2c3d1cf' +``` +Then the return value should be: +```javascript +'0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c02abff407e8901bb37d13d724a2e3a8a1a5af300adc286aa2ec65ef2a38c10c5cec68a949d0a20dbad2a8e5dfd7a14bbcb' ``` +With compressed public key `S` leading `cipher`, which in turn is (added prefix '0x'): +```javascript +'0xabff407e8901bb37d13d724a2e3a8a1a5af300adc286aa2ec65ef2a38c10c5cec68a949d0a20dbad2a8e5dfd7a14bbcb' +``` +Then **R3** is provided as: +```javascript request({ - method: 'eth_generateEphemeralKeyPair', + method: 'eth_intakePrivateKey', params: [ version: 'secp256k1-AES-128-GCM', - signerPubKey: '' + recipientPublicKey: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09', + oob: '0x313233343536', + salt: '0x6569703a2070726976617465206b657920656e63617073756c6174696f6e', + data: '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c02abff407e8901bb37d13d724a2e3a8a1a5af300adc286aa2ec65ef2a38c10c5cec68a949d0a20dbad2a8e5dfd7a14bbcb' ], }) ``` -Suppose the implementation generates an ephemeral key pair `(r, R)` as: +The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This matches the `account` parameter in **R2**. -`r`: `0x83816953554b4c8e33f340cc4d0f5995229ec2c88a2c1aaf5a9f52606ea2de13` -`R`: `0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac` +### Case 2 -Then `0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac` should be returned. Note that `R` is compressed. +Use `version` as: `secp256k1-AES-256-GCM`. The calculated symmetric key `skey`, `IV`, and `cipher` will be different. **R1** is provided as: +```javascript +request({ + method: 'eth_generateEphemeralKeyPair', + params: [ + version: 'secp256k1-AES-256-GCM', + signerPubKey: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b' + ], +}) +``` +Note that only `version` is different (AES key size). We keep using the same `(r, R)` (this is just a test vector). Therefore **R2** is provided as: -``` +```javascript request({ method: 'eth_encapsulatePrivateKey', params: [ - version: 'secp256k1-AES-128-GCM', - recipient: '0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac', - signerPubKey: '', + version: 'secp256k1-AES-256-GCM', + recipient: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d0927778652f08952d93014db52375bddc5a687724fff339e4ed908e640b54ffa1c6f893666a34a06b36eaf4a811661741a43587dd458858b75c582ca7db82fa77b', + signerPubKey: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0bae1b131cfb4c4ad3b7117285480cf8cf7964c7099b0f5d91a0a61bd403447dfb35b2b0e979b7d8eefe4df5415b09aa4ffcdb591c72868fff475460526a353f1b', oob: '0x313233343536', salt: '0x6569703a2070726976617465206b657920656e63617073756c6174696f6e', account: '0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9' ], }) ``` -Suppose the implementation generates an ephemeral key pair `(s, S)` as: - -`s`: `0xea1843f0e8b498be263f358cf8ceaeef9eac28cf0a6be8f5f774a3d9a39b8439` -`S`: `0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915f` +Suppose the implementation generates the same `(s, S)` as **Case 1**. The shared secret, symmetric key and IV should be: +```javascript +ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', +skey: '0x6ccc02a61aa16d6c66a1277e5e2434b89c7a0f870d17ced2d2c3d1cfd0e6f199', +IV: '0x3369b9570b9d207a0a8ebe27' +``` +With shared secret `ss` remains the same as **Case 1**, symmetric key `skey` contains both the `skey` and `IV` from **Case 1**. IV is changed. -Then the return value should be: `0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915fdd00e421f6b62bcb78b772669d937ac2edbf378d8534d39fec43cf9bab7fd437b4a890da1faa734c8fba0663b3840b7a` +Then the return value should be the follows, with `S` part the same as **Case 1** and `cipher` part different: +```javascript +'0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c0293910a91270b5deb0a645cc33604ed91668daf72328739d52a5af5a4760c4f3a9592b8f6d9b3ebe25127e7bf1c43b839' +``` Then **R3** is provided as: -``` +```javascript request({ method: 'eth_intakePrivateKey', params: [ - version: 'secp256k1-AES-128-GCM', - recipientPublicKey: '0x0246189d2fd030e2ac32c8d0e6ba5f12b3aac25c6a17225145deb88bd9c93755ac', + version: 'secp256k1-AES-256-GCM', + recipientPublicKey: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09', oob: '0x313233343536', salt: '0x6569703a2070726976617465206b657920656e63617073756c6174696f6e', - data: '0x027edefb3bfe6675a520da1878f176862b4cf9c134563ecda0d6d0bc81376f915fdd00e421f6b62bcb78b772669d937ac2edbf378d8534d39fec43cf9bab7fd437b4a890da1faa734c8fba0663b3840b7a' + data: '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c0293910a91270b5deb0a645cc33604ed91668daf72328739d52a5af5a4760c4f3a9592b8f6d9b3ebe25127e7bf1c43b839' ], }) ``` The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This matches the `account` parameter in **R2**. +### Case 3 + +Use `version` as: `Curve-25519-Chacha20-Poly1305`. **R1** is provided as: +```javascript +request({ + method: 'eth_generateEphemeralKeyPair', + params: [ + version: 'Curve25519-Chacha20-Poly1305', + signerPubKey: '0xe509fb840f6d5a69333ef68d69b86de55b9b905e45b16e3591912c097ba69938' + ], +}) +``` +Note that with Curve25519 the size of either public key or private key is 32 (bytes). And there is no compression for public key. `signerPubKey` is calculated as: +``` +//signer is '0xac304db075d1685284ba5e10c343f2324ee32df3394fc093c98932517d36e344' +s := SHA512(signer)[:32] +signerPubKey := Curve25519.ScalarBaseMult(s).ToHex() +``` +Same technique for `trustedPubKey`. With `r` value same as in **Case 1** and **Case 2** and the curve being changed, the return value is `R = [r]G || sig`: +```javascript +R = '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79cac06de279ec7f65f6b75f6bee740496df0650a6de61da5e691d7c5da1c7cb1ece61c669dd588a1029c38f11ad1714c1c9742232f9562ca6bbc7bad57882da04' +``` +**R2** is provided as: + +```javascript +request({ + method: 'eth_encapsulatePrivateKey', + params: [ + version: 'Curve25519-Chacha20-Poly1305', + recipient: '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79cac06de279ec7f65f6b75f6bee740496df0650a6de61da5e691d7c5da1c7cb1ece61c669dd588a1029c38f11ad1714c1c9742232f9562ca6bbc7bad57882da04', + signerPubKey: '0xe509fb840f6d5a69333ef68d69b86de55b9b905e45b16e3591912c097ba6993839c873ae4486413053fddff55ad9846f7c5492a7f0b7a60cd2f909aedc68b5343f61766b13def512a2acf053c0a9890a535e16767910890e5b15985b86d22f04', + oob: '0x313233343536', + salt: '0x6569703a2070726976617465206b657920656e63617073756c6174696f6e', + account: '0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9' + ], +}) +``` +Both `recipient` and `signerPubKey` have been signed in Ed25519. Verifying signature to `R` is carried out as: +```javascript +//string representation of R as message +msg: '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79', +sig: '0xcac06de279ec7f65f6b75f6bee740496df0650a6de61da5e691d7c5da1c7cb1ece61c669dd588a1029c38f11ad1714c1c9742232f9562ca6bbc7bad57882da04', +//signerPubKey +pub: '0xe509fb840f6d5a69333ef68d69b86de55b9b905e45b16e3591912c097ba69938' +``` +After successfully verifies the signature, the implementation then generates ephemeral key pair `(s, S)` in Curve25519: +```javascript +// s same as Case 1 and Case 2 +s = '0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8', +S = '0xd2fd6fcaac231d08363e736e61edb7e7696b13a727e3d2a239415cb8dc6ee278' +``` +The shared secret, symmetric key and IV should be: +```javascript +ss: '0xe0b36f56cdb63c27e933a5a67a5e97db4b566c9276a36aeee5dc6e87da118867', +skey: '0x7c6fa749e6df13c8578dc44cb24cdf46a44cb163e1e570c2e590c720aed5783f', +IV: '0x3c98ef6fc34b0d6e7e16bd78' +``` +Then the return value should be `S || cipher`: +```javascript +'0xd2fd6fcaac231d08363e736e61edb7e7696b13a727e3d2a239415cb8dc6ee2786a7e2e40efb86dc68f44f3e032bbedb1259fa820e548ac5adbf191784c568d4f642ca5b60c0b2142189dff6ee464b95c' +``` + +Then **R3** is provided as: +```javascript +request({ + method: 'eth_intakePrivateKey', + params: [ + version: 'Curve25519-Chacha20-Poly1305', + recipientPublicKey: '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79', + oob: '0x313233343536', + salt: '0x6569703a2070726976617465206b657920656e63617073756c6174696f6e', + data: '0xd2fd6fcaac231d08363e736e61edb7e7696b13a727e3d2a239415cb8dc6ee2786a7e2e40efb86dc68f44f3e032bbedb1259fa820e548ac5adbf191784c568d4f642ca5b60c0b2142189dff6ee464b95c' + ], +}) +``` +The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This matches the `account` parameter in **R2**. + ## Reference Implementation Contributions are welcome. @@ -244,13 +429,13 @@ Contributions are welcome. `R` could be signed so that Sender application can verify if `R` could be trusted or not. This involves both signature verification and if the signer could be trusted or not. While signature verification is quite straightforward in itself, the latter should be managed with care. To facilitate this trust management issue, `signerPubKey` could be further signed, creating a dual-layer trust structure: ``` -R <-- signerPubKey <-- trusted public key +R <-- signerPubKey <-- trustedPubKey ``` This allows various strategies to mange the trust. For example: -- A hardware wallet vendor which takes it very serious about the brand reputation and the fund safety for its customers, could choose to trust only its own public keys. These public keys only sign `signerPubKey` from selected partners. -- A MPC service could publish its `signerPubKey` online so that users won't verify the signature against a wrong or fake public key. +- A hardware wallet vendor which takes it very serious about the brand reputation and the fund safety for its customers, could choose to trust only its own public keys, all instances of `trustedPubKey`. These public keys only sign `signerPubKey` from selected partners. +- A MPC service could publish its `signerPubKey` online so that Sender application won't verify the signature against a wrong or fake public key. Note that it is advised that a separate key pair should be used for signing on each curve. @@ -264,13 +449,9 @@ AES-128, AES-256 and ChaCha20 are provided. **Randomness**. `r` and `s` must be generated with a cryptographic secure random number generator (CSRNG). -`salt` could be random bytes generated the same way as `r` or `s`. `salt` could be in any length but the general suggestion is 12 or 16, which could be displayed as QR code by the screen of some hardware wallet (so that another application could scan to read). If `salt` is not provided, this EIP uses default value as “EIP-xxxx” (to be determined). +`salt` could be random bytes generated the same way as `r` or `s`. `salt` could be in any length but the general suggestion is 12 or 16, which could be displayed as QR code by the screen of some hardware wallet (so that another application could scan to read). If `salt` is not provided, this EIP uses default value as “EIP-xxxx” (TODO). **Out of Band Data**: `oob` data is optional. When non-empty, its content is digits or an alpha-numeric string from user. Sender application may mandate `oob` from user. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). - -## Citation -Please cite this document as: -TODO From d578ddfe3fc8c670a9c4cc7e1a5fd2664170e0ba Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Fri, 25 Nov 2022 19:23:31 +0800 Subject: [PATCH 07/38] changed signature to against byte values --- EIPS/eip-kem.md | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/EIPS/eip-kem.md b/EIPS/eip-kem.md index cf50bfab63206e..27dfa7610840e9 100644 --- a/EIPS/eip-kem.md +++ b/EIPS/eip-kem.md @@ -59,7 +59,7 @@ A much simplified example flow without signature and verification is: 5. Recipient application decrypts `cipher` to get `sk`. 6. Recipient application derives the address corresponding to `sk` so that user can confirm the correctness. -With signature and verification, the signature to `R` by `singerPubKey` is appended to `R`. `signerPubKey` itself could have been already signed by `trustedPubKey`, and that signature is appended to `signerPubKey`. See **Requests** and **Test Cases** for further clarification and examples. +With signature and verification, the signature to `R` by `singerPubKey` is appended to `R`. `signerPubKey` itself could have been already signed by `trustedPubKey`, and that signature is appended to `signerPubKey`. Note that the signature is applied to the byte array data instead of its string representation, which might lead to confusion and interoperability issues (such as hex or base64, lower case v.s. upper case, etc.). See **Requests** and **Test Cases** for further clarification and examples. ### Requests #### R1. Request for Recipient to generate ephemeral key pair @@ -175,6 +175,8 @@ Version could be decided by user or negotiated by both sides. When there is no u It is expected that implementations cover curve supports separately from encryption support, that is, all the versions that could be derived from supported curve and supported encryption scheme, should work. +Signatures to `R` and `signerPubKey` are applied to byte array values instead of the encoded string. + ### UX Recommendations `salt` and/or `oob` data: both are inputs to the HKDF function (`oob` as “info” parameter). For better user experiences we suggest to require from users only one of them but this is up to the implementation. @@ -213,9 +215,9 @@ R: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09' ``` The return value could be: ```javascript -'0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d0927778652f08952d93014db52375bddc5a687724fff339e4ed908e640b54ffa1c6f893666a34a06b36eaf4a811661741a43587dd458858b75c582ca7db82fa77b' +'0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac77547fbdde194f71db52860b9e10ca2b089646f133d172124504ac1996a' ``` -Note that `R` is compressed and R leads the return value. +Note that `R` is compressed and `R` leads the return value: `R || sig`. Therefore **R2** could be provided as: ```javascript @@ -223,8 +225,8 @@ request({ method: 'eth_encapsulatePrivateKey', params: [ version: 'secp256k1-AES-128-GCM', - recipient: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d0927778652f08952d93014db52375bddc5a687724fff339e4ed908e640b54ffa1c6f893666a34a06b36eaf4a811661741a43587dd458858b75c582ca7db82fa77b', - signerPubKey: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0bae1b131cfb4c4ad3b7117285480cf8cf7964c7099b0f5d91a0a61bd403447dfb35b2b0e979b7d8eefe4df5415b09aa4ffcdb591c72868fff475460526a353f1b', + recipient: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac77547fbdde194f71db52860b9e10ca2b089646f133d172124504ac1996a', + signerPubKey: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b5bd427c527b7f1012b8edfd179b9002a7f2d7fc326bb6ae9aaf38b44eb93c397631fd8bb05fd78fa16ecca1eb19652b200f9048611265bc81f485cf60f29d6de', oob: '0x313233343536', salt: '0x6569703a2070726976617465206b657920656e63617073756c6174696f6e', account: '0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9' @@ -233,17 +235,17 @@ request({ ``` The Sender implementation first verifies first layer signature as ECDSA over secp256k1: ```javascript -//string representation of R as message +// actual message to be signed should be the decoded byte array msg: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09', -sig: '27778652f08952d93014db52375bddc5a687724fff339e4ed908e640b54ffa1c6f893666a34a06b36eaf4a811661741a43587dd458858b75c582ca7db82fa77b', +sig: '0x536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac77547fbdde194f71db52860b9e10ca2b089646f133d172124504ac1996aaf4a811661741a43587dd458858b75c582ca7db82fa77b', //signerPubKey pub: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b' ``` Then it proceeds to verify second layer signature, also as ECDSA over secp256k1: ```javascript -//string representation of signerPubKey as message +// actual message to be signed should be the decoded byte array msg: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b', -sig: '0xae1b131cfb4c4ad3b7117285480cf8cf7964c7099b0f5d91a0a61bd403447dfb35b2b0e979b7d8eefe4df5415b09aa4ffcdb591c72868fff475460526a353f1b', +sig: '0x5bd427c527b7f1012b8edfd179b9002a7f2d7fc326bb6ae9aaf38b44eb93c397631fd8bb05fd78fa16ecca1eb19652b200f9048611265bc81f485cf60f29d6de', //trustedPubKey pub: '0x027fb72176f1f9852ce7dd9dc3aa4711675d3d8dc5102b86d758d853002137e839' ``` @@ -303,8 +305,8 @@ request({ method: 'eth_encapsulatePrivateKey', params: [ version: 'secp256k1-AES-256-GCM', - recipient: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d0927778652f08952d93014db52375bddc5a687724fff339e4ed908e640b54ffa1c6f893666a34a06b36eaf4a811661741a43587dd458858b75c582ca7db82fa77b', - signerPubKey: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0bae1b131cfb4c4ad3b7117285480cf8cf7964c7099b0f5d91a0a61bd403447dfb35b2b0e979b7d8eefe4df5415b09aa4ffcdb591c72868fff475460526a353f1b', + recipient: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac77547fbdde194f71db52860b9e10ca2b089646f133d172124504ac1996a', + signerPubKey: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b5bd427c527b7f1012b8edfd179b9002a7f2d7fc326bb6ae9aaf38b44eb93c397631fd8bb05fd78fa16ecca1eb19652b200f9048611265bc81f485cf60f29d6de', oob: '0x313233343536', salt: '0x6569703a2070726976617465206b657920656e63617073756c6174696f6e', account: '0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9' @@ -369,8 +371,8 @@ request({ method: 'eth_encapsulatePrivateKey', params: [ version: 'Curve25519-Chacha20-Poly1305', - recipient: '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79cac06de279ec7f65f6b75f6bee740496df0650a6de61da5e691d7c5da1c7cb1ece61c669dd588a1029c38f11ad1714c1c9742232f9562ca6bbc7bad57882da04', - signerPubKey: '0xe509fb840f6d5a69333ef68d69b86de55b9b905e45b16e3591912c097ba6993839c873ae4486413053fddff55ad9846f7c5492a7f0b7a60cd2f909aedc68b5343f61766b13def512a2acf053c0a9890a535e16767910890e5b15985b86d22f04', + recipient: '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79879d900f04a955078ff6ae86f1d1b69b3e1265370e64bf064adaecb895c51effa3bdae7964bf8f9a6bfaef3b66306c1bc36afa5607a51b9768aa42ac2c961f02', + signerPubKey: '0xe509fb840f6d5a69333ef68d69b86de55b9b905e45b16e3591912c097ba69938d43e06a0f32c9e5ddb39fce34fac2b6f5314a1b1583134f27426d50af7094b0c101e848737e7f717da8c8497be06bab2a9536856c56eee194e89e94fd1bba509', oob: '0x313233343536', salt: '0x6569703a2070726976617465206b657920656e63617073756c6174696f6e', account: '0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9' @@ -379,13 +381,13 @@ request({ ``` Both `recipient` and `signerPubKey` have been signed in Ed25519. Verifying signature to `R` is carried out as: ```javascript -//string representation of R as message +// actual message to be signed should be the decoded byte array msg: '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79', -sig: '0xcac06de279ec7f65f6b75f6bee740496df0650a6de61da5e691d7c5da1c7cb1ece61c669dd588a1029c38f11ad1714c1c9742232f9562ca6bbc7bad57882da04', +sig: '0x879d900f04a955078ff6ae86f1d1b69b3e1265370e64bf064adaecb895c51effa3bdae7964bf8f9a6bfaef3b66306c1bc36afa5607a51b9768aa42ac2c961f02', //signerPubKey pub: '0xe509fb840f6d5a69333ef68d69b86de55b9b905e45b16e3591912c097ba69938' ``` -After successfully verifies the signature, the implementation then generates ephemeral key pair `(s, S)` in Curve25519: +After successfully verifies the signature (and the one by `trustedPubKey`), the implementation then generates ephemeral key pair `(s, S)` in Curve25519: ```javascript // s same as Case 1 and Case 2 s = '0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8', From cafb364f38d4a517744aca1b7f1cd7403e2681c5 Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Mon, 28 Nov 2022 14:45:06 +0800 Subject: [PATCH 08/38] added test vector generator --- EIPS/eip-kem.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/EIPS/eip-kem.md b/EIPS/eip-kem.md index 27dfa7610840e9..81e51aad177182 100644 --- a/EIPS/eip-kem.md +++ b/EIPS/eip-kem.md @@ -1,3 +1,4 @@ + --- eip: KEM title: Private Key Encapsulation @@ -184,6 +185,8 @@ Recipient application is assumed to be powerful enough. Sender application could ## Test Cases +For review purposes, the program to generate the test vectors is open-sourced [here](https://github.com/Base-Labs/encapsulation-sample). + ### Data Fixation Through out the test cases, we fix values for below data: @@ -420,8 +423,11 @@ request({ The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This matches the `account` parameter in **R2**. ## Reference Implementation + Contributions are welcome. +Test vector generator is provided earlier in the **Test Cases** section. + ## Security Considerations **Perfect Forward Secrecy**: PFS is achieved by using ephemeral key pairs in both sides. From 131c3da2a17b40a6f8ad07269191ff148a5fa1cf Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Mon, 28 Nov 2022 14:56:00 +0800 Subject: [PATCH 09/38] renamed file to assigned EIP number --- EIPS/{eip-kem.md => eip-6051.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename EIPS/{eip-kem.md => eip-6051.md} (99%) diff --git a/EIPS/eip-kem.md b/EIPS/eip-6051.md similarity index 99% rename from EIPS/eip-kem.md rename to EIPS/eip-6051.md index 81e51aad177182..6a97c706c6c6a7 100644 --- a/EIPS/eip-kem.md +++ b/EIPS/eip-6051.md @@ -1,6 +1,6 @@ --- -eip: KEM +eip: 6051 title: Private Key Encapsulation description: defines a specification for encapsulating private keys. author: Base Labs (@Base-Labs), Weiji Guo (@weijiguo) From b7cf93e1db9ee9fa080141045a1b53be6e064408 Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Mon, 28 Nov 2022 15:03:09 +0800 Subject: [PATCH 10/38] fixed file header --- EIPS/eip-6051.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 6a97c706c6c6a7..ab937e2c861e33 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -1,9 +1,8 @@ - --- eip: 6051 title: Private Key Encapsulation description: defines a specification for encapsulating private keys. -author: Base Labs (@Base-Labs), Weiji Guo (@weijiguo) +author: Base Labs (@Base-Labs), Weiji Guo (@weiji-cryptonatty) discussions-to: https://ethereum-magicians.org/t/private-key-encapsulation-to-move-around-securely-without-entering-seed/11604 status: Draft type: Standards Track From 8bb326a2626805f2d1a25681dc67d1a6b3c4935a Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Mon, 28 Nov 2022 16:39:06 +0800 Subject: [PATCH 11/38] updated default value for salt --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index ab937e2c861e33..69ce4c1ac690ea 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -456,7 +456,7 @@ AES-128, AES-256 and ChaCha20 are provided. **Randomness**. `r` and `s` must be generated with a cryptographic secure random number generator (CSRNG). -`salt` could be random bytes generated the same way as `r` or `s`. `salt` could be in any length but the general suggestion is 12 or 16, which could be displayed as QR code by the screen of some hardware wallet (so that another application could scan to read). If `salt` is not provided, this EIP uses default value as “EIP-xxxx” (TODO). +`salt` could be random bytes generated the same way as `r` or `s`. `salt` could be in any length but the general suggestion is 12 or 16, which could be displayed as QR code by the screen of some hardware wallet (so that another application could scan to read). If `salt` is not provided, this EIP uses default value as “EIP-6051”. **Out of Band Data**: `oob` data is optional. When non-empty, its content is digits or an alpha-numeric string from user. Sender application may mandate `oob` from user. From 5b5ced889c6ebd3a1e1cf6b9fbf160ff7ce522f3 Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Tue, 29 Nov 2022 11:53:18 +0800 Subject: [PATCH 12/38] fixed offending links etc. --- EIPS/eip-6051.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 69ce4c1ac690ea..051db1c844ebd3 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -159,11 +159,11 @@ Signature algorithms for each curve is: ## Rationale -A crucial difference of this proposal with [EIP-5630](eip-5630.md) is that, with key encapsulation in order to transport private key securely, the public key from the key-recipient should be ephemeral, and mostly used only one-time. While in EIP-5630 settings, the public key of message recipient shall be stable for a while so that message senders can encrypt messages without key discovery every time. +A crucial difference of this [EIP-6051](./eip-6051.md) with [EIP-5630](./eip-5630.md) is that, as the purpose of key encapsulation is to transport private key securely, the public key from the key-recipient should be ephemeral, and mostly used only one-time. While in EIP-5630 settings, the public key of message recipient shall be stable for a while so that message senders can encrypt messages without key discovery every time. There is security implication to this difference, including perfect forward secrecy. We aim to achieve perfect forward secrecy by generating ephemeral key pairs in both sides every time: 1) first the recipient shall generate an ephemeral key pair, retain the private key securely, and export the public key; 2) then the key sender securely wrap the private key in ECIES, with another ephemeral key pair, then destroy the ephemeral key securely; 3) finally the recipient can unwrap the private key, then destroy its ephemeral key pair securely. After these steps, the cipher text in transport intercepted by a malicious 3rd party, is no longer decrypt-able. -## Backward Compatibility +## Backwards Compatibility No backward compatibility issues for this new proposal. @@ -184,19 +184,19 @@ Recipient application is assumed to be powerful enough. Sender application could ## Test Cases -For review purposes, the program to generate the test vectors is open-sourced [here](https://github.com/Base-Labs/encapsulation-sample). +For review purposes, the program to generate the test vectors is open-sourced and provided in the corresponding discussion thread. ### Data Fixation Through out the test cases, we fix values for below data: -- `sk`, the private key to be encapsulated, fixed to: `0xf8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315`. The corresponding address is `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`, called `account`. Note that these values come from [Mastering Ethereum](https://github.com/ethereumbook/ethereumbook/blob/develop/04keys-addresses.asciidoc) +- `sk`, the private key to be encapsulated, fixed to: `0xf8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315`. The corresponding address is `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`, called `account`. Note that these values come from the book *Mastering Ethereum* by Andreas M. Antonopoulos and Gavin Wood. - `r`, the recipient private key, fixed to `0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79` - `s`, the sender private key, fixed to `0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8` - `signer`, the private key to sign the ephemeral public key, fixed to `0xac304db075d1685284ba5e10c343f2324ee32df3394fc093c98932517d36e344`. When used for Ed25519 signing, however, this values acts as `seed`, while the actual private key is calculated as `SHA512(seed)[:32]`. Or put it another way, the public key is the scalar multiplication of hashed private key to the base point. Same for `trusted`. - `trusted`, the private key to sign `signerPubKey`, fixed to `0xda6649d68fc03b807e444e0034b3b59ec60716212007d72c9ddbfd33e25d38d1` -- `oob`, fixed to `0x313233343536` (string value "123456") -- `salt`, fixed to `0x6569703a2070726976617465206b657920656e63617073756c6174696f6e` (string value "eip: private key encapsulation") +- `oob`, fixed to `0x313233343536` (string value: `123456`) +- `salt`, fixed to `0x6569703a2070726976617465206b657920656e63617073756c6174696f6e` (string value: `eip: private key encapsulation`) ### Case 1 @@ -456,7 +456,7 @@ AES-128, AES-256 and ChaCha20 are provided. **Randomness**. `r` and `s` must be generated with a cryptographic secure random number generator (CSRNG). -`salt` could be random bytes generated the same way as `r` or `s`. `salt` could be in any length but the general suggestion is 12 or 16, which could be displayed as QR code by the screen of some hardware wallet (so that another application could scan to read). If `salt` is not provided, this EIP uses default value as “EIP-6051”. +`salt` could be random bytes generated the same way as `r` or `s`. `salt` could be in any length but the general suggestion is 12 or 16, which could be displayed as QR code by the screen of some hardware wallet (so that another application could scan to read). If `salt` is not provided, this EIP uses default value as `EIP-6051`. **Out of Band Data**: `oob` data is optional. When non-empty, its content is digits or an alpha-numeric string from user. Sender application may mandate `oob` from user. From 89a4aca02f80b02f3af288305379336fe7f68ccf Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Wed, 30 Nov 2022 10:27:12 +0800 Subject: [PATCH 13/38] fixed typo Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 051db1c844ebd3..e6ceac9583224f 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -48,7 +48,7 @@ In addition to `version` string, parameters, involved keys and functions include 4. Share secret `ss := [s]R = [r]S` according to ECDH. Note that for secp256k1 this EIP follows RFC5903 and uses compact representation, which means to use *only* the `x` coordinate as the shared secret. For Curve25519 this EIP follows RFC7748. 5. `oob`, out of band data, optional. This could be digits or an alpha-numeric string entered by user. 6. Let `derivedKey := HKDF(hash=SHA256, ikm=ss, info=oob, salt, length)`. HKDF is defined in RFC5869. The `length` should be determined by `skey` and `IV` requirements such that the symmetric key `skey = derivedKey[0:keySize]`, and `IV = derivedKey[keySize:length]`. `keySize` denotes the key size of underlying symmetric algorithm, for example, 16 (bytes) for AES-128, and 32 (bytes) for Chacha20. See **Security Considerations** for the use of `salt`. -7. `cipher := authenticated_cncryption(symAlg, skey, IV, data=sk)`. The symmetric cipher algorithm `symAlg` and authentication scheme are decided by the version parameter. No additional authentication data `aad` is used. +7. `cipher := authenticated_encryption(symAlg, skey, IV, data=sk)`. The symmetric cipher algorithm `symAlg` and authentication scheme are decided by the version parameter. No additional authentication data `aad` is used. A much simplified example flow without signature and verification is: From a5594cf4979e6b8c672b60cf84145091e1eea741 Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Wed, 30 Nov 2022 11:22:55 +0800 Subject: [PATCH 14/38] updated based on review comments --- EIPS/eip-6051.md | 55 ++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index e6ceac9583224f..79de5579a39704 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -13,7 +13,7 @@ created: 2022-11-21 ## Abstract -This EIP proposes a mechanism to encapsulate a private key so that it could be securely relocated to another application without providing the seed. This EIP combines `ECIES` and optional signature verification under various choices to ensure that the private key is encapsulated to a known or trusted party. +This EIP proposes a mechanism to encapsulate a private key so that it could be securely relocated to another application without providing the seed. This EIP combines `ECIES` (Elliptic Curve Integrated Encryption Scheme) and optional signature verification under various choices to ensure that the private key is encapsulated to a known or trusted party. ## Motivation @@ -42,7 +42,7 @@ There should be a mandatory `version` parameter. This allows various kinds of In addition to `version` string, parameters, involved keys and functions include: -1. Sender private key `sk`, to be encapsulated to recipient. +1. Sender private key `sk`, to be encapsulated to recipient, and the corresponding address `account`. 2. Ephemeral recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes the scalar multiplication. Optionally, `R` could be signed, and `signerPubKey` and `signature` are then provided for sender to verify if `R` could be trusted or not. 3. Ephemeral sender key pair `(s, S)` such that `S = [s]G`. 4. Share secret `ss := [s]R = [r]S` according to ECDH. Note that for secp256k1 this EIP follows RFC5903 and uses compact representation, which means to use *only* the `x` coordinate as the shared secret. For Curve25519 this EIP follows RFC7748. @@ -62,6 +62,15 @@ A much simplified example flow without signature and verification is: With signature and verification, the signature to `R` by `singerPubKey` is appended to `R`. `signerPubKey` itself could have been already signed by `trustedPubKey`, and that signature is appended to `signerPubKey`. Note that the signature is applied to the byte array data instead of its string representation, which might lead to confusion and interoperability issues (such as hex or base64, lower case v.s. upper case, etc.). See **Requests** and **Test Cases** for further clarification and examples. ### Requests + +#### Encoding of data and messages + +- Raw bytes are encoded in hex and prefixed with '0x'. +- Unless specified otherwise, all parameters and return values are hex-encoded bytes. +- `cipher` is encoded into single byte buffer as: `[IV || encrypted_sk || tag]`. +- `R`, `S`, `signerPubKey` and `trustedPubKey` are compressed if applicable. +- `R` or `signerPubKey` could be followed by a signature to it: `[pub || sig]`. Note that for the secp256k1 curve, the signature is just 64 bytes without the `v` indicator as found in a typical Ethereum signature. + #### R1. Request for Recipient to generate ephemeral key pair ```javascript @@ -69,13 +78,14 @@ request({ method: 'eth_generateEphemeralKeyPair', params: [version, signerPubKey], }) +// expected return value: R ``` `signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and Ed25519 for Curve25519. And in this situation, it should be the case that the sender trusts `signerPubKey`, no matter how this trust is maintained. If not, next request WILL be rejected by Sender application. Also see **Security Considerations**. Implementation then MUST generate random private key `r` with cryptographic secure random number generator (CSRNG), and derives ephemeral public key `R = [r]G`. Implementation SHOULD safe keep the generated key pair `(r, R)` in a secure manner in accordance with the circumstances, and SHOULD keep it only for limited duration, but the specific duration is left to individual implementations. Implementation SHOULD be able to retrieve `r` when given back the corresponding public key `R` if within said duration. -Returned value is `R`, compressed if applicable. Also see **Encoding of data and messages**. If `signerPubKey` is provided, then the `signature` is appended to `R`, also hex-encoded. +Returned value is `R`, compressed if applicable. If `signerPubKey` is provided, then the `signature` is appended to `R`, also hex-encoded. Alternatively, `signature` could be calculated separately, and then appended to the returned data. @@ -93,6 +103,7 @@ request({ account ], }) +// expected return value: S || cipher ``` `recipient` is the return value from the call to generate ephemeral key pair, with optional `signature` appended either as returned or separately. @@ -122,6 +133,7 @@ request({ data ], }) +// expected return value: account ``` This time `recipientPublicKey` is only the ephemeral public key `R` generated earlier in the recipient side, just for the implementation to retrieve the corresponding private key `r`. `data` is the return value from the call to encapsulate private key, which is `S || cipher`. @@ -150,13 +162,6 @@ Signature algorithms for each curve is: - secp256k1 --> ECDSA - Curve25519 --> Ed25519 -### Encoding of data and messages - -- Raw bytes are encoded in hex and prefixed with '0x'. -- `cipher` is encoded into single byte buffer as: `[IV || encrypted_sk || tag]`. -- `R`, `S`, `signerPubKey` and `trustedPubKey` are compressed if applicable. -- `R` or `signerPubKey` could be followed by a signature to it: `[pub || sig]`. Note that for the secp256k1 curve, the signature is just 64 bytes without the `v` indicator as found in a typical Ethereum signature. - ## Rationale A crucial difference of this [EIP-6051](./eip-6051.md) with [EIP-5630](./eip-5630.md) is that, as the purpose of key encapsulation is to transport private key securely, the public key from the key-recipient should be ephemeral, and mostly used only one-time. While in EIP-5630 settings, the public key of message recipient shall be stable for a while so that message senders can encrypt messages without key discovery every time. @@ -211,12 +216,12 @@ request({ }) ``` Suppose the implementation generates an ephemeral key pair `(r, R)`: -```javascript +```json r: '0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79', R: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09' ``` The return value could be: -```javascript +```json '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac77547fbdde194f71db52860b9e10ca2b089646f133d172124504ac1996a' ``` Note that `R` is compressed and `R` leads the return value: `R || sig`. @@ -236,7 +241,7 @@ request({ }) ``` The Sender implementation first verifies first layer signature as ECDSA over secp256k1: -```javascript +```json // actual message to be signed should be the decoded byte array msg: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09', sig: '0x536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac77547fbdde194f71db52860b9e10ca2b089646f133d172124504ac1996aaf4a811661741a43587dd458858b75c582ca7db82fa77b', @@ -244,7 +249,7 @@ sig: '0x536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac775 pub: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b' ``` Then it proceeds to verify second layer signature, also as ECDSA over secp256k1: -```javascript +```json // actual message to be signed should be the decoded byte array msg: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b', sig: '0x5bd427c527b7f1012b8edfd179b9002a7f2d7fc326bb6ae9aaf38b44eb93c397631fd8bb05fd78fa16ecca1eb19652b200f9048611265bc81f485cf60f29d6de', @@ -254,22 +259,22 @@ pub: '0x027fb72176f1f9852ce7dd9dc3aa4711675d3d8dc5102b86d758d853002137e839' Since Sender application trusts `trustedPubKey`, the signature verification succeeds. Suppose the implementation generates an ephemeral key pair `(s, S)` as: -```javascript +```json s: '0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8', S: '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c02' ``` The shared secret, symmetric key and IV should be: -```javascript +```json ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', skey: '0x6ccc02a61aa16d6c66a1277e5e2434b8', IV: '0x9c7a0f870d17ced2d2c3d1cf' ``` Then the return value should be: -```javascript +```json '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c02abff407e8901bb37d13d724a2e3a8a1a5af300adc286aa2ec65ef2a38c10c5cec68a949d0a20dbad2a8e5dfd7a14bbcb' ``` With compressed public key `S` leading `cipher`, which in turn is (added prefix '0x'): -```javascript +```json '0xabff407e8901bb37d13d724a2e3a8a1a5af300adc286aa2ec65ef2a38c10c5cec68a949d0a20dbad2a8e5dfd7a14bbcb' ``` Then **R3** is provided as: @@ -316,7 +321,7 @@ request({ }) ``` Suppose the implementation generates the same `(s, S)` as **Case 1**. The shared secret, symmetric key and IV should be: -```javascript +```json ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', skey: '0x6ccc02a61aa16d6c66a1277e5e2434b89c7a0f870d17ced2d2c3d1cfd0e6f199', IV: '0x3369b9570b9d207a0a8ebe27' @@ -324,7 +329,7 @@ IV: '0x3369b9570b9d207a0a8ebe27' With shared secret `ss` remains the same as **Case 1**, symmetric key `skey` contains both the `skey` and `IV` from **Case 1**. IV is changed. Then the return value should be the follows, with `S` part the same as **Case 1** and `cipher` part different: -```javascript +```json '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c0293910a91270b5deb0a645cc33604ed91668daf72328739d52a5af5a4760c4f3a9592b8f6d9b3ebe25127e7bf1c43b839' ``` @@ -363,7 +368,7 @@ s := SHA512(signer)[:32] signerPubKey := Curve25519.ScalarBaseMult(s).ToHex() ``` Same technique for `trustedPubKey`. With `r` value same as in **Case 1** and **Case 2** and the curve being changed, the return value is `R = [r]G || sig`: -```javascript +```json R = '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79cac06de279ec7f65f6b75f6bee740496df0650a6de61da5e691d7c5da1c7cb1ece61c669dd588a1029c38f11ad1714c1c9742232f9562ca6bbc7bad57882da04' ``` **R2** is provided as: @@ -382,7 +387,7 @@ request({ }) ``` Both `recipient` and `signerPubKey` have been signed in Ed25519. Verifying signature to `R` is carried out as: -```javascript +```json // actual message to be signed should be the decoded byte array msg: '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79', sig: '0x879d900f04a955078ff6ae86f1d1b69b3e1265370e64bf064adaecb895c51effa3bdae7964bf8f9a6bfaef3b66306c1bc36afa5607a51b9768aa42ac2c961f02', @@ -390,19 +395,19 @@ sig: '0x879d900f04a955078ff6ae86f1d1b69b3e1265370e64bf064adaecb895c51effa3bdae79 pub: '0xe509fb840f6d5a69333ef68d69b86de55b9b905e45b16e3591912c097ba69938' ``` After successfully verifies the signature (and the one by `trustedPubKey`), the implementation then generates ephemeral key pair `(s, S)` in Curve25519: -```javascript +```json // s same as Case 1 and Case 2 s = '0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8', S = '0xd2fd6fcaac231d08363e736e61edb7e7696b13a727e3d2a239415cb8dc6ee278' ``` The shared secret, symmetric key and IV should be: -```javascript +```json ss: '0xe0b36f56cdb63c27e933a5a67a5e97db4b566c9276a36aeee5dc6e87da118867', skey: '0x7c6fa749e6df13c8578dc44cb24cdf46a44cb163e1e570c2e590c720aed5783f', IV: '0x3c98ef6fc34b0d6e7e16bd78' ``` Then the return value should be `S || cipher`: -```javascript +```json '0xd2fd6fcaac231d08363e736e61edb7e7696b13a727e3d2a239415cb8dc6ee2786a7e2e40efb86dc68f44f3e032bbedb1259fa820e548ac5adbf191784c568d4f642ca5b60c0b2142189dff6ee464b95c' ``` From 301ffe43debbfe099b02860b2b46371956ce0519 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Wed, 30 Nov 2022 16:51:16 +0800 Subject: [PATCH 15/38] replaced json formatting with none for better rendering --- EIPS/eip-6051.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 79de5579a39704..0a0be315a7948a 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -216,12 +216,12 @@ request({ }) ``` Suppose the implementation generates an ephemeral key pair `(r, R)`: -```json +``` r: '0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79', R: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09' ``` The return value could be: -```json +``` '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac77547fbdde194f71db52860b9e10ca2b089646f133d172124504ac1996a' ``` Note that `R` is compressed and `R` leads the return value: `R || sig`. @@ -241,7 +241,7 @@ request({ }) ``` The Sender implementation first verifies first layer signature as ECDSA over secp256k1: -```json +``` // actual message to be signed should be the decoded byte array msg: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09', sig: '0x536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac77547fbdde194f71db52860b9e10ca2b089646f133d172124504ac1996aaf4a811661741a43587dd458858b75c582ca7db82fa77b', @@ -249,7 +249,7 @@ sig: '0x536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac775 pub: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b' ``` Then it proceeds to verify second layer signature, also as ECDSA over secp256k1: -```json +``` // actual message to be signed should be the decoded byte array msg: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b', sig: '0x5bd427c527b7f1012b8edfd179b9002a7f2d7fc326bb6ae9aaf38b44eb93c397631fd8bb05fd78fa16ecca1eb19652b200f9048611265bc81f485cf60f29d6de', @@ -259,22 +259,22 @@ pub: '0x027fb72176f1f9852ce7dd9dc3aa4711675d3d8dc5102b86d758d853002137e839' Since Sender application trusts `trustedPubKey`, the signature verification succeeds. Suppose the implementation generates an ephemeral key pair `(s, S)` as: -```json +``` s: '0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8', S: '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c02' ``` The shared secret, symmetric key and IV should be: -```json +``` ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', skey: '0x6ccc02a61aa16d6c66a1277e5e2434b8', IV: '0x9c7a0f870d17ced2d2c3d1cf' ``` Then the return value should be: -```json +``` '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c02abff407e8901bb37d13d724a2e3a8a1a5af300adc286aa2ec65ef2a38c10c5cec68a949d0a20dbad2a8e5dfd7a14bbcb' ``` With compressed public key `S` leading `cipher`, which in turn is (added prefix '0x'): -```json +``` '0xabff407e8901bb37d13d724a2e3a8a1a5af300adc286aa2ec65ef2a38c10c5cec68a949d0a20dbad2a8e5dfd7a14bbcb' ``` Then **R3** is provided as: @@ -321,7 +321,7 @@ request({ }) ``` Suppose the implementation generates the same `(s, S)` as **Case 1**. The shared secret, symmetric key and IV should be: -```json +``` ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', skey: '0x6ccc02a61aa16d6c66a1277e5e2434b89c7a0f870d17ced2d2c3d1cfd0e6f199', IV: '0x3369b9570b9d207a0a8ebe27' @@ -329,7 +329,7 @@ IV: '0x3369b9570b9d207a0a8ebe27' With shared secret `ss` remains the same as **Case 1**, symmetric key `skey` contains both the `skey` and `IV` from **Case 1**. IV is changed. Then the return value should be the follows, with `S` part the same as **Case 1** and `cipher` part different: -```json +``` '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c0293910a91270b5deb0a645cc33604ed91668daf72328739d52a5af5a4760c4f3a9592b8f6d9b3ebe25127e7bf1c43b839' ``` @@ -368,7 +368,7 @@ s := SHA512(signer)[:32] signerPubKey := Curve25519.ScalarBaseMult(s).ToHex() ``` Same technique for `trustedPubKey`. With `r` value same as in **Case 1** and **Case 2** and the curve being changed, the return value is `R = [r]G || sig`: -```json +``` R = '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79cac06de279ec7f65f6b75f6bee740496df0650a6de61da5e691d7c5da1c7cb1ece61c669dd588a1029c38f11ad1714c1c9742232f9562ca6bbc7bad57882da04' ``` **R2** is provided as: @@ -387,7 +387,7 @@ request({ }) ``` Both `recipient` and `signerPubKey` have been signed in Ed25519. Verifying signature to `R` is carried out as: -```json +``` // actual message to be signed should be the decoded byte array msg: '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79', sig: '0x879d900f04a955078ff6ae86f1d1b69b3e1265370e64bf064adaecb895c51effa3bdae7964bf8f9a6bfaef3b66306c1bc36afa5607a51b9768aa42ac2c961f02', @@ -395,19 +395,19 @@ sig: '0x879d900f04a955078ff6ae86f1d1b69b3e1265370e64bf064adaecb895c51effa3bdae79 pub: '0xe509fb840f6d5a69333ef68d69b86de55b9b905e45b16e3591912c097ba69938' ``` After successfully verifies the signature (and the one by `trustedPubKey`), the implementation then generates ephemeral key pair `(s, S)` in Curve25519: -```json +``` // s same as Case 1 and Case 2 s = '0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8', S = '0xd2fd6fcaac231d08363e736e61edb7e7696b13a727e3d2a239415cb8dc6ee278' ``` The shared secret, symmetric key and IV should be: -```json +``` ss: '0xe0b36f56cdb63c27e933a5a67a5e97db4b566c9276a36aeee5dc6e87da118867', skey: '0x7c6fa749e6df13c8578dc44cb24cdf46a44cb163e1e570c2e590c720aed5783f', IV: '0x3c98ef6fc34b0d6e7e16bd78' ``` Then the return value should be `S || cipher`: -```json +``` '0xd2fd6fcaac231d08363e736e61edb7e7696b13a727e3d2a239415cb8dc6ee2786a7e2e40efb86dc68f44f3e032bbedb1259fa820e548ac5adbf191784c568d4f642ca5b60c0b2142189dff6ee464b95c' ``` From e96a9f9ae7e7680dfd4cafcc40d3feb0d8227454 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 16:45:07 +0800 Subject: [PATCH 16/38] fixed grammar Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 0a0be315a7948a..70126cc3148fc9 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -17,7 +17,7 @@ This EIP proposes a mechanism to encapsulate a private key so that it could be s ## Motivation -There are various cases that we might want to export one of many private keys from a much securer but less convenient wallet, which is controlled with a seed or pass phrase. +There are various cases in which we might want to export one of many private keys from a much more secure but less convenient wallet, which is controlled with a seed or passphrase. 1. We might dedicate one of many private keys for messaging purpose, and that private key is probably managed in a not-so-secure manner; 2. We might want to export one of many private keys from a hardware wallet, and split it with MPC technology so that a 3rd party service could help us identify potential frauds or known bad addresses, enforce 2FA, etc., meanwhile we can initiate transactions from a mobile device with much better UX and without carrying a hardware wallet. From e29ed0d619d24807b3d9fb8bb8569fa143dc8373 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:14:56 +0800 Subject: [PATCH 17/38] fixed grammar Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 70126cc3148fc9..923bd769d1e7bb 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -19,7 +19,7 @@ This EIP proposes a mechanism to encapsulate a private key so that it could be s There are various cases in which we might want to export one of many private keys from a much more secure but less convenient wallet, which is controlled with a seed or passphrase. -1. We might dedicate one of many private keys for messaging purpose, and that private key is probably managed in a not-so-secure manner; +1. We might dedicate one of many private keys for messaging purposes, and that private key is probably managed in a not-so-secure manner; 2. We might want to export one of many private keys from a hardware wallet, and split it with MPC technology so that a 3rd party service could help us identify potential frauds or known bad addresses, enforce 2FA, etc., meanwhile we can initiate transactions from a mobile device with much better UX and without carrying a hardware wallet. In both cases, it is safer not to provide the seed which controls the whole wallet and might contains many addresses in multiple chains. From e6dc6cb2253deeb0ac391eaf96ece5a9d1b35bd9 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:20:11 +0800 Subject: [PATCH 18/38] revision suggestions taken with gratitudes Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 923bd769d1e7bb..ca7591cbe5ec1f 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -30,7 +30,8 @@ This EIP aims to enable such use cases. ### Sender and Recipient -Sender is the party who has the private key to be encapsulated. Sender application is what Sender uses. +We hereby define: +- *Sender* as the party who holds in custody the private key to be encapsulated. *Sender Application* as the client-side application that said Sender uses to send the encapsulated private key. Recipient is the party who accepts the encapsulated private key, unwrap and then use it. Recipient application is what Recipient uses. From 4369c2f9d898fc5a2e658ca1c0a1121deb01c3dc Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:20:26 +0800 Subject: [PATCH 19/38] revision suggestions taken with gratitudes Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index ca7591cbe5ec1f..26a9f210866939 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -33,7 +33,7 @@ This EIP aims to enable such use cases. We hereby define: - *Sender* as the party who holds in custody the private key to be encapsulated. *Sender Application* as the client-side application that said Sender uses to send the encapsulated private key. -Recipient is the party who accepts the encapsulated private key, unwrap and then use it. Recipient application is what Recipient uses. +- *Recipient* as the party who accepts the encapsulated private key, unwrap and then use it. *Recipient Application* as the client-side application that Recipient uses to receive the encapsulated private key. ### Core Algorithms From cfa2af2fc470c5f0098a2bb3bef004ee199e2a3c Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:26:21 +0800 Subject: [PATCH 20/38] fixed grammar Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 26a9f210866939..219fe601a5cf98 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -37,7 +37,7 @@ We hereby define: ### Core Algorithms -The basic idea is to encapsulate the private key with ECIES. To ensure that the ephemeral public key to encapsulate the private key to is indeed generated from a trusted party and has not been tampered with, an option is provided to sign that ephemeral public key. +The basic idea is to encapsulate the private key with ECIES. To ensure that the ephemeral public key to encapsulate the private key is indeed generated from a trusted party and has not been tampered with, we also provided an option to sign that ephemeral public key in this standard. There should be a mandatory `version` parameter. This allows various kinds of Key Encapsulation Mechanisms to be adopted depending on the security considerations or preference. The list shall be short to minimize compatibility issues among different vendors. From 11b178b4a1980c448b921fa4f8e29cd89b017bcc Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:27:15 +0800 Subject: [PATCH 21/38] fixed grammar Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 219fe601a5cf98..42c77729af8cc5 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -39,7 +39,7 @@ We hereby define: The basic idea is to encapsulate the private key with ECIES. To ensure that the ephemeral public key to encapsulate the private key is indeed generated from a trusted party and has not been tampered with, we also provided an option to sign that ephemeral public key in this standard. -There should be a mandatory `version` parameter. This allows various kinds of Key Encapsulation Mechanisms to be adopted depending on the security considerations or preference. The list shall be short to minimize compatibility issues among different vendors. +There should be a mandatory `version` parameter. This allows various kinds of Key Encapsulation Mechanisms to be adopted depending on the security considerations or preferences. The list shall be short to minimize compatibility issues among different vendors. In addition to `version` string, parameters, involved keys and functions include: From 15278d31d1810d687f28ce2aa94d529cd56cb5dc Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:27:50 +0800 Subject: [PATCH 22/38] fixed grammar Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 42c77729af8cc5..bd66806d4f8750 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -41,7 +41,7 @@ The basic idea is to encapsulate the private key with ECIES. To ensure that the There should be a mandatory `version` parameter. This allows various kinds of Key Encapsulation Mechanisms to be adopted depending on the security considerations or preferences. The list shall be short to minimize compatibility issues among different vendors. -In addition to `version` string, parameters, involved keys and functions include: +In addition to a `version` parameter, the following keys and functions are involved: 1. Sender private key `sk`, to be encapsulated to recipient, and the corresponding address `account`. 2. Ephemeral recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes the scalar multiplication. Optionally, `R` could be signed, and `signerPubKey` and `signature` are then provided for sender to verify if `R` could be trusted or not. From a84d483d0f3ccb90840bcfc3723645e1df28d221 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:31:15 +0800 Subject: [PATCH 23/38] fixed grammar Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index bd66806d4f8750..f2b90068204782 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -43,7 +43,7 @@ There should be a mandatory `version` parameter. This allows various kinds of In addition to a `version` parameter, the following keys and functions are involved: -1. Sender private key `sk`, to be encapsulated to recipient, and the corresponding address `account`. +1. The Sender's private key `sk`, which is to be encapsulated to the Recipient, and the corresponding address `account`. 2. Ephemeral recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes the scalar multiplication. Optionally, `R` could be signed, and `signerPubKey` and `signature` are then provided for sender to verify if `R` could be trusted or not. 3. Ephemeral sender key pair `(s, S)` such that `S = [s]G`. 4. Share secret `ss := [s]R = [r]S` according to ECDH. Note that for secp256k1 this EIP follows RFC5903 and uses compact representation, which means to use *only* the `x` coordinate as the shared secret. For Curve25519 this EIP follows RFC7748. From ca592ace100e5612695278933cc5bbb105049bc4 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:39:00 +0800 Subject: [PATCH 24/38] fixed grammar Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index f2b90068204782..a626017885a477 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -82,7 +82,7 @@ request({ // expected return value: R ``` -`signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and Ed25519 for Curve25519. And in this situation, it should be the case that the sender trusts `signerPubKey`, no matter how this trust is maintained. If not, next request WILL be rejected by Sender application. Also see **Security Considerations**. +`signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and Ed25519 for Curve25519. And in this situation, it should be the case that the sender trusts `signerPubKey`, no matter how this trust is maintained. If not, the next request WILL be rejected by Sender application. Also see **Security Considerations**. Implementation then MUST generate random private key `r` with cryptographic secure random number generator (CSRNG), and derives ephemeral public key `R = [r]G`. Implementation SHOULD safe keep the generated key pair `(r, R)` in a secure manner in accordance with the circumstances, and SHOULD keep it only for limited duration, but the specific duration is left to individual implementations. Implementation SHOULD be able to retrieve `r` when given back the corresponding public key `R` if within said duration. From 027c0f8710ef3b858e753d98bd667f3ed62c3109 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:48:05 +0800 Subject: [PATCH 25/38] fixed grammar Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index a626017885a477..48c6d8dc11726d 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -111,7 +111,7 @@ request({ `oob` and `salt` are just byte arrays. -`account` is used to identify which private key to be encapsulated. With Ethereum it is an address. Also see **Encoding of data and messages**. +`account` is used to identify which private key to be encapsulated. With Ethereum, it is an address. Also see **Encoding of data and messages**. If `signerPubKey` is provided or `recipient` contains `signature` data, the implementation MUST perform signature verification. Missing data or incorrect format etc. SHALL fail the call and result in empty return and optional error logs. From 2f6abbad5ece7a945334b52eef0c007cec5ceaad Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:51:01 +0800 Subject: [PATCH 26/38] revision suggestions taken with gratitudes Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 48c6d8dc11726d..1c4d16ddaeda22 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -113,7 +113,7 @@ request({ `account` is used to identify which private key to be encapsulated. With Ethereum, it is an address. Also see **Encoding of data and messages**. -If `signerPubKey` is provided or `recipient` contains `signature` data, the implementation MUST perform signature verification. Missing data or incorrect format etc. SHALL fail the call and result in empty return and optional error logs. +If `signerPubKey` is provided or `recipient` contains `signature` data, the implementation MUST perform signature verification. Missing data or incorrect format MUST either fail the call or result in empty return and optional error logs. `signerPubKey` could have been further signed by another key pair `(trusted, trustedPubKey)`, which is trusted by the Sender application. In that case, `signerPubKey` is appended with the corresponding signature data, which SHOULD be verified against `trustedPubKey`. See **Test Cases** for further clarification. From aa3902b864919d2bda19a8b0b3f9c46e78c1d21a Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:51:57 +0800 Subject: [PATCH 27/38] fixed grammar as suggested Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 1c4d16ddaeda22..9ecdb7692f7420 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -165,7 +165,7 @@ Signature algorithms for each curve is: ## Rationale -A crucial difference of this [EIP-6051](./eip-6051.md) with [EIP-5630](./eip-5630.md) is that, as the purpose of key encapsulation is to transport private key securely, the public key from the key-recipient should be ephemeral, and mostly used only one-time. While in EIP-5630 settings, the public key of message recipient shall be stable for a while so that message senders can encrypt messages without key discovery every time. +A critical difference of this [EIP-6051](./eip-6051.md) with [EIP-5630](./eip-5630.md) is that, as the purpose of key encapsulation is to transport a private key securely, the public key from the key-recipient should be ephemeral, and mostly used only one-time. While in EIP-5630 settings, the public key of the message recipient shall be stable for a while so that message senders can encrypt messages without key discovery every time. There is security implication to this difference, including perfect forward secrecy. We aim to achieve perfect forward secrecy by generating ephemeral key pairs in both sides every time: 1) first the recipient shall generate an ephemeral key pair, retain the private key securely, and export the public key; 2) then the key sender securely wrap the private key in ECIES, with another ephemeral key pair, then destroy the ephemeral key securely; 3) finally the recipient can unwrap the private key, then destroy its ephemeral key pair securely. After these steps, the cipher text in transport intercepted by a malicious 3rd party, is no longer decrypt-able. From f714692935b4b0d25f70afee0eb41951d866ba09 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:53:34 +0800 Subject: [PATCH 28/38] revision suggestions taken with gratitudes Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 9ecdb7692f7420..3b38cfa6f0f626 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -167,7 +167,11 @@ Signature algorithms for each curve is: A critical difference of this [EIP-6051](./eip-6051.md) with [EIP-5630](./eip-5630.md) is that, as the purpose of key encapsulation is to transport a private key securely, the public key from the key-recipient should be ephemeral, and mostly used only one-time. While in EIP-5630 settings, the public key of the message recipient shall be stable for a while so that message senders can encrypt messages without key discovery every time. -There is security implication to this difference, including perfect forward secrecy. We aim to achieve perfect forward secrecy by generating ephemeral key pairs in both sides every time: 1) first the recipient shall generate an ephemeral key pair, retain the private key securely, and export the public key; 2) then the key sender securely wrap the private key in ECIES, with another ephemeral key pair, then destroy the ephemeral key securely; 3) finally the recipient can unwrap the private key, then destroy its ephemeral key pair securely. After these steps, the cipher text in transport intercepted by a malicious 3rd party, is no longer decrypt-able. +There is security implication to this difference, including perfect forward secrecy. We aim to achieve perfect forward secrecy by generating ephemeral key pairs in both sides every time: + +1) first the recipient shall generate an ephemeral key pair, retain the private key securely, and export the public key; +2) then the key sender securely wrap the private key in ECIES, with another ephemeral key pair, then destroy the ephemeral key securely; +3) finally the recipient can unwrap the private key, then destroy its ephemeral key pair securely. After these steps, the cipher text in transport intercepted by a malicious 3rd party, is no longer decrypt-able. ## Backwards Compatibility From 51ed122201c958e75e5c14bf4c40b9a91ece90ef Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:54:14 +0800 Subject: [PATCH 29/38] fixed grammar as suggested Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 3b38cfa6f0f626..4dfdba71129249 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -253,7 +253,7 @@ sig: '0x536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac775 //signerPubKey pub: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b' ``` -Then it proceeds to verify second layer signature, also as ECDSA over secp256k1: +Then it proceeds to verify the second layer signature, also as ECDSA over secp256k1: ``` // actual message to be signed should be the decoded byte array msg: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b', From 729ef3c938a84c1556cc0d8076c40fa7dcd2299d Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:56:26 +0800 Subject: [PATCH 30/38] fixed grammar as suggested Co-authored-by: xinbenlv --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 4dfdba71129249..11b0b5992e37f0 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -458,7 +458,7 @@ Note that it is advised that a separate key pair should be used for signing on e **Security Level**: -1. We are not considering post-quantum security. If quantum computer becomes a materialized threat, the underlying cipher of Ethereum and other L1 chains would have been replaced, and this EIP will be outdated then (as the EC part of ECIES is also broken). +1. We are not considering post-quantum security. If the quantum computer becomes a materialized threat, the underlying cipher of Ethereum and other L1 chains would have been replaced, and this EIP will be outdated then (as the EC part of ECIES is also broken). 2. The security level shall match that of the elliptic curve used by the underlying chains. It does not make much sense to use AES-256 to safeguard a secp256k1 private key but implementation could choose freely. 3. That being said, a key might be used in multiple chains. So the security level shall cover the most demanding requirement and potential future developments. From fd3ea2be2d943fd7f1b71cf5020e3ee49483d0a9 Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Fri, 9 Dec 2022 18:23:11 +0800 Subject: [PATCH 31/38] fixed grammar as suggested --- EIPS/eip-6051.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 11b0b5992e37f0..58513c502a8f36 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -44,12 +44,12 @@ There should be a mandatory `version` parameter. This allows various kinds of In addition to a `version` parameter, the following keys and functions are involved: 1. The Sender's private key `sk`, which is to be encapsulated to the Recipient, and the corresponding address `account`. -2. Ephemeral recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes the scalar multiplication. Optionally, `R` could be signed, and `signerPubKey` and `signature` are then provided for sender to verify if `R` could be trusted or not. -3. Ephemeral sender key pair `(s, S)` such that `S = [s]G`. -4. Share secret `ss := [s]R = [r]S` according to ECDH. Note that for secp256k1 this EIP follows RFC5903 and uses compact representation, which means to use *only* the `x` coordinate as the shared secret. For Curve25519 this EIP follows RFC7748. -5. `oob`, out of band data, optional. This could be digits or an alpha-numeric string entered by user. +2. The ephemeral Recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes the scalar multiplication. Optionally, `R` could be signed, and `signerPubKey` and `signature` are then provided for Sender to verify if `R` could be trusted or not. +3. The ephemeral Sender key pair `(s, S)` such that `S = [s]G`. +4. The share secret `ss := [s]R = [r]S` according to ECDH. Note that for secp256k1 this EIP follows RFC5903 and uses compact representation, which means to use *only* the `x` coordinate as the shared secret. For Curve25519 this EIP follows RFC7748. +5. The out of band data `oob`, optional. This could be digits or an alpha-numeric string entered by user. 6. Let `derivedKey := HKDF(hash=SHA256, ikm=ss, info=oob, salt, length)`. HKDF is defined in RFC5869. The `length` should be determined by `skey` and `IV` requirements such that the symmetric key `skey = derivedKey[0:keySize]`, and `IV = derivedKey[keySize:length]`. `keySize` denotes the key size of underlying symmetric algorithm, for example, 16 (bytes) for AES-128, and 32 (bytes) for Chacha20. See **Security Considerations** for the use of `salt`. -7. `cipher := authenticated_encryption(symAlg, skey, IV, data=sk)`. The symmetric cipher algorithm `symAlg` and authentication scheme are decided by the version parameter. No additional authentication data `aad` is used. +7. Let `cipher := authenticated_encryption(symAlg, skey, IV, data=sk)`. The symmetric cipher algorithm `symAlg` and authentication scheme are decided by the version parameter. No additional authentication data `aad` is used. A much simplified example flow without signature and verification is: @@ -82,7 +82,7 @@ request({ // expected return value: R ``` -`signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and Ed25519 for Curve25519. And in this situation, it should be the case that the sender trusts `signerPubKey`, no matter how this trust is maintained. If not, the next request WILL be rejected by Sender application. Also see **Security Considerations**. +`signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and Ed25519 for Curve25519. And in this situation, it should be the case that Sender trusts `signerPubKey`, no matter how this trust is maintained. If not, the next request WILL be rejected by Sender application. Also see **Security Considerations**. Implementation then MUST generate random private key `r` with cryptographic secure random number generator (CSRNG), and derives ephemeral public key `R = [r]G`. Implementation SHOULD safe keep the generated key pair `(r, R)` in a secure manner in accordance with the circumstances, and SHOULD keep it only for limited duration, but the specific duration is left to individual implementations. Implementation SHOULD be able to retrieve `r` when given back the corresponding public key `R` if within said duration. @@ -119,7 +119,7 @@ If `signerPubKey` is provided or `recipient` contains `signature` data, the The implementation shall then proceed to retrieve the private key `sk` corresponding to `account`, and follow the **Core Algorithms** to encrypt it. -The return data is a byte array which contains first the ephemeral sender public key `S` (compressed if applicable), then `cipher` including any authentication tag, that is, `S || cipher`. +The return data is a byte array which contains first Sender's ephemeral public key `S` (compressed if applicable), then `cipher` including any authentication tag, that is, `S || cipher`. #### R3. Request for Recipient to unwrap and intake the private key @@ -137,7 +137,7 @@ request({ // expected return value: account ``` -This time `recipientPublicKey` is only the ephemeral public key `R` generated earlier in the recipient side, just for the implementation to retrieve the corresponding private key `r`. `data` is the return value from the call to encapsulate private key, which is `S || cipher`. +This time `recipientPublicKey` is only the ephemeral public key `R` generated earlier in the Recipient side, just for the implementation to retrieve the corresponding private key `r`. `data` is the return value from the call to encapsulate private key, which is `S || cipher`. When the encapsulated private key `sk` is decrypted successfully, the implementation can process it further according to the designated purposes. Some general security guidelines SHALL be followed, for example, do *not* log the value, do securely wipe it after use, etc. @@ -165,12 +165,12 @@ Signature algorithms for each curve is: ## Rationale -A critical difference of this [EIP-6051](./eip-6051.md) with [EIP-5630](./eip-5630.md) is that, as the purpose of key encapsulation is to transport a private key securely, the public key from the key-recipient should be ephemeral, and mostly used only one-time. While in EIP-5630 settings, the public key of the message recipient shall be stable for a while so that message senders can encrypt messages without key discovery every time. +A critical difference of this [EIP-6051](./eip-6051.md) with [EIP-5630](./eip-5630.md) is that, as the purpose of key encapsulation is to transport a private key securely, the public key from the key recipient should be ephemeral, and mostly used only one-time. While in EIP-5630 settings, the public key of the message recipient shall be stable for a while so that message senders can encrypt messages without key discovery every time. -There is security implication to this difference, including perfect forward secrecy. We aim to achieve perfect forward secrecy by generating ephemeral key pairs in both sides every time: +There is security implication to this difference, including perfect forward secrecy. We aim to achieve perfect forward secrecy by generating ephemeral key pairs in both sides every time: -1) first the recipient shall generate an ephemeral key pair, retain the private key securely, and export the public key; -2) then the key sender securely wrap the private key in ECIES, with another ephemeral key pair, then destroy the ephemeral key securely; +1) first Recipient shall generate an ephemeral key pair, retain the private key securely, and export the public key; +2) then Sender can securely wrap the private key in ECIES, with another ephemeral key pair, then destroy the ephemeral key securely; 3) finally the recipient can unwrap the private key, then destroy its ephemeral key pair securely. After these steps, the cipher text in transport intercepted by a malicious 3rd party, is no longer decrypt-able. ## Backwards Compatibility @@ -201,8 +201,8 @@ For review purposes, the program to generate the test vectors is open-sourced an Through out the test cases, we fix values for below data: - `sk`, the private key to be encapsulated, fixed to: `0xf8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315`. The corresponding address is `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`, called `account`. Note that these values come from the book *Mastering Ethereum* by Andreas M. Antonopoulos and Gavin Wood. -- `r`, the recipient private key, fixed to `0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79` -- `s`, the sender private key, fixed to `0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8` +- `r`, the Recipient private key, fixed to `0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79` +- `s`, the Sender private key, fixed to `0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8` - `signer`, the private key to sign the ephemeral public key, fixed to `0xac304db075d1685284ba5e10c343f2324ee32df3394fc093c98932517d36e344`. When used for Ed25519 signing, however, this values acts as `seed`, while the actual private key is calculated as `SHA512(seed)[:32]`. Or put it another way, the public key is the scalar multiplication of hashed private key to the base point. Same for `trusted`. - `trusted`, the private key to sign `signerPubKey`, fixed to `0xda6649d68fc03b807e444e0034b3b59ec60716212007d72c9ddbfd33e25d38d1` - `oob`, fixed to `0x313233343536` (string value: `123456`) From 505fe09a7adae05d32859a86f55c02290f4b213b Mon Sep 17 00:00:00 2001 From: Weiji Guo Date: Sat, 10 Dec 2022 00:07:33 +0800 Subject: [PATCH 32/38] fixed based on grammarly.com suggestions --- EIPS/eip-6051.md | 118 +++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 58513c502a8f36..2b7be61ca6259d 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -13,7 +13,7 @@ created: 2022-11-21 ## Abstract -This EIP proposes a mechanism to encapsulate a private key so that it could be securely relocated to another application without providing the seed. This EIP combines `ECIES` (Elliptic Curve Integrated Encryption Scheme) and optional signature verification under various choices to ensure that the private key is encapsulated to a known or trusted party. +This EIP proposes a mechanism to encapsulate a private key so that it could be securely relocated to another application without providing the seed. This EIP combines `ECIES` (Elliptic Curve Integrated Encryption Scheme) and optional signature verification under various choices to ensure that the private key is encapsulated for a known or trusted party. ## Motivation @@ -31,34 +31,34 @@ This EIP aims to enable such use cases. ### Sender and Recipient We hereby define: -- *Sender* as the party who holds in custody the private key to be encapsulated. *Sender Application* as the client-side application that said Sender uses to send the encapsulated private key. +- *Sender* as the party who holds in custody the private key to be encapsulated; *Sender Application* as the client-side application that said *Sender* uses to send the encapsulated private key. -- *Recipient* as the party who accepts the encapsulated private key, unwrap and then use it. *Recipient Application* as the client-side application that Recipient uses to receive the encapsulated private key. +- *Recipient* as the party who accepts the encapsulated private key, unwraps, and then uses it; *Recipient Application* as the client-side application that *Recipient* uses to receive the encapsulated private key. ### Core Algorithms The basic idea is to encapsulate the private key with ECIES. To ensure that the ephemeral public key to encapsulate the private key is indeed generated from a trusted party and has not been tampered with, we also provided an option to sign that ephemeral public key in this standard. -There should be a mandatory `version` parameter. This allows various kinds of Key Encapsulation Mechanisms to be adopted depending on the security considerations or preferences. The list shall be short to minimize compatibility issues among different vendors. +There should be a mandatory `version` parameter. This allows various kinds of Key Encapsulation Mechanisms to be adopted depending on security considerations or preferences. The list shall be short to minimize compatibility issues among different vendors. In addition to a `version` parameter, the following keys and functions are involved: 1. The Sender's private key `sk`, which is to be encapsulated to the Recipient, and the corresponding address `account`. -2. The ephemeral Recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes the scalar multiplication. Optionally, `R` could be signed, and `signerPubKey` and `signature` are then provided for Sender to verify if `R` could be trusted or not. +2. The ephemeral Recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes scalar multiplication. Optionally, `R` could be signed, and `signerPubKey` and `signature` are then provided for Sender to verify if `R` could be trusted or not. 3. The ephemeral Sender key pair `(s, S)` such that `S = [s]G`. 4. The share secret `ss := [s]R = [r]S` according to ECDH. Note that for secp256k1 this EIP follows RFC5903 and uses compact representation, which means to use *only* the `x` coordinate as the shared secret. For Curve25519 this EIP follows RFC7748. -5. The out of band data `oob`, optional. This could be digits or an alpha-numeric string entered by user. -6. Let `derivedKey := HKDF(hash=SHA256, ikm=ss, info=oob, salt, length)`. HKDF is defined in RFC5869. The `length` should be determined by `skey` and `IV` requirements such that the symmetric key `skey = derivedKey[0:keySize]`, and `IV = derivedKey[keySize:length]`. `keySize` denotes the key size of underlying symmetric algorithm, for example, 16 (bytes) for AES-128, and 32 (bytes) for Chacha20. See **Security Considerations** for the use of `salt`. +5. The out-of-band data `oob`, optional. This could be digits or an alpha-numeric string entered by the user. +6. Let `derivedKey := HKDF(hash=SHA256, ikm=ss, info=oob, salt, length)`. HKDF is defined in RFC5869. The `length` should be determined by `skey` and `IV` requirements such that the symmetric key `skey = derivedKey[0:keySize]`, and `IV = derivedKey[keySize:length]`. `keySize` denotes the key size of the underlying symmetric algorithm, for example, 16 (bytes) for AES-128, and 32 (bytes) for Chacha20. See **Security Considerations** for the use of `salt`. 7. Let `cipher := authenticated_encryption(symAlg, skey, IV, data=sk)`. The symmetric cipher algorithm `symAlg` and authentication scheme are decided by the version parameter. No additional authentication data `aad` is used. -A much simplified example flow without signature and verification is: +A much-simplified example flow without signature and verification is: -1. Recipient application generates `(r, R)`. -2. User inputs `R` to Sender application, along with a six-digit code “123456” as `oob`. -3. Sender application generates `(s, S)`, and computes `cipher`, then returns `S || cipher` -4. Recipient application scans to read `S` and `cipher`. User enters “123456” as `oob` to Recipient application. -5. Recipient application decrypts `cipher` to get `sk`. -6. Recipient application derives the address corresponding to `sk` so that user can confirm the correctness. +1. *Recipient Application* generates `(r, R)`. +2. User inputs `R` to *Sender Application*, along with a six-digit code “123456” as `oob`. +3. *Sender Application* generates `(s, S)`, and computes `cipher`, then returns `S || cipher`. +4. *Recipient Application* scans to read `S` and `cipher`. The user enters “123456” as `oob` to *Recipient Application*. +5. *Recipient Application* decrypts `cipher` to get `sk`. +6. *Recipient Application* derives the address corresponding to `sk` so that the user can confirm the correctness. With signature and verification, the signature to `R` by `singerPubKey` is appended to `R`. `signerPubKey` itself could have been already signed by `trustedPubKey`, and that signature is appended to `signerPubKey`. Note that the signature is applied to the byte array data instead of its string representation, which might lead to confusion and interoperability issues (such as hex or base64, lower case v.s. upper case, etc.). See **Requests** and **Test Cases** for further clarification and examples. @@ -68,8 +68,8 @@ With signature and verification, the signature to `R` by `singerPubKey` is appen - Raw bytes are encoded in hex and prefixed with '0x'. - Unless specified otherwise, all parameters and return values are hex-encoded bytes. -- `cipher` is encoded into single byte buffer as: `[IV || encrypted_sk || tag]`. -- `R`, `S`, `signerPubKey` and `trustedPubKey` are compressed if applicable. +- `cipher` is encoded into a single byte buffer as: `[IV || encrypted_sk || tag]`. +- `R`, `S`, `signerPubKey`, and `trustedPubKey` are compressed if applicable. - `R` or `signerPubKey` could be followed by a signature to it: `[pub || sig]`. Note that for the secp256k1 curve, the signature is just 64 bytes without the `v` indicator as found in a typical Ethereum signature. #### R1. Request for Recipient to generate ephemeral key pair @@ -82,11 +82,11 @@ request({ // expected return value: R ``` -`signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and Ed25519 for Curve25519. And in this situation, it should be the case that Sender trusts `signerPubKey`, no matter how this trust is maintained. If not, the next request WILL be rejected by Sender application. Also see **Security Considerations**. +`signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and Ed25519 for Curve25519. And in this situation, it should be the case that *Sender* trusts `signerPubKey`, no matter how this trust is maintained. If not, the next request WILL be rejected by *Sender Application*. Also, see **Security Considerations**. -Implementation then MUST generate random private key `r` with cryptographic secure random number generator (CSRNG), and derives ephemeral public key `R = [r]G`. Implementation SHOULD safe keep the generated key pair `(r, R)` in a secure manner in accordance with the circumstances, and SHOULD keep it only for limited duration, but the specific duration is left to individual implementations. Implementation SHOULD be able to retrieve `r` when given back the corresponding public key `R` if within said duration. +The implementation then MUST generate random private key `r` with a cryptographic secure random number generator (CSRNG), and derive ephemeral public key `R = [r]G`. The implementation SHOULD keep the generated key pair `(r, R)` in a secure manner in accordance with the circumstances, and SHOULD keep it only for a limited duration, but the specific duration is left to individual implementations. The implementation SHOULD be able to retrieve `r` when given back the corresponding public key `R` if within the said duration. -Returned value is `R`, compressed if applicable. If `signerPubKey` is provided, then the `signature` is appended to `R`, also hex-encoded. +The return value is `R`, compressed if applicable. If `signerPubKey` is provided, then the `signature` is appended to `R`, also hex-encoded. Alternatively, `signature` could be calculated separately, and then appended to the returned data. @@ -107,19 +107,19 @@ request({ // expected return value: S || cipher ``` -`recipient` is the return value from the call to generate ephemeral key pair, with optional `signature` appended either as returned or separately. +`recipient` is the return value from the call to generate ephemeral key pair, with the optional `signature` appended either as returned or separately. `oob` and `salt` are just byte arrays. -`account` is used to identify which private key to be encapsulated. With Ethereum, it is an address. Also see **Encoding of data and messages**. +`account` is used to identify which private key to be encapsulated. With Ethereum, it is an address. Also, see **Encoding of data and messages**. -If `signerPubKey` is provided or `recipient` contains `signature` data, the implementation MUST perform signature verification. Missing data or incorrect format MUST either fail the call or result in empty return and optional error logs. +If `signerPubKey` is provided or `recipient` contains `signature` data, the implementation MUST perform signature verification. Missing data or incorrect format MUST either fail the call or result in an empty return and optional error logs. -`signerPubKey` could have been further signed by another key pair `(trusted, trustedPubKey)`, which is trusted by the Sender application. In that case, `signerPubKey` is appended with the corresponding signature data, which SHOULD be verified against `trustedPubKey`. See **Test Cases** for further clarification. +`signerPubKey` could have been further signed by another key pair `(trusted, trustedPubKey)`, which is trusted by *Sender Application*. In that case, `signerPubKey` is appended with the corresponding signature data, which SHOULD be verified against `trustedPubKey`. See **Test Cases** for further clarification. The implementation shall then proceed to retrieve the private key `sk` corresponding to `account`, and follow the **Core Algorithms** to encrypt it. -The return data is a byte array which contains first Sender's ephemeral public key `S` (compressed if applicable), then `cipher` including any authentication tag, that is, `S || cipher`. +The return data is a byte array that contains first *Sender*'s ephemeral public key `S` (compressed if applicable), then `cipher` including any authentication tag, that is, `S || cipher`. #### R3. Request for Recipient to unwrap and intake the private key @@ -156,22 +156,22 @@ Available authenticated encryption schemes are: - AES-256-GCM - Chacha20-Poly1305 -Version string is simply concatenation of elliptic curve and AE scheme, for example, secp256k1-AES-128-GCM. Above lists allows a combination of six different concrete schemes. Implementations are encouraged to implement curve related logic separately from authenticated encryption scheme to avoid duplication and to promote interoperability. +The version string is simply the concatenation of the elliptic curve and AE scheme, for example, secp256k1-AES-128-GCM. The above lists allow a combination of six different concrete schemes. Implementations are encouraged to implement curve-related logic separately from authenticated encryption schemes to avoid duplication and to promote interoperability. -Signature algorithms for each curve is: +Signature algorithms for each curve are: - secp256k1 --> ECDSA - Curve25519 --> Ed25519 ## Rationale -A critical difference of this [EIP-6051](./eip-6051.md) with [EIP-5630](./eip-5630.md) is that, as the purpose of key encapsulation is to transport a private key securely, the public key from the key recipient should be ephemeral, and mostly used only one-time. While in EIP-5630 settings, the public key of the message recipient shall be stable for a while so that message senders can encrypt messages without key discovery every time. +A critical difference between this [EIP-6051](./eip-6051.md) with [EIP-5630](./eip-5630.md) is that, as the purpose of key encapsulation is to transport a private key securely, the public key from the key recipient should be ephemeral, and mostly used only one-time. While in EIP-5630 settings, the public key of the message recipient shall be stable for a while so that message senders can encrypt messages without key discovery every time. -There is security implication to this difference, including perfect forward secrecy. We aim to achieve perfect forward secrecy by generating ephemeral key pairs in both sides every time: +There is security implication to this difference, including perfect forward secrecy. We aim to achieve perfect forward secrecy by generating ephemeral key pairs on both sides every time: -1) first Recipient shall generate an ephemeral key pair, retain the private key securely, and export the public key; -2) then Sender can securely wrap the private key in ECIES, with another ephemeral key pair, then destroy the ephemeral key securely; -3) finally the recipient can unwrap the private key, then destroy its ephemeral key pair securely. After these steps, the cipher text in transport intercepted by a malicious 3rd party, is no longer decrypt-able. +1) first *Recipient* shall generate an ephemeral key pair, retain the private key securely, and export the public key; +2) then *Sender* can securely wrap the private key in ECIES, with another ephemeral key pair, then destroy the ephemeral key securely; +3) finally *Recipient* can unwrap the private key, then destroy its ephemeral key pair securely. After these steps, the cipher text in transport intercepted by a malicious 3rd party is no longer decryptable. ## Backwards Compatibility @@ -181,16 +181,16 @@ No backward compatibility issues for this new proposal. To minimize potential compatibility issues among applications (including hardware wallets), this EIP requires that version secp256k1-AES-128-GCM MUST be supported. -Version could be decided by user or negotiated by both sides. When there is no user input or negotiation, secp256k1-AES-128-GCM is assumed. +The version could be decided by the user or negotiated by both sides. When there is no user input or negotiation, secp256k1-AES-128-GCM is assumed. -It is expected that implementations cover curve supports separately from encryption support, that is, all the versions that could be derived from supported curve and supported encryption scheme, should work. +It is expected that implementations cover curve supports separately from encryption support, that is, all the versions that could be derived from the supported curve and supported encryption scheme should work. Signatures to `R` and `signerPubKey` are applied to byte array values instead of the encoded string. ### UX Recommendations `salt` and/or `oob` data: both are inputs to the HKDF function (`oob` as “info” parameter). For better user experiences we suggest to require from users only one of them but this is up to the implementation. -Recipient application is assumed to be powerful enough. Sender application could have very limited computing power and user interaction capabilities. +*Recipient Application* is assumed to be powerful enough. *Sender Application* could have very limited computing power and user interaction capabilities. ## Test Cases @@ -198,19 +198,19 @@ For review purposes, the program to generate the test vectors is open-sourced an ### Data Fixation -Through out the test cases, we fix values for below data: +Throughout the test cases, we fix values for the below data: - `sk`, the private key to be encapsulated, fixed to: `0xf8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315`. The corresponding address is `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`, called `account`. Note that these values come from the book *Mastering Ethereum* by Andreas M. Antonopoulos and Gavin Wood. - `r`, the Recipient private key, fixed to `0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79` - `s`, the Sender private key, fixed to `0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8` -- `signer`, the private key to sign the ephemeral public key, fixed to `0xac304db075d1685284ba5e10c343f2324ee32df3394fc093c98932517d36e344`. When used for Ed25519 signing, however, this values acts as `seed`, while the actual private key is calculated as `SHA512(seed)[:32]`. Or put it another way, the public key is the scalar multiplication of hashed private key to the base point. Same for `trusted`. +- `signer`, the private key to sign the ephemeral public key, fixed to `0xac304db075d1685284ba5e10c343f2324ee32df3394fc093c98932517d36e344`. When used for Ed25519 signing, however, this value acts as `seed`, while the actual private key is calculated as `SHA512(seed)[:32]`. Or put another way, the public key is the scalar multiplication of hashed private key to the base point. Same for `trusted`. - `trusted`, the private key to sign `signerPubKey`, fixed to `0xda6649d68fc03b807e444e0034b3b59ec60716212007d72c9ddbfd33e25d38d1` - `oob`, fixed to `0x313233343536` (string value: `123456`) - `salt`, fixed to `0x6569703a2070726976617465206b657920656e63617073756c6174696f6e` (string value: `eip: private key encapsulation`) ### Case 1 -Use `version` as: `secp256k1-AES-128-GCM`. **R1** is provided as: +Use `version` as `secp256k1-AES-128-GCM`. **R1** is provided as: ```javascript request({ method: 'eth_generateEphemeralKeyPair', @@ -245,7 +245,7 @@ request({ ], }) ``` -The Sender implementation first verifies first layer signature as ECDSA over secp256k1: +*Sender Application* first verifies first layer signature as ECDSA over secp256k1: ``` // actual message to be signed should be the decoded byte array msg: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09', @@ -261,14 +261,14 @@ sig: '0x5bd427c527b7f1012b8edfd179b9002a7f2d7fc326bb6ae9aaf38b44eb93c397631fd8bb //trustedPubKey pub: '0x027fb72176f1f9852ce7dd9dc3aa4711675d3d8dc5102b86d758d853002137e839' ``` -Since Sender application trusts `trustedPubKey`, the signature verification succeeds. +Since *Sender Application* trusts `trustedPubKey`, the signature verification succeeds. Suppose the implementation generates an ephemeral key pair `(s, S)` as: ``` s: '0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8', S: '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c02' ``` -The shared secret, symmetric key and IV should be: +The shared secret, symmetric key, and IV should be: ``` ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', skey: '0x6ccc02a61aa16d6c66a1277e5e2434b8', @@ -299,7 +299,7 @@ The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This ma ### Case 2 -Use `version` as: `secp256k1-AES-256-GCM`. The calculated symmetric key `skey`, `IV`, and `cipher` will be different. **R1** is provided as: +Use `version` as `secp256k1-AES-256-GCM`. The calculated symmetric key `skey`, `IV`, and `cipher` will be different. **R1** is provided as: ```javascript request({ method: 'eth_generateEphemeralKeyPair', @@ -309,7 +309,7 @@ request({ ], }) ``` -Note that only `version` is different (AES key size). We keep using the same `(r, R)` (this is just a test vector). +Note that only the `version` is different (AES key size). We keep using the same `(r, R)` (this is just a test vector). Therefore **R2** is provided as: ```javascript @@ -325,15 +325,15 @@ request({ ], }) ``` -Suppose the implementation generates the same `(s, S)` as **Case 1**. The shared secret, symmetric key and IV should be: +Suppose the implementation generates the same `(s, S)` as **Case 1**. The shared secret, symmetric key, and IV should be: ``` ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', skey: '0x6ccc02a61aa16d6c66a1277e5e2434b89c7a0f870d17ced2d2c3d1cfd0e6f199', IV: '0x3369b9570b9d207a0a8ebe27' ``` -With shared secret `ss` remains the same as **Case 1**, symmetric key `skey` contains both the `skey` and `IV` from **Case 1**. IV is changed. +With shared secret `ss` remaining the same as **Case 1**, symmetric key `skey` contains both the `skey` and `IV` from **Case 1**. IV is changed. -Then the return value should be the follows, with `S` part the same as **Case 1** and `cipher` part different: +Then the return value should be the following, with the `S` part the same as **Case 1** and the `cipher` part different: ``` '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c0293910a91270b5deb0a645cc33604ed91668daf72328739d52a5af5a4760c4f3a9592b8f6d9b3ebe25127e7bf1c43b839' ``` @@ -366,13 +366,13 @@ request({ ], }) ``` -Note that with Curve25519 the size of either public key or private key is 32 (bytes). And there is no compression for public key. `signerPubKey` is calculated as: +Note that with Curve25519 the size is 32 (bytes) for both the public key and private key. And there is no compression for the public key. `signerPubKey` is calculated as: ``` //signer is '0xac304db075d1685284ba5e10c343f2324ee32df3394fc093c98932517d36e344' s := SHA512(signer)[:32] signerPubKey := Curve25519.ScalarBaseMult(s).ToHex() ``` -Same technique for `trustedPubKey`. With `r` value same as in **Case 1** and **Case 2** and the curve being changed, the return value is `R = [r]G || sig`: +The same technique applies to `trustedPubKey`. With `r` the same as in **Case 1** and **Case 2** and the curve being changed, the return value is `R = [r]G || sig`: ``` R = '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79cac06de279ec7f65f6b75f6bee740496df0650a6de61da5e691d7c5da1c7cb1ece61c669dd588a1029c38f11ad1714c1c9742232f9562ca6bbc7bad57882da04' ``` @@ -399,13 +399,13 @@ sig: '0x879d900f04a955078ff6ae86f1d1b69b3e1265370e64bf064adaecb895c51effa3bdae79 //signerPubKey pub: '0xe509fb840f6d5a69333ef68d69b86de55b9b905e45b16e3591912c097ba69938' ``` -After successfully verifies the signature (and the one by `trustedPubKey`), the implementation then generates ephemeral key pair `(s, S)` in Curve25519: +After successfully verifying the signature (and the one by `trustedPubKey`), the implementation then generates ephemeral key pair `(s, S)` in Curve25519: ``` // s same as Case 1 and Case 2 s = '0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8', S = '0xd2fd6fcaac231d08363e736e61edb7e7696b13a727e3d2a239415cb8dc6ee278' ``` -The shared secret, symmetric key and IV should be: +The shared secret, symmetric key, and IV should be: ``` ss: '0xe0b36f56cdb63c27e933a5a67a5e97db4b566c9276a36aeee5dc6e87da118867', skey: '0x7c6fa749e6df13c8578dc44cb24cdf46a44cb163e1e570c2e590c720aed5783f', @@ -435,40 +435,40 @@ The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This ma Contributions are welcome. -Test vector generator is provided earlier in the **Test Cases** section. +The test vector generator is provided earlier in the **Test Cases** section. ## Security Considerations -**Perfect Forward Secrecy**: PFS is achieved by using ephemeral key pairs in both sides. +**Perfect Forward Secrecy**: PFS is achieved by using ephemeral key pairs on both sides. **Optional Signature and Trusted Public Keys** -`R` could be signed so that Sender application can verify if `R` could be trusted or not. This involves both signature verification and if the signer could be trusted or not. While signature verification is quite straightforward in itself, the latter should be managed with care. To facilitate this trust management issue, `signerPubKey` could be further signed, creating a dual-layer trust structure: +`R` could be signed so that *Sender Application* can verify if `R` could be trusted or not. This involves both signature verification and if the signer could be trusted or not. While signature verification is quite straightforward in itself, the latter should be managed with care. To facilitate this trust management issue, `signerPubKey` could be further signed, creating a dual-layer trust structure: ``` R <-- signerPubKey <-- trustedPubKey ``` -This allows various strategies to mange the trust. For example: +This allows various strategies to manage trust. For example: -- A hardware wallet vendor which takes it very serious about the brand reputation and the fund safety for its customers, could choose to trust only its own public keys, all instances of `trustedPubKey`. These public keys only sign `signerPubKey` from selected partners. -- A MPC service could publish its `signerPubKey` online so that Sender application won't verify the signature against a wrong or fake public key. +- A hardware wallet vendor which takes it very seriously about the brand reputation and the fund safety for its customers, could choose to trust only its own public keys, all instances of `trustedPubKey`. These public keys only sign `signerPubKey` from selected partners. +- A MPC service could publish its `signerPubKey` online so that *Sender Application* won't verify the signature against a wrong or fake public key. Note that it is advised that a separate key pair should be used for signing on each curve. **Security Level**: 1. We are not considering post-quantum security. If the quantum computer becomes a materialized threat, the underlying cipher of Ethereum and other L1 chains would have been replaced, and this EIP will be outdated then (as the EC part of ECIES is also broken). -2. The security level shall match that of the elliptic curve used by the underlying chains. It does not make much sense to use AES-256 to safeguard a secp256k1 private key but implementation could choose freely. +2. The security level shall match that of the elliptic curve used by the underlying chains. It does not make much sense to use AES-256 to safeguard a secp256k1 private key but implementations could choose freely. 3. That being said, a key might be used in multiple chains. So the security level shall cover the most demanding requirement and potential future developments. -AES-128, AES-256 and ChaCha20 are provided. +AES-128, AES-256, and ChaCha20 are provided. **Randomness**. `r` and `s` must be generated with a cryptographic secure random number generator (CSRNG). -`salt` could be random bytes generated the same way as `r` or `s`. `salt` could be in any length but the general suggestion is 12 or 16, which could be displayed as QR code by the screen of some hardware wallet (so that another application could scan to read). If `salt` is not provided, this EIP uses default value as `EIP-6051`. +`salt` could be random bytes generated the same way as `r` or `s`. `salt` could be in any length but the general suggestion is 12 or 16, which could be displayed as a QR code by the screen of some hardware wallet (so that another application could scan to read). If `salt` is not provided, this EIP uses the default value as `EIP-6051`. -**Out of Band Data**: `oob` data is optional. When non-empty, its content is digits or an alpha-numeric string from user. Sender application may mandate `oob` from user. +**Out of Band Data**: `oob` data is optional. When non-empty, its content is digits or an alpha-numeric string from the user. *Sender Application* may mandate `oob` from the user. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). From 716148dc4bef08206857dcfa87b05a7ef49545e2 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Thu, 22 Dec 2022 09:43:41 +0800 Subject: [PATCH 33/38] Update EIPS/eip-6051.md Co-authored-by: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 2b7be61ca6259d..fa6df3ee130d33 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -6,7 +6,7 @@ author: Base Labs (@Base-Labs), Weiji Guo (@weiji-cryptonatty) discussions-to: https://ethereum-magicians.org/t/private-key-encapsulation-to-move-around-securely-without-entering-seed/11604 status: Draft type: Standards Track -category: ERC +category: Interface created: 2022-11-21 --- From 93dd18c5a3848b20a0f97634aa7dda4564839e4b Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Thu, 22 Dec 2022 09:44:00 +0800 Subject: [PATCH 34/38] Update EIPS/eip-6051.md Co-authored-by: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> --- EIPS/eip-6051.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index fa6df3ee130d33..5e2858abd239df 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -431,12 +431,6 @@ request({ ``` The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This matches the `account` parameter in **R2**. -## Reference Implementation - -Contributions are welcome. - -The test vector generator is provided earlier in the **Test Cases** section. - ## Security Considerations **Perfect Forward Secrecy**: PFS is achieved by using ephemeral key pairs on both sides. From b63e6c42f9454bcfc0aae5a9688313e20a1de9cd Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Thu, 22 Dec 2022 09:44:17 +0800 Subject: [PATCH 35/38] Update EIPS/eip-6051.md Co-authored-by: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> --- EIPS/eip-6051.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 5e2858abd239df..0d384c0598350b 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -60,7 +60,7 @@ A much-simplified example flow without signature and verification is: 5. *Recipient Application* decrypts `cipher` to get `sk`. 6. *Recipient Application* derives the address corresponding to `sk` so that the user can confirm the correctness. -With signature and verification, the signature to `R` by `singerPubKey` is appended to `R`. `signerPubKey` itself could have been already signed by `trustedPubKey`, and that signature is appended to `signerPubKey`. Note that the signature is applied to the byte array data instead of its string representation, which might lead to confusion and interoperability issues (such as hex or base64, lower case v.s. upper case, etc.). See **Requests** and **Test Cases** for further clarification and examples. +With signature and verification, the signature to `R` by `singerPubKey` is appended to `R`. `signerPubKey` itself could have been already signed by `trustedPubKey`, and that signature is appended to `signerPubKey`. Note that the signature is applied to the byte array data instead of its string representation, which might lead to confusion and interoperability issues (such as hex or base64, lower case v.s. upper case, etc.). See [Requests](#requests) and [Test Cases](#test-cases) for further clarification and examples. ### Requests From 6cd2cbdba410b9543f7e339616fa0d80dd70b7f5 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Thu, 22 Dec 2022 09:56:25 +0800 Subject: [PATCH 36/38] replacing bold fonts with links as suggested --- EIPS/eip-6051.md | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 0d384c0598350b..3959fb369ec386 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -82,7 +82,7 @@ request({ // expected return value: R ``` -`signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and Ed25519 for Curve25519. And in this situation, it should be the case that *Sender* trusts `signerPubKey`, no matter how this trust is maintained. If not, the next request WILL be rejected by *Sender Application*. Also, see **Security Considerations**. +`signerPubKey` is optional. If provided, it is assumed that the implementation has the corresponding private key and the implementation MUST sign the ephemeral public key (in the form of what is to be returned). The signature algorithm is determined by the curve part of the `version` parameter, that is, ECDSA for secp256k1, and Ed25519 for Curve25519. And in this situation, it should be the case that *Sender* trusts `signerPubKey`, no matter how this trust is maintained. If not, the next request WILL be rejected by *Sender Application*. Also, see [Security Considerations](#security-considerations). The implementation then MUST generate random private key `r` with a cryptographic secure random number generator (CSRNG), and derive ephemeral public key `R = [r]G`. The implementation SHOULD keep the generated key pair `(r, R)` in a secure manner in accordance with the circumstances, and SHOULD keep it only for a limited duration, but the specific duration is left to individual implementations. The implementation SHOULD be able to retrieve `r` when given back the corresponding public key `R` if within the said duration. @@ -111,13 +111,13 @@ request({ `oob` and `salt` are just byte arrays. -`account` is used to identify which private key to be encapsulated. With Ethereum, it is an address. Also, see **Encoding of data and messages**. +`account` is used to identify which private key to be encapsulated. With Ethereum, it is an address. Also, see [Encoding of data and messages](encoding-of-data-and-messages). If `signerPubKey` is provided or `recipient` contains `signature` data, the implementation MUST perform signature verification. Missing data or incorrect format MUST either fail the call or result in an empty return and optional error logs. -`signerPubKey` could have been further signed by another key pair `(trusted, trustedPubKey)`, which is trusted by *Sender Application*. In that case, `signerPubKey` is appended with the corresponding signature data, which SHOULD be verified against `trustedPubKey`. See **Test Cases** for further clarification. +`signerPubKey` could have been further signed by another key pair `(trusted, trustedPubKey)`, which is trusted by *Sender Application*. In that case, `signerPubKey` is appended with the corresponding signature data, which SHOULD be verified against `trustedPubKey`. See [Test Cases](test-cases) for further clarification. -The implementation shall then proceed to retrieve the private key `sk` corresponding to `account`, and follow the **Core Algorithms** to encrypt it. +The implementation shall then proceed to retrieve the private key `sk` corresponding to `account`, and follow the [Core Algorithms](core-algorithms) to encrypt it. The return data is a byte array that contains first *Sender*'s ephemeral public key `S` (compressed if applicable), then `cipher` including any authentication tag, that is, `S || cipher`. @@ -325,15 +325,15 @@ request({ ], }) ``` -Suppose the implementation generates the same `(s, S)` as **Case 1**. The shared secret, symmetric key, and IV should be: +Suppose the implementation generates the same `(s, S)` as [Case 1](case-1). The shared secret, symmetric key, and IV should be: ``` ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', skey: '0x6ccc02a61aa16d6c66a1277e5e2434b89c7a0f870d17ced2d2c3d1cfd0e6f199', IV: '0x3369b9570b9d207a0a8ebe27' ``` -With shared secret `ss` remaining the same as **Case 1**, symmetric key `skey` contains both the `skey` and `IV` from **Case 1**. IV is changed. +With shared secret `ss` remaining the same as [Case 1](case-1), symmetric key `skey` contains both the `skey` and `IV` from [Case 1](case-1). IV is changed. -Then the return value should be the following, with the `S` part the same as **Case 1** and the `cipher` part different: +Then the return value should be the following, with the `S` part the same as [Case 1](case-1) and the `cipher` part different: ``` '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c0293910a91270b5deb0a645cc33604ed91668daf72328739d52a5af5a4760c4f3a9592b8f6d9b3ebe25127e7bf1c43b839' ``` @@ -372,7 +372,7 @@ Note that with Curve25519 the size is 32 (bytes) for both the public key and pri s := SHA512(signer)[:32] signerPubKey := Curve25519.ScalarBaseMult(s).ToHex() ``` -The same technique applies to `trustedPubKey`. With `r` the same as in **Case 1** and **Case 2** and the curve being changed, the return value is `R = [r]G || sig`: +The same technique applies to `trustedPubKey`. With `r` the same as in [Case 1](case-1) and [Case 2](case-2) and the curve being changed, the return value is `R = [r]G || sig`: ``` R = '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79cac06de279ec7f65f6b75f6bee740496df0650a6de61da5e691d7c5da1c7cb1ece61c669dd588a1029c38f11ad1714c1c9742232f9562ca6bbc7bad57882da04' ``` @@ -433,9 +433,11 @@ The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This ma ## Security Considerations -**Perfect Forward Secrecy**: PFS is achieved by using ephemeral key pairs on both sides. +### Perfect Forward Secrecy -**Optional Signature and Trusted Public Keys** +PFS is achieved by using ephemeral key pairs on both sides. + +### Optional Signature and Trusted Public Keys `R` could be signed so that *Sender Application* can verify if `R` could be trusted or not. This involves both signature verification and if the signer could be trusted or not. While signature verification is quite straightforward in itself, the latter should be managed with care. To facilitate this trust management issue, `signerPubKey` could be further signed, creating a dual-layer trust structure: @@ -450,7 +452,7 @@ This allows various strategies to manage trust. For example: Note that it is advised that a separate key pair should be used for signing on each curve. -**Security Level**: +### Security Level 1. We are not considering post-quantum security. If the quantum computer becomes a materialized threat, the underlying cipher of Ethereum and other L1 chains would have been replaced, and this EIP will be outdated then (as the EC part of ECIES is also broken). 2. The security level shall match that of the elliptic curve used by the underlying chains. It does not make much sense to use AES-256 to safeguard a secp256k1 private key but implementations could choose freely. @@ -458,11 +460,15 @@ Note that it is advised that a separate key pair should be used for signing on e AES-128, AES-256, and ChaCha20 are provided. -**Randomness**. `r` and `s` must be generated with a cryptographic secure random number generator (CSRNG). +### Randomness + +`r` and `s` must be generated with a cryptographic secure random number generator (CSRNG). `salt` could be random bytes generated the same way as `r` or `s`. `salt` could be in any length but the general suggestion is 12 or 16, which could be displayed as a QR code by the screen of some hardware wallet (so that another application could scan to read). If `salt` is not provided, this EIP uses the default value as `EIP-6051`. -**Out of Band Data**: `oob` data is optional. When non-empty, its content is digits or an alpha-numeric string from the user. *Sender Application* may mandate `oob` from the user. +### Out of Band Data + +`oob` data is optional. When non-empty, its content is digits or an alpha-numeric string from the user. *Sender Application* may mandate `oob` from the user. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). From 6fe15a822399cdb118f71b552828256855b87643 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Thu, 22 Dec 2022 10:00:58 +0800 Subject: [PATCH 37/38] fixed dead links --- EIPS/eip-6051.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 3959fb369ec386..04fd06e4236556 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -111,13 +111,13 @@ request({ `oob` and `salt` are just byte arrays. -`account` is used to identify which private key to be encapsulated. With Ethereum, it is an address. Also, see [Encoding of data and messages](encoding-of-data-and-messages). +`account` is used to identify which private key to be encapsulated. With Ethereum, it is an address. Also, see [Encoding of data and messages](#encoding-of-data-and-messages). If `signerPubKey` is provided or `recipient` contains `signature` data, the implementation MUST perform signature verification. Missing data or incorrect format MUST either fail the call or result in an empty return and optional error logs. -`signerPubKey` could have been further signed by another key pair `(trusted, trustedPubKey)`, which is trusted by *Sender Application*. In that case, `signerPubKey` is appended with the corresponding signature data, which SHOULD be verified against `trustedPubKey`. See [Test Cases](test-cases) for further clarification. +`signerPubKey` could have been further signed by another key pair `(trusted, trustedPubKey)`, which is trusted by *Sender Application*. In that case, `signerPubKey` is appended with the corresponding signature data, which SHOULD be verified against `trustedPubKey`. See [Test Cases](#test-cases) for further clarification. -The implementation shall then proceed to retrieve the private key `sk` corresponding to `account`, and follow the [Core Algorithms](core-algorithms) to encrypt it. +The implementation shall then proceed to retrieve the private key `sk` corresponding to `account`, and follow the [Core Algorithms](#core-algorithms) to encrypt it. The return data is a byte array that contains first *Sender*'s ephemeral public key `S` (compressed if applicable), then `cipher` including any authentication tag, that is, `S || cipher`. @@ -325,15 +325,15 @@ request({ ], }) ``` -Suppose the implementation generates the same `(s, S)` as [Case 1](case-1). The shared secret, symmetric key, and IV should be: +Suppose the implementation generates the same `(s, S)` as [Case 1](#case-1). The shared secret, symmetric key, and IV should be: ``` ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', skey: '0x6ccc02a61aa16d6c66a1277e5e2434b89c7a0f870d17ced2d2c3d1cfd0e6f199', IV: '0x3369b9570b9d207a0a8ebe27' ``` -With shared secret `ss` remaining the same as [Case 1](case-1), symmetric key `skey` contains both the `skey` and `IV` from [Case 1](case-1). IV is changed. +With shared secret `ss` remaining the same as [Case 1](#case-1), symmetric key `skey` contains both the `skey` and `IV` from [Case 1](#case-1). IV is changed. -Then the return value should be the following, with the `S` part the same as [Case 1](case-1) and the `cipher` part different: +Then the return value should be the following, with the `S` part the same as [Case 1](#case-1) and the `cipher` part different: ``` '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c0293910a91270b5deb0a645cc33604ed91668daf72328739d52a5af5a4760c4f3a9592b8f6d9b3ebe25127e7bf1c43b839' ``` @@ -372,7 +372,7 @@ Note that with Curve25519 the size is 32 (bytes) for both the public key and pri s := SHA512(signer)[:32] signerPubKey := Curve25519.ScalarBaseMult(s).ToHex() ``` -The same technique applies to `trustedPubKey`. With `r` the same as in [Case 1](case-1) and [Case 2](case-2) and the curve being changed, the return value is `R = [r]G || sig`: +The same technique applies to `trustedPubKey`. With `r` the same as in [Case 1](#case-1) and [Case 2](#case-2) and the curve being changed, the return value is `R = [r]G || sig`: ``` R = '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79cac06de279ec7f65f6b75f6bee740496df0650a6de61da5e691d7c5da1c7cb1ece61c669dd588a1029c38f11ad1714c1c9742232f9562ca6bbc7bad57882da04' ``` From 5c8a588a40ee8584196274dcfee6f85444c9ccb3 Mon Sep 17 00:00:00 2001 From: Weiji Guo <118721011+weiji-cryptonatty@users.noreply.github.com> Date: Thu, 22 Dec 2022 10:18:03 +0800 Subject: [PATCH 38/38] fixed markdown linter errors --- EIPS/eip-6051.md | 111 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 80 insertions(+), 31 deletions(-) diff --git a/EIPS/eip-6051.md b/EIPS/eip-6051.md index 04fd06e4236556..c1f380db909a2e 100644 --- a/EIPS/eip-6051.md +++ b/EIPS/eip-6051.md @@ -19,8 +19,8 @@ This EIP proposes a mechanism to encapsulate a private key so that it could be s There are various cases in which we might want to export one of many private keys from a much more secure but less convenient wallet, which is controlled with a seed or passphrase. -1. We might dedicate one of many private keys for messaging purposes, and that private key is probably managed in a not-so-secure manner; -2. We might want to export one of many private keys from a hardware wallet, and split it with MPC technology so that a 3rd party service could help us identify potential frauds or known bad addresses, enforce 2FA, etc., meanwhile we can initiate transactions from a mobile device with much better UX and without carrying a hardware wallet. +1. We might dedicate one of many private keys for messaging purposes, and that private key is probably managed in a not-so-secure manner; +2. We might want to export one of many private keys from a hardware wallet, and split it with MPC technology so that a 3rd party service could help us identify potential frauds or known bad addresses, enforce 2FA, etc., meanwhile we can initiate transactions from a mobile device with much better UX and without carrying a hardware wallet. In both cases, it is safer not to provide the seed which controls the whole wallet and might contains many addresses in multiple chains. @@ -31,6 +31,7 @@ This EIP aims to enable such use cases. ### Sender and Recipient We hereby define: + - *Sender* as the party who holds in custody the private key to be encapsulated; *Sender Application* as the client-side application that said *Sender* uses to send the encapsulated private key. - *Recipient* as the party who accepts the encapsulated private key, unwraps, and then uses it; *Recipient Application* as the client-side application that *Recipient* uses to receive the encapsulated private key. @@ -43,22 +44,22 @@ There should be a mandatory `version` parameter. This allows various kinds of In addition to a `version` parameter, the following keys and functions are involved: -1. The Sender's private key `sk`, which is to be encapsulated to the Recipient, and the corresponding address `account`. -2. The ephemeral Recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes scalar multiplication. Optionally, `R` could be signed, and `signerPubKey` and `signature` are then provided for Sender to verify if `R` could be trusted or not. -3. The ephemeral Sender key pair `(s, S)` such that `S = [s]G`. -4. The share secret `ss := [s]R = [r]S` according to ECDH. Note that for secp256k1 this EIP follows RFC5903 and uses compact representation, which means to use *only* the `x` coordinate as the shared secret. For Curve25519 this EIP follows RFC7748. -5. The out-of-band data `oob`, optional. This could be digits or an alpha-numeric string entered by the user. -6. Let `derivedKey := HKDF(hash=SHA256, ikm=ss, info=oob, salt, length)`. HKDF is defined in RFC5869. The `length` should be determined by `skey` and `IV` requirements such that the symmetric key `skey = derivedKey[0:keySize]`, and `IV = derivedKey[keySize:length]`. `keySize` denotes the key size of the underlying symmetric algorithm, for example, 16 (bytes) for AES-128, and 32 (bytes) for Chacha20. See **Security Considerations** for the use of `salt`. -7. Let `cipher := authenticated_encryption(symAlg, skey, IV, data=sk)`. The symmetric cipher algorithm `symAlg` and authentication scheme are decided by the version parameter. No additional authentication data `aad` is used. +1. The Sender's private key `sk`, which is to be encapsulated to the Recipient, and the corresponding address `account`. +2. The ephemeral Recipient key pair `(r, R)` such that `R = [r]G`. `G` denotes the base point of the elliptic curve, and `[r]G` denotes scalar multiplication. Optionally, `R` could be signed, and `signerPubKey` and `signature` are then provided for Sender to verify if `R` could be trusted or not. +3. The ephemeral Sender key pair `(s, S)` such that `S = [s]G`. +4. The share secret `ss := [s]R = [r]S` according to ECDH. Note that for secp256k1 this EIP follows RFC5903 and uses compact representation, which means to use *only* the `x` coordinate as the shared secret. For Curve25519 this EIP follows RFC7748. +5. The out-of-band data `oob`, optional. This could be digits or an alpha-numeric string entered by the user. +6. Let `derivedKey := HKDF(hash=SHA256, ikm=ss, info=oob, salt, length)`. HKDF is defined in RFC5869. The `length` should be determined by `skey` and `IV` requirements such that the symmetric key `skey = derivedKey[0:keySize]`, and `IV = derivedKey[keySize:length]`. `keySize` denotes the key size of the underlying symmetric algorithm, for example, 16 (bytes) for AES-128, and 32 (bytes) for Chacha20. See **Security Considerations** for the use of `salt`. +7. Let `cipher := authenticated_encryption(symAlg, skey, IV, data=sk)`. The symmetric cipher algorithm `symAlg` and authentication scheme are decided by the version parameter. No additional authentication data `aad` is used. A much-simplified example flow without signature and verification is: -1. *Recipient Application* generates `(r, R)`. -2. User inputs `R` to *Sender Application*, along with a six-digit code “123456” as `oob`. -3. *Sender Application* generates `(s, S)`, and computes `cipher`, then returns `S || cipher`. -4. *Recipient Application* scans to read `S` and `cipher`. The user enters “123456” as `oob` to *Recipient Application*. -5. *Recipient Application* decrypts `cipher` to get `sk`. -6. *Recipient Application* derives the address corresponding to `sk` so that the user can confirm the correctness. +1. *Recipient Application* generates `(r, R)`. +2. User inputs `R` to *Sender Application*, along with a six-digit code “123456” as `oob`. +3. *Sender Application* generates `(s, S)`, and computes `cipher`, then returns `S || cipher`. +4. *Recipient Application* scans to read `S` and `cipher`. The user enters “123456” as `oob` to *Recipient Application*. +5. *Recipient Application* decrypts `cipher` to get `sk`. +6. *Recipient Application* derives the address corresponding to `sk` so that the user can confirm the correctness. With signature and verification, the signature to `R` by `singerPubKey` is appended to `R`. `signerPubKey` itself could have been already signed by `trustedPubKey`, and that signature is appended to `signerPubKey`. Note that the signature is applied to the byte array data instead of its string representation, which might lead to confusion and interoperability issues (such as hex or base64, lower case v.s. upper case, etc.). See [Requests](#requests) and [Test Cases](#test-cases) for further clarification and examples. @@ -147,21 +148,21 @@ The return value is the corresponding Ethereum address for `sk`, or empty if any Available elliptic curves are: -- secp256k1 (mandatory) -- Curve25519 +- secp256k1 (mandatory) +- Curve25519 Available authenticated encryption schemes are: -- AES-128-GCM (mandatory) -- AES-256-GCM -- Chacha20-Poly1305 +- AES-128-GCM (mandatory) +- AES-256-GCM +- Chacha20-Poly1305 The version string is simply the concatenation of the elliptic curve and AE scheme, for example, secp256k1-AES-128-GCM. The above lists allow a combination of six different concrete schemes. Implementations are encouraged to implement curve-related logic separately from authenticated encryption schemes to avoid duplication and to promote interoperability. Signature algorithms for each curve are: -- secp256k1 --> ECDSA -- Curve25519 --> Ed25519 +- secp256k1 --> ECDSA +- Curve25519 --> Ed25519 ## Rationale @@ -188,6 +189,7 @@ It is expected that implementations cover curve supports separately from encrypt Signatures to `R` and `signerPubKey` are applied to byte array values instead of the encoded string. ### UX Recommendations + `salt` and/or `oob` data: both are inputs to the HKDF function (`oob` as “info” parameter). For better user experiences we suggest to require from users only one of them but this is up to the implementation. *Recipient Application* is assumed to be powerful enough. *Sender Application* could have very limited computing power and user interaction capabilities. @@ -200,17 +202,18 @@ For review purposes, the program to generate the test vectors is open-sourced an Throughout the test cases, we fix values for the below data: -- `sk`, the private key to be encapsulated, fixed to: `0xf8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315`. The corresponding address is `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`, called `account`. Note that these values come from the book *Mastering Ethereum* by Andreas M. Antonopoulos and Gavin Wood. -- `r`, the Recipient private key, fixed to `0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79` -- `s`, the Sender private key, fixed to `0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8` -- `signer`, the private key to sign the ephemeral public key, fixed to `0xac304db075d1685284ba5e10c343f2324ee32df3394fc093c98932517d36e344`. When used for Ed25519 signing, however, this value acts as `seed`, while the actual private key is calculated as `SHA512(seed)[:32]`. Or put another way, the public key is the scalar multiplication of hashed private key to the base point. Same for `trusted`. -- `trusted`, the private key to sign `signerPubKey`, fixed to `0xda6649d68fc03b807e444e0034b3b59ec60716212007d72c9ddbfd33e25d38d1` -- `oob`, fixed to `0x313233343536` (string value: `123456`) -- `salt`, fixed to `0x6569703a2070726976617465206b657920656e63617073756c6174696f6e` (string value: `eip: private key encapsulation`) +- `sk`, the private key to be encapsulated, fixed to: `0xf8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315`. The corresponding address is `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`, called `account`. Note that these values come from the book *Mastering Ethereum* by Andreas M. Antonopoulos and Gavin Wood. +- `r`, the Recipient private key, fixed to `0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79` +- `s`, the Sender private key, fixed to `0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8` +- `signer`, the private key to sign the ephemeral public key, fixed to `0xac304db075d1685284ba5e10c343f2324ee32df3394fc093c98932517d36e344`. When used for Ed25519 signing, however, this value acts as `seed`, while the actual private key is calculated as `SHA512(seed)[:32]`. Or put another way, the public key is the scalar multiplication of hashed private key to the base point. Same for `trusted`. +- `trusted`, the private key to sign `signerPubKey`, fixed to `0xda6649d68fc03b807e444e0034b3b59ec60716212007d72c9ddbfd33e25d38d1` +- `oob`, fixed to `0x313233343536` (string value: `123456`) +- `salt`, fixed to `0x6569703a2070726976617465206b657920656e63617073756c6174696f6e` (string value: `eip: private key encapsulation`) ### Case 1 Use `version` as `secp256k1-AES-128-GCM`. **R1** is provided as: + ```javascript request({ method: 'eth_generateEphemeralKeyPair', @@ -220,18 +223,24 @@ request({ ], }) ``` + Suppose the implementation generates an ephemeral key pair `(r, R)`: + ``` r: '0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79', R: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09' ``` + The return value could be: + ``` '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac77547fbdde194f71db52860b9e10ca2b089646f133d172124504ac1996a' ``` + Note that `R` is compressed and `R` leads the return value: `R || sig`. Therefore **R2** could be provided as: + ```javascript request({ method: 'eth_encapsulatePrivateKey', @@ -245,7 +254,9 @@ request({ ], }) ``` + *Sender Application* first verifies first layer signature as ECDSA over secp256k1: + ``` // actual message to be signed should be the decoded byte array msg: '0x039ef98feddb39664450c3876878093c70652caba7e3fd04333c0558ffdf798d09', @@ -253,7 +264,9 @@ sig: '0x536da06b8d9207040ada179dc2c38f701a1a21c9ab5a7d52f5da50ea438e8ccf47dac775 //signerPubKey pub: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b' ``` + Then it proceeds to verify the second layer signature, also as ECDSA over secp256k1: + ``` // actual message to be signed should be the decoded byte array msg: '0x035a5ca16997f9b9ead9572c9bde36c5dab584b17bc965cdd7c2945c776e981b0b', @@ -261,28 +274,38 @@ sig: '0x5bd427c527b7f1012b8edfd179b9002a7f2d7fc326bb6ae9aaf38b44eb93c397631fd8bb //trustedPubKey pub: '0x027fb72176f1f9852ce7dd9dc3aa4711675d3d8dc5102b86d758d853002137e839' ``` + Since *Sender Application* trusts `trustedPubKey`, the signature verification succeeds. Suppose the implementation generates an ephemeral key pair `(s, S)` as: + ``` s: '0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8', S: '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c02' ``` + The shared secret, symmetric key, and IV should be: + ``` ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', skey: '0x6ccc02a61aa16d6c66a1277e5e2434b8', IV: '0x9c7a0f870d17ced2d2c3d1cf' ``` + Then the return value should be: + ``` '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c02abff407e8901bb37d13d724a2e3a8a1a5af300adc286aa2ec65ef2a38c10c5cec68a949d0a20dbad2a8e5dfd7a14bbcb' ``` + With compressed public key `S` leading `cipher`, which in turn is (added prefix '0x'): + ``` '0xabff407e8901bb37d13d724a2e3a8a1a5af300adc286aa2ec65ef2a38c10c5cec68a949d0a20dbad2a8e5dfd7a14bbcb' ``` + Then **R3** is provided as: + ```javascript request({ method: 'eth_intakePrivateKey', @@ -295,11 +318,13 @@ request({ ], }) ``` + The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This matches the `account` parameter in **R2**. ### Case 2 Use `version` as `secp256k1-AES-256-GCM`. The calculated symmetric key `skey`, `IV`, and `cipher` will be different. **R1** is provided as: + ```javascript request({ method: 'eth_generateEphemeralKeyPair', @@ -309,9 +334,11 @@ request({ ], }) ``` + Note that only the `version` is different (AES key size). We keep using the same `(r, R)` (this is just a test vector). Therefore **R2** is provided as: + ```javascript request({ method: 'eth_encapsulatePrivateKey', @@ -325,20 +352,25 @@ request({ ], }) ``` + Suppose the implementation generates the same `(s, S)` as [Case 1](#case-1). The shared secret, symmetric key, and IV should be: + ``` ss: '0x8e83bc5a9c77b11afc12c9a8262b16e899678d1720459e3b73ca2abcfed1fca3', skey: '0x6ccc02a61aa16d6c66a1277e5e2434b89c7a0f870d17ced2d2c3d1cfd0e6f199', IV: '0x3369b9570b9d207a0a8ebe27' ``` + With shared secret `ss` remaining the same as [Case 1](#case-1), symmetric key `skey` contains both the `skey` and `IV` from [Case 1](#case-1). IV is changed. Then the return value should be the following, with the `S` part the same as [Case 1](#case-1) and the `cipher` part different: + ``` '0x02ced2278d9ebb193f166d4ee5bbbc5ab8ca4b9ddf23c4172ad11185c079944c0293910a91270b5deb0a645cc33604ed91668daf72328739d52a5af5a4760c4f3a9592b8f6d9b3ebe25127e7bf1c43b839' ``` Then **R3** is provided as: + ```javascript request({ method: 'eth_intakePrivateKey', @@ -357,6 +389,7 @@ The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This ma ### Case 3 Use `version` as: `Curve-25519-Chacha20-Poly1305`. **R1** is provided as: + ```javascript request({ method: 'eth_generateEphemeralKeyPair', @@ -366,16 +399,21 @@ request({ ], }) ``` + Note that with Curve25519 the size is 32 (bytes) for both the public key and private key. And there is no compression for the public key. `signerPubKey` is calculated as: + ``` //signer is '0xac304db075d1685284ba5e10c343f2324ee32df3394fc093c98932517d36e344' s := SHA512(signer)[:32] signerPubKey := Curve25519.ScalarBaseMult(s).ToHex() ``` + The same technique applies to `trustedPubKey`. With `r` the same as in [Case 1](#case-1) and [Case 2](#case-2) and the curve being changed, the return value is `R = [r]G || sig`: + ``` R = '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79cac06de279ec7f65f6b75f6bee740496df0650a6de61da5e691d7c5da1c7cb1ece61c669dd588a1029c38f11ad1714c1c9742232f9562ca6bbc7bad57882da04' ``` + **R2** is provided as: ```javascript @@ -391,7 +429,9 @@ request({ ], }) ``` + Both `recipient` and `signerPubKey` have been signed in Ed25519. Verifying signature to `R` is carried out as: + ``` // actual message to be signed should be the decoded byte array msg: '0xc0ea3514b0ab83b2fe4f4ef96159cda8fa836ce549ef09569b901eef0723bf79', @@ -399,24 +439,31 @@ sig: '0x879d900f04a955078ff6ae86f1d1b69b3e1265370e64bf064adaecb895c51effa3bdae79 //signerPubKey pub: '0xe509fb840f6d5a69333ef68d69b86de55b9b905e45b16e3591912c097ba69938' ``` + After successfully verifying the signature (and the one by `trustedPubKey`), the implementation then generates ephemeral key pair `(s, S)` in Curve25519: + ``` // s same as Case 1 and Case 2 s = '0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8', S = '0xd2fd6fcaac231d08363e736e61edb7e7696b13a727e3d2a239415cb8dc6ee278' ``` + The shared secret, symmetric key, and IV should be: + ``` ss: '0xe0b36f56cdb63c27e933a5a67a5e97db4b566c9276a36aeee5dc6e87da118867', skey: '0x7c6fa749e6df13c8578dc44cb24cdf46a44cb163e1e570c2e590c720aed5783f', IV: '0x3c98ef6fc34b0d6e7e16bd78' ``` + Then the return value should be `S || cipher`: + ``` '0xd2fd6fcaac231d08363e736e61edb7e7696b13a727e3d2a239415cb8dc6ee2786a7e2e40efb86dc68f44f3e032bbedb1259fa820e548ac5adbf191784c568d4f642ca5b60c0b2142189dff6ee464b95c' ``` Then **R3** is provided as: + ```javascript request({ method: 'eth_intakePrivateKey', @@ -429,6 +476,7 @@ request({ ], }) ``` + The return value should be `0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9`. This matches the `account` parameter in **R2**. ## Security Considerations @@ -447,8 +495,8 @@ R <-- signerPubKey <-- trustedPubKey This allows various strategies to manage trust. For example: -- A hardware wallet vendor which takes it very seriously about the brand reputation and the fund safety for its customers, could choose to trust only its own public keys, all instances of `trustedPubKey`. These public keys only sign `signerPubKey` from selected partners. -- A MPC service could publish its `signerPubKey` online so that *Sender Application* won't verify the signature against a wrong or fake public key. +- A hardware wallet vendor which takes it very seriously about the brand reputation and the fund safety for its customers, could choose to trust only its own public keys, all instances of `trustedPubKey`. These public keys only sign `signerPubKey` from selected partners. +- A MPC service could publish its `signerPubKey` online so that *Sender Application* won't verify the signature against a wrong or fake public key. Note that it is advised that a separate key pair should be used for signing on each curve. @@ -471,4 +519,5 @@ AES-128, AES-256, and ChaCha20 are provided. `oob` data is optional. When non-empty, its content is digits or an alpha-numeric string from the user. *Sender Application* may mandate `oob` from the user. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md).