{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

module Test.Cardano.Crypto.Wallet.SignSpec (tests) where

import qualified Data.ByteString as BS
import Foreign.C.Types (CInt (..), CSize (..))
import Foreign.Ptr (Ptr, castPtr)
import System.IO.Unsafe (unsafePerformIO)
import Test.Hspec

import Cardano.Crypto.WalletHD.Encrypted

foreign import ccall "cardano_crypto_ed25519_sign_open"
  c_ed25519_sign_open ::
    Ptr a ->
    CSize ->
    Ptr a ->
    Ptr a ->
    IO CInt

verifySignature :: BS.ByteString -> BS.ByteString -> Signature -> Bool
verifySignature :: ByteString -> ByteString -> Signature -> Bool
verifySignature ByteString
pub ByteString
msg (Signature ByteString
sig) = IO Bool -> Bool
forall a. IO a -> a
unsafePerformIO (IO Bool -> Bool) -> IO Bool -> Bool
forall a b. (a -> b) -> a -> b
$
  ByteString -> (CStringLen -> IO Bool) -> IO Bool
forall a. ByteString -> (CStringLen -> IO a) -> IO a
BS.useAsCStringLen ByteString
msg ((CStringLen -> IO Bool) -> IO Bool)
-> (CStringLen -> IO Bool) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \(Ptr CChar
mp, Int
ml) ->
    ByteString -> (Ptr CChar -> IO Bool) -> IO Bool
forall a. ByteString -> (Ptr CChar -> IO a) -> IO a
BS.useAsCString ByteString
pub ((Ptr CChar -> IO Bool) -> IO Bool)
-> (Ptr CChar -> IO Bool) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \Ptr CChar
pkp ->
      ByteString -> (Ptr CChar -> IO Bool) -> IO Bool
forall a. ByteString -> (Ptr CChar -> IO a) -> IO a
BS.useAsCString ByteString
sig ((Ptr CChar -> IO Bool) -> IO Bool)
-> (Ptr CChar -> IO Bool) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \Ptr CChar
sigp ->
        (CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
0)
          (CInt -> Bool) -> IO CInt -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr Any -> CSize -> Ptr Any -> Ptr Any -> IO CInt
forall a. Ptr a -> CSize -> Ptr a -> Ptr a -> IO CInt
c_ed25519_sign_open
            (Ptr CChar -> Ptr Any
forall a b. Ptr a -> Ptr b
castPtr Ptr CChar
mp)
            (forall a b. (Integral a, Num b) => a -> b
fromIntegral @Int @CSize Int
ml)
            (Ptr CChar -> Ptr Any
forall a b. Ptr a -> Ptr b
castPtr Ptr CChar
pkp)
            (Ptr CChar -> Ptr Any
forall a b. Ptr a -> Ptr b
castPtr Ptr CChar
sigp)

testSeed :: BS.ByteString
testSeed :: ByteString
testSeed = Int -> Word8 -> ByteString
BS.replicate Int
32 Word8
0x02

testCC :: BS.ByteString
testCC :: ByteString
testCC = Int -> Word8 -> ByteString
BS.replicate Int
32 Word8
0xAB

testPass :: BS.ByteString
testPass :: ByteString
testPass = Int -> Word8 -> ByteString
BS.replicate Int
32 Word8
0x42

testMsg :: BS.ByteString
testMsg :: ByteString
testMsg = ByteString
"cardano transaction body hash"

tests :: Spec
tests :: Spec
tests = [Char] -> Spec -> Spec
forall a. HasCallStack => [Char] -> SpecWith a -> SpecWith a
describe [Char]
"Sign" (Spec -> Spec) -> Spec -> Spec
forall a b. (a -> b) -> a -> b
$ do
  [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"encryptedSign produces a verifiable signature" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
    Either XPrvError EncryptedKey
res <- ByteString
-> ByteString -> ByteString -> IO (Either XPrvError EncryptedKey)
forall passphrase secret cc.
(ByteArrayAccess passphrase, ByteArrayAccess secret,
 ByteArrayAccess cc) =>
secret -> passphrase -> cc -> IO (Either XPrvError EncryptedKey)
encryptedCreate ByteString
testSeed ByteString
testPass ByteString
testCC
    case Either XPrvError EncryptedKey
res of
      Left XPrvError
err -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"encryptedCreate failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ XPrvError -> [Char]
forall a. Show a => a -> [Char]
show XPrvError
err
      Right EncryptedKey
key -> do
        Either XPrvError Signature
res' <- EncryptedKey
-> ByteString -> ByteString -> IO (Either XPrvError Signature)
forall passphrase msg.
(ByteArrayAccess passphrase, ByteArrayAccess msg) =>
EncryptedKey
-> passphrase -> msg -> IO (Either XPrvError Signature)
encryptedSign EncryptedKey
key ByteString
testPass ByteString
testMsg
        case Either XPrvError Signature
res' of
          Left XPrvError
err -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"encryptedSign failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ XPrvError -> [Char]
forall a. Show a => a -> [Char]
show XPrvError
err
          Right Signature
sig -> ByteString -> ByteString -> Signature -> Bool
verifySignature (EncryptedKey -> ByteString
encryptedPublic EncryptedKey
key) ByteString
testMsg Signature
sig Bool -> Bool -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` Bool
True

  [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"encryptedSign fails with wrong passphrase" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
    Either XPrvError EncryptedKey
res <- ByteString
-> ByteString -> ByteString -> IO (Either XPrvError EncryptedKey)
forall passphrase secret cc.
(ByteArrayAccess passphrase, ByteArrayAccess secret,
 ByteArrayAccess cc) =>
secret -> passphrase -> cc -> IO (Either XPrvError EncryptedKey)
encryptedCreate ByteString
testSeed ByteString
testPass ByteString
testCC
    case Either XPrvError EncryptedKey
res of
      Left XPrvError
err -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"encryptedCreate failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ XPrvError -> [Char]
forall a. Show a => a -> [Char]
show XPrvError
err
      Right EncryptedKey
key -> do
        Either XPrvError Signature
r <- EncryptedKey
-> ByteString -> ByteString -> IO (Either XPrvError Signature)
forall passphrase msg.
(ByteArrayAccess passphrase, ByteArrayAccess msg) =>
EncryptedKey
-> passphrase -> msg -> IO (Either XPrvError Signature)
encryptedSign EncryptedKey
key (Int -> Word8 -> ByteString
BS.replicate Int
32 Word8
0x00) ByteString
testMsg
        Either XPrvError Signature
r Either XPrvError Signature -> Either XPrvError Signature -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` XPrvError -> Either XPrvError Signature
forall a b. a -> Either a b
Left XPrvError
XPrvAuthenticationFailed

  [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"encryptedSign produces a 64-byte signature" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
    Either XPrvError EncryptedKey
res <- ByteString
-> ByteString -> ByteString -> IO (Either XPrvError EncryptedKey)
forall passphrase secret cc.
(ByteArrayAccess passphrase, ByteArrayAccess secret,
 ByteArrayAccess cc) =>
secret -> passphrase -> cc -> IO (Either XPrvError EncryptedKey)
encryptedCreate ByteString
testSeed ByteString
testPass ByteString
testCC
    case Either XPrvError EncryptedKey
res of
      Left XPrvError
err -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"encryptedCreate failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ XPrvError -> [Char]
forall a. Show a => a -> [Char]
show XPrvError
err
      Right EncryptedKey
key -> do
        Either XPrvError Signature
res' <- EncryptedKey
-> ByteString -> ByteString -> IO (Either XPrvError Signature)
forall passphrase msg.
(ByteArrayAccess passphrase, ByteArrayAccess msg) =>
EncryptedKey
-> passphrase -> msg -> IO (Either XPrvError Signature)
encryptedSign EncryptedKey
key ByteString
testPass ByteString
testMsg
        case Either XPrvError Signature
res' of
          Left XPrvError
err -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"encryptedSign failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ XPrvError -> [Char]
forall a. Show a => a -> [Char]
show XPrvError
err
          Right (Signature ByteString
s) -> ByteString -> Int
BS.length ByteString
s Int -> Int -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` Int
64

  [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"encryptedSign after passphrase change produces a verifiable signature" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
    Either XPrvError EncryptedKey
res <- ByteString
-> ByteString -> ByteString -> IO (Either XPrvError EncryptedKey)
forall passphrase secret cc.
(ByteArrayAccess passphrase, ByteArrayAccess secret,
 ByteArrayAccess cc) =>
secret -> passphrase -> cc -> IO (Either XPrvError EncryptedKey)
encryptedCreate ByteString
testSeed ByteString
testPass ByteString
testCC
    case Either XPrvError EncryptedKey
res of
      Left XPrvError
err -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"encryptedCreate failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ XPrvError -> [Char]
forall a. Show a => a -> [Char]
show XPrvError
err
      Right EncryptedKey
key -> do
        let newPass :: ByteString
newPass = Int -> Word8 -> ByteString
BS.replicate Int
32 Word8
0xFF
        Either XPrvError EncryptedKey
res' <- ByteString
-> ByteString -> EncryptedKey -> IO (Either XPrvError EncryptedKey)
forall oldPassPhrase newPassPhrase.
(ByteArrayAccess oldPassPhrase, ByteArrayAccess newPassPhrase) =>
oldPassPhrase
-> newPassPhrase
-> EncryptedKey
-> IO (Either XPrvError EncryptedKey)
encryptedChangePass ByteString
testPass ByteString
newPass EncryptedKey
key
        case Either XPrvError EncryptedKey
res' of
          Left XPrvError
err -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"encryptedChangePass failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ XPrvError -> [Char]
forall a. Show a => a -> [Char]
show XPrvError
err
          Right EncryptedKey
key' -> do
            Either XPrvError Signature
res'' <- EncryptedKey
-> ByteString -> ByteString -> IO (Either XPrvError Signature)
forall passphrase msg.
(ByteArrayAccess passphrase, ByteArrayAccess msg) =>
EncryptedKey
-> passphrase -> msg -> IO (Either XPrvError Signature)
encryptedSign EncryptedKey
key' ByteString
newPass ByteString
testMsg
            case Either XPrvError Signature
res'' of
              Left XPrvError
err -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"encryptedSign after changePass failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ XPrvError -> [Char]
forall a. Show a => a -> [Char]
show XPrvError
err
              Right Signature
sig -> ByteString -> ByteString -> Signature -> Bool
verifySignature (EncryptedKey -> ByteString
encryptedPublic EncryptedKey
key') ByteString
testMsg Signature
sig Bool -> Bool -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` Bool
True

  [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"signature from original key verifies against public key preserved by passphrase change" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
    Either XPrvError EncryptedKey
res <- ByteString
-> ByteString -> ByteString -> IO (Either XPrvError EncryptedKey)
forall passphrase secret cc.
(ByteArrayAccess passphrase, ByteArrayAccess secret,
 ByteArrayAccess cc) =>
secret -> passphrase -> cc -> IO (Either XPrvError EncryptedKey)
encryptedCreate ByteString
testSeed ByteString
testPass ByteString
testCC
    case Either XPrvError EncryptedKey
res of
      Left XPrvError
err -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"encryptedCreate failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ XPrvError -> [Char]
forall a. Show a => a -> [Char]
show XPrvError
err
      Right EncryptedKey
key -> do
        Either XPrvError Signature
res' <- EncryptedKey
-> ByteString -> ByteString -> IO (Either XPrvError Signature)
forall passphrase msg.
(ByteArrayAccess passphrase, ByteArrayAccess msg) =>
EncryptedKey
-> passphrase -> msg -> IO (Either XPrvError Signature)
encryptedSign EncryptedKey
key ByteString
testPass ByteString
testMsg
        case Either XPrvError Signature
res' of
          Left XPrvError
err -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"encryptedSign failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ XPrvError -> [Char]
forall a. Show a => a -> [Char]
show XPrvError
err
          Right Signature
sig -> do
            let newPass :: ByteString
newPass = Int -> Word8 -> ByteString
BS.replicate Int
32 Word8
0xFF
            Either XPrvError EncryptedKey
res'' <- ByteString
-> ByteString -> EncryptedKey -> IO (Either XPrvError EncryptedKey)
forall oldPassPhrase newPassPhrase.
(ByteArrayAccess oldPassPhrase, ByteArrayAccess newPassPhrase) =>
oldPassPhrase
-> newPassPhrase
-> EncryptedKey
-> IO (Either XPrvError EncryptedKey)
encryptedChangePass ByteString
testPass ByteString
newPass EncryptedKey
key
            case Either XPrvError EncryptedKey
res'' of
              Left XPrvError
err -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"encryptedChangePass failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ XPrvError -> [Char]
forall a. Show a => a -> [Char]
show XPrvError
err
              Right EncryptedKey
key' ->
                ByteString -> ByteString -> Signature -> Bool
verifySignature (EncryptedKey -> ByteString
encryptedPublic EncryptedKey
key') ByteString
testMsg Signature
sig Bool -> Bool -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` Bool
True