Finish first pass of SECIO spec

This commit is contained in:
Cole Brown
2018-10-24 17:35:24 -04:00
parent 0dc2992717
commit 2194c68599

View File

@@ -53,3 +53,108 @@ required to implement SECIO.
## Protocol
### Proposal Generation
SECIO channel negotiation begins with a proposal phase. Both sides generate a
proposal as follows:
*Note: the public key should be serialized per the `Bytes` method from
[go-libp2p-crypto](https://godoc.org/github.com/libp2p/go-libp2p-crypto#Key).*
```
Propose{
Rand: <16 secure random bytes>,
Pubkey: <public key bytes>,
Exchanges: <comma separated string of supported key exchanges>,
Ciphers: <comma separated string of supported ciphers>,
Hashes: <comma separated string of supported hashes>,
}
```
Both peers serialize this message and send it over the wire. Upon deserializing
their peer's message, they verify that the pubkey matches that described by the
peer's peer ID (NOTE: this is sometimes only possible for the peer *initiating*
the connection.) If the key doesn't match the peer ID, the peer can close the
connection. If the peer doesn't know the remote peer's ID, they can compute and
store the remote peer ID for later use.
### Determining Roles and Algorithms
Next, the peers use a deterministic formula to compute their roles in the coming
exchanges. Each peer computes:
```
oh1 := sha256(concat(remotePeerPubKeyBytes, myNonce))
oh2 := sha256(concat(myPubKeyBytes, remotePeerNonce))
```
With these hashes, determine which peer's preferences to favor. This peer will
be referred to as the "preferred peer". If `oh1 == oh2`, then the peer is
communicating with itself and should return an error. If `oh1 < oh2`, use the
remote peer's preferences. If `oh1 > oh2`, prefer the local peer's preferences.
Given our preference, we now sort through each of the `Exchanges`, `Ciphers`,
and `Hashes` provided by both peers, selecting the first item from our preferred
peer's set that is also shared by the other peer.
### Key Exchange
Now the peers prepare a key exchange. Both peers generate an ephemeral key based
on the agreed upon exchange (currently support is only available for elliptic
curve algorithms). Ephemeral keys are generated via
[go-libp2p-crypto](https://godoc.org/github.com/libp2p/go-libp2p-crypto#GenerateEKeyPair).
With keys generated, both peers create a `Exchange` message. First, they start by
generating a "corpus" that they will sign.
```
corpus := concat(myProposalBytes, remotePeerProposalBytes, ephemeralPubKey)
```
Then, generate the `Exchange`:
```
Exchange{
Epubkey: <ephemeral pubkey>,
Signature: <sign corpus with local private key>,
}
```
The peers serialize these and write them over the wire. Upon receiving the
remote peer's `Exchange`, validate the signature by computing the `corpus` you
expect them to have generated with their public key. Peers should close the
connection if the signature does not validate.
### Key Stretching
Peers now generate their shared secret based on the function generated by the
ephemeral key generation, passing it the remote peer's ephemeral key. With the
shared secret generated, both peers stretch the key using the algorithm
described by
[go-libp2p-crypto](https://godoc.org/github.com/libp2p/go-libp2p-crypto#KeyStretcher).
```
k1, k2 := KeyStretcher(sharedSecret)
```
With `k1` and `k2` computed, swap the two values if the remote peer is the
preferred peer. After swapping if necessary, `k1` becomes the local peer's key
and `k2` the remote peer's key.
Each peer now generates a MAC key and cipher for the remote and local keys
generated in the previous step using the `MacKey` and `CipherKey` from the
generated
[`StretchedKeys`](https://godoc.org/github.com/libp2p/go-libp2p-crypto#StretchedKeys)
objects.
### Initiate Secure Channel
With the cipher and HMAC signer created, the secure channel is ready to be
opened. Each packet is of the form:
```
[uint32 length of packet | encrypted body | hmac signature of encrypted body]
```
The first packet transmitted by each peer must be the remote peer's nonce. Peers
validate that the remote peer sent them their nonce, closing if unsuccessful.
Otherwise, a secure channel has been successfully opened.