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 13eaef80bd6998ee9dac6475687c364847d5240c
parent aeed9c7a24b3fc4d51d0240b005a36ee1bc531da
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 10 Oct 2026 13:39:19 +0400

bench: use the public API; add streaming and rotation cases

Benches share a fixture module that builds its inputs in IO without
partial patterns, and rely on the library's NFData instances instead of
orphans derived through Generic. Adds 64KB encrypt/decrypt, a rotating
encrypt, and decrypt_header/decrypt_body cases.

Diffstat:
Abench/Fixture.hs | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbench/Main.hs | 141++++++++++++++++++++++---------------------------------------------------------
Mbench/Weight.hs | 130++++++++++++++++++++++++-------------------------------------------------------
Mppad-bolt8.cabal | 8++++----
4 files changed, 159 insertions(+), 197 deletions(-)

diff --git a/bench/Fixture.hs b/bench/Fixture.hs @@ -0,0 +1,77 @@ +module Fixture ( + i_s_ent + , i_e_ent + , r_s_ent + , r_e_ent + , msg_32 + , msg_1k + , msg_64k + , Fixture(..) + , fixture + ) where + +import qualified Data.ByteString as BS +import qualified Lightning.Protocol.BOLT8 as BOLT8 + +-- key material from BOLT #8 Appendix A +i_s_ent, i_e_ent, r_s_ent, r_e_ent :: BS.ByteString +i_s_ent = BS.replicate 32 0x11 +i_e_ent = BS.replicate 32 0x12 +r_s_ent = BS.replicate 32 0x21 +r_e_ent = BS.replicate 32 0x22 + +data Fixture = Fixture { + fx_i :: !BOLT8.Keypair + , fx_r :: !BOLT8.Keypair + , fx_msg1 :: !BS.ByteString + , fx_i_hs :: !BOLT8.Initiator + , fx_msg2 :: !BS.ByteString + , fx_r_hs :: !BOLT8.Responder + , fx_msg3 :: !BS.ByteString + , fx_snd :: !BOLT8.Sender + , fx_snd_rot :: !BOLT8.Sender -- its next encrypt rotates the key + , fx_rcv :: !BOLT8.Receiver + , fx_ct_32 :: !BS.ByteString + , fx_ct_1k :: !BS.ByteString + , fx_ct_64k :: !BS.ByteString + , fx_pend_1k :: !BOLT8.Pending -- awaiting the body of fx_ct_1k + } + +fixture :: IO Fixture +fixture = do + i <- orFail "keypair" (maybe (Left ()) Right (BOLT8.keypair i_s_ent)) + r <- orFail "keypair" (maybe (Left ()) Right (BOLT8.keypair r_s_ent)) + (msg1, i_hs) <- orFail "act1" (BOLT8.act1 i (BOLT8.keypair_pub r) i_e_ent) + (msg2, r_hs) <- orFail "act2" (BOLT8.act2 r r_e_ent msg1) + (msg3, i_res) <- orFail "act3" (BOLT8.act3 i_hs msg2) + r_res <- orFail "finalize" (BOLT8.finalize r_hs msg3) + let snd0 = BOLT8.handshake_sender i_res + rcv0 = BOLT8.handshake_receiver r_res + snd_rot <- advance (499 :: Int) snd0 + (ct_32, _) <- orFail "encrypt" (BOLT8.encrypt snd0 msg_32) + (ct_1k, _) <- orFail "encrypt" (BOLT8.encrypt snd0 msg_1k) + (ct_64k, _) <- orFail "encrypt" (BOLT8.encrypt snd0 msg_64k) + (_, pend_1k) <- orFail "header" + (BOLT8.decrypt_header rcv0 (BS.take 18 ct_1k)) + pure Fixture { + fx_i = i, fx_r = r + , fx_msg1 = msg1, fx_i_hs = i_hs + , fx_msg2 = msg2, fx_r_hs = r_hs + , fx_msg3 = msg3 + , fx_snd = snd0, fx_snd_rot = snd_rot + , fx_rcv = rcv0 + , fx_ct_32 = ct_32, fx_ct_1k = ct_1k, fx_ct_64k = ct_64k + , fx_pend_1k = pend_1k + } + where + orFail :: Show e => String -> Either e a -> IO a + orFail msg = either (\e -> fail (msg <> ": " <> show e)) pure + advance 0 s = pure s + advance n s = do + (_, s') <- orFail "encrypt" (BOLT8.encrypt s msg_32) + advance (n - 1) s' + +msg_32, msg_1k, msg_64k :: BS.ByteString +msg_32 = BS.replicate 32 0x00 +msg_1k = BS.replicate 1024 0x00 +msg_64k = BS.replicate 65535 0x00 diff --git a/bench/Main.hs b/bench/Main.hs @@ -1,113 +1,50 @@ -{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-} -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} - module Main where -import Control.DeepSeq import Criterion.Main import qualified Data.ByteString as BS -import qualified Lightning.Protocol.BOLT8.Internal as BOLT8 - -instance NFData BOLT8.Pub where - rnf p = rnf (BOLT8.serialize_pub p) - -instance NFData BOLT8.Sec -instance NFData BOLT8.Error -instance NFData BOLT8.Key32 -instance NFData BOLT8.SessionNonce -instance NFData BOLT8.Session -instance NFData BOLT8.HandshakeState -instance NFData BOLT8.Handshake -instance NFData (BOLT8.HandshakeFor a) where - rnf (BOLT8.HandshakeFor s) = rnf s +import Fixture +import qualified Lightning.Protocol.BOLT8 as BOLT8 main :: IO () -main = defaultMain [ - keys - , handshake - , messages - ] - --- test keys (from BOLT #8 spec) -i_s_ent, i_e_ent, r_s_ent, r_e_ent :: BS.ByteString -i_s_ent = BS.replicate 32 0x11 -i_e_ent = BS.replicate 32 0x12 -r_s_ent = BS.replicate 32 0x21 -r_e_ent = BS.replicate 32 0x22 +main = do + fx <- fixture + defaultMain [ + keys fx + , handshake fx + , transport fx + ] -keys :: Benchmark -keys = bgroup "keys" [ +keys :: Fixture -> Benchmark +keys fx = bgroup "keys" [ bench "keypair" $ nf BOLT8.keypair i_s_ent - , bench "parse_pub" $ nf BOLT8.parse_pub r_s_pub_bs - , bench "serialize_pub" $ - nf BOLT8.serialize_pub r_s_pub + , bench "parse_pub" $ nf BOLT8.parse_pub pub_bs + , bench "serialize_pub" $ nf BOLT8.serialize_pub pub ] where - Just (_, r_s_pub) = BOLT8.keypair r_s_ent - r_s_pub_bs = BOLT8.serialize_pub r_s_pub - -handshake :: Benchmark -handshake = env setup $ - \ ~(i_s_sec, i_s_pub, r_s_sec, r_s_pub, - msg1, i_hs, msg2, r_hs, msg3) -> - bgroup "handshake" [ - bench "act1" $ - nf (BOLT8.act1 i_s_sec i_s_pub r_s_pub) i_e_ent - , bench "act2" $ - nf (BOLT8.act2 r_s_sec r_s_pub r_e_ent) msg1 - , bench "act3" $ - nf (BOLT8.act3 i_hs) msg2 - , bench "finalize" $ - nf (BOLT8.finalize r_hs) msg3 - ] - where - setup = do - let Just (!i_s_sec, !i_s_pub) = - BOLT8.keypair i_s_ent - Just (!r_s_sec, !r_s_pub) = - BOLT8.keypair r_s_ent - Right (!msg1, !i_hs) = - BOLT8.act1 i_s_sec i_s_pub r_s_pub i_e_ent - Right (!msg2, !r_hs) = - BOLT8.act2 r_s_sec r_s_pub r_e_ent msg1 - Right (!msg3, _) = BOLT8.act3 i_hs msg2 - pure ( i_s_sec, i_s_pub, r_s_sec, r_s_pub - , msg1, i_hs, msg2, r_hs, msg3 ) + pub = BOLT8.keypair_pub (fx_r fx) + pub_bs = BOLT8.serialize_pub pub + +handshake :: Fixture -> Benchmark +handshake fx = bgroup "handshake" [ + bench "act1" $ + nf (BOLT8.act1 (fx_i fx) (BOLT8.keypair_pub (fx_r fx))) i_e_ent + , bench "act2" $ nf (BOLT8.act2 (fx_r fx) r_e_ent) (fx_msg1 fx) + , bench "act3" $ nf (BOLT8.act3 (fx_i_hs fx)) (fx_msg2 fx) + , bench "finalize" $ nf (BOLT8.finalize (fx_r_hs fx)) (fx_msg3 fx) + ] -messages :: Benchmark -messages = env setup $ - \ ~(i_sess, r_sess, ct_small, ct_large) -> - bgroup "messages" [ - bench "encrypt (32B)" $ - nf (BOLT8.encrypt i_sess) small_msg - , bench "encrypt (1KB)" $ - nf (BOLT8.encrypt i_sess) large_msg - , bench "decrypt (32B)" $ - nf (BOLT8.decrypt r_sess) ct_small - , bench "decrypt (1KB)" $ - nf (BOLT8.decrypt r_sess) ct_large - ] - where - small_msg = BS.replicate 32 0x00 - large_msg = BS.replicate 1024 0x00 - setup = do - let Just (!i_s_sec, !i_s_pub) = - BOLT8.keypair i_s_ent - Just (!r_s_sec, !r_s_pub) = - BOLT8.keypair r_s_ent - Right (msg1, i_hs) = - BOLT8.act1 i_s_sec i_s_pub r_s_pub i_e_ent - Right (msg2, r_hs) = - BOLT8.act2 r_s_sec r_s_pub r_e_ent msg1 - Right (msg3, i_result) = - BOLT8.act3 i_hs msg2 - Right r_result = - BOLT8.finalize r_hs msg3 - !i_sess = BOLT8.session i_result - !r_sess = BOLT8.session r_result - Right (!ct_small, _) = - BOLT8.encrypt i_sess small_msg - Right (!ct_large, _) = - BOLT8.encrypt i_sess large_msg - pure (i_sess, r_sess, ct_small, ct_large) +transport :: Fixture -> Benchmark +transport fx = bgroup "transport" [ + bench "encrypt (32B)" $ nf (BOLT8.encrypt (fx_snd fx)) msg_32 + , bench "encrypt (1KB)" $ nf (BOLT8.encrypt (fx_snd fx)) msg_1k + , bench "encrypt (64KB)" $ nf (BOLT8.encrypt (fx_snd fx)) msg_64k + , bench "encrypt (32B, rotating)" $ + nf (BOLT8.encrypt (fx_snd_rot fx)) msg_32 + , bench "decrypt (32B)" $ nf (BOLT8.decrypt (fx_rcv fx)) (fx_ct_32 fx) + , bench "decrypt (1KB)" $ nf (BOLT8.decrypt (fx_rcv fx)) (fx_ct_1k fx) + , bench "decrypt (64KB)" $ nf (BOLT8.decrypt (fx_rcv fx)) (fx_ct_64k fx) + , bench "decrypt_header" $ + nf (BOLT8.decrypt_header (fx_rcv fx)) (BS.take 18 (fx_ct_1k fx)) + , bench "decrypt_body (1KB)" $ + nf (BOLT8.decrypt_body (fx_pend_1k fx)) (BS.drop 18 (fx_ct_1k fx)) + ] diff --git a/bench/Weight.hs b/bench/Weight.hs @@ -1,98 +1,46 @@ -{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-} -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} - module Main where -import Control.DeepSeq import qualified Data.ByteString as BS -import qualified Lightning.Protocol.BOLT8.Internal as BOLT8 +import Fixture +import qualified Lightning.Protocol.BOLT8 as BOLT8 import Weigh -instance NFData BOLT8.Pub where - rnf p = rnf (BOLT8.serialize_pub p) - -instance NFData BOLT8.Sec -instance NFData BOLT8.Error -instance NFData BOLT8.Key32 -instance NFData BOLT8.SessionNonce -instance NFData BOLT8.Session -instance NFData BOLT8.HandshakeState -instance NFData BOLT8.Handshake -instance NFData (BOLT8.HandshakeFor a) where - rnf (BOLT8.HandshakeFor s) = rnf s - -- note that 'weigh' doesn't work properly in a repl main :: IO () -main = mainWith $ do - keys - handshake - messages - --- test keys (from BOLT #8 spec) -i_s_ent, i_e_ent, r_s_ent, r_e_ent :: BS.ByteString -i_s_ent = BS.replicate 32 0x11 -i_e_ent = BS.replicate 32 0x12 -r_s_ent = BS.replicate 32 0x21 -r_e_ent = BS.replicate 32 0x22 - -keys :: Weigh () -keys = - let Just (_, !r_s_pub) = BOLT8.keypair r_s_ent - !r_s_pub_bs = BOLT8.serialize_pub r_s_pub - in wgroup "keys" $ do - func "keypair" BOLT8.keypair i_s_ent - func "parse_pub" BOLT8.parse_pub r_s_pub_bs - func "serialize_pub" - BOLT8.serialize_pub r_s_pub - -handshake :: Weigh () -handshake = - let Just (!i_s_sec, !i_s_pub) = - BOLT8.keypair i_s_ent - Just (!r_s_sec, !r_s_pub) = - BOLT8.keypair r_s_ent - Right (!msg1, !i_hs) = - BOLT8.act1 i_s_sec i_s_pub r_s_pub i_e_ent - Right (!msg2, !r_hs) = - BOLT8.act2 r_s_sec r_s_pub r_e_ent msg1 - Right (!msg3, _) = BOLT8.act3 i_hs msg2 - in wgroup "handshake" $ do - func "act1" - (BOLT8.act1 i_s_sec i_s_pub r_s_pub) i_e_ent - func "act2" - (BOLT8.act2 r_s_sec r_s_pub r_e_ent) msg1 - func "act3" (BOLT8.act3 i_hs) msg2 - func "finalize" (BOLT8.finalize r_hs) msg3 - -messages :: Weigh () -messages = - let Just (!i_s_sec, !i_s_pub) = - BOLT8.keypair i_s_ent - Just (!r_s_sec, !r_s_pub) = - BOLT8.keypair r_s_ent - Right (msg1, i_hs) = - BOLT8.act1 i_s_sec i_s_pub r_s_pub i_e_ent - Right (msg2, r_hs) = - BOLT8.act2 r_s_sec r_s_pub r_e_ent msg1 - Right (msg3, i_result) = - BOLT8.act3 i_hs msg2 - Right r_result = - BOLT8.finalize r_hs msg3 - !i_sess = BOLT8.session i_result - !r_sess = BOLT8.session r_result - !small_msg = BS.replicate 32 0x00 - !large_msg = BS.replicate 1024 0x00 - Right (!ct_small, _) = - BOLT8.encrypt i_sess small_msg - Right (!ct_large, _) = - BOLT8.encrypt i_sess large_msg - in wgroup "messages" $ do - func "encrypt (32B)" - (BOLT8.encrypt i_sess) small_msg - func "encrypt (1KB)" - (BOLT8.encrypt i_sess) large_msg - func "decrypt (32B)" - (BOLT8.decrypt r_sess) ct_small - func "decrypt (1KB)" - (BOLT8.decrypt r_sess) ct_large +main = do + fx <- fixture + mainWith $ do + keys fx + handshake fx + transport fx + +keys :: Fixture -> Weigh () +keys fx = wgroup "keys" $ do + func "keypair" BOLT8.keypair i_s_ent + func "parse_pub" BOLT8.parse_pub pub_bs + func "serialize_pub" BOLT8.serialize_pub pub + where + pub = BOLT8.keypair_pub (fx_r fx) + pub_bs = BOLT8.serialize_pub pub + +handshake :: Fixture -> Weigh () +handshake fx = wgroup "handshake" $ do + func "act1" + (BOLT8.act1 (fx_i fx) (BOLT8.keypair_pub (fx_r fx))) i_e_ent + func "act2" (BOLT8.act2 (fx_r fx) r_e_ent) (fx_msg1 fx) + func "act3" (BOLT8.act3 (fx_i_hs fx)) (fx_msg2 fx) + func "finalize" (BOLT8.finalize (fx_r_hs fx)) (fx_msg3 fx) + +transport :: Fixture -> Weigh () +transport fx = wgroup "transport" $ do + func "encrypt (32B)" (BOLT8.encrypt (fx_snd fx)) msg_32 + func "encrypt (1KB)" (BOLT8.encrypt (fx_snd fx)) msg_1k + func "encrypt (64KB)" (BOLT8.encrypt (fx_snd fx)) msg_64k + func "encrypt (32B, rotating)" (BOLT8.encrypt (fx_snd_rot fx)) msg_32 + func "decrypt (32B)" (BOLT8.decrypt (fx_rcv fx)) (fx_ct_32 fx) + func "decrypt (1KB)" (BOLT8.decrypt (fx_rcv fx)) (fx_ct_1k fx) + func "decrypt (64KB)" (BOLT8.decrypt (fx_rcv fx)) (fx_ct_64k fx) + func "decrypt_header" + (BOLT8.decrypt_header (fx_rcv fx)) (BS.take 18 (fx_ct_1k fx)) + func "decrypt_body (1KB)" + (BOLT8.decrypt_body (fx_pend_1k fx)) (BS.drop 18 (fx_ct_1k fx)) diff --git a/ppad-bolt8.cabal b/ppad-bolt8.cabal @@ -67,15 +67,15 @@ benchmark bolt8-bench default-language: Haskell2010 hs-source-dirs: bench main-is: Main.hs + other-modules: Fixture ghc-options: - -rtsopts -O2 -Wall -fno-warn-orphans + -rtsopts -O2 -Wall build-depends: base , bytestring , criterion - , deepseq , ppad-bolt8 benchmark bolt8-weigh @@ -83,14 +83,14 @@ benchmark bolt8-weigh default-language: Haskell2010 hs-source-dirs: bench main-is: Weight.hs + other-modules: Fixture ghc-options: - -rtsopts -O2 -Wall -fno-warn-orphans + -rtsopts -O2 -Wall build-depends: base , bytestring - , deepseq , ppad-bolt8 , weigh