EIP-6051: Private Key Encapsulation
defines a specification for encapsulating private keys.
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 for a known or trusted party.
Motivation
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.
- We might dedicate one of many private keys for messaging purposes, and that private key is probably managed in a not-so-secure manner;
- 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
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.
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 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:
- The Sender's private key
sk
, which is to be encapsulated to the Recipient, and the corresponding addressaccount
. - The ephemeral Recipient key pair
(r, R)
such thatR = [r]G
.G
denotes the base point of the elliptic curve, and[r]G
denotes scalar multiplication. Optionally,R
could be signed, andsignerPubKey
andsignature
are then provided for Sender to verify ifR
could be trusted or not. - The ephemeral Sender key pair
(s, S)
such thatS = [s]G
. - 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 thex
coordinate as the shared secret. For Curve25519 this EIP follows RFC7748. - The out-of-band data
oob
, optional. This could be digits or an alpha-numeric string entered by the user. - Let
derivedKey := HKDF(hash=SHA256, ikm=ss, info=oob, salt, length)
. HKDF is defined in RFC5869. Thelength
should be determined byskey
andIV
requirements such that the symmetric keyskey = derivedKey[0:keySize]
, andIV = 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 ofsalt
. - Let
cipher := authenticated_encryption(symAlg, skey, IV, data=sk)
. The symmetric cipher algorithmsymAlg
and authentication scheme are decided by the version parameter. No additional authentication dataaad
is used.
A much-simplified example flow without signature and verification is:
- Recipient Application generates
(r, R)
. - User inputs
R
to Sender Application, along with a six-digit code “123456” asoob
. - Sender Application generates
(s, S)
, and computescipher
, then returnsS || cipher
. - Recipient Application scans to read
S
andcipher
. The user enters “123456” asoob
to Recipient Application. - Recipient Application decrypts
cipher
to getsk
. - 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.
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 a single byte buffer as:[IV || encrypted_sk || tag]
.R
,S
,signerPubKey
, andtrustedPubKey
are compressed if applicable.R
orsignerPubKey
could be followed by a signature to it:[pub || sig]
. Note that for the secp256k1 curve, the signature is just 64 bytes without thev
indicator as found in a typical Ethereum signature.
R1. Request for Recipient to generate ephemeral key pair
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.
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.
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.
R2. Request for Sender to encapsulate the private key
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.
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.
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 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
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 return value is the corresponding Ethereum address for sk
, or empty if any error.
Options and Parameters
Available elliptic curves are:
- secp256k1 (mandatory)
- Curve25519
Available authenticated encryption schemes are:
- 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
Rationale
A critical difference between this EIP-6051 with EIP-5630 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 on both sides every time:
- first Recipient shall generate an ephemeral key pair, retain the private key securely, and export the public key;
- then Sender can securely wrap the private key in ECIES, with another ephemeral key pair, then destroy the ephemeral key securely;
- 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
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.
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 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.
Test Cases
For review purposes, the program to generate the test vectors is open-sourced and provided in the corresponding discussion thread.
Data Fixation
Throughout the test cases, we fix values for the below data:
sk
, the private key to be encapsulated, fixed to:0xf8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315
. The corresponding address is0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9
, calledaccount
. Note that these values come from the book Mastering Ethereum by Andreas M. Antonopoulos and Gavin Wood.r
, the Recipient private key, fixed to0x6f2dd2a7804705d2d536bee92221051865a639efa23f5ca7c810e77048253a79
s
, the Sender private key, fixed to0x28fa2db9f916e44fcc88370bedaf5eb3ec45632f040f4c1450c0f101e1e8bac8
signer
, the private key to sign the ephemeral public key, fixed to0xac304db075d1685284ba5e10c343f2324ee32df3394fc093c98932517d36e344
. When used for Ed25519 signing, however, this value acts asseed
, while the actual private key is calculated asSHA512(seed)[:32]
. Or put another way, the public key is the scalar multiplication of hashed private key to the base point. Same fortrusted
.trusted
, the private key to signsignerPubKey
, fixed to0xda6649d68fc03b807e444e0034b3b59ec60716212007d72c9ddbfd33e25d38d1
oob
, fixed to0x313233343536
(string value:123456
)salt
, fixed to0x6569703a2070726976617465206b657920656e63617073756c6174696f6e
(string value:eip: private key encapsulation
)
Case 1
Use version
as secp256k1-AES-128-GCM
. R1 is provided as:
Suppose the implementation generates an ephemeral key pair (r, R)
:
The return value could be:
Note that R
is compressed and R
leads the return value: R || sig
.
Therefore R2 could be provided as:
Sender Application first verifies first layer signature as ECDSA over secp256k1:
Then it proceeds to verify the second layer signature, also as ECDSA over secp256k1:
Since Sender Application trusts trustedPubKey
, the signature verification succeeds.
Suppose the implementation generates an ephemeral key pair (s, S)
as:
The shared secret, symmetric key, and IV should be:
Then the return value should be:
With compressed public key S
leading cipher
, which in turn is (added prefix '0x'):
Then R3 is provided as:
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:
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:
Suppose the implementation generates the same (s, S)
as Case 1. The shared secret, symmetric key, and IV should be:
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 following, with the S
part the same as Case 1 and the cipher
part different:
Then R3 is provided as:
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:
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:
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
:
R2 is provided as:
Both recipient
and signerPubKey
have been signed in Ed25519. Verifying signature to R
is carried out as:
After successfully verifying the signature (and the one by trustedPubKey
), the implementation then generates ephemeral key pair (s, S)
in Curve25519:
The shared secret, symmetric key, and IV should be:
Then the return value should be S || cipher
:
Then R3 is provided as:
The return value should be 0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9
. This matches the account
parameter in R2.
Security Considerations
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:
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 signsignerPubKey
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
- 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).
- 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.
- 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 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.
Copyright
Copyright and related rights waived via CC0.