module LibP2P.Protocol.GossipSub.Validation
( ValidationError (..)
, validateMessage
, signaturePrefix
, marshalForSigning
, signingBytes
) where
import Prelude
import Control.Monad (unless, when)
import Data.ByteString (ByteString)
import LibP2P.Core.Multihash (HashFunction (..), validateMultihash)
import LibP2P.Crypto.Key (PublicKey, verify)
import LibP2P.Crypto.PeerId (PeerId (..), fromPublicKey)
import LibP2P.Crypto.Protobuf (decodePublicKey)
import LibP2P.Protocol.GossipSub.Message (encodePubSubMessageBS)
import LibP2P.Protocol.GossipSub.Types
data ValidationError
= MissingField String
| UnexpectedField String
| MalformedKey String
| KeyPeerIdMismatch
| BadSignature
deriving (Int -> ValidationError -> ShowS
[ValidationError] -> ShowS
ValidationError -> String
(Int -> ValidationError -> ShowS)
-> (ValidationError -> String)
-> ([ValidationError] -> ShowS)
-> Show ValidationError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ValidationError -> ShowS
showsPrec :: Int -> ValidationError -> ShowS
$cshow :: ValidationError -> String
show :: ValidationError -> String
$cshowList :: [ValidationError] -> ShowS
showList :: [ValidationError] -> ShowS
Show, ValidationError -> ValidationError -> Bool
(ValidationError -> ValidationError -> Bool)
-> (ValidationError -> ValidationError -> Bool)
-> Eq ValidationError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ValidationError -> ValidationError -> Bool
== :: ValidationError -> ValidationError -> Bool
$c/= :: ValidationError -> ValidationError -> Bool
/= :: ValidationError -> ValidationError -> Bool
Eq)
signaturePrefix :: ByteString
signaturePrefix :: ByteString
signaturePrefix = ByteString
"libp2p-pubsub:"
marshalForSigning :: PubSubMessage -> ByteString
marshalForSigning :: PubSubMessage -> ByteString
marshalForSigning PubSubMessage
msg = PubSubMessage -> ByteString
encodePubSubMessageBS
(PubSubMessage
msg { msgSignature = Nothing, msgKey = Nothing })
signingBytes :: PubSubMessage -> ByteString
signingBytes :: PubSubMessage -> ByteString
signingBytes PubSubMessage
msg = ByteString
signaturePrefix ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> PubSubMessage -> ByteString
marshalForSigning PubSubMessage
msg
validateMessage :: SignaturePolicy -> PubSubMessage -> Either ValidationError ()
validateMessage :: SignaturePolicy -> PubSubMessage -> Either ValidationError ()
validateMessage SignaturePolicy
StrictSign PubSubMessage
msg = do
from <- String -> Maybe ByteString -> Either ValidationError ByteString
forall a. String -> Maybe a -> Either ValidationError a
required String
"from" (PubSubMessage -> Maybe ByteString
msgFrom PubSubMessage
msg)
_ <- required "seqno" (msgSeqNo msg)
sig <- required "signature" (msgSignature msg)
pk <- authorKey from (msgKey msg)
let PeerId derived = fromPublicKey pk
unless (derived == from) (Left KeyPeerIdMismatch)
unless (verify pk (signingBytes msg) sig) (Left BadSignature)
validateMessage SignaturePolicy
StrictNoSign PubSubMessage
msg = do
String -> Maybe ByteString -> Either ValidationError ()
forall a. String -> Maybe a -> Either ValidationError ()
forbid String
"signature" (PubSubMessage -> Maybe ByteString
msgSignature PubSubMessage
msg)
String -> Maybe ByteString -> Either ValidationError ()
forall a. String -> Maybe a -> Either ValidationError ()
forbid String
"key" (PubSubMessage -> Maybe ByteString
msgKey PubSubMessage
msg)
String -> Maybe ByteString -> Either ValidationError ()
forall a. String -> Maybe a -> Either ValidationError ()
forbid String
"from" (PubSubMessage -> Maybe ByteString
msgFrom PubSubMessage
msg)
String -> Maybe ByteString -> Either ValidationError ()
forall a. String -> Maybe a -> Either ValidationError ()
forbid String
"seqno" (PubSubMessage -> Maybe ByteString
msgSeqNo PubSubMessage
msg)
authorKey :: ByteString -> Maybe ByteString -> Either ValidationError PublicKey
authorKey :: ByteString -> Maybe ByteString -> Either ValidationError PublicKey
authorKey ByteString
from Maybe ByteString
Nothing =
case ByteString -> Either String (HashFunction, ByteString)
validateMultihash ByteString
from of
Left String
err -> ValidationError -> Either ValidationError PublicKey
forall a b. a -> Either a b
Left (String -> ValidationError
MalformedKey (String
"from is not a valid multihash: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
err))
Right (HashFunction
SHA256, ByteString
_) -> ValidationError -> Either ValidationError PublicKey
forall a b. a -> Either a b
Left (String -> ValidationError
MissingField String
"key")
Right (HashFunction
Identity, ByteString
inlined) -> ByteString -> Either ValidationError PublicKey
decodeKey ByteString
inlined
authorKey ByteString
_ (Just ByteString
keyBytes) = ByteString -> Either ValidationError PublicKey
decodeKey ByteString
keyBytes
decodeKey :: ByteString -> Either ValidationError PublicKey
decodeKey :: ByteString -> Either ValidationError PublicKey
decodeKey ByteString
bs = (String -> Either ValidationError PublicKey)
-> (PublicKey -> Either ValidationError PublicKey)
-> Either String PublicKey
-> Either ValidationError PublicKey
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (ValidationError -> Either ValidationError PublicKey
forall a b. a -> Either a b
Left (ValidationError -> Either ValidationError PublicKey)
-> (String -> ValidationError)
-> String
-> Either ValidationError PublicKey
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ValidationError
MalformedKey) PublicKey -> Either ValidationError PublicKey
forall a b. b -> Either a b
Right (ByteString -> Either String PublicKey
decodePublicKey ByteString
bs)
required :: String -> Maybe a -> Either ValidationError a
required :: forall a. String -> Maybe a -> Either ValidationError a
required String
name = Either ValidationError a
-> (a -> Either ValidationError a)
-> Maybe a
-> Either ValidationError a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (ValidationError -> Either ValidationError a
forall a b. a -> Either a b
Left (String -> ValidationError
MissingField String
name)) a -> Either ValidationError a
forall a b. b -> Either a b
Right
forbid :: String -> Maybe a -> Either ValidationError ()
forbid :: forall a. String -> Maybe a -> Either ValidationError ()
forbid String
name Maybe a
v = Bool -> Either ValidationError () -> Either ValidationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> (a -> Bool) -> Maybe a -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (Bool -> a -> Bool
forall a b. a -> b -> a
const Bool
True) Maybe a
v) (ValidationError -> Either ValidationError ()
forall a b. a -> Either a b
Left (String -> ValidationError
UnexpectedField String
name))