{-# OPTIONS_HADDOCK prune #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveGeneric #-}
module Lightning.Protocol.BOLT3.Keys (
derive_per_commitment_point
, derive_pubkey
, derive_localpubkey
, derive_local_htlcpubkey
, derive_remote_htlcpubkey
, derive_local_delayedpubkey
, derive_remote_delayedpubkey
, derive_revocationpubkey
, generate_from_seed
, derive_secret
, SecretStore
, empty_store
, insert_secret
, derive_old_secret
, obscured_commitment_number
) where
import Data.Bits ((.&.), xor, shiftL, testBit, complementBit)
import qualified Data.ByteString as BS
import Data.Word (Word64)
import GHC.Generics (Generic)
import qualified Crypto.Curve.Secp256k1 as S
import qualified Crypto.Hash.SHA256 as SHA256
import Lightning.Protocol.BOLT3.Types
derive_per_commitment_point
:: PerCommitmentSecret
-> Maybe PerCommitmentPoint
derive_per_commitment_point :: PerCommitmentSecret -> Maybe PerCommitmentPoint
derive_per_commitment_point (PerCommitmentSecret ByteString
sec) = do
sk <- ByteString -> Maybe Wider
S.parse_int256 ByteString
sec
pk <- S.derive_pub sk
let !bs = Pub -> ByteString
S.serialize_point Pub
pk
pure $! PerCommitmentPoint (Point bs)
{-# INLINE derive_per_commitment_point #-}
derive_pubkey
:: Point
-> PerCommitmentPoint
-> Maybe Pubkey
derive_pubkey :: Point -> PerCommitmentPoint -> Maybe Pubkey
derive_pubkey (Point ByteString
basepointBs) (PerCommitmentPoint (Point ByteString
pcpBs)) = do
basepoint <- ByteString -> Maybe Pub
S.parse_point ByteString
basepointBs
let !h = ByteString -> ByteString
SHA256.hash (ByteString
pcpBs ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
basepointBs)
tweak <- S.parse_int256 h
tweakPoint <- S.derive_pub tweak
let !result = Pub -> Pub -> Pub
S.add Pub
basepoint Pub
tweakPoint
!bs = Pub -> ByteString
S.serialize_point Pub
result
pure $! Pubkey bs
{-# INLINE derive_pubkey #-}
derive_localpubkey
:: PaymentBasepoint
-> PerCommitmentPoint
-> Maybe LocalPubkey
derive_localpubkey :: PaymentBasepoint -> PerCommitmentPoint -> Maybe LocalPubkey
derive_localpubkey (PaymentBasepoint Point
pt) PerCommitmentPoint
pcp =
Pubkey -> LocalPubkey
LocalPubkey (Pubkey -> LocalPubkey) -> Maybe Pubkey -> Maybe LocalPubkey
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Point -> PerCommitmentPoint -> Maybe Pubkey
derive_pubkey Point
pt PerCommitmentPoint
pcp
{-# INLINE derive_localpubkey #-}
derive_local_htlcpubkey
:: HtlcBasepoint
-> PerCommitmentPoint
-> Maybe LocalHtlcPubkey
derive_local_htlcpubkey :: HtlcBasepoint -> PerCommitmentPoint -> Maybe LocalHtlcPubkey
derive_local_htlcpubkey (HtlcBasepoint Point
pt) PerCommitmentPoint
pcp =
Pubkey -> LocalHtlcPubkey
LocalHtlcPubkey (Pubkey -> LocalHtlcPubkey)
-> Maybe Pubkey -> Maybe LocalHtlcPubkey
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Point -> PerCommitmentPoint -> Maybe Pubkey
derive_pubkey Point
pt PerCommitmentPoint
pcp
{-# INLINE derive_local_htlcpubkey #-}
derive_remote_htlcpubkey
:: HtlcBasepoint
-> PerCommitmentPoint
-> Maybe RemoteHtlcPubkey
derive_remote_htlcpubkey :: HtlcBasepoint -> PerCommitmentPoint -> Maybe RemoteHtlcPubkey
derive_remote_htlcpubkey (HtlcBasepoint Point
pt) PerCommitmentPoint
pcp =
Pubkey -> RemoteHtlcPubkey
RemoteHtlcPubkey (Pubkey -> RemoteHtlcPubkey)
-> Maybe Pubkey -> Maybe RemoteHtlcPubkey
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Point -> PerCommitmentPoint -> Maybe Pubkey
derive_pubkey Point
pt PerCommitmentPoint
pcp
{-# INLINE derive_remote_htlcpubkey #-}
derive_local_delayedpubkey
:: DelayedPaymentBasepoint
-> PerCommitmentPoint
-> Maybe LocalDelayedPubkey
derive_local_delayedpubkey :: DelayedPaymentBasepoint
-> PerCommitmentPoint -> Maybe LocalDelayedPubkey
derive_local_delayedpubkey (DelayedPaymentBasepoint Point
pt) PerCommitmentPoint
pcp =
Pubkey -> LocalDelayedPubkey
LocalDelayedPubkey (Pubkey -> LocalDelayedPubkey)
-> Maybe Pubkey -> Maybe LocalDelayedPubkey
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Point -> PerCommitmentPoint -> Maybe Pubkey
derive_pubkey Point
pt PerCommitmentPoint
pcp
{-# INLINE derive_local_delayedpubkey #-}
derive_remote_delayedpubkey
:: DelayedPaymentBasepoint
-> PerCommitmentPoint
-> Maybe RemoteDelayedPubkey
derive_remote_delayedpubkey :: DelayedPaymentBasepoint
-> PerCommitmentPoint -> Maybe RemoteDelayedPubkey
derive_remote_delayedpubkey (DelayedPaymentBasepoint Point
pt) PerCommitmentPoint
pcp =
Pubkey -> RemoteDelayedPubkey
RemoteDelayedPubkey (Pubkey -> RemoteDelayedPubkey)
-> Maybe Pubkey -> Maybe RemoteDelayedPubkey
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Point -> PerCommitmentPoint -> Maybe Pubkey
derive_pubkey Point
pt PerCommitmentPoint
pcp
{-# INLINE derive_remote_delayedpubkey #-}
derive_revocationpubkey
:: RevocationBasepoint
-> PerCommitmentPoint
-> Maybe RevocationPubkey
derive_revocationpubkey :: RevocationBasepoint -> PerCommitmentPoint -> Maybe RevocationPubkey
derive_revocationpubkey
(RevocationBasepoint (Point ByteString
rbpBs))
(PerCommitmentPoint (Point ByteString
pcpBs)) = do
rbp <- ByteString -> Maybe Pub
S.parse_point ByteString
rbpBs
pcp <- S.parse_point pcpBs
let !h1 = ByteString -> ByteString
SHA256.hash (ByteString
rbpBs ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
pcpBs)
let !h2 = ByteString -> ByteString
SHA256.hash (ByteString
pcpBs ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
rbpBs)
s1 <- S.parse_int256 h1
s2 <- S.parse_int256 h2
p1 <- S.mul rbp s1
p2 <- S.mul pcp s2
let !result = Pub -> Pub -> Pub
S.add Pub
p1 Pub
p2
!bs = Pub -> ByteString
S.serialize_point Pub
result
pure $! RevocationPubkey (Pubkey bs)
{-# INLINE derive_revocationpubkey #-}
generate_from_seed
:: BS.ByteString
-> Word64
-> BS.ByteString
generate_from_seed :: ByteString -> Word64 -> ByteString
generate_from_seed ByteString
seed Word64
idx = Int -> ByteString -> ByteString
go Int
47 ByteString
seed where
go :: Int -> BS.ByteString -> BS.ByteString
go :: Int -> ByteString -> ByteString
go !Int
b !ByteString
p
| Int
b Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = ByteString
p
| Word64 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word64
idx Int
b =
let !p' :: ByteString
p' = Int -> ByteString -> ByteString
flip_bit Int
b ByteString
p
!p'' :: ByteString
p'' = ByteString -> ByteString
SHA256.hash ByteString
p'
in Int -> ByteString -> ByteString
go (Int
b Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) ByteString
p''
| Bool
otherwise = Int -> ByteString -> ByteString
go (Int
b Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) ByteString
p
{-# INLINE generate_from_seed #-}
derive_secret
:: BS.ByteString
-> Int
-> Word64
-> BS.ByteString
derive_secret :: ByteString -> Int -> Word64 -> ByteString
derive_secret ByteString
base Int
bits Word64
idx = Int -> ByteString -> ByteString
go (Int
bits Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) ByteString
base where
go :: Int -> BS.ByteString -> BS.ByteString
go :: Int -> ByteString -> ByteString
go !Int
b !ByteString
p
| Int
b Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = ByteString
p
| Word64 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word64
idx Int
b =
let !p' :: ByteString
p' = Int -> ByteString -> ByteString
flip_bit Int
b ByteString
p
!p'' :: ByteString
p'' = ByteString -> ByteString
SHA256.hash ByteString
p'
in Int -> ByteString -> ByteString
go (Int
b Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) ByteString
p''
| Bool
otherwise = Int -> ByteString -> ByteString
go (Int
b Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) ByteString
p
{-# INLINE derive_secret #-}
flip_bit :: Int -> BS.ByteString -> BS.ByteString
flip_bit :: Int -> ByteString -> ByteString
flip_bit Int
b ByteString
bs =
let !byteIdx :: Int
byteIdx = Int
b Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
8
!bitIdx :: Int
bitIdx = Int
b Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
8
!len :: Int
len = ByteString -> Int
BS.length ByteString
bs
in if Int
byteIdx Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
len
then ByteString
bs
else
let !prefix :: ByteString
prefix = Int -> ByteString -> ByteString
BS.take Int
byteIdx ByteString
bs
!byte :: Word8
byte = HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
byteIdx
!byte' :: Word8
byte' = Word8 -> Int -> Word8
forall a. Bits a => a -> Int -> a
complementBit Word8
byte Int
bitIdx
!suffix :: ByteString
suffix = Int -> ByteString -> ByteString
BS.drop (Int
byteIdx Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) ByteString
bs
in ByteString
prefix ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Word8 -> ByteString
BS.singleton Word8
byte' ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
suffix
{-# INLINE flip_bit #-}
data SecretEntry = SecretEntry
{ SecretEntry -> Int
se_bucket :: {-# UNPACK #-} !Int
, SecretEntry -> Word64
se_index :: {-# UNPACK #-} !Word64
, SecretEntry -> ByteString
se_secret :: !BS.ByteString
} deriving (SecretEntry -> SecretEntry -> Bool
(SecretEntry -> SecretEntry -> Bool)
-> (SecretEntry -> SecretEntry -> Bool) -> Eq SecretEntry
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SecretEntry -> SecretEntry -> Bool
== :: SecretEntry -> SecretEntry -> Bool
$c/= :: SecretEntry -> SecretEntry -> Bool
/= :: SecretEntry -> SecretEntry -> Bool
Eq, Int -> SecretEntry -> ShowS
[SecretEntry] -> ShowS
SecretEntry -> String
(Int -> SecretEntry -> ShowS)
-> (SecretEntry -> String)
-> ([SecretEntry] -> ShowS)
-> Show SecretEntry
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SecretEntry -> ShowS
showsPrec :: Int -> SecretEntry -> ShowS
$cshow :: SecretEntry -> String
show :: SecretEntry -> String
$cshowList :: [SecretEntry] -> ShowS
showList :: [SecretEntry] -> ShowS
Show, (forall x. SecretEntry -> Rep SecretEntry x)
-> (forall x. Rep SecretEntry x -> SecretEntry)
-> Generic SecretEntry
forall x. Rep SecretEntry x -> SecretEntry
forall x. SecretEntry -> Rep SecretEntry x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. SecretEntry -> Rep SecretEntry x
from :: forall x. SecretEntry -> Rep SecretEntry x
$cto :: forall x. Rep SecretEntry x -> SecretEntry
to :: forall x. Rep SecretEntry x -> SecretEntry
Generic)
newtype SecretStore = SecretStore { SecretStore -> [SecretEntry]
unSecretStore :: [SecretEntry] }
deriving (SecretStore -> SecretStore -> Bool
(SecretStore -> SecretStore -> Bool)
-> (SecretStore -> SecretStore -> Bool) -> Eq SecretStore
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SecretStore -> SecretStore -> Bool
== :: SecretStore -> SecretStore -> Bool
$c/= :: SecretStore -> SecretStore -> Bool
/= :: SecretStore -> SecretStore -> Bool
Eq, Int -> SecretStore -> ShowS
[SecretStore] -> ShowS
SecretStore -> String
(Int -> SecretStore -> ShowS)
-> (SecretStore -> String)
-> ([SecretStore] -> ShowS)
-> Show SecretStore
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SecretStore -> ShowS
showsPrec :: Int -> SecretStore -> ShowS
$cshow :: SecretStore -> String
show :: SecretStore -> String
$cshowList :: [SecretStore] -> ShowS
showList :: [SecretStore] -> ShowS
Show, (forall x. SecretStore -> Rep SecretStore x)
-> (forall x. Rep SecretStore x -> SecretStore)
-> Generic SecretStore
forall x. Rep SecretStore x -> SecretStore
forall x. SecretStore -> Rep SecretStore x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. SecretStore -> Rep SecretStore x
from :: forall x. SecretStore -> Rep SecretStore x
$cto :: forall x. Rep SecretStore x -> SecretStore
to :: forall x. Rep SecretStore x -> SecretStore
Generic)
empty_store :: SecretStore
empty_store :: SecretStore
empty_store = [SecretEntry] -> SecretStore
SecretStore []
{-# INLINE empty_store #-}
where_to_put_secret :: Word64 -> Int
where_to_put_secret :: Word64 -> Int
where_to_put_secret Word64
idx = Int -> Int
go Int
0 where
go :: Int -> Int
go !Int
b
| Int
b Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
47 = Int
48
| Word64 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word64
idx Int
b = Int
b
| Bool
otherwise = Int -> Int
go (Int
b Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
{-# INLINE where_to_put_secret #-}
insert_secret
:: BS.ByteString
-> Word64
-> SecretStore
-> Maybe SecretStore
insert_secret :: ByteString -> Word64 -> SecretStore -> Maybe SecretStore
insert_secret ByteString
secret Word64
idx (SecretStore [SecretEntry]
known) = do
let !bucket :: Int
bucket = Word64 -> Int
where_to_put_secret Word64
idx
validated <- Int -> [SecretEntry] -> Maybe Bool
validateBuckets Int
bucket [SecretEntry]
known
if validated
then
let !known' = (SecretEntry -> Bool) -> [SecretEntry] -> [SecretEntry]
forall a. (a -> Bool) -> [a] -> [a]
filter (\SecretEntry
e -> SecretEntry -> Int
se_bucket SecretEntry
e Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
bucket) [SecretEntry]
known
!entry = Int -> Word64 -> ByteString -> SecretEntry
SecretEntry Int
bucket Word64
idx ByteString
secret
in pure $! SecretStore (known' ++ [entry])
else Nothing
where
validateBuckets :: Int -> [SecretEntry] -> Maybe Bool
validateBuckets :: Int -> [SecretEntry] -> Maybe Bool
validateBuckets Int
b [SecretEntry]
entries = [SecretEntry] -> Maybe Bool
go [SecretEntry]
entries where
go :: [SecretEntry] -> Maybe Bool
go [] = Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
go (SecretEntry Int
entryBucket Word64
knownIdx ByteString
knownSecret : [SecretEntry]
rest)
| Int
entryBucket Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
b = [SecretEntry] -> Maybe Bool
go [SecretEntry]
rest
| Bool
otherwise =
let !derived :: ByteString
derived = ByteString -> Int -> Word64 -> ByteString
derive_secret ByteString
secret Int
b Word64
knownIdx
in if ByteString
derived ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString
knownSecret
then [SecretEntry] -> Maybe Bool
go [SecretEntry]
rest
else Maybe Bool
forall a. Maybe a
Nothing
{-# INLINE insert_secret #-}
derive_old_secret
:: Word64
-> SecretStore
-> Maybe BS.ByteString
derive_old_secret :: Word64 -> SecretStore -> Maybe ByteString
derive_old_secret Word64
targetIdx (SecretStore [SecretEntry]
known) = [SecretEntry] -> Maybe ByteString
go [SecretEntry]
known where
go :: [SecretEntry] -> Maybe BS.ByteString
go :: [SecretEntry] -> Maybe ByteString
go [] = Maybe ByteString
forall a. Maybe a
Nothing
go (SecretEntry Int
bucket Word64
knownIdx ByteString
knownSecret : [SecretEntry]
rest) =
let !mask :: Word64
mask = Word64 -> Word64
complement ((Word64
1 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
bucket) Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
1)
in if (Word64
targetIdx Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.&. Word64
mask) Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
== Word64
knownIdx
then ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString) -> ByteString -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$! ByteString -> Int -> Word64 -> ByteString
derive_secret ByteString
knownSecret Int
bucket Word64
targetIdx
else [SecretEntry] -> Maybe ByteString
go [SecretEntry]
rest
complement :: Word64 -> Word64
complement :: Word64 -> Word64
complement Word64
x = Word64
x Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
`xor` Word64
0xFFFFFFFFFFFFFFFF
{-# INLINE derive_old_secret #-}
obscured_commitment_number
:: PaymentBasepoint
-> PaymentBasepoint
-> CommitmentNumber
-> Word64
obscured_commitment_number :: PaymentBasepoint -> PaymentBasepoint -> CommitmentNumber -> Word64
obscured_commitment_number
(PaymentBasepoint (Point ByteString
openerBs))
(PaymentBasepoint (Point ByteString
accepterBs))
(CommitmentNumber Word64
cn) =
let !h :: ByteString
h = ByteString -> ByteString
SHA256.hash (ByteString
openerBs ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
accepterBs)
!lower48 :: Word64
lower48 = ByteString -> Word64
extractLower48 ByteString
h
!cn48 :: Word64
cn48 = Word64
cn Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.&. Word64
0xFFFFFFFFFFFF
in Word64
cn48 Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
`xor` Word64
lower48
{-# INLINE obscured_commitment_number #-}
extractLower48 :: BS.ByteString -> Word64
ByteString
h =
let !b0 :: Word64
b0 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
h Int
26) Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
40
!b1 :: Word64
b1 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
h Int
27) Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
32
!b2 :: Word64
b2 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
h Int
28) Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
24
!b3 :: Word64
b3 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
h Int
29) Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
16
!b4 :: Word64
b4 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
h Int
30) Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
8
!b5 :: Word64
b5 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
h Int
31)
in Word64
b0 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
b1 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
b2 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
b3 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
b4 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
b5
{-# INLINE extractLower48 #-}