module Test.Cardano.Crypto.Wallet.V2FormatSpec (tests) where
import qualified Codec.CBOR.Decoding as CBOR
import qualified Codec.CBOR.Read as CBOR
import qualified Codec.CBOR.Write as CBOR (toLazyByteString)
import Control.Monad.Trans.Fail.String (errorFail)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import Test.HUnit.Base (assertFailure)
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
expectedPublicKey :: PublicKey
expectedPublicKey :: PublicKey
expectedPublicKey =
Fail PublicKey -> PublicKey
forall a. HasCallStack => Fail a -> a
errorFail (Fail PublicKey -> PublicKey) -> Fail PublicKey -> PublicKey
forall a b. (a -> b) -> a -> b
$
ByteString -> Fail PublicKey
forall (f :: * -> *). MonadFail f => ByteString -> f PublicKey
mkPublicKey (ByteString -> Fail PublicKey) -> ByteString -> Fail PublicKey
forall a b. (a -> b) -> a -> b
$
[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
let
createTestKey :: HasCallStack => IO EncryptedKey
createTestKey :: HasCallStack => IO EncryptedKey
createTestKey = 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 -> String -> IO EncryptedKey
forall a. HasCallStack => String -> IO a
assertFailure (String -> IO EncryptedKey) -> String -> IO EncryptedKey
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 -> IO EncryptedKey
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure EncryptedKey
key
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
EncryptedKey
key <- IO EncryptedKey
HasCallStack => IO EncryptedKey
createTestKey
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
EncryptedKey
key <- IO EncryptedKey
HasCallStack => IO EncryptedKey
createTestKey
EncryptedKey -> ByteString -> IO (Either XPrvError ())
forall passphrase.
ByteArrayAccess passphrase =>
EncryptedKey -> passphrase -> IO (Either XPrvError ())
encryptedValidatePassphrase EncryptedKey
key ByteString
testPass IO (Either XPrvError ()) -> Either XPrvError () -> IO ()
forall a. (HasCallStack, Show a, Eq a) => IO a -> a -> IO ()
`shouldReturn` () -> 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
EncryptedKey
key <- IO EncryptedKey
HasCallStack => IO EncryptedKey
createTestKey
EncryptedKey -> ByteString -> IO (Either XPrvError ())
forall passphrase.
ByteArrayAccess passphrase =>
EncryptedKey -> passphrase -> IO (Either XPrvError ())
encryptedValidatePassphrase EncryptedKey
key ByteString
wrongPass IO (Either XPrvError ()) -> Either XPrvError () -> IO ()
forall a. (HasCallStack, Show a, Eq a) => IO a -> a -> IO ()
`shouldReturn` 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 5-element array" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
ByteString
bs <- EncryptedKey -> ByteString
unEncryptedKey (EncryptedKey -> ByteString) -> IO EncryptedKey -> IO ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO EncryptedKey
HasCallStack => IO EncryptedKey
createTestKey
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
BSL.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
5) -> () -> 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 5-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
"v2 envelope roundtrips CBOR serialization" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
Either XPrvError Envelope
res <- EncryptedKey -> Either XPrvError Envelope
decodeEncryptedKey (EncryptedKey -> Either XPrvError Envelope)
-> IO EncryptedKey -> IO (Either XPrvError Envelope)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO EncryptedKey
HasCallStack => IO EncryptedKey
createTestKey
case Either XPrvError Envelope
res of
Left XPrvError
e -> HasCallStack => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"decodeEncryptedKey: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ XPrvError -> String
forall a. Show a => a -> String
show XPrvError
e
Right Envelope
envelope -> do
let bs :: ByteString
bs = Encoding -> ByteString
CBOR.toLazyByteString (Encoding -> ByteString) -> Encoding -> ByteString
forall a b. (a -> b) -> a -> b
$ Envelope -> Encoding
encodeEnvelope Envelope
envelope
case (forall s. Decoder s Envelope)
-> ByteString -> Either DeserialiseFailure (ByteString, Envelope)
forall a.
(forall s. Decoder s a)
-> ByteString -> Either DeserialiseFailure (ByteString, a)
CBOR.deserialiseFromBytes Decoder s Envelope
forall s. Decoder s Envelope
decodeEnvelope 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
rest, Envelope
envelope') -> do
ByteString -> Int64
BSL.length ByteString
rest Int64 -> Int64 -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` Int64
0
Envelope
envelope' Envelope -> Envelope -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` Envelope
envelope
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
EncryptedKey
key <- IO EncryptedKey
HasCallStack => IO EncryptedKey
createTestKey
let pub :: PublicKey
pub = HasCallStack => EncryptedKey -> PublicKey
EncryptedKey -> PublicKey
encryptedPublic EncryptedKey
key
cc :: ChainCode
cc = HasCallStack => EncryptedKey -> ChainCode
EncryptedKey -> ChainCode
encryptedChainCode EncryptedKey
key
case ByteString -> Either XPrvError EncryptedKey
mkEncryptedKey (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
HasCallStack => EncryptedKey -> PublicKey
EncryptedKey -> PublicKey
encryptedPublic EncryptedKey
key' PublicKey -> PublicKey -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` PublicKey
pub
HasCallStack => EncryptedKey -> ChainCode
EncryptedKey -> ChainCode
encryptedChainCode EncryptedKey
key' ChainCode -> ChainCode -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` ChainCode
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
mkEncryptedKey ByteString
v1blob of
Left XPrvError
err -> HasCallStack => String -> IO ()
String -> IO ()
expectationFailure (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"mkEncryptedKey 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
EncryptedKey
key <- IO EncryptedKey
HasCallStack => IO EncryptedKey
createTestKey
ByteString -> Either XPrvError EncryptedKey
mkEncryptedKey (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
"encryptedChangePassphrase re-randomizes envelope (different bytes, same public key)" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
EncryptedKey
key <- IO EncryptedKey
HasCallStack => IO EncryptedKey
createTestKey
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)
encryptedChangePassphrase 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
HasCallStack => EncryptedKey -> PublicKey
EncryptedKey -> PublicKey
encryptedPublic EncryptedKey
key PublicKey -> PublicKey -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` HasCallStack => EncryptedKey -> PublicKey
EncryptedKey -> PublicKey
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
EncryptedKey
key <- IO EncryptedKey
HasCallStack => IO EncryptedKey
createTestKey
HasCallStack => EncryptedKey -> PublicKey
EncryptedKey -> PublicKey
encryptedPublic EncryptedKey
key PublicKey -> PublicKey -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` PublicKey
expectedPublicKey