packages feed

pipes-cacophony-0.1.0: tests/Handshake.hs

{-# LANGUAGE OverloadedStrings, RankNTypes #-}
module Handshake where

import Imports
import Instances()
import HandshakeStates

import Control.Concurrent.Async (concurrently)
import Control.Concurrent.MVar  (MVar, newEmptyMVar, takeMVar,
                                 putMVar, newMVar)
import Control.Monad            (forever)
import Data.ByteString          (ByteString)

import Crypto.Noise.Cipher.ChaChaPoly1305
import Crypto.Noise.Curve
import Crypto.Noise.Curve.Curve25519
import Crypto.Noise.Types

import Pipes hiding (Proxy)
import Pipes.Noise

is :: KeyPair Curve25519
is = curveBytesToPair . bsToSB' $ "I\f\232\218A\210\230\147\FS\222\167\v}l\243!\168.\ESC\t\SYN\"\169\179A`\DC28\211\169tC"

rs :: KeyPair Curve25519
rs = curveBytesToPair . bsToSB' $ "\ETB\157\&7\DC2\252\NUL\148\172\148\133\218\207\&8\221y\144\209\168FX\224Ser_\178|\153.\FSg&"

re :: KeyPair Curve25519
re = curveBytesToPair . bsToSB' $ "<\231\151\151\180\217\146\DLEI}\160N\163iKc\162\210Y\168R\213\206&gm\169r\SUB[\\'"

handshakeKeys :: HandshakeKeys
handshakeKeys = HandshakeKeys is rs re

data HandshakeType = NoiseNN
                   | NoiseKN
                   | NoiseNK
                   | NoiseKK
                   | NoiseNE
                   | NoiseKE
                   | NoiseNX
                   | NoiseKX
                   | NoiseXN
                   | NoiseIN
                   | NoiseXK
                   | NoiseIK
                   | NoiseXE
                   | NoiseIE
                   | NoiseXX
                   | NoiseIX
                   | NoiseN
                   | NoiseK
                   | NoiseX
                   deriving (Eq)

mkHandshakePipe :: HandshakeType
                -> HandshakeKeys
                -> MVar (CipherStatePair ChaChaPoly1305)
                -> MVar (CipherStatePair ChaChaPoly1305)
                -> (HandshakePipe IO (), HandshakePipe IO ())
mkHandshakePipe ht hks csmv1 csmv2 =
  case ht of
    NoiseNN -> (noiseNNIPipe noiseNNIHS csmv1,
                noiseNNRPipe noiseNNRHS csmv2)
    NoiseKN -> (noiseKNIPipe (noiseKNIHS hks) csmv1,
                noiseKNRPipe (noiseKNRHS hks) csmv2)
    NoiseNK -> (noiseNKIPipe (noiseNKIHS hks) csmv1,
                noiseNKRPipe (noiseNKRHS hks) csmv2)
    NoiseKK -> (noiseKKIPipe (noiseKKIHS hks) csmv1,
                noiseKKRPipe (noiseKKRHS hks) csmv2)
    NoiseNE -> (noiseNEIPipe (noiseNEIHS hks) csmv1,
                noiseNERPipe (noiseNERHS hks) csmv2)
    NoiseKE -> (noiseKEIPipe (noiseKEIHS hks) csmv1,
                noiseKERPipe (noiseKERHS hks) csmv2)
    NoiseNX -> (noiseNXIPipe (noiseNXIHS hks) csmv1,
                noiseNXRPipe (noiseNXRHS hks) csmv2)
    NoiseKX -> (noiseKXIPipe (noiseKXIHS hks) csmv1,
                noiseKXRPipe (noiseKXRHS hks) csmv2)
    NoiseXN -> (noiseXNIPipe (noiseXNIHS hks) csmv1,
                noiseXNRPipe (noiseXNRHS hks) csmv2)
    NoiseIN -> (noiseINIPipe (noiseINIHS hks) csmv1,
                noiseINRPipe (noiseINRHS hks) csmv2)
    NoiseXK -> (noiseXKIPipe (noiseXKIHS hks) csmv1,
                noiseXKRPipe (noiseXKRHS hks) csmv2)
    NoiseIK -> (noiseIKIPipe (noiseIKIHS hks) csmv1,
                noiseIKRPipe (noiseIKRHS hks) csmv2)
    NoiseXE -> (noiseXEIPipe (noiseXEIHS hks) csmv1,
                noiseXERPipe (noiseXERHS hks) csmv2)
    NoiseIE -> (noiseIEIPipe (noiseIEIHS hks) csmv1,
                noiseIERPipe (noiseIERHS hks) csmv2)
    NoiseXX -> (noiseXXIPipe (noiseXXIHS hks) csmv1,
                noiseXXRPipe (noiseXXRHS hks) csmv2)
    NoiseIX -> (noiseIXIPipe (noiseIXIHS hks) csmv1,
                noiseIXRPipe (noiseIXRHS hks) csmv2)
    NoiseN  -> (noiseNIPipe  (noiseNIHS  hks) csmv1,
                noiseNRPipe  (noiseNRHS  hks) csmv2)
    NoiseK  -> (noiseKIPipe  (noiseKIHS  hks) csmv1,
                noiseKRPipe  (noiseKRHS  hks) csmv2)
    NoiseX  -> (noiseXIPipe  (noiseXIHS  hks) csmv1,
                noiseXRPipe  (noiseXRHS  hks) csmv2)

aggregator :: MVar [ByteString]
           -> Consumer' ByteString IO ()
aggregator mv = forever $ do
  bs <- await
  l <- lift $ takeMVar mv
  lift $ putMVar mv (bs : l)

mVarProducer :: MVar ByteString
             -> Producer' ByteString IO ()
mVarProducer mv = forever $ do
  x <- lift $ takeMVar mv
  yield x

mVarConsumer :: MVar ByteString
             -> Consumer' ByteString IO ()
mVarConsumer mv = forever $ do
  x <- await
  lift $ putMVar mv x

runPipe :: HandshakeType -> Property
runPipe ht = ioProperty $ do
  resultsmv <- newMVar []
  msgmv1    <- newEmptyMVar
  msgmv2    <- newEmptyMVar
  csmv1     <- newEmptyMVar
  csmv2     <- newEmptyMVar

  let (initPipe, respPipe) = mkHandshakePipe ht handshakeKeys csmv1 csmv2

  _ <- concurrently (runEffect (mVarProducer msgmv1 >-> initPipe >-> mVarConsumer msgmv2))
                    (runEffect (mVarProducer msgmv2 >-> respPipe >-> mVarConsumer msgmv1))

  testData <- generate . listOf1 $ arbitrary

  if ht == NoiseN || ht == NoiseK || ht == NoiseX then
    runEffect $
      each testData
      >-> messageEncryptPipe csmv1
      >-> messageDecryptPipe csmv2
      >-> aggregator resultsmv
  else
    runEffect $
      each testData
      >-> messageEncryptPipe csmv1
      >-> messageDecryptPipe csmv2
      >-> messageEncryptPipe csmv2
      >-> messageDecryptPipe csmv1
      >-> aggregator resultsmv

  results <- takeMVar resultsmv
  return $ reverse results === testData

tests :: TestTree
tests = testGroup "Handshakes"
  [ testProperty "Noise_NN" . property . runPipe $ NoiseNN
  , testProperty "Noise_KN" . property . runPipe $ NoiseKN
  , testProperty "Noise_NK" . property . runPipe $ NoiseNK
  , testProperty "Noise_KK" . property . runPipe $ NoiseKK
  , testProperty "Noise_NE" . property . runPipe $ NoiseNE
  , testProperty "Noise_KE" . property . runPipe $ NoiseKE
  , testProperty "Noise_NX" . property . runPipe $ NoiseNX
  , testProperty "Noise_KX" . property . runPipe $ NoiseKX
  , testProperty "Noise_XN" . property . runPipe $ NoiseXN
  , testProperty "Noise_IN" . property . runPipe $ NoiseIN
  , testProperty "Noise_XK" . property . runPipe $ NoiseXK
  , testProperty "Noise_IK" . property . runPipe $ NoiseIK
  , testProperty "Noise_XE" . property . runPipe $ NoiseXE
  , testProperty "Noise_IE" . property . runPipe $ NoiseIE
  , testProperty "Noise_XX" . property . runPipe $ NoiseXX
  , testProperty "Noise_IX" . property . runPipe $ NoiseIX
  , testProperty "Noise_N"  . property . runPipe $ NoiseN
  , testProperty "Noise_K"  . property . runPipe $ NoiseK
  , testProperty "Noise_X"  . property . runPipe $ NoiseX
  ]