Hash.hs (5089B)
1 {-# OPTIONS_HADDOCK hide #-} 2 {-# LANGUAGE BangPatterns #-} 3 4 -- | 5 -- Module: Lightning.Protocol.BOLT7.Hash 6 -- Copyright: (c) 2025 Jared Tobin 7 -- License: MIT 8 -- Maintainer: Jared Tobin <jared@ppad.tech> 9 -- 10 -- Signature digests, signature verification and checksums for BOLT #7 11 -- messages. 12 -- 13 -- Signatures cover the encoded message after its signature fields, 14 -- extension included. Since decoding keeps every byte, the digest of a 15 -- decoded message is the digest of the bytes received. 16 17 module Lightning.Protocol.BOLT7.Hash ( 18 channel_announcement_hash 19 , node_announcement_hash 20 , channel_update_hash 21 , verify_channel_announcement 22 , verify_node_announcement 23 , verify_channel_update 24 , channel_update_checksum 25 ) where 26 27 import qualified Crypto.Curve.Secp256k1 as Secp256k1 28 import qualified Crypto.Hash.SHA256 as SHA256 29 import qualified Data.ByteString as BS 30 import Data.Word (Word32) 31 import Lightning.Protocol.BOLT1 (Point, Signature) 32 import qualified Lightning.Protocol.BOLT1 as BOLT1 33 import Lightning.Protocol.BOLT7.CRC32C (crc32c) 34 import Lightning.Protocol.BOLT7.Codec 35 import Lightning.Protocol.BOLT7.Messages 36 37 -- the signed bytes of each message 38 announcement_signed :: ChannelAnnouncement -> Either EncodeError BS.ByteString 39 announcement_signed = fmap (BS.drop 256) . encode_channel_announcement 40 {-# INLINE announcement_signed #-} 41 42 node_signed :: NodeAnnouncement -> Either EncodeError BS.ByteString 43 node_signed = fmap (BS.drop 64) . encode_node_announcement 44 {-# INLINE node_signed #-} 45 46 update_signed :: ChannelUpdate -> BS.ByteString 47 update_signed = BS.drop 64 . encode_channel_update 48 {-# INLINE update_signed #-} 49 50 double_sha256 :: BS.ByteString -> BS.ByteString 51 double_sha256 = SHA256.hash . SHA256.hash 52 {-# INLINE double_sha256 #-} 53 54 -- | The digest the four signatures of a t'ChannelAnnouncement' sign: the 55 -- double SHA-256 of its encoding from offset 256 (after the 56 -- signatures) to the end. Fails as 'encode_channel_announcement' does. 57 channel_announcement_hash 58 :: ChannelAnnouncement -> Either EncodeError BS.ByteString 59 channel_announcement_hash = fmap double_sha256 . announcement_signed 60 61 -- | The digest the signature of a t'NodeAnnouncement' signs: the double 62 -- SHA-256 of its encoding after the signature. Fails as 63 -- 'encode_node_announcement' does. 64 node_announcement_hash 65 :: NodeAnnouncement -> Either EncodeError BS.ByteString 66 node_announcement_hash = fmap double_sha256 . node_signed 67 68 -- | The digest the signature of a t'ChannelUpdate' signs: the double 69 -- SHA-256 of its encoding after the signature. 70 channel_update_hash :: ChannelUpdate -> BS.ByteString 71 channel_update_hash = double_sha256 . update_signed 72 73 -- | Verify the four signatures of a t'ChannelAnnouncement': both node 74 -- signatures and both bitcoin signatures, each over 75 -- 'channel_announcement_hash' under the corresponding key. 76 -- 77 -- As with libsecp256k1, signatures must be in low-s form. A key that 78 -- isn't a point on the curve fails verification. 79 verify_channel_announcement :: ChannelAnnouncement -> Bool 80 verify_channel_announcement m = case announcement_signed m of 81 Left _ -> False 82 Right signed -> 83 let !d = SHA256.hash signed 84 in verify d (ca_node_id_1 m) (ca_node_signature_1 m) 85 && verify d (ca_node_id_2 m) (ca_node_signature_2 m) 86 && verify d (ca_bitcoin_key_1 m) (ca_bitcoin_signature_1 m) 87 && verify d (ca_bitcoin_key_2 m) (ca_bitcoin_signature_2 m) 88 89 -- | Verify the signature of a t'NodeAnnouncement' under its node id. 90 -- 91 -- As with libsecp256k1, the signature must be in low-s form. 92 verify_node_announcement :: NodeAnnouncement -> Bool 93 verify_node_announcement m = case node_signed m of 94 Left _ -> False 95 Right signed -> verify (SHA256.hash signed) (na_node_id m) (na_signature m) 96 97 -- | Verify the signature of a t'ChannelUpdate' under the given node id. 98 -- 99 -- The key is that of the channel's t'ChannelAnnouncement' selected by 100 -- the update's direction: 'ca_node_id_1' for 101 -- 'Lightning.Protocol.BOLT7.NodeOne', 'ca_node_id_2' for 102 -- 'Lightning.Protocol.BOLT7.NodeTwo'. As with libsecp256k1, the 103 -- signature must be in low-s form. 104 verify_channel_update :: Point -> ChannelUpdate -> Bool 105 verify_channel_update k m = 106 verify (SHA256.hash (update_signed m)) k (cu_signature m) 107 108 -- verify a signature over the double SHA-256 of some bytes, given their 109 -- single SHA-256 (secp256k1's verify_ecdsa hashes once more) 110 verify :: BS.ByteString -> Point -> Signature -> Bool 111 verify d k s = 112 case ( Secp256k1.parse_point (BOLT1.un_point k) 113 , Secp256k1.parse_sig (BOLT1.un_signature s) ) of 114 (Just p, Just sig) -> Secp256k1.verify_ecdsa' Secp256k1.precompute d p sig 115 _ -> False 116 117 -- | The checksum of a t'ChannelUpdate', as used in the @checksums_tlv@ of 118 -- @reply_channel_range@: the CRC-32C (RFC 3720) of the update without 119 -- its signature and timestamp. Extension records are included. 120 channel_update_checksum :: ChannelUpdate -> Word32 121 channel_update_checksum m = 122 let !bs = encode_channel_update m 123 in crc32c (BS.take 40 (BS.drop 64 bs) <> BS.drop 108 bs)