mirror of
https://github.com/pezkuwichain/consensus.git
synced 2026-04-22 06:48:01 +00:00
key discussions
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# Signing keys in Polkadot
|
||||
|
||||
In this post, we shall first give a high level view of the various signing keys planned for use in Polkadot. We then turn the discussion towards the certificate chain that stretches between staked account keys and the session keys used for our proof-of-stake design. In other words, we aim to lay out the important questions on the "glue" between keys rolls here, but first this requires introducing the full spectrum of key rolls.
|
||||
|
||||
We have roughly four cryptographic layers in Polkadot:
|
||||
|
||||
- *Account keys* are owned by users and tied to one actual dot denominated account on Polkadot. Accounts could be staked/bonded, unstaked/unbonded, or unstaking/unbonding, but only an unstaked/unbonded account key can transfer dots from one account to another.
|
||||
- *Nominator keys* provide a certificate chain between staked/bonded account keys and the session keys used by nodes in block production or validating. As nominator keys cannot transfer dots, they insulate account keys, which may remain air gapped, from nodes actually running on the internet.
|
||||
- *Session keys* are actually several keys kept together that provide the various signing functions required by validators, including a couple types of verifiable random function (VRF) keys.
|
||||
- *Transport layer signing keys* are used by libp2p to authenticate connections between nodes. We shall either certify these with the session key or perhaps include them directly in the session key.
|
||||
|
||||
|
||||
## Account keys
|
||||
|
||||
We believe Polkadot accounts should primarily use Schnorr signatures with both public keys and the `R` point in the signature encoded using the [Ristretto](https://ristretto.group) point compression for the Ed25519 curve. We should collaborate with the [dalek ecosystem](https://github.com/dalek-cryptography) for which Ristretto was developed, but provide a simpler signature crate, for which [schnorr-dalek](https://github.com/w3f/schnorr-dalek) provides a first step.
|
||||
|
||||
I'll write a second longer post outlining the reasons for this choice, while providing only a high level summary here:
|
||||
|
||||
Account keys must support the diverse functionality desired of account keys on other systems like Ethereum and Bitcoin. As such, our account keys shall use Schnorr signatures because these support fast batch verification and hierarchical deterministic key derivation ala [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Child_key_derivation_CKD_functions). All features from the [Bitcoin Schnoor wishlist](https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki) provides a case for Schnorr signatures matter too, like
|
||||
|
||||
- interactive threshold and multi-signaturtes, as well as
|
||||
- adaptor, and perhaps even blind, signatures for swaps and payment channels.
|
||||
|
||||
We make conservative curve choices here because account keys must live for decades. In particular, we avoid pairing-based cryptography and BLS signatures for accounts, at the cost of true aggregation of the signatures in a block when verifying blocks, and less interactive threshold and multi-signaturtes. [1].
|
||||
|
||||
In the past, there was a tricky choice between the more secure curves:
|
||||
|
||||
- miss-implementation resistance is stronger with Edwards curves, including the Ed25519 curve, but
|
||||
- miss-use resistance in stronger when curves have cofactor 1, like secp256k1.
|
||||
|
||||
In fact, miss-use resistance was historically a major selling point for Ed25519, which itself is a Schnorr variant, but this miss-use resistance extends only so far as the rudimentary signature scheme properties it provided. Yet, any advanced signature scheme functions, beyond batch verification, break precisely due to Ed25519's miss-use resistance. In fact, there are tricks for doing at least hierarchical deterministic key derivation on Ed25519, as implemented in [hd-ed25519](https://github.com/w3f/hd-ed25519), but almost all previous efforts [produced insecure results](https://forum.web3.foundation/t/key-recovery-attack-on-bip32-ed25519/44).
|
||||
|
||||
We observe that secp256k1 provides a good curve choice from among the curves of cofactor 1, which simplify make implementing fancier protocols. We do worry that such curves appear at least slightly weaker than Edwards curves. We worry much more than such curves tend to be harder to implement well, due to having incomplete addition formulas, and thus require more review (see [safecurves.cr.yp.to](https://safecurves.cr.yp.to)). We could select only solid implementations for Polkadot itself, but we cannot control the implementations selected elsewhere in our ecosystem, especially by wallet software.
|
||||
|
||||
In short, we want an Edwards curve but without the cofactor, which do not exist, except..
|
||||
|
||||
In Edwards curve of with cofactor 4, [Mike Hamburg's Decaf point compression](https://www.shiftleft.org/papers/decaf/) only permits serialising and deserialising points on the subgroup of order $l$, which provides a perfect solution. [Ristretto](https://ristretto.group) pushes this point compression to cofactor 8, making it applicable to the Ed25519 curve. Implementations exist in both [Rust](https://doc.dalek.rs/curve25519_dalek/ristretto/index.html) and [C](https://github.com/Ristretto/libristretto255). If required in another language, the compression and decompression functions are reasonable to implement using an existing field implementation, and fairly easy to audit.
|
||||
|
||||
In the author's words, "Rather than bit-twiddling, point mangling, or otherwise kludged-in ad-hoc fixes, Ristretto is a thin layer that provides protocol implementors with the correct abstraction: a prime-order group."
|
||||
|
||||
|
||||
[1] Aggregation can dramatically reduce signed message size when applying numerous signatures, but if performance is the only goal then batch verification techniques similar results, and exist for mny signature schemes, including Schnorr. There are clear advantages to reducing interactiveness in threshold and multi-signaturtes, but parachains can always provide these on Polkadot. Importantly, there are numerous weaknesses in all known curves that support pairings, but the single most damning weakness is the pairing itself.
|
||||
$$ e : G_1 \times G_2 \to G_T $$
|
||||
In essence, we use elliptic curves in the first palce because they insulate us somewhat from mathematicians ever advancing understanding of number theory. Yet, any known pairing maps into a group $G_T$ that re-exposes us, so attacks based on index-calculus, etc. improve more quickly. As a real world example, there were weaknesses found in BN curve of the sort used by ZCash during development, so after launch they needed to develop and migrate to a [new curve](https://z.cash/blog/new-snark-curve/). We expect this to happen again for roughly the same reasons that RSA key sizes increase slowly over time.
|
||||
|
||||
|
||||
## Nominator keys
|
||||
|
||||
Any certificate format should work for nominator keys, but some certificate schemes provide more flexibility, while others save space. We do not require much flexibility per se, but these certificates must live inside the staked account, and habe some associated data:
|
||||
|
||||
- block height when staked,
|
||||
- unstaking block height, if unstaking, and
|
||||
- permissions, like block production, validation, and validator operator.
|
||||
|
||||
We could save some space by using implicit certificates to issue nominator keys, as implemented in [`schnorr-dalek/src/cert.rs`](https://github.com/w3f/schnorr-dalek/blob/master/src/cert.rs#L181). In other words, an accounts nominator key could be defined by an additional 32 bytes attached to the account, along with any associated data.
|
||||
|
||||
We need to hold a conversation about (a) what form this associated data should take, and (b) if the space savings are worth the complexity of an implicit certificates scheme, mostly [reviewing the literature](https://github.com/w3f/schnorr-dalek/issues/4).
|
||||
|
||||
We clearly need non-implicit certificates for non-owning nominators. As a result, we might actually reduce the code complexity by not using implicit certificates in the nomination process. We might then achieve better code reuse by not using implicit certificates anywhere.
|
||||
|
||||
In any case, we need a conversation about the certificate schemes delegating from staked account keys to nominator keys and delegating from nominator keys to session keys.
|
||||
|
||||
|
||||
## Session keys
|
||||
|
||||
A session public key should consist of roughly three public keys:
|
||||
|
||||
- Ristretto Schnorr public key (32 bytes public keys, 64 byte signatures, 96 byte VRFs) - We issue this from the nominator keys acting as validator operators. We might use an implicit certificate but doing so either restricts us to one validator operator, or else increases code complexity and forces a primary validator operator. Implicit certificates also make session key records impossible to authenticate without the nominator account, but this sounds desirable.
|
||||
|
||||
- Small curve of BLS12-381 (48 byte public keys, 96 byte signatures) - Aggregated signatures in which many signers sign the same message, as in GRANDPA, verify can faster when using this key. Actual signatures are slower than the opposite orientation, and non-constant time extension field arithmetic makes them even slower, or more risky.
|
||||
|
||||
- Big curve of BLS12-381 (96 bytes public keys, 48 byte signatures) - Aggregated signatures in which we verify many messages by the same signer verify considerably faster when using this key. We might use these for block production VRFs because they require non-winner VRF proofs sometimes. We also expect faster aggregate verification from these when signer sets get repeated frequently.
|
||||
|
||||
A session public key record has a prefix consisting of the above three keys, along with a certificate from the validator operator on the Ristretto Schnorr public key. We follow this prefix with a first signature block consisting two BLS signatures on the prefix, one by each the BLS keys. We close the session public key record with a second signature block consisting of a Ristretto Schnorr signature on both the prefix and first signature block. In this way, we may rotate our BLS12-381 keys without rotating our Ristretto Schnorr public key.
|
||||
|
||||
## Transport layer - libp2p
|
||||
|
||||
There are numerous transports for libp2p, but only QUIC was designed to be secure. Instead, one routes traffic through libp2p's secio protocol. We trust QUIC's cryptographic layer which is TLS 1.3, but secio itself is a home brew jobs by Protocol Labs with no serious security analysis, which usually [goes](https://github.com/tendermint/tendermint/issues/3010) [poorly](https://github.com/tendermint/kms/issues/111).
|
||||
|
||||
There was some minimal discussion of secio's security but [Dominic Tarr](https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel) raised some concerns in the [original pull request](https://github.com/ipfs/go-ipfs/pull/34). I'll raise several concerns from that discussion:
|
||||
|
||||
First, there is no effort made to hide secio node keys because "IPFS has no interest in [metadata privacy]" according to Brendan McMillion, so nodes leak their identities onto the raw internet. We think identifying nodes might be sounds easy anyways, but at minimum this invites attacks. There is an asymmetry to key exchanges that leaks less by first establishing a secure channel and only then authenticating. We might reasonably break this asymmetry by deciding that specific roles require more privacy. We might for example help protect validator operators or improve censorship resistance in some cases, like fishermen.
|
||||
|
||||
Second, there is cipher suit agility in secio, at minimum in their use of multihash, but maybe even in the key exchange. We've seen numerous attacks on TLS <= 1.2 due to cipher suit agility, especially the downgrade attacks. I therefore strongly recommend using TLS 1.3 if cipher suit agility is required. There is no place in a key exchange for poorly controlled constructs like multihash.
|
||||
|
||||
As QUIC uses UDP only, we could add TCP based transport that uses TLS 1.3, perhaps by extending libp2p's existing transport with support for TLS 1.3, or perhaps adding a more flexible TLS 1.3 layer. We might prefer a flexible TLS 1.3 layer over conventional TLS integration into libp2p extending transports because our authentication privacy demands might work differently from TLS's server oriented model. We could identify some reasonable [Noise](https://noiseprotocol.org/noise.html) variant, if avoiding the complexity of TLS sounds like a priority.
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
# Account keys on Polkadot
|
||||
|
||||
We believe Polkadot accounts should primarily use Schnorr signatures with both public keys and the `R` point in the signature encoded using the [Ristretto](https://ristretto.group) point compression for the Ed25519 curve. We should collaborate with the [dalek ecosystem](https://github.com/dalek-cryptography) for which Ristretto was developed, but provide a simpler signature crate, for which [schnorr-dalek](https://github.com/w3f/schnorr-dalek) provides a first step.
|
||||
|
||||
|
||||
## Schnorr signatures
|
||||
|
||||
We choose Schnorr signatures because they satisfy the [Bitcoin Schnoor wishlist](https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki) and work fine with extremely secure curves, like secp256k1 or the Ed25519 curve. You could do fancier tricks, including like aggregation, with a pairing based curve like BLS12-381 and the BLS signature scheme. These curves are slower for single verifications, and worse accounts should last decades while pairing friendly curves should be expected become less secure as number theory advances.
|
||||
|
||||
There is one sacrifice we make by choosing Schnorr signatures over ECDSA signatures for account keys: Both require 64 bytes, but only [ECDSA signatures communicate their public key](https://crypto.stackexchange.com/questions/18105/how-does-recovering-the-public-key-from-an-ecdsa-signature-work). There are obsolete Schnorr variants that [support recovering the public key from a signature](https://crypto.stackexchange.com/questions/60825/schnorr-pubkey-recovery), but
|
||||
they break important functionality like [hierarchical deterministic key derivation](https://www.deadalnix.me/2017/02/17/schnorr-signatures-for-not-so-dummies/). In consequence, Schnorr signatures often take an extra 32 bytes for the public key.
|
||||
|
||||
In exchange, we gain a slightly faster signature scheme with far simpler batch verification than [ECDSA batch verification](http://cse.iitkgp.ac.in/~abhij/publications/ECDSA-SP-ACNS2014.pdf) and more natural threshold and multi-signatures, as well as tricks used by payment channels. I also foresee the presence of this public key data may improve locality in block verification, possibly openning up larger optimisations.
|
||||
|
||||
Yet most importantly, we can protect Schnorr signatures using both the derandomization tricks of EdDSA along with a random number generator, which gives us stronger side-channel protections than conventional ECDSA schemes provide. If we ever do want to support ECDSA as well, then we would first explore improvements in side-channel protections like [rfc6979](https://tools.ietf.org/html/rfc6979), along with concerns like batch verification, etc.
|
||||
|
||||
|
||||
## Curves
|
||||
|
||||
There are two normal curve choices for accounts on a blockchain system, either secp256k1 or the Ed25519 curve, so we confine our discussion to them. If you wanted slightly more speed, you might choose FourQ, but it sounds excessive for blockchains, implementations are rare, and it appears covered by older but not quite expired patents. Also, you might choose Zcash's JubJub if you wanted fast signature verification in zkSNARKs, but that's not on our roadmap for Polkadot, and Jubjub also lacks many implementations.
|
||||
|
||||
### How much secp256k1 support?
|
||||
|
||||
We need some minimal support for secp256k1 keys because token sale accounts are tied to secp256k1 keys on Ethereum, so some "account" type must necessarily use secp256k1 keys. At the same time, we should not encourage using the same private keys on Ethereum and Polkadot. We might pressure users into switching key types in numerous ways, like secp256k1 accounts need not support balance increases, or might not support anything but replacing themselves with an ed25519 key. There are conceivable reasons for fuller secp256k1 support though, like wanting ethereum smart contracts to verify some signatures on Polkadot. We might support secp256k1 accounts with limited functionality, but consider expanding that functionality if such use cases arise.
|
||||
|
||||
### Is secp256k1 risky?
|
||||
|
||||
There are two theoretical reasons for preferring an twisted Edwards curve over secp256k1: First, secp256k1 has a [small CM field discriminant](https://safecurves.cr.yp.to/disc.html), which might yield better attacks in the distant future. Second, secp256k1 has fairly rigid paramater choices but [not the absolute best](https://safecurves.cr.yp.to/rigid.html). I do not believe either to be serious cause for concern. Among more practical curve weaknesses, secp256k1 does have [twist security](https://safecurves.cr.yp.to/twist.html) which eliminates many attack classes.
|
||||
|
||||
I foresee only one substancial reason for avoiding secp256k1: All short Weierstrass curves like secp256k1 have [incomplete addition formulas](https://safecurves.cr.yp.to/complete.html), meaning certain curve points cannot be added to other curve points. As a result, addition code must check for failures, but these checks make writing constant time code harder. We could examine any secp256k1 library we use in Polkadot to ensure it both does these checks and has constant-time code. We cannot however ensure that all implementations used by third party wallet software does so.
|
||||
|
||||
I believe incomplete addition formulas looks relatively harmless when used for simple Schnorr signatures, although forgery attacks might exist. I'd worry more however if we began using secp256k1 for less well explored protocols, like multi-signaturtes and key derivation. We ware about such use cases however, especially those listed in the [Bitcoin Schnoor wishlist](https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki).
|
||||
|
||||
### Is Ed25519 risky? Aka use Ristretto
|
||||
|
||||
Any elliptic curve used in cryptography has order h*l where l is a big prime, normally close to a power of two, and h is some very small number called the cofactor. Almost all protocol implementations are complicated by these cofactors, so implementing complex protocols is safer on curves with cofactor h=1 like secp256k1.
|
||||
|
||||
The Ed25519 curve has cofactor 8 but a simple convention called "clamping" that makes two particularly common protocols secure. We must restrict or drop "clamping" for more complex protocols, like multi-signaturtes and key derivation, or anything else in the [Bitcoin Schnoor wishlist](https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki).
|
||||
|
||||
If we simple dropped "clamping" then we'd make implementing protocols harder, but luckily the [Ristretto](https://ristretto.group) encoding for the Ed25519 curve ensures we avoid any curve points with 2-torsion. I thus recommend:
|
||||
- our secret key continue being Ed25519 "expanded" secret keys, while
|
||||
- our on-chain encoding, aka "point compression" becomes Ristretto for both public keys and the `R` component of Schnoor signatures.
|
||||
|
||||
In principle, we could use the usual Ed25519 "mini" secret keys for simple use cases, but not when doing key derivation. We could thus easily verify standrad Ed25519 signatures with Ristretto encoded public keys. We should ideally use Ristretto throughout instead of the standard Ed25519 point compression.
|
||||
|
||||
In fact, we can import standard Ed25519 compressed points like I do [here](https://github.com/w3f/schnorr-dalek/blob/master/src/ristretto.rs#L877) but this requires the scalar exponentiation done in the [`is_torsion_free` method](https://doc.dalek.rs/curve25519_dalek/edwards/struct.EdwardsPoint.html#method.is_torsion_free), which runs slower than normal signature verification. We might ideally do this only for key migration between PoCs.
|
||||
|
||||
Ristretto is far simpler than the Ed25519 curve itself, so Ristretto can be added to Ed25519 implementations, but the [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek) crate already provides a highly optimised rust implementation.
|
||||
|
||||
### Zero-knowledge proofs in the dalek ecosystem
|
||||
|
||||
In fact, the [dalek ecosystem](https://github.com/dalek-cryptography) has an remarkably well designed infrastructure for zero-knowledge proofs without pairings. See:
|
||||
https://medium.com/interstellar/bulletproofs-pre-release-fcb1feb36d4b
|
||||
https://medium.com/interstellar/programmable-constraint-systems-for-bulletproofs-365b9feb92f7
|
||||
|
||||
All these crates use Ristretto points so using Ristretto for account public keys ourselves gives us the most advanced tools for building protocols not based on pairings, meaning that use our account keys. In principle, these tools might be abstracted for twisted Edwards curves like FourQ and Zcash's Jubjub, but yu might loose some batching operations in abstracting them for short Weierstrass curves like secp256k1.
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user