packages feed

cacophony-0.3.0: tests/Handshake.hs

{-# LANGUAGE OverloadedStrings #-}
module Handshake where

import Crypto.Noise.Cipher
import Crypto.Noise.Curve
import Crypto.Noise.Handshake
import Crypto.Noise.Hash
import Crypto.Noise.Types

import HandshakeStates
import Imports
import Instances()

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

sampleHSPT :: Plaintext
sampleHSPT = Plaintext . bsToSB' $ "cacophony"

mkHandshakeProp :: HandshakeType
                -> Plaintext
                -> Property
mkHandshakeProp ht =
  case ht of
    NoiseNN -> twoMessage noiseNNIHS noiseNNRHS
    NoiseKN -> twoMessage noiseKNIHS noiseKNRHS
    NoiseNK -> twoMessage noiseNKIHS noiseNKRHS
    NoiseKK -> twoMessage noiseKKIHS noiseKKRHS
    NoiseNE -> twoMessage noiseNEIHS noiseNERHS
    NoiseKE -> twoMessage noiseKEIHS noiseKERHS
    NoiseNX -> twoMessage noiseNXIHS noiseNXRHS
    NoiseKX -> twoMessage noiseKXIHS noiseKXRHS
    NoiseXN -> threeMessage noiseXNIHS noiseXNRHS
    NoiseIN -> twoMessage noiseINIHS noiseINRHS
    NoiseXK -> threeMessage noiseXKIHS noiseXKRHS
    NoiseIK -> twoMessage noiseIKIHS noiseIKRHS
    NoiseXE -> threeMessage noiseXEIHS noiseXERHS
    NoiseIE -> twoMessage noiseIEIHS noiseIERHS
    NoiseXX -> threeMessage noiseXXIHS noiseXXRHS
    NoiseIX -> twoMessage noiseIXIHS noiseIXRHS
    NoiseN  -> oneMessage noiseNIHS noiseNRHS
    NoiseK  -> oneMessage noiseKIHS noiseKRHS
    NoiseX  -> oneMessage noiseXIHS noiseXRHS

oneMessage :: (Cipher c, Curve d, Hash h)
           => HandshakeState c d h
           -> HandshakeState c d h
           -> Plaintext
           -> Property
oneMessage ihs rhs pt = ioProperty $ do
  (aliceToBob1, csAlice1, _) <- writeMessageFinal ihs sampleHSPT
  let (hsptFromBob1, csBob1, _) = readMessageFinal rhs aliceToBob1

  return $ conjoin
    [ (decrypt csBob1 . encrypt csAlice1) pt === pt
    , (decrypt csAlice1 . encrypt csBob1) pt === pt
    , hsptFromBob1 === sampleHSPT
    ]

  where
    encrypt cs p  = fst $ encryptPayload p  cs
    decrypt cs ct = fst $ decryptPayload ct cs

twoMessage :: (Cipher c, Curve d, Hash h)
           => HandshakeState c d h
           -> HandshakeState c d h
           -> Plaintext
           -> Property
twoMessage ihs rhs pt = ioProperty $ do
  (aliceToBob1, ihs') <- writeMessage ihs sampleHSPT
  let (hsptFromAlice1, rhs') = readMessage rhs aliceToBob1

  (bobToAlice1, csBob1, csBob2) <- writeMessageFinal rhs' sampleHSPT
  let (hsptFromBob1, csAlice1, csAlice2) = readMessageFinal ihs' bobToAlice1

  return $ conjoin
    [ (decrypt csBob1 . encrypt csAlice1) pt === pt
    , (decrypt csBob2 . encrypt csAlice2) pt === pt
    , (decrypt csAlice1 . encrypt csBob1) pt === pt
    , (decrypt csAlice2 . encrypt csBob2) pt === pt
    , hsptFromAlice1 === sampleHSPT
    , hsptFromBob1   === sampleHSPT
    ]

  where
    encrypt cs p  = fst $ encryptPayload p  cs
    decrypt cs ct = fst $ decryptPayload ct cs

threeMessage :: (Cipher c, Curve d, Hash h)
             => HandshakeState c d h
             -> HandshakeState c d h
             -> Plaintext
             -> Property
threeMessage ihs rhs pt =
  ioProperty $ do
    (aliceToBob1, ihs') <- writeMessage ihs sampleHSPT
    let (hsptFromAlice1, rhs') = readMessage rhs aliceToBob1

    (bobToAlice1, rhs'') <- writeMessage rhs' sampleHSPT
    let (hsptFromBob1, ihs'') = readMessage ihs' bobToAlice1

    (aliceToBob2, csAlice1, csAlice2) <- writeMessageFinal ihs'' sampleHSPT
    let (hsptFromBob2, csBob1, csBob2) = readMessageFinal rhs'' aliceToBob2

    return $ conjoin
      [ (decrypt csBob1 . encrypt csAlice1) pt === pt
      , (decrypt csBob2 . encrypt csAlice2) pt === pt
      , (decrypt csAlice1 . encrypt csBob1) pt === pt
      , (decrypt csAlice2 . encrypt csBob2) pt === pt
      , hsptFromAlice1 === sampleHSPT
      , hsptFromBob1   === sampleHSPT
      , hsptFromBob2   === sampleHSPT
      ]

    where
      encrypt cs p  = fst $ encryptPayload p  cs
      decrypt cs ct = fst $ decryptPayload ct cs

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