cleveland-0.1.1: morley-test/Test/Tezos/Crypto.hs
-- SPDX-FileCopyrightText: 2021 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA
-- | Tests for 'Morley.Tezos.Crypto'.
module Test.Tezos.Crypto
( test_Roundtrip
, test_Signing
, test_parseRawSignature
, test_parseRawPublicKey
, test_parseRawKeyHash
, unit_Key_Hashing
, unit_Failing_Key_Hashing
) where
import Fmt (pretty)
import Test.HUnit (Assertion, assertBool, (@?=))
import Test.Hspec (Expectation, shouldSatisfy)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase)
import Hedgehog.Gen.Tezos.Crypto (genKeyHash, genPublicKey, genSecretKey, genSignature)
import Morley.Tezos.Crypto
import Test.Cleveland.Util (fromHex)
import Test.Util.Hedgehog (ShowThroughBuild(..), aesonRoundtrip, roundtripTreeSTB)
test_Roundtrip :: [TestTree]
test_Roundtrip =
[ testGroup "parse . format ≡ pure"
[ roundtripTreeSTB genPublicKey formatPublicKey parsePublicKey
, roundtripTreeSTB genSignature formatSignature parseSignature
, roundtripTreeSTB genKeyHash formatKeyHash parseKeyHash
, roundtripTreeSTB genSecretKey formatSecretKey parseSecretKey
]
, testGroup "JSON encoding/deconding"
[ aesonRoundtrip genPublicKey
, aesonRoundtrip genSignature
, aesonRoundtrip genKeyHash
]
]
----------------------------------------------------------------------------
-- 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 "parseSecretKey"
[ testCase "Successfully parses valid sample data" $
mapM_ parseSecretKeySample
[ "spsk1wt9QxrpLg8UCR2S9YqXSP3YaTBCpSx3dnf62pQuw2wj7Sb8uL"
, "p2sk32DYyag6h9GSMmysxsvM9LzWwC5rV1QqZAwivUuJ3zrjSKK1Pj"
, "unencrypted:spsk1wt9QxrpLg8UCR2S9YqXSP3YaTBCpSx3dnf62pQuw2wj7Sb8uL"
, "unencrypted:p2sk32DYyag6h9GSMmysxsvM9LzWwC5rV1QqZAwivUuJ3zrjSKK1Pj"
]
]
, 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
, testCase "checkRawSignature" $ mapM_ checkRawSignatureSample sampleRawSignatures
]
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"
, sdValid = True
}
, SignatureData
{ sdPublicKey = "sppk7Ze7NMs6EHF2uB8qq8GrEgJvE9PWYkUijN3LcesafzQuGyniHBD"
, sdBytes = "\0\0"
, sdSignature = "spsig1aP7D9oheiraNuM1NgziMPSPKS1F9kSWyFqkE8WigaeU5Uzb3LwY34F7Y7RsF6sY5ZfUda1NWdrC5V4KEfm9jeU1eniHmy"
, sdValid = True
}
, SignatureData
{ sdPublicKey = "sppk7Ze7NMs6EHF2uB8qq8GrEgJvE9PWYkUijN3LcesafzQuGyniHBD"
, sdBytes = "kot"
, sdSignature = "spsig1PJ9LG9ovbpVJ3CucFWL7iBaQZjqEWMvppgLjYiiSwzcxpuUqHr2BUVZDUwkmZKzMNDWJdgtyhYiicz197TbhS4LPpnxDY"
, sdValid = False
}
]
<>
[ SignatureData
{ sdPublicKey = "p2pk66qfVMXhFJWhtFDCT6F3JUM3M1iQpfWe4nPZKWcsqsKQtXXHFkQ"
, sdBytes = "\0"
, sdSignature = "p2sigv6HrN6xB5gQDnmKLC2P3ynwiPn4zfUj7CcZD1cepfFzX7xBDWFQu9uoKWbEzVgxCQxrE1J5X6FGYwF2dpoYcjpdPCBhuD"
, sdValid = True
}
, SignatureData
{ sdPublicKey = "p2pk64bybDUtSjSQnsexpzhedhBo4vkoRX4tWfQQbBxKbA58wJqKkT2"
, sdBytes = "\10"
, sdSignature = "p2siggzjojhabur7zZvmNnhkhnU3nYA1ZUR9JSas57RVhNdAmQk6y3hns3F2zPBGsC964PFAE2HC3fbPkcqpVFbjoQQq9dFiZg"
, sdValid = True
}
, SignatureData
{ sdPublicKey = "p2pk64bybDUtSjSQnsexpzhedhBo4vkoRX4tWfQQbBxKbA58wJqKkT2"
, sdBytes = "kot"
, sdSignature = "p2siggzjojhabur7zZvmNnhkhnU3nYA1ZUR9JSas57RVhNdAmQk6y3hns3F2zPBGsC964PFAE2HC3fbPkcqpVFbjoQQq9dFiZg"
, sdValid = False
}
]
sampleRawSignatures :: [SignatureData]
sampleRawSignatures =
[ SignatureData
{ sdPublicKey = "00aad3f16293766169f7db278c5e0e9db4fb82ffe1cbcc35258059617dc0fec082"
, sdBytes = "\0"
, sdSignature = "91ac1e7fd668854fc7a40feec4034e42c06c068cce10622c607fda232db34c8cf5d8da83098dd891cd4cb4299b3fa0352ae323ad99b24541e54b91888fdc8201"
, sdValid = True
}
, SignatureData
{ sdPublicKey = "0103b524d0184276467c848ac13557fb0ff8bec5907960f72683f22af430503edfc1"
, sdBytes = "\0"
, sdSignature = "80e4e72ffecf72953789625b1125e9f45f432c14e53a01ec68a1e1b77d60cfe96a97443733ba0f7f42db3a56d7a433df2b4fc0035c05ab92d062f33c5bab0244"
, sdValid = True
}
, SignatureData
{ sdPublicKey = "0202041e5cb7fb3d7bc6fb7b9e94790919a9e76ccc372e6cc9cae925027c08ff95f3"
, sdBytes = "\10"
, sdSignature = "12d25210bb02998516bf6a776e1cd55a06c5fbe3c21afbeef29b99d96305e43263c75a4449906e0f2d79ecc973fff9ce7f8c43fee40b04d07c191f00ee176175"
, sdValid = True
}
]
parsePublicKeySample :: Text -> Expectation
parsePublicKeySample publicKeyText =
parsePublicKey publicKeyText `shouldSatisfy` isRight
parseSecretKeySample :: Text -> Expectation
parseSecretKeySample secretKeyText =
parseSecretKey secretKeyText `shouldSatisfy` isRight
parsePublicKeyInvalid :: Text -> Expectation
parsePublicKeyInvalid invalidPublicKeyText =
parsePublicKey invalidPublicKeyText `shouldSatisfy` isLeft
parseSignatureSample :: Text -> Expectation
parseSignatureSample signatureText =
parseSignature signatureText `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 = unsafe . parsePublicKey $ sdPublicKey sd
signature = unsafe . parseSignature $ sdSignature sd
checkRawSignatureSample :: SignatureData -> Assertion
checkRawSignatureSample sd =
checkSignature publicKey signature (sdBytes sd) @?= sdValid sd
where
publicKey = unsafe . parsePublicKeyRaw . unsafe . fromHex $ sdPublicKey sd
signature = unsafe . parseSignatureRaw . unsafe . fromHex $ sdSignature sd
----------------------------------------------------------------------------
-- Key hashing
----------------------------------------------------------------------------
unit_Key_Hashing :: Assertion
unit_Key_Hashing = mapM_ hashKeySample sampleKeyHashes
unit_Failing_Key_Hashing :: Assertion
unit_Failing_Key_Hashing = assertBool "Parsed key hash of invalid length" $
isLeft $ parseKeyHash "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSxZ"
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 = unsafe $ parsePublicKey pkText
keyHash = unsafe $ parseKeyHash keyHashText
----------------------------------------------------------------------------
-- Parsing raw data
----------------------------------------------------------------------------
test_parseRawSignature :: [TestTree]
test_parseRawSignature =
[ testCase "Successfully parses valid sample signatures in canonical form" $
forM_ samplePairSignatures (\(a, _) -> bimap STB STB (parseSignature a) `shouldSatisfy` isRight)
, testCase "Successfully parses valid sample signatures in raw byte form" $
forM_ samplePairSignatures (\(_, b) -> fmap STB (parseSignatureRaw . unsafe . fromHex $ b) `shouldSatisfy` isRight)
, testCase "Parsed signatures are equivalent in canonical and raw byte form" $
forM_ samplePairSignatures (\(a, b) ->
(STB . unsafe . parseSignatureRaw . unsafe . fromHex $ b)
@?= (STB . unsafe . parseSignature $ a))
, testGroup "Fails to parse invalid signatures:"
[ testCase (toString a)
(parseInvalidSignature a `shouldSatisfy` isLeft) | a <- invalidRawSignatures
]
]
where
parseInvalidSignature :: Text -> Either Text Signature
parseInvalidSignature a = do
bs <- fromHex a
first pretty $ parseSignatureRaw bs
samplePairSignatures :: [(Text, Text)]
samplePairSignatures =
[ ( "edsigtrs8bK7vNfiR4Kd9dWasVa1bAWaQSu2ipnmLGZuwQa8ktCEMYVKqbWsbJ7zTS8dgYT9tiSUKorWCPFHosL5zPsiDwBQ6vb"
, "91ac1e7fd668854fc7a40feec4034e42c06c068cce10622c607fda232db34c8cf5d8da83098dd891cd4cb4299b3fa0352ae323ad99b24541e54b91888fdc8201")
, ( "edsigtj8LhbJ2B3qhZvqzA49raG65dydFcWZW9b9L7ntF3bb29zxaBFFL8SM1jeBUY66hG122znyVA4wpzLdwxcNZwSK3Szu7iD"
, "568722d9942f516ab369e7803b5646775459ef93080842400354cb638800861bb051509fcef5ad6940ba38af3697d09f1be5be829b8f6f50610ef28606755d0e")
, ( "spsig1PJ9LG9ovbpVJ3CucFWL7iBaQZjqEWMvppgLjYiiSwzcxpuUqHr2BUVZDUwkmZKzMNDWJdgtyhYiicz197TbhS4LPpnxDY"
, "85a75c026ae48f5e8d4891e30da2eef1174987f94401881e216c9e217d14247908ea0303579c1df08d70364e7309b410adb4f71787464df001bf7699a0749c8f")
, ( "p2sigRmXDp38VNVaEQH28LYukfLPn8QB5hPEberhvQrrUpRscDZJrrApbRh2u46PTVTwKXjxTLKNN9dyLhPQU6U6jWPGxe4d9v"
, "22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
]
invalidRawSignatures :: [Text]
invalidRawSignatures =
[ ""
, "ac1e7fd668854fc7a40feec4034e42c06c068cce10622c607fda232db34c8cf5d8da83098dd891cd4cb4299b3fa0352ae323ad99b24541e54b91888fdc8201"
, "a91ac1e7fd668854fc7a40feec4034e42c06c068cce10622c607fda232db34c8cf5d8da83098dd891cd4cb4299b3fa0352ae323ad99b24541e54b91888fdc8201"
, "222222222222222222222222222222222222222222222222222222222222222222222222222"
]
test_parseRawPublicKey :: [TestTree]
test_parseRawPublicKey =
[ testCase "Successfully parses valid sample public keys in canonical form" $
forM_ sampleRawPublicKeys (\(a, _) -> bimap STB STB (parsePublicKey a) `shouldSatisfy` isRight)
, testCase "Successfully parses valid sample public keys in raw byte form" $
forM_ sampleRawPublicKeys (\(_, b) -> fmap STB (parsePublicKeyRaw . unsafe . fromHex $ b) `shouldSatisfy` isRight)
, testCase "Parsed public keys are equivalent in canonical and raw byte form" $
forM_ sampleRawPublicKeys (\(a, b) -> (STB . unsafe . parsePublicKeyRaw . unsafe . fromHex $ b) @?= (STB . unsafe . parsePublicKey $ a))
, testGroup "Fails to parse invalid public keys:"
[ testCase (toString a)
(parseInvalidKey a `shouldSatisfy` isLeft) | a <- invalidRawPublicKeys
]
]
where
parseInvalidKey :: Text -> Either Text PublicKey
parseInvalidKey a = do
bs <- fromHex a
first pretty $ parsePublicKeyRaw bs
sampleRawPublicKeys :: [(Text, Text)]
sampleRawPublicKeys =
[ ( "edpkupH22qrz1sNQt5HSvWfRJFfyJ9dhNbZLptE6GR4JbMoBcACZZH"
, "009a85e0f3f47852869ae667adc3b03a20fa9f324d046174dff6834e7d1fab0e8d")
, ( "edpkupH22qrz1sNQt5HSvWfRJFfyJ9dhNbZLptE6GR4JbMoBcACZZH"
, "009a85e0f3f47852869ae667adc3b03a20fa9f324d046174dff6834e7d1fab0e8d")
, ( "sppk7cdA7Afj8MvuBFrP6KsTLfbM5DtH9GwYaRZwCf5tBVCz6UKGQFR"
, "0103b524d0184276467c848ac13557fb0ff8bec5907960f72683f22af430503edfc1")
, ( "p2pk67K1dwkDFPB63RZU5H3SoMCvmJdKZDZszc7U4FiGKN2YypKdDCB"
, "020368afbb09255d849813712108a4144237dc1fdd5bb74e68335f4c68c12c1e5723")
]
invalidRawPublicKeys :: [Text]
invalidRawPublicKeys =
[ ""
, "009a85e0f3f47852869ae667adc3b03a20fa9f324d046174dff6834e7d1fab0e8"
, "0009a85e0f3f47852869ae667adc3b03a20fa9f324d046174dff6834e7d1fab0e8d"
]
test_parseRawKeyHash :: [TestTree]
test_parseRawKeyHash =
[ testCase "Successfully parses valid sample key hashes in canonical form" $
forM_ sampleRawKeyHashes (\(a, _) -> bimap STB STB (parseKeyHash a) `shouldSatisfy` isRight)
, testCase "Successfully parses valid sample key hashes in raw byte form" $
forM_ sampleRawKeyHashes (\(_, b) -> fmap STB (parseKeyHashRaw . unsafe . fromHex $ b) `shouldSatisfy` isRight)
, testCase "Parsed key hashes are equivalent in canonical and raw byte form" $
forM_ sampleRawKeyHashes (\(a, b) -> (STB . unsafe . parseKeyHashRaw . unsafe . fromHex $ b) @?= (STB . unsafe . parseKeyHash $ a))
, testGroup "Fails to parse invalid key hashes:"
[ testCase (toString a)
(parseInvalidKey a `shouldSatisfy` isLeft) | a <- invalidRawKeyHashes
]
]
where
parseInvalidKey :: Text -> Either Text KeyHash
parseInvalidKey a = do
bs <- fromHex a
first pretty $ parseKeyHashRaw bs
sampleRawKeyHashes :: [(Text, Text)]
sampleRawKeyHashes =
[ ("tz1NaZzLvdDBLfV2LWC6F4SJfNV2jHdZJXkJ"
,"002040d6c2ede06fbd1cbc16d51c3085dc2e43e396")
, ("tz1Yz3VPaCNB5FjhdEVnSoN8Xv3ZM8g2LYhw"
,"0092629ed0afa9cd42835ce09ee2623c1efa0b590d")
, ("tz2EfqCbLmpfv7mNiLcMmhxAwdgHtPTcwR4W"
,"0145b5e7d31bf6612e61ebfa7a6d929ce7800a55a4")
, ("tz2Darj3LyQzekU98ZK8diHvuyn1YYjcHpc6"
,"0139ccf913874519b2d20917bf5f2de537420d2726")
]
invalidRawKeyHashes :: [Text]
invalidRawKeyHashes =
[ ""
, "15002040d6c2ede06fbd1cbc16d51c3085dc2e43e396"
, "100000015002040d6c2ede06fbd1cbc16d51c3085dc2e43e396"
, "1100000015002040d6c2ede06fbd1cbc16d51c3085dc2e43e396"
]