bolt8

Encrypted and authenticated transport, per BOLT #8 (docs.ppad.tech/bolt8).
git clone git://git.ppad.tech/bolt8.git
Log | Files | Refs | README | LICENSE

commit 5438496f5e754c96e413b8529c0ac29d1dc4cf3d
parent 7c3372ebf0144a246cfbedcb3e71a4bb010dc21e
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 10 Oct 2026 13:39:20 +0400

docs: update README for the new API

States the spec commit implemented and what is left to the caller, and
replaces the pre-release security note.

Diffstat:
MREADME.md | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 77 insertions(+), 25 deletions(-)

diff --git a/README.md b/README.md @@ -4,9 +4,11 @@ ![](https://img.shields.io/badge/license-MIT-brightgreen) [![](https://img.shields.io/badge/haddock-bolt8-lightblue)](https://docs.ppad.tech/bolt8) -Haskell implementation of BOLT #8 (Lightning Network encrypted -transport), including the `Noise_XK_secp256k1_ChaChaPoly_SHA256` handshake -and encrypted message transport. +A pure Haskell implementation of [BOLT #8][bolt8], the Lightning +Network's encrypted and authenticated transport: the +`Noise_XK_secp256k1_ChaChaPoly_SHA256` handshake, followed by +length-prefixed, authenticated encryption of messages with periodic key +rotation. ## Usage @@ -14,46 +16,89 @@ A sample GHCi session: ``` > :set -XOverloadedStrings - > > import qualified Data.ByteString as BS > import qualified Lightning.Protocol.BOLT8 as BOLT8 > - > let Just (i_s_sec, i_s_pub) = BOLT8.keypair (BS.replicate 32 0x11) - > let Just (r_s_sec, r_s_pub) = BOLT8.keypair (BS.replicate 32 0x21) + > -- static keypairs (use 32 bytes of secure randomness in practice) + > let Just i = BOLT8.keypair (BS.replicate 32 0x11) + > let Just r = BOLT8.keypair (BS.replicate 32 0x21) + > + > -- ephemeral entropy MUST be fresh, uniformly random bytes + > let i_ent = BS.replicate 32 0x12 + > let r_ent = BS.replicate 32 0x22 > - > -- initiator knows responder static pubkey - > let Right (msg1, i_hs) = BOLT8.act1 i_s_sec i_s_pub r_s_pub - > (BS.replicate 32 0x12) - > let Right (msg2, r_hs) = BOLT8.act2 r_s_sec r_s_pub - > (BS.replicate 32 0x22) msg1 + > -- the initiator knows the responder's static public key + > let Right (msg1, i_hs) = BOLT8.act1 i (BOLT8.keypair_pub r) i_ent + > let Right (msg2, r_hs) = BOLT8.act2 r r_ent msg1 > let Right (msg3, i_res) = BOLT8.act3 i_hs msg2 > let Right r_res = BOLT8.finalize r_hs msg3 > - > let i_sess = BOLT8.session i_res - > let r_sess = BOLT8.session r_res + > -- the responder has authenticated the initiator's static key + > BOLT8.handshake_remote_static r_res == BOLT8.keypair_pub i + True + > + > -- each side gets a Sender and a Receiver + > let i_snd = BOLT8.handshake_sender i_res + > let r_rcv = BOLT8.handshake_receiver r_res > - > let Right (ct, i_sess') = BOLT8.encrypt i_sess "hello" - > let Right (pt, r_sess') = BOLT8.decrypt r_sess ct + > -- every operation returns the next state; never reuse an old one + > let Right (ct, i_snd') = BOLT8.encrypt i_snd "hello" + > let Right (pt, r_rcv') = BOLT8.decrypt r_rcv ct > pt "hello" + > + > -- on a byte stream: decrypt the 18-byte header, which says how many + > -- bytes to read next, then decrypt the rest of the frame + > let Right (ct', _) = BOLT8.encrypt i_snd' "world" + > let (hdr, rest) = BS.splitAt 18 ct' + > let Right (n, pending) = BOLT8.decrypt_header r_rcv' hdr + > n + 21 + > fmap fst (BOLT8.decrypt_body pending (BS.take n rest)) + Right "world" ``` -## Framing - -On a byte stream, use `decrypt` when you have exactly one frame, -`decrypt_frame` when a buffer starts with a complete frame (it returns -the unconsumed remainder), or `decrypt_frame_partial` to work -incrementally and learn how many bytes are still required for the next -step. +The `Sender` and `Receiver` are independent, so a writer thread can own +one and a reader thread the other. Any error from a handshake or +transport function means the connection must be closed. ## Documentation -Haddocks are hosted at [docs.ppad.tech/bolt8][hadoc]. +Haddocks (API documentation, etc.) are hosted at +[docs.ppad.tech/bolt8][hadoc]. + +## Specification + +This library implements BOLT #8 as of [lightning/bolts@1aadb719][spec] +(2026-09-21), a copy of which is in `etc/08-transport.md`. + +The library is pure, and leaves the following to the caller: + +* Network I/O, including reading exactly the number of bytes each step + requires, and terminating the connection on any failure. +* Generating ephemeral keys: `act1` and `act2` take the ephemeral + secret key as 32 bytes of caller-supplied entropy. ## Security -This is a pre-release library that, at present, claims no security -properties whatsoever. +This library aims at the maximum security achievable in a +garbage-collected language under an optimizing compiler such as GHC, in +which strict constant-timeness can be [challenging to achieve][const]. + +The implementation passes all BOLT #8 Appendix A test vectors, +including every handshake failure case and the message vectors spanning +two key rotations. The cryptography is provided by +[ppad-secp256k1][secp] (ECDH, using constant-time scalar +multiplication), [ppad-aead][aead] (ChaCha20-Poly1305, with +constant-time MAC verification), [ppad-hkdf][hkdf] and +[ppad-sha256][sha256]. + +Session state is a pure value that each operation advances. Reusing an +old `Sender` reuses a nonce, and reusing an old `Receiver` accepts +replayed frames; see the module documentation. + +If you discover any vulnerabilities, please disclose them via +security@ppad.tech. ## Development @@ -72,6 +117,13 @@ $ cabal test $ cabal bench ``` +[bolt8]: https://github.com/lightning/bolts/blob/master/08-transport.md +[spec]: https://github.com/lightning/bolts/blob/1aadb719b4007c4cea0ba6e36b08c4fb53788dee/08-transport.md [nixos]: https://nixos.org/ [flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html [hadoc]: https://docs.ppad.tech/bolt8 +[const]: https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html +[secp]: https://git.ppad.tech/secp256k1 +[aead]: https://git.ppad.tech/aead +[hkdf]: https://git.ppad.tech/hkdf +[sha256]: https://git.ppad.tech/sha256