From rusty at rustcorp.com.au Mon Mar 7 03:51:02 2016 From: rusty at rustcorp.com.au (Rusty Russell) Date: Mon, 07 Mar 2016 14:21:02 +1030 Subject: [Lightning-dev] [BOLT RFC#1] Encryption spec Message-ID: <87io0zndy1.fsf@rustcorp.com.au> Hi all, I'm back, and I've even had some sleep! I've decided I need to do more documentation in parallel with actual implementation, so I've put this temporarily in a fork of Mats' document repo: https://github.com/rustyrussell/lightning/blob/master/communications/low/01-encryption.md But here it is inline (I think I prefer plaintext, but MD isn't too painful to read). Feedback welcome, this is what I'll be moving my implementation towards... Thanks! Rusty. # Basis of Lightning Technology RFC 1 # # Status # Initial draft # Author # Rusty Russell, Blockstream # Abstract # All communications between lightning nodes should be encrypted to make analysis more difficult, and authenticated to avoid malicious interference. Each node has a known identifier which is a unique bitcoin-style public key[1]. ## Initial Handshake ## The first packet sent by a node is of form: 1. `length`: 4 byte little-endian 2. `sessionpubkey`: 33 byte DER-encoded compressed public EC-key The `length` field is the length after the field itself, and MUST be 33 or greater. `length` MUST NOT exceed 1MB (1048576 bytes). The `sessionpubkey` field is a compressed public key corresponding to a `sessionsecretkey`. The receiver MUST check that `sessionpubkey` is a valid point. The `sessionsecretkey` MUST be is unguessable, MUST BE unique for this session, MUST NOT be zero and MUST BE a valid EC key. Additional fields MAY be added, and MUST be included in the `length` field. These MUST be ignored by implementations which do not understand them. ### Derivation of the Shared Secret and Encryption Keys ### Once a node has received the initial handshake, it can derive the shared secret using the received `sessionpubkey` point and its own `sessionsecretkey` scalar using EC Diffie-Hellman. Now both nodes have obtained the shared secret, all packets are encrypted using keys derived from the shared secret. Keys are derived as follows: * sending-key: SHA256(shared-secret || sending-node-id) * receiving-key: SHA256(shared-secret || receiving-node-id) ie. each node combines the secret with its node id to produce the key to encrypt data it sends. ## Encryption of Packets ## The protocol uses Authenticated Encryption with Additional Data using ChaCha20-Poly1305[2]. Each packet contains a header and a body. The header consists of a 4-byte length indicating the size of the unencrypted body, and an 8-byte packet counter. The 4-byte length and 8-byte counter MUST be encoded in little-endian. The 8-byte counter MUST begin at 0 and be incremented before each transmission after the initial authentication packet; it MAY be non-zero for the authentication packet for re-establishing an existing session. The 12-byte header for each packet is encrypted separately (resulting in a 28 byte header, when the authentication tag is appended), to offer additional protection from traffic analysis. The body also has a 16-byte authentication tag appended. Nonces are 64-bit little-endian numbers, which MUST begin at 0 and MUST be incremented after each encryption (ie. twice per packet), such that headers are encrypted with even nonces and the packet bodies encrypted with odd nonces. ## Authentication Packet ## Once the shared secret has been exchanged, the identity of the peer has still not been authenticated. The first packet sent MUST be an authentication packet: message authenticate { // Which node this is. required bitcoin_pubkey node_id = 1; // Signature of your session key. required signature session_sig = 2; }; The receiving node MUST check that: 1. `node_id` is the expected value for the sending node. 2. `session_sig` is a valid secp256k1 ECDSA signature encoded as a 32-byte big endian R value, followed by a 32-byte big endian S value. 3. `session_sig` is the signature of the SHA256 of SHA256 of the receivers `node_id`, using the secret key corresponding to the sender's `node_id`. Additional fields MAY be included, and MUST BE ignored if not understood (to allow for future extensions). ## Rationale ## Multiple choices are possible for symmetric encryption; AES256-GSM is the other obvious choice but it is slower if there is no hardware acceleration, and the well-supported libsodium[3] doesn't support it on non-accelerated platforms. The header encryption could use a different key for encryption and eschew 16-bytes for the authentication tag, but modern APIs tend to shy away from offering unauthenticated encryption. While multiple choices are possible for public-key cryptography, the bitcoin protocol already relies on the secp256k1 elliptic curve, so reusing it here avoids additional dependencies. The authentication signature ensures that the node owning the `node_id` has specifically initiated this session; using double-sha256 is done because bitcoin transaction signatures use that scheme. # Security Considerations # It is strongly recommended that existing, commonly-used, validated libraries be used for encryption and decryption, to avoid the many implementation pitfalls possible. # References # 1. https://en.bitcoin.it/wiki/Secp256k1 2. https://tools.ietf.org/html/rfc7539 3. https://download.libsodium.org/doc/index.html # Acknowledgements # Thanks to Olaoluwa Osuntokun for pointing out the idea of encrypting the length, and noting that it needed a separate key if it didn't include the authentication tag. Thanks to Mats Jerratsch and Anthony Towns for feedback on initial handshake design. # Feedback # Feedback is welcome on the [lightning-dev list](https://lists.linuxfoundation.org/mailman/listinfo/lightning-dev).