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

import qualified Codec.CBOR.Decoding as CBOR
import qualified Codec.CBOR.Read as CBOR
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import Test.Hspec

import Cardano.Crypto.WalletHD.Encrypted

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

wrongPass :: BS.ByteString
wrongPass :: ByteString
wrongPass = Int -> Word8 -> ByteString
BS.replicate Int
32 Word8
0x00

-- ---------------------------------------------------------------------------
-- Public-key golden vector
--
-- This is the ed25519 public key derived from testSeed via cardano_crypto_ed25519_extend
-- + cardano_crypto_ed25519_publickey.  It is fully deterministic (no randomness).
-- If this changes, the key derivation C code has silently changed.
-- ---------------------------------------------------------------------------

expectedPublicKey :: BS.ByteString
expectedPublicKey :: ByteString
expectedPublicKey =
  [Word8] -> ByteString
BS.pack
    [ Word8
129
    , Word8
57
    , Word8
119
    , Word8
14
    , Word8
168
    , Word8
125
    , Word8
23
    , Word8
95
    , Word8
86
    , Word8
163
    , Word8
84
    , Word8
102
    , Word8
195
    , Word8
76
    , Word8
126
    , Word8
204
    , Word8
203
    , Word8
141
    , Word8
138
    , Word8
145
    , Word8
180
    , Word8
238
    , Word8
55
    , Word8
162
    , Word8
93
    , Word8
246
    , Word8
15
    , Word8
91
    , Word8
143
    , Word8
201
    , Word8
179
    , Word8
148
    ]

tests :: Spec
tests :: Spec
tests = String -> Spec -> Spec
forall a. HasCallStack => String -> SpecWith a -> SpecWith a
describe String
"V2Format" (Spec -> Spec) -> Spec -> Spec
forall a b. (a -> b) -> a -> b
$ do
  String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"encryptedCreate produces EnvelopeV2 format" (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 => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"encryptedCreate failed: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
err
      Right EncryptedKey
key -> EncryptedKey -> XPrvFormat
encryptedKeyFormat EncryptedKey
key XPrvFormat -> XPrvFormat -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` XPrvFormat
EnvelopeV2

  String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"v2 key validates with correct 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 => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"encryptedCreate failed: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
err
      Right EncryptedKey
key -> do
        Either XPrvError ()
r <- EncryptedKey -> ByteString -> IO (Either XPrvError ())
forall passphrase.
ByteArrayAccess passphrase =>
EncryptedKey -> passphrase -> IO (Either XPrvError ())
encryptedValidatePassphrase EncryptedKey
key ByteString
testPass
        Either XPrvError ()
r Either XPrvError () -> Either XPrvError () -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` () -> Either XPrvError ()
forall a b. b -> Either a b
Right ()

  String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"v2 key rejects wrong passphrase with XPrvAuthenticationFailed" (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 => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"encryptedCreate failed: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
err
      Right EncryptedKey
key -> do
        Either XPrvError ()
r <- EncryptedKey -> ByteString -> IO (Either XPrvError ())
forall passphrase.
ByteArrayAccess passphrase =>
EncryptedKey -> passphrase -> IO (Either XPrvError ())
encryptedValidatePassphrase EncryptedKey
key ByteString
wrongPass
        Either XPrvError ()
r Either XPrvError () -> Either XPrvError () -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` XPrvError -> Either XPrvError ()
forall a b. a -> Either a b
Left XPrvError
XPrvAuthenticationFailed

  String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"v2 envelope is a CBOR 9-element array" (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 => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"encryptedCreate failed: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
err
      Right EncryptedKey
key -> do
        let bs :: ByteString
bs = EncryptedKey -> ByteString
unEncryptedKey EncryptedKey
key
        case (forall s. Decoder s Int)
-> ByteString -> Either DeserialiseFailure (ByteString, Int)
forall a.
(forall s. Decoder s a)
-> ByteString -> Either DeserialiseFailure (ByteString, a)
CBOR.deserialiseFromBytes Decoder s Int
forall s. Decoder s Int
CBOR.decodeListLen (ByteString -> ByteString
BL.fromStrict ByteString
bs) of
          Left DeserialiseFailure
e -> HasCallStack => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"CBOR decode failed: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ DeserialiseFailure -> String
forall a. Show a => a -> String
show DeserialiseFailure
e
          Right (ByteString
_, Int
9) -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
          Right (ByteString
_, Int
n) ->
            HasCallStack => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$
              String
"Expected 9-element CBOR array, got: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
n

  String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"public key and chain code in envelope match accessors" (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 => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"encryptedCreate failed: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
err
      Right EncryptedKey
key -> do
        let pub :: ByteString
pub = EncryptedKey -> ByteString
encryptedPublic EncryptedKey
key
            cc :: ByteString
cc = EncryptedKey -> ByteString
encryptedChainCode EncryptedKey
key
        case ByteString -> Either XPrvError EncryptedKey
encryptedKey (EncryptedKey -> ByteString
unEncryptedKey EncryptedKey
key) of
          Left XPrvError
err -> HasCallStack => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"re-parse failed: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
err
          Right EncryptedKey
key' -> do
            EncryptedKey -> ByteString
encryptedPublic EncryptedKey
key' ByteString -> ByteString -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` ByteString
pub
            EncryptedKey -> ByteString
encryptedChainCode EncryptedKey
key' ByteString -> ByteString -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` ByteString
cc

  String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"presenting a v1 raw blob returns Left XPrvDecodeError" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
    let v1blob :: ByteString
v1blob = Int -> Word8 -> ByteString
BS.replicate Int
128 Word8
0x00
    case ByteString -> Either XPrvError EncryptedKey
encryptedKey ByteString
v1blob of
      Left XPrvError
err -> HasCallStack => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"encryptedKey rejected v1 blob: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
err
      Right EncryptedKey
key -> do
        Either XPrvError ()
r <- EncryptedKey -> ByteString -> IO (Either XPrvError ())
forall passphrase.
ByteArrayAccess passphrase =>
EncryptedKey -> passphrase -> IO (Either XPrvError ())
encryptedValidatePassphrase EncryptedKey
key ByteString
testPass
        Either XPrvError ()
r Either XPrvError () -> Either XPrvError () -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` XPrvError -> Either XPrvError ()
forall a b. a -> Either a b
Left XPrvError
XPrvDecodeError

  String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"truncated CBOR bytes return Left XPrvDecodeError" (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 => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"encryptedCreate failed: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
err
      Right EncryptedKey
key ->
        ByteString -> Either XPrvError EncryptedKey
encryptedKey (Int -> ByteString -> ByteString
BS.take Int
10 (EncryptedKey -> ByteString
unEncryptedKey EncryptedKey
key))
          Either XPrvError EncryptedKey
-> Either XPrvError EncryptedKey -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` XPrvError -> Either XPrvError EncryptedKey
forall a b. a -> Either a b
Left XPrvError
XPrvDecodeError

  String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"encryptedChangePass re-randomizes envelope (different bytes, same public key)" (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 => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"encryptedCreate failed: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
err
      Right EncryptedKey
key -> do
        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
testPass EncryptedKey
key
        case Either XPrvError EncryptedKey
res' of
          Left XPrvError
err -> HasCallStack => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"changePass failed: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
err
          Right EncryptedKey
key' -> do
            EncryptedKey -> ByteString
encryptedPublic EncryptedKey
key ByteString -> ByteString -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` EncryptedKey -> ByteString
encryptedPublic EncryptedKey
key'
            EncryptedKey -> ByteString
unEncryptedKey EncryptedKey
key ByteString -> ByteString -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldNotBe` EncryptedKey -> ByteString
unEncryptedKey EncryptedKey
key'

  String -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"golden: public key matches deterministic ed25519 derivation from testSeed" (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 => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"encryptedCreate failed: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
err
      Right EncryptedKey
key -> EncryptedKey -> ByteString
encryptedPublic EncryptedKey
key ByteString -> ByteString -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` ByteString
expectedPublicKey