pipes-cacophony-0.1.2: examples/echo-client/Handshakes.hs
{-# LANGUAGE DeriveGeneric, RankNTypes, ImpredicativeTypes,
OverloadedStrings, RecordWildCards #-}
module Handshakes
( HandshakeKeys(..),
HandshakeType(..),
processHandshake
) where
import Control.Concurrent.Async (race_)
import Control.Concurrent.MVar (MVar, newEmptyMVar)
import Control.Exception (Exception, throw, throwIO)
import Data.Aeson (ToJSON, FromJSON, parseJSON, (.:),
Value(..), (.=), toJSON, object, withObject)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Base64 as B64 (encode, decode)
import Data.Text (Text)
import Data.Text.Encoding (encodeUtf8, decodeUtf8)
import qualified Data.Text as T (concat)
import Data.Typeable (Typeable)
import GHC.Generics
import Pipes
import Pipes.Aeson
import Pipes.Parse
import qualified Pipes.ByteString as P
import Crypto.Noise.Handshake
import Crypto.Noise.HandshakePatterns
import Crypto.Noise.Cipher
import Crypto.Noise.Cipher.ChaChaPoly1305
import Crypto.Noise.Curve
import Crypto.Noise.Curve.Curve25519
import Crypto.Noise.Hash.SHA256
import Pipes.Noise
data HandshakeKeys =
HandshakeKeys { psk :: Maybe Plaintext
, initStatic :: KeyPair Curve25519
, respStatic :: PublicKey Curve25519
, respEphemeral :: PublicKey Curve25519
}
data HandshakeException = HandshakeFailed
| InvalidHandshakeType Text
| Base64DecodingFailure String
deriving (Show, Typeable)
instance Exception HandshakeException
data HandshakeType = NoiseNN
| NoiseKN
| NoiseNK
| NoiseKK
| NoiseNE
| NoiseKE
| NoiseNX
| NoiseKX
| NoiseXN
| NoiseIN
| NoiseXK
| NoiseIK
| NoiseXE
| NoiseIE
| NoiseXX
| NoiseIX
instance ToJSON HandshakeType where
toJSON (NoiseNN) = String . makeHSN $ "NN"
toJSON (NoiseKN) = String . makeHSN $ "KN"
toJSON (NoiseNK) = String . makeHSN $ "NK"
toJSON (NoiseKK) = String . makeHSN $ "KK"
toJSON (NoiseNE) = String . makeHSN $ "NE"
toJSON (NoiseKE) = String . makeHSN $ "KE"
toJSON (NoiseNX) = String . makeHSN $ "NX"
toJSON (NoiseKX) = String . makeHSN $ "KX"
toJSON (NoiseXN) = String . makeHSN $ "XN"
toJSON (NoiseIN) = String . makeHSN $ "IN"
toJSON (NoiseXK) = String . makeHSN $ "XK"
toJSON (NoiseIK) = String . makeHSN $ "IK"
toJSON (NoiseXE) = String . makeHSN $ "XE"
toJSON (NoiseIE) = String . makeHSN $ "IE"
toJSON (NoiseXX) = String . makeHSN $ "XX"
toJSON (NoiseIX) = String . makeHSN $ "IX"
data InitialMessage =
InitialMessage { handshakeType :: HandshakeType
} deriving (Generic)
instance ToJSON InitialMessage
newtype HandshakeMessage = HandshakeMessage ByteString
instance FromJSON HandshakeMessage where
parseJSON = withObject "handshake data" $
\o -> pure
. either
(throw . Base64DecodingFailure)
HandshakeMessage
. B64.decode
. encodeUtf8
=<< (o .: "handshakeData")
instance ToJSON HandshakeMessage where
toJSON (HandshakeMessage hm) =
object [ "handshakeData" .= encodedData ]
where
encodedData = decodeUtf8 . B64.encode $ hm
newtype Message = Message ByteString
instance FromJSON Message where
parseJSON = withObject "message" $
\o -> pure
. either
(throw . Base64DecodingFailure)
Message
. B64.decode
. encodeUtf8
=<< (o .: "message")
instance ToJSON Message where
toJSON (Message m) =
object [ "message" .= encodedData ]
where
encodedData = decodeUtf8 . B64.encode $ m
type ClientReceiver = Producer' ByteString IO ()
type ClientSender = Consumer' ByteString IO ()
makeHSN :: Text -> Text
makeHSN ht = T.concat ["Noise_", ht, "_25519_ChaChaPoly_SHA256"]
processHandshake :: HandshakeKeys
-> (ClientSender, ClientReceiver)
-> HandshakeType
-> IO ()
processHandshake hks (cs, cr) ht = do
csmv <- newEmptyMVar
let im = InitialMessage ht
imo = case toJSON im of
(Object o) -> o
_ -> undefined
runEffect $ encodeObject imo >-> cs
runEffect $ cr >-> deserializeHM >-> mkHandshakePipe ht hks csmv >-> serializeHM >-> cs
putStrLn "Handshake complete"
race_ (runEffect (P.stdin >-> messageEncryptPipe csmv >-> serializeM >-> cs))
(runEffect (cr >-> deserializeM >-> messageDecryptPipe csmv >-> P.stdout))
deserializeHM :: Pipe ByteString ByteString IO ()
deserializeHM = parseForever_ decode >-> grabResult
where
grabResult = do
mer <- await
case mer of
Left e -> lift $ throwIO e
Right (HandshakeMessage r) -> yield r
grabResult
serializeHM :: Pipe ByteString ByteString IO ()
serializeHM = encodeResult >-> for cat encodeObject
where
encodeResult = do
hm <- await
case toJSON . HandshakeMessage $ hm of
(Object o) -> yield o
_ -> undefined
encodeResult
deserializeM :: Pipe ByteString ByteString IO ()
deserializeM = parseForever_ decode >-> grabResult
where
grabResult = do
mer <- await
case mer of
Left e -> lift $ throwIO e
Right (Message r) -> yield r
grabResult
serializeM :: Pipe ByteString ByteString IO ()
serializeM = encodeResult >-> for cat encodeObject
where
encodeResult = do
m <- await
case toJSON . Message $ m of
(Object o) -> yield o
_ -> undefined
encodeResult
mkHandshakePipe :: HandshakeType
-> HandshakeKeys
-> MVar (CipherStatePair ChaChaPoly1305)
-> HandshakePipe IO ()
mkHandshakePipe ht hks csmv =
case ht of
NoiseNN -> noiseNNIPipe (noiseNNIHS hks) csmv
NoiseKN -> noiseKNIPipe (noiseKNIHS hks) csmv
NoiseNK -> noiseNKIPipe (noiseNKIHS hks) csmv
NoiseKK -> noiseKKIPipe (noiseKKIHS hks) csmv
NoiseNE -> noiseNEIPipe (noiseNEIHS hks) csmv
NoiseKE -> noiseKEIPipe (noiseKEIHS hks) csmv
NoiseNX -> noiseNXIPipe (noiseNXIHS hks) csmv
NoiseKX -> noiseKXIPipe (noiseKXIHS hks) csmv
NoiseXN -> noiseXNIPipe (noiseXNIHS hks) csmv
NoiseIN -> noiseINIPipe (noiseINIHS hks) csmv
NoiseXK -> noiseXKIPipe (noiseXKIHS hks) csmv
NoiseIK -> noiseIKIPipe (noiseIKIHS hks) csmv
NoiseXE -> noiseXEIPipe (noiseXEIHS hks) csmv
NoiseIE -> noiseIEIPipe (noiseIEIHS hks) csmv
NoiseXX -> noiseXXIPipe (noiseXXIHS hks) csmv
NoiseIX -> noiseIXIPipe (noiseIXIHS hks) csmv
noiseNNIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseNNIHS HandshakeKeys{..} =
handshakeState
noiseNNI
""
psk
Nothing
Nothing
Nothing
Nothing
noiseKNIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseKNIHS HandshakeKeys{..} =
handshakeState
noiseKNI
""
psk
(Just initStatic)
Nothing
Nothing
Nothing
noiseNKIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseNKIHS HandshakeKeys{..} =
handshakeState
noiseNKI
""
psk
Nothing
Nothing
(Just respStatic)
Nothing
noiseKKIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseKKIHS HandshakeKeys{..} =
handshakeState
noiseKKI
""
psk
(Just initStatic)
Nothing
(Just respStatic)
Nothing
noiseNEIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseNEIHS HandshakeKeys{..} =
handshakeState
noiseNEI
""
psk
Nothing
Nothing
(Just respStatic)
(Just respEphemeral)
noiseKEIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseKEIHS HandshakeKeys{..} =
handshakeState
noiseKEI
""
psk
(Just initStatic)
Nothing
(Just respStatic)
(Just respEphemeral)
noiseNXIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseNXIHS HandshakeKeys{..} =
handshakeState
noiseNXI
""
psk
Nothing
Nothing
Nothing
Nothing
noiseKXIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseKXIHS HandshakeKeys{..} =
handshakeState
noiseKXI
""
psk
(Just initStatic)
Nothing
(Just respStatic)
Nothing
noiseXNIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseXNIHS HandshakeKeys{..} =
handshakeState
noiseXNI
""
psk
(Just initStatic)
Nothing
Nothing
Nothing
noiseINIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseINIHS HandshakeKeys{..} =
handshakeState
noiseINI
""
psk
(Just initStatic)
Nothing
Nothing
Nothing
noiseXKIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseXKIHS HandshakeKeys{..} =
handshakeState
noiseXKI
""
psk
(Just initStatic)
Nothing
(Just respStatic)
Nothing
noiseIKIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseIKIHS HandshakeKeys{..} =
handshakeState
noiseIKI
""
psk
(Just initStatic)
Nothing
(Just respStatic)
Nothing
noiseXEIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseXEIHS HandshakeKeys{..} =
handshakeState
noiseXEI
""
psk
(Just initStatic)
Nothing
(Just respStatic)
(Just respEphemeral)
noiseIEIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseIEIHS HandshakeKeys{..} =
handshakeState
noiseIEI
""
psk
(Just initStatic)
Nothing
(Just respStatic)
(Just respEphemeral)
noiseXXIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseXXIHS HandshakeKeys{..} =
handshakeState
noiseXXI
""
psk
(Just initStatic)
Nothing
Nothing
Nothing
noiseIXIHS :: HandshakeKeys
-> HandshakeState ChaChaPoly1305 Curve25519 SHA256
noiseIXIHS HandshakeKeys{..} =
handshakeState
noiseIXI
""
psk
(Just initStatic)
Nothing
Nothing
Nothing