commit 62af5202761b82a58c373be9e3591e51e8fb95da
parent 7e9196bd1f726947eea0aebd2179454fffa62304
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 10 Oct 2026 13:44:51 +0400
lib: check the secret key length in nextEphemeral
nextEphemeral is exported and passed its secret key to mulSecKey,
which reads it with unsafe_roll32. A key shorter than 32 bytes read
past the end of the buffer. Return Nothing unless it is 32 bytes.
Diffstat:
2 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/lib/Lightning/Protocol/BOLT4/Blinding.hs b/lib/Lightning/Protocol/BOLT4/Blinding.hs
@@ -134,20 +134,24 @@ deriveBlindedNodeId (SharedSecret !ss) !nodePub = do
--
-- @e_{i+1} = SHA256(E_i || ss_i) * e_i@
-- @E_{i+1} = SHA256(E_i || ss_i) * E_i@
+--
+-- Returns 'Nothing' if the secret key is not 32 bytes.
nextEphemeral
:: BS.ByteString -- ^ e_i (32-byte secret key)
-> Secp256k1.Projective -- ^ E_i
-> SharedSecret -- ^ ss_i
-> Maybe (BS.ByteString, Secp256k1.Projective) -- ^ (e_{i+1}, E_{i+1})
-nextEphemeral !secKey !pubKey (SharedSecret !ss) = do
- let !pubBytes = Secp256k1.serialize_point pubKey
- !blindingFactor = SHA256.hash (pubBytes <> ss)
- bfInt <- Secp256k1.roll32 blindingFactor
- -- Compute e_{i+1} = e_i * blindingFactor (mod q)
- let !newSecKey = mulSecKey secKey blindingFactor
- -- Compute E_{i+1} = E_i * blindingFactor
- newPubKey <- Secp256k1.mul pubKey bfInt
- pure (newSecKey, newPubKey)
+nextEphemeral !secKey !pubKey (SharedSecret !ss)
+ | BS.length secKey /= 32 = Nothing
+ | otherwise = do
+ let !pubBytes = Secp256k1.serialize_point pubKey
+ !blindingFactor = SHA256.hash (pubBytes <> ss)
+ bfInt <- Secp256k1.roll32 blindingFactor
+ -- Compute e_{i+1} = e_i * blindingFactor (mod q)
+ let !newSecKey = mulSecKey secKey blindingFactor
+ -- Compute E_{i+1} = E_i * blindingFactor
+ newPubKey <- Secp256k1.mul pubKey bfInt
+ pure (newSecKey, newPubKey)
{-# INLINE nextEphemeral #-}
-- | Compute blinding factor for next path key (public key only).
diff --git a/test/Main.hs b/test/Main.hs
@@ -1255,6 +1255,12 @@ blindingEphemeralKeyTests =
testSeed testSeedPubKey ss2
assertBool "results should differ"
(result1 /= result2)
+
+ , testCase "nextEphemeral rejects a secret not of 32 bytes" $ do
+ nextEphemeral (BS.singleton 0x07) testSeedPubKey testSharedSecretBS
+ @?= Nothing
+ nextEphemeral (BS.cons 0x00 testSeed) testSeedPubKey
+ testSharedSecretBS @?= Nothing
]
-- 3. TLV Encoding/Decoding Tests -----------------------------------------