{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE Trustworthy #-} module Network.Tox.Crypto.KeySpec where import Test.Hspec import Test.QuickCheck import qualified Crypto.Saltine.Class as Sodium import Data.Binary (Binary) import Data.ByteString (ByteString) import qualified Data.ByteString as ByteString import qualified Data.MessagePack.Result as R import Data.Proxy (Proxy (..)) import Data.Typeable (Typeable) import qualified Network.Tox.Binary as Binary import Network.Tox.Crypto.Key (Key (..)) import qualified Network.Tox.Crypto.Key as Key import Network.Tox.EncodingSpec import qualified Text.Read as Read readMaybe :: String -> Maybe Key.PublicKey readMaybe = Read.readMaybe decodeM :: Monad m => ByteString -> m Key.PublicKey decodeM = Key.decode keyToInteger :: String -> Integer keyToInteger string = Key.keyToInteger (read string :: Key.PublicKey) encodeDecodePublicKey :: Key.PublicKey -> Expectation encodeDecodePublicKey key = Sodium.decode (Sodium.encode key) `shouldBe` Just key localEncodingSpec :: (Typeable a, Read a, Show a, Binary a, Arbitrary a, Eq a) => Proxy a -> Spec localEncodingSpec proxy = describe (Binary.typeName proxy) $ do binarySpec proxy readShowSpec proxy spec :: Spec spec = do -- PublicKey for RPC tests. rpcSpec (Proxy :: Proxy Key.PublicKey) -- All others only local tests. localEncodingSpec (Proxy :: Proxy Key.CombinedKey) localEncodingSpec (Proxy :: Proxy Key.Nonce) localEncodingSpec (Proxy :: Proxy Key.PublicKey) localEncodingSpec (Proxy :: Proxy Key.SecretKey) describe "IsEncoding" $ it "decodes encoded public keys correctly" $ property encodeDecodePublicKey describe "read" $ do it "decodes valid hex string to PublicKey" $ let actual = readMaybe "\"0100000000000000000000000000000000000000000000000000000000000010\"" Just expected = Sodium.decode $ ByteString.pack [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x10] in actual `shouldBe` (Just $ Key expected) it "decodes empty string to Nothing" $ do let actual = readMaybe "" actual `shouldBe` Nothing case decodeM ByteString.empty of R.Failure msg -> msg `shouldStartWith` "unable to decode" R.Success val -> expectationFailure $ "unexpected success: " ++ show val it "decodes valid hex string of wrong length to Nothing" $ let actual = readMaybe "\"0110\"" in actual `shouldBe` Nothing describe "keyToInteger" $ do it "converts keys to Integer in big endian" $ do keyToInteger "\"fe00000000000000000000000000000000000000000000000000000000000000\"" `shouldBe` 0xfe00000000000000000000000000000000000000000000000000000000000000 keyToInteger "\"00000000000000000000000000000000000000000000000000000000000000fe\"" `shouldBe` 0x00000000000000000000000000000000000000000000000000000000000000fe it "encodes all keys to positive Integers" $ property $ \key -> Key.keyToInteger (key :: Key.PublicKey) `shouldSatisfy` (0 <=)