-- | 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