packages feed

morley-0.6.0: test/Test/Tezos/Crypto.hs

-- | Tests for 'Tezos.Crypto'.

module Test.Tezos.Crypto
  ( test_Roundtrip
  , test_Signing
  , unit_Key_Hashing
  ) where

import Fmt (pretty)
import Test.Hspec (Expectation, shouldSatisfy)
import Test.HUnit (Assertion, (@?=))
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase)

import Tezos.Crypto

import Test.Util.QuickCheck (aesonRoundtrip, roundtripTestSTB)

test_Roundtrip :: [TestTree]
test_Roundtrip =
  [ testGroup "parse . format ≡ pure"
    [ roundtripTestSTB formatPublicKey parsePublicKey
    , roundtripTestSTB formatSignature parseSignature
    , roundtripTestSTB formatKeyHash parseKeyHash
    ]
  , testGroup "JSON encoding/deconding"
    [ aesonRoundtrip @PublicKey
    , aesonRoundtrip @Signature
    , aesonRoundtrip @KeyHash
    ]
  ]

----------------------------------------------------------------------------
-- Signing
----------------------------------------------------------------------------

test_Signing :: [TestTree]
test_Signing =
  [ testGroup "Formatting"
      [ testGroup "parsePublicKey"
        [ testCase "Successfully parses valid sample data" $
          mapM_ (parsePublicKeySample . sdPublicKey) sampleSignatures
        , testCase "Fails to parse invalid data" $ do
            parsePublicKeyInvalid ""
            parsePublicKeyInvalid "aaa"

            parsePublicKeyInvalid
              "edpkuwTWKgQNnhR5v17H2DYHbfcxYepARyrPGbf1tbMoGQAj8Ljr3v"
            parsePublicKeyInvalid
              "edsigtrs8bK7vNfiR4Kd9dWasVa1bAWaQSu2ipnmLGZuwQa8ktCEMYVKqbWsbJ7zTS8dgYT9tiSUKorWCPFHosL5zPsiDwBQ6vb"

            parsePublicKeyInvalid
              "sppk7Ze7NMs6EHF2uB8qq8GrEgJvE9PWYkUijN3LcesafzQuGyniHBd"
            parsePublicKeyInvalid
              "spsig1Ng2bs4PXCbjaFGuojk9K5Pt3CkfbUZyHLLrBxHSmTqrUUxQggi4yJBit3Ljqnqr61UpdTewTLiu4schSCfZvaRwu412oZ"

            parsePublicKeyInvalid
              "p2pk68C6tJr7pNLvgBH63K3hBVoztCPCA36zcWhXFUGywQJTjYbfpxk"
            parsePublicKeyInvalid
              "p2sigRmXDp38VNVaEQH28LYukfLPn8QB5hPEberhvQrrUpRscDZJrrApbRh2u46PTVTwKXjxTLKNN9dyLhPQU6U6jWPGxe4d9v"
        ]
      , testGroup "parseSignature"
        [ testCase "Successfully parses valid sample data" $
          mapM_ (parseSignatureSample . sdSignature) sampleSignatures
        , testCase "Fails to parse invalid data" $ do
            parseSignatureInvalid ""
            parseSignatureInvalid "bbb"

            parseSignatureInvalid
              "edpkuwTWKgQNnhR5v17H2DYHbfcxYepARyrPGbf1tbMoGQAj8Ljr3V"
            parseSignatureInvalid
              "edsigtrs8bK7vNfiR4Kd9dWasVa1bAWaQSu2ipnmLGZuwQa8ktCEMYVKqbWsbJ7zTS8dgYT9tiSUKorWCPFHosL5zPsiDwBQ6vB"

            parseSignatureInvalid
              "sppk7cdA7Afj8MvuBFrP6KsTLfbM5DtH9GwYaRZwCf5tBVCz6UKGQFR"
            parseSignatureInvalid
              "spsig1Ng2bs4PXCbjaFGuojk9K5Pt3ckfbUZyHLLrBxHSmTqrUUxQggi4yJBit3Ljqnqr61UpdTewTLiu4schSCfZvaRwu412oZ"

            parseSignatureInvalid
              "p2pk68C6tJr7pNLvgBH63K3hBVoztCPCA36zcWhXFUGywQJTjYBfpxk"
            parseSignatureInvalid
              "p2sigRmXDp38VNVaEQH28LYukfLPn8QB5hPEberhvQrrUpRscDZJrrApbrh2u46PTVTwKXjxTLKNN9dyLhPQU6U6jWPGxe4d9v"
        ]
      ]
  , testCase "checkSignature" $ mapM_ checkSignatureSample sampleSignatures
  ]

data SignatureData = SignatureData
  { sdPublicKey :: Text
  , sdBytes :: ByteString
  , sdSignature :: Text
  , sdValid :: Bool
  }

-- These signatures have been produced by `tezos-client`.
sampleSignatures :: [SignatureData]
sampleSignatures =
  [ SignatureData
    { sdPublicKey = "edpkuwTWKgQNnhR5v17H2DYHbfcxYepARyrPGbf1tbMoGQAj8Ljr3V"
    , sdBytes = "\0"
    , sdSignature = "edsigtrs8bK7vNfiR4Kd9dWasVa1bAWaQSu2ipnmLGZuwQa8ktCEMYVKqbWsbJ7zTS8dgYT9tiSUKorWCPFHosL5zPsiDwBQ6vb"
    , sdValid = True
    }
  , SignatureData
    { sdPublicKey = "edpkupH22qrz1sNQt5HSvWfRJFfyJ9dhNbZLptE6GR4JbMoBcACZZH"
    , sdBytes = "\0\0"
    , sdSignature = "edsigtj8LhbJ2B3qhZvqzA49raG65dydFcWZW9b9L7ntF3bb29zxaBFFL8SM1jeBUY66hG122znyVA4wpzLdwxcNZwSK3Szu7iD"
    , sdValid = True
    }
  , SignatureData
    { sdPublicKey = "edpkupH22qrz1sNQt5HSvWfRJFfyJ9dhNbZLptE6GR4JbMoBcACZZH"
    , sdBytes = "kot"
    , sdSignature = "edsigtrs8bK7vNfiR4Kd9dWasVa1bAWaQSu2ipnmLGZuwQa8ktCEMYVKqbWsbJ7zTS8dgYT9tiSUKorWCPFHosL5zPsiDwBQ6vb"
    , sdValid = False
    }
  ]
  <>
  [ SignatureData
    { sdPublicKey = "sppk7cdA7Afj8MvuBFrP6KsTLfbM5DtH9GwYaRZwCf5tBVCz6UKGQFR"
    , sdBytes = "\0"
    , sdSignature = "spsig1Ng2bs4PXCbjaFGuojk9K5Pt3CkfbUZyHLLrBxHSmTqrUUxQggi4yJBit3Ljqnqr61UpdTewTLiu4schSCfZvaRwu412oZ"
    -- TODO (#18): should be valid, but crypto is not implemented yet.
    , sdValid = False
    }
  , SignatureData
    { sdPublicKey = "sppk7Ze7NMs6EHF2uB8qq8GrEgJvE9PWYkUijN3LcesafzQuGyniHBD"
    , sdBytes = "\0\0"
    , sdSignature = "spsig1aP7D9oheiraNuM1NgziMPSPKS1F9kSWyFqkE8WigaeU5Uzb3LwY34F7Y7RsF6sY5ZfUda1NWdrC5V4KEfm9jeU1eniHmy"
    -- TODO (#18): should be valid, but crypto is not implemented yet.
    , sdValid = False
    }
  , SignatureData
    { sdPublicKey = "sppk7Ze7NMs6EHF2uB8qq8GrEgJvE9PWYkUijN3LcesafzQuGyniHBD"
    , sdBytes = "kot"
    , sdSignature = "spsig1PJ9LG9ovbpVJ3CucFWL7iBaQZjqEWMvppgLjYiiSwzcxpuUqHr2BUVZDUwkmZKzMNDWJdgtyhYiicz197TbhS4LPpnxDY"
    , sdValid = False
    }
  ]
  <>
  -- TODO (#18): add valid samples for P256
  [ SignatureData
    { sdPublicKey = "p2pk68C6tJr7pNLvgBH63K3hBVoztCPCA36zcWhXFUGywQJTjYBfpxk"
    , sdBytes = "kot"
    , sdSignature = "p2sigRmXDp38VNVaEQH28LYukfLPn8QB5hPEberhvQrrUpRscDZJrrApbRh2u46PTVTwKXjxTLKNN9dyLhPQU6U6jWPGxe4d9v"
    , sdValid = False
    }
  ]

parsePublicKeySample :: Text -> Expectation
parsePublicKeySample publicKeyText =
  parsePublicKey publicKeyText `shouldSatisfy` isRight

parsePublicKeyInvalid :: Text -> Expectation
parsePublicKeyInvalid invalidPublicKeyText =
  parsePublicKey invalidPublicKeyText `shouldSatisfy` isLeft

parseSignatureSample :: Text -> Expectation
parseSignatureSample publicKeyText =
  parseSignature publicKeyText `shouldSatisfy` isRight

parseSignatureInvalid :: Text -> Expectation
parseSignatureInvalid invalidSignatureText =
  parseSignature invalidSignatureText `shouldSatisfy` isLeft

checkSignatureSample :: SignatureData -> Assertion
checkSignatureSample sd =
  checkSignature publicKey signature (sdBytes sd) @?= sdValid sd
  where
    publicKey = partialParse parsePublicKey (sdPublicKey sd)
    signature = partialParse parseSignature (sdSignature sd)

----------------------------------------------------------------------------
-- Key hashing
----------------------------------------------------------------------------

unit_Key_Hashing :: Assertion
unit_Key_Hashing = mapM_ hashKeySample sampleKeyHashes

sampleKeyHashes :: [(Text, Text)]
sampleKeyHashes =
  [ ( "edpkupH22qrz1sNQt5HSvWfRJFfyJ9dhNbZLptE6GR4JbMoBcACZZH"
    , "tz1NaZzLvdDBLfV2LWC6F4SJfNV2jHdZJXkJ"
    )
  , ( "edpkuwTWKgQNnhR5v17H2DYHbfcxYepARyrPGbf1tbMoGQAj8Ljr3V"
    , "tz1Yz3VPaCNB5FjhdEVnSoN8Xv3ZM8g2LYhw"
    )
  , ( "sppk7cdA7Afj8MvuBFrP6KsTLfbM5DtH9GwYaRZwCf5tBVCz6UKGQFR"
    , "tz2EfqCbLmpfv7mNiLcMmhxAwdgHtPTcwR4W"
    )
  , ( "sppk7Ze7NMs6EHF2uB8qq8GrEgJvE9PWYkUijN3LcesafzQuGyniHBD"
    , "tz2Darj3LyQzekU98ZK8diHvuyn1YYjcHpc6"
    )
  , ( "p2pk67K1dwkDFPB63RZU5H3SoMCvmJdKZDZszc7U4FiGKN2YypKdDCB"
    , "tz3S7wbUwQV581kroR81fYbgDBskFicZ6czW"
    )
  , ( "p2pk68C6tJr7pNLvgBH63K3hBVoztCPCA36zcWhXFUGywQJTjYBfpxk"
    , "tz3QEbmdCdsMcnUo2rNjXdbKwg5tyack3goN"
    )
  ]

hashKeySample :: (Text, Text) -> Assertion
hashKeySample (pkText, keyHashText) = hashKey pk @?= keyHash
  where
    pk = partialParse parsePublicKey pkText
    keyHash = partialParse parseKeyHash keyHashText

----------------------------------------------------------------------------
-- Utils
----------------------------------------------------------------------------

-- If passed textual data is invalid, it's a programmer mistake.
partialParse :: (Text -> Either CryptoParseError a) -> Text -> a
partialParse parse = either (error . pretty) id . parse