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

README.md (4572B)


      1 # ppad-bolt8
      2 
      3 [![](https://img.shields.io/hackage/v/ppad-bolt8?color=blue)](https://hackage.haskell.org/package/ppad-bolt8)
      4 ![](https://img.shields.io/badge/license-MIT-brightgreen)
      5 [![](https://img.shields.io/badge/haddock-bolt8-lightblue)](https://docs.ppad.tech/bolt8)
      6 
      7 A pure Haskell implementation of [BOLT #8][bolt8], the Lightning
      8 Network's encrypted and authenticated transport: the
      9 `Noise_XK_secp256k1_ChaChaPoly_SHA256` handshake, followed by
     10 length-prefixed, authenticated encryption of messages with periodic key
     11 rotation.
     12 
     13 ## Usage
     14 
     15 A sample GHCi session:
     16 
     17 ```
     18   > :set -XOverloadedStrings
     19   > import qualified Data.ByteString as BS
     20   > import qualified Lightning.Protocol.BOLT8 as BOLT8
     21   >
     22   > -- static keypairs (use 32 bytes of secure randomness in practice)
     23   > let Just i = BOLT8.keypair (BS.replicate 32 0x11)
     24   > let Just r = BOLT8.keypair (BS.replicate 32 0x21)
     25   >
     26   > -- ephemeral entropy MUST be fresh, uniformly random bytes
     27   > let i_ent = BS.replicate 32 0x12
     28   > let r_ent = BS.replicate 32 0x22
     29   >
     30   > -- the initiator knows the responder's static public key
     31   > let Right (msg1, i_hs) = BOLT8.act1 i (BOLT8.keypair_pub r) i_ent
     32   > let Right (msg2, r_hs) = BOLT8.act2 r r_ent msg1
     33   > let Right (msg3, i_res) = BOLT8.act3 i_hs msg2
     34   > let Right r_res = BOLT8.finalize r_hs msg3
     35   >
     36   > -- the responder has authenticated the initiator's static key
     37   > BOLT8.handshake_remote_static r_res == BOLT8.keypair_pub i
     38   True
     39   >
     40   > -- each side gets a Sender and a Receiver
     41   > let i_snd = BOLT8.handshake_sender i_res
     42   > let r_rcv = BOLT8.handshake_receiver r_res
     43   >
     44   > -- every operation returns the next state; never reuse an old one
     45   > let Right (ct, i_snd') = BOLT8.encrypt i_snd "hello"
     46   > let Right (pt, r_rcv') = BOLT8.decrypt r_rcv ct
     47   > pt
     48   "hello"
     49   >
     50   > -- on a byte stream: decrypt the 18-byte header, which says how many
     51   > -- bytes to read next, then decrypt the rest of the frame
     52   > let Right (ct', _) = BOLT8.encrypt i_snd' "world"
     53   > let (hdr, rest) = BS.splitAt 18 ct'
     54   > let Right (n, pending) = BOLT8.decrypt_header r_rcv' hdr
     55   > n
     56   21
     57   > fmap fst (BOLT8.decrypt_body pending (BS.take n rest))
     58   Right "world"
     59 ```
     60 
     61 The `Sender` and `Receiver` are independent, so a writer thread can own
     62 one and a reader thread the other. Any error from a handshake or
     63 transport function means the connection must be closed.
     64 
     65 ## Documentation
     66 
     67 Haddocks (API documentation, etc.) are hosted at
     68 [docs.ppad.tech/bolt8][hadoc].
     69 
     70 ## Specification
     71 
     72 This library implements BOLT #8 as of [lightning/bolts@1aadb719][spec]
     73 (2026-09-21), a copy of which is in `etc/08-transport.md`.
     74 
     75 The library is pure, and leaves the following to the caller:
     76 
     77 * Network I/O, including reading exactly the number of bytes each step
     78   requires, and terminating the connection on any failure.
     79 * Generating ephemeral keys: `act1` and `act2` take the ephemeral
     80   secret key as 32 bytes of caller-supplied entropy.
     81 
     82 ## Security
     83 
     84 This library aims at the maximum security achievable in a
     85 garbage-collected language under an optimizing compiler such as GHC, in
     86 which strict constant-timeness can be [challenging to achieve][const].
     87 
     88 The implementation passes all BOLT #8 Appendix A test vectors,
     89 including every handshake failure case and the message vectors spanning
     90 two key rotations. The cryptography is provided by
     91 [ppad-secp256k1][secp] (ECDH, using constant-time scalar
     92 multiplication), [ppad-aead][aead] (ChaCha20-Poly1305, with
     93 constant-time MAC verification), [ppad-hkdf][hkdf] and
     94 [ppad-sha256][sha256].
     95 
     96 Session state is a pure value that each operation advances. Reusing an
     97 old `Sender` reuses a nonce, and reusing an old `Receiver` accepts
     98 replayed frames; see the module documentation.
     99 
    100 If you discover any vulnerabilities, please disclose them via
    101 security@ppad.tech.
    102 
    103 ## Development
    104 
    105 You'll require [Nix][nixos] with [flake][flake] support enabled. Enter a
    106 development shell with:
    107 
    108 ```
    109 $ nix develop
    110 ```
    111 
    112 Then do e.g.:
    113 
    114 ```
    115 $ cabal build
    116 $ cabal test
    117 $ cabal bench
    118 ```
    119 
    120 [bolt8]: https://github.com/lightning/bolts/blob/master/08-transport.md
    121 [spec]: https://github.com/lightning/bolts/blob/1aadb719b4007c4cea0ba6e36b08c4fb53788dee/08-transport.md
    122 [nixos]: https://nixos.org/
    123 [flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html
    124 [hadoc]: https://docs.ppad.tech/bolt8
    125 [const]: https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html
    126 [secp]: https://git.ppad.tech/secp256k1
    127 [aead]: https://git.ppad.tech/aead
    128 [hkdf]: https://git.ppad.tech/hkdf
    129 [sha256]: https://git.ppad.tech/sha256