Skip to content

Commit

Permalink
Use more hex in passphrase tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rvl committed Apr 1, 2022
1 parent 3ab72a2 commit cdf9a54
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 91 deletions.
9 changes: 3 additions & 6 deletions lib/core-integration/src/Test/Integration/Plutus.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import Cardano.Wallet.Api.Types
import Cardano.Wallet.Primitive.Types.Hash
( Hash (..) )
import Cardano.Wallet.Unsafe
( unsafeFromHex, unsafeRight )
( unsafeFromHexText, unsafeRight )
import Codec.Binary.Bech32.TH
( humanReadablePart )
import Codec.Serialise
Expand Down Expand Up @@ -100,7 +100,7 @@ alwaysTrueValidator =

hashScript :: Text -> ApiT (Hash "TokenPolicy")
hashScript =
ApiT . Hash . blake2b224 . unsafeFromHex . ("01" <>) . T.encodeUtf8
ApiT . Hash . blake2b224 . unsafeFromHexText . ("01" <>)

--
-- Ping Pong
Expand Down Expand Up @@ -365,7 +365,7 @@ currencyTx input = Aeson.object
, CBOR.TBool True, CBOR.TNull
]
transaction_witness_set = CBOR.TMap
[ (c_plutus_script, CBOR.TList [ CBOR.TBytes (fromHex policy) ])
[ (c_plutus_script, CBOR.TList [CBOR.TBytes (unsafeFromHexText policy)])
, (c_plutus_data, CBOR.TList []) -- leave empty
]
[c_plutus_script, c_plutus_data] = map CBOR.TInt [3,4]
Expand Down Expand Up @@ -395,9 +395,6 @@ currencyTx input = Aeson.object
toHex :: BS.ByteString -> Text
toHex = T.decodeUtf8 . Base16.encode

fromHex :: Text -> BS.ByteString
fromHex = Base16.decodeLenient . T.encodeUtf8

-- | Minting policy that mints 1_000 units of "apfel" and 1 unit of "banana"
-- when a specific UTxO is spent (which can be done only once).
mkCurrencyPolicy :: ApiWalletInput n -> (Text, ApiT (Hash "TokenPolicy"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import Cardano.Wallet.Primitive.SyncProgress
import Cardano.Wallet.Primitive.Types
( walletNameMaxLength, walletNameMinLength )
import Cardano.Wallet.Unsafe
( unsafeFromHex, unsafeXPub )
( unsafeFromHexText, unsafeXPub )
import Control.Monad
( forM, forM_ )
import Control.Monad.IO.Class
Expand Down Expand Up @@ -1150,7 +1150,7 @@ spec = describe "SHELLEY_WALLETS" $ do
let sigBytes = BL.toStrict $ getFromResponse id rSig
let sig = CC.xsignature sigBytes
let key = unsafeXPub $ fst (getFromResponse #getApiVerificationKey rKey) <> dummyChainCode
let msgHash = unsafeFromHex "1228cd0fea46f9a091172829f0c492c0516dceff67de08f585a4e048a28a6c9f"
let msgHash = unsafeFromHexText "1228cd0fea46f9a091172829f0c492c0516dceff67de08f585a4e048a28a6c9f"
liftIO $ CC.verify key msgHash <$> sig `shouldBe` Right True

let goldenSig = "680739414d89eb9f4377192171ce3990c7beea6132a04f327d7c954ae9e7fcfe747dd7b4b9b11acefa1aa75216b837fc81e59c24001b96356ba65598ec159d0c" :: ByteString
Expand Down
4 changes: 2 additions & 2 deletions lib/core/src/Cardano/Wallet/Primitive/Passphrase/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import Crypto.Random.Types
import Data.Bifunctor
( first )
import Data.ByteArray
( ByteArrayAccess, ScrubbedBytes )
( ByteArray, ByteArrayAccess, ScrubbedBytes )
import Data.ByteArray.Encoding
( Base (..), convertToBase )
import Data.Proxy
Expand Down Expand Up @@ -169,7 +169,7 @@ instance ToText PassphraseScheme where

newtype PassphraseHash = PassphraseHash { getPassphraseHash :: ScrubbedBytes }
deriving stock (Show)
deriving newtype (Eq, NFData, ByteArrayAccess)
deriving newtype (Eq, Ord, Semigroup, Monoid, NFData, ByteArrayAccess, ByteArray)

instance ToText PassphraseHash where
toText = T.decodeUtf8 . convertToBase Base16 . getPassphraseHash
Expand Down
13 changes: 11 additions & 2 deletions lib/core/src/Cardano/Wallet/Unsafe.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
module Cardano.Wallet.Unsafe
( unsafeRight
, unsafeFromHex
, unsafeFromHexText
, unsafeFromBase64
, unsafeFromHexFile
, unsafeDecodeAddress
Expand Down Expand Up @@ -70,6 +71,8 @@ import Control.Monad.Trans.Except
( ExceptT (..), runExceptT )
import Data.Binary.Get
( Get, runGet )
import Data.ByteArray
( ByteArray )
import Data.ByteArray.Encoding
( Base (..), convertFromBase )
import Data.ByteString
Expand Down Expand Up @@ -104,15 +107,21 @@ import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.IO as TIO

-- | Take the right side of an 'Either' value. Crash badly if it was a left.
unsafeRight :: (Buildable e, HasCallStack) => Either e a -> a
unsafeRight = either (internalError . build) id

-- | Decode an hex-encoded 'ByteString' into raw bytes, or fail.
unsafeFromHex :: HasCallStack => ByteString -> ByteString
unsafeFromHex = unsafeRight . convertFromBase @ByteString @ByteString Base16
unsafeFromHex :: forall b. (HasCallStack, ByteArray b) => ByteString -> b
unsafeFromHex = unsafeRight . convertFromBase @ByteString @b Base16

-- | Decode hex-encoded 'Text' into a 'ByteString', or fail. This variant of
-- 'unsafeFromHex' may be easier to use because it's not polymorphic.
unsafeFromHexText :: HasCallStack => Text -> ByteString
unsafeFromHexText = unsafeFromHex . T.encodeUtf8

-- | Decode a base64-encoded 'ByteString' into raw bytes, or fail.
unsafeFromBase64 :: HasCallStack => ByteString -> ByteString
Expand Down
148 changes: 82 additions & 66 deletions lib/core/test/unit/Cardano/Wallet/Primitive/Passphrase/LegacySpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ import Cardano.Wallet.Unsafe
( unsafeFromHex )
import Control.Monad.IO.Class
( liftIO )
import Data.ByteArray
( ByteArray )
import Data.ByteArray.Encoding
( Base (..), convertToBase )
import Data.ByteString
( ByteString )
import GHC.Stack
Expand All @@ -48,17 +52,27 @@ import Test.QuickCheck
import Test.QuickCheck.Monadic
( monadicIO )

import qualified Data.ByteArray as BA
import qualified Data.Text.Encoding as T
spec :: Spec
spec = parallel $ do
scryptoniteSpec
onlyWithScrypt $ do
scryptPropsSpec
scryptGoldenSpec

onlyWithScrypt :: HasCallStack => Spec -> Spec
onlyWithScrypt =
if haveScrypt
then id
else before_ (pendingWith "needs to be compiled with scrypt to run")

spec :: Spec
spec = onlyWithScrypt $ describe "Scrypt tests-only cryptonite version" $ do
{-------------------------------------------------------------------------------
Cryptonite-based implementation
-------------------------------------------------------------------------------}

scryptoniteSpec :: Spec
scryptoniteSpec = describe "Scrypt tests-only cryptonite version" $ do
it "Verify passphrase" $ do
let Just salt = getSalt fixturePassphraseEncrypted
let
unwrap :: PassphraseHash -> ByteString
unwrap (PassphraseHash x) = BA.convert x
let x = encryptPassphraseTestingOnly
(preparePassphrase fixturePassphrase)
salt
Expand All @@ -79,63 +93,13 @@ spec = onlyWithScrypt $ describe "Scrypt tests-only cryptonite version" $ do
checkPassphraseTestingOnly (preparePassphrase fixturePassphrase)
fixturePassphraseEncryptedWrongHash `shouldBe` False
it "getSalt" $ do
let
unwrap :: Passphrase "salt" -> ByteString
unwrap (Passphrase x) = BA.convert x
unwrap <$> (getSalt fixturePassphraseEncrypted)
`shouldBe` Just "\250:-\180R\EM\141Y\151\160.\203\149\&0YZV3\ENQ~\
\\DLEC\a\221\184[\177\nC\aR+"

it "checkPassphrase p h(p) == Right () for Scrypt passwords" $
property prop_passphraseFromScryptRoundtrip
it "p /= p' => checkPassphrase p' h(p) == Left ErrWrongPassphrase for Scrypt passwords" $
property prop_passphraseFromScryptRoundtripFail
let hex :: Passphrase "salt" -> ByteString
hex = convertToBase Base16 . unPassphrase
hex <$> (getSalt fixturePassphraseEncrypted)
`shouldBe` Just "fa3a2db452198d5997a02ecb9530595a5633057e104307ddb85bb10a4307522b"

parallel $ describe "golden test legacy passphrase encryption" $ do
it "compare new implementation with cardano-sl - short password" $ do
let pwd = Passphrase $ BA.convert $ T.encodeUtf8 "patate"
let hash = PassphraseHash $ BA.convert $ unsafeFromHex $ mconcat
[ "31347c387c317c574342652b796362417576356c2b4258676a344a314c"
, "6343675375414c2f5653393661364e576a2b7550766655513d3d7c2f37"
, "6738486c59723174734e394f6e4e753253302b6a65515a6b5437316b45"
, "414941366a515867386539493d"
]
checkPassphrase EncryptWithScrypt pwd hash `shouldBe` Right ()
it "compare new implementation with cardano-sl - normal password" $ do
let pwd = Passphrase @"user" $ BA.convert $ T.encodeUtf8 "Secure Passphrase"
let hash = PassphraseHash $ BA.convert $ unsafeFromHex $ mconcat
[ "31347c387c317c714968506842665966555a336f5156434c384449744b"
, "677642417a6c584d62314d6d4267695433776a556f3d7c53672b436e30"
, "4232766b4475682f704265335569694577633364385845756f55737661"
, "42514e62464443353569474f4135736e453144326743346f47564c472b"
, "524331385958326c6863552f36687a38432f496172773d3d"
]
checkPassphrase EncryptWithScrypt pwd hash `shouldBe` Right ()
it "compare new implementation with cardano-sl - empty password" $ do
let pwd = Passphrase @"user" $ BA.convert $ T.encodeUtf8 ""
let hash = PassphraseHash $ BA.convert $ unsafeFromHex $ mconcat
[ "31347c387c317c5743424875746242496c6a66734d764934314a30727a7"
, "9663076657375724954796376766a793150554e377452673d3d7c54753"
, "434596d6e547957546c5759674a3164494f7974474a7842632b432f786"
, "2507657382b5135356a38303d"
]
checkPassphrase EncryptWithScrypt pwd hash `shouldBe` Right ()
it "compare new implementation with cardano-sl - cardano-wallet password" $ do
let pwd = Passphrase @"user" $ BA.convert $ T.encodeUtf8 "cardano-wallet"
let hash = PassphraseHash $ BA.convert $ unsafeFromHex $ mconcat
[ "31347c387c317c2b6a6f747446495a6a566d586f43374c6c54425a576c"
, "597a425834515177666475467578436b4d485569733d7c78324d646738"
, "49554a3232507235676531393575445a76583646552b7757395a6a6a2f"
, "51303054356c654751794279732f7662753367526d726c316c657a7150"
, "43676d364e6758476d4d2f4b6438343265304b4945773d3d"
]
checkPassphrase EncryptWithScrypt pwd hash `shouldBe` Right ()

onlyWithScrypt :: HasCallStack => Spec -> Spec
onlyWithScrypt =
if haveScrypt
then id
else before_ (pendingWith "needs to be compiled with scrypt to run")
unwrap :: ByteArray b => b -> ByteString
unwrap = convertToBase Base16

-- | Default passphrase used for fixture wallets
fixturePassphrase :: Passphrase "user"
Expand All @@ -146,33 +110,85 @@ fixturePassphraseWrong = Passphrase "wrong"

-- | fixturePassphrase encrypted by Scrypt function
fixturePassphraseEncrypted :: PassphraseHash
fixturePassphraseEncrypted = PassphraseHash $ BA.convert $ unsafeFromHex
fixturePassphraseEncrypted = unsafeFromHex
"31347c387c317c2b6a6f747446495a6a566d586f43374c6c54425a576c\
\597a425834515177666475467578436b4d485569733d7c78324d646738\
\49554a3232507235676531393575445a76583646552b7757395a6a6a2f\
\51303054356c654751794279732f7662753367526d726c316c657a7150\
\43676d364e6758476d4d2f4b6438343265304b4945773d3d"

fixturePassphraseEncryptedWrongSalt :: PassphraseHash
fixturePassphraseEncryptedWrongSalt = PassphraseHash $ BA.convert $ unsafeFromHex
fixturePassphraseEncryptedWrongSalt = unsafeFromHex
"31347c387c317c206a6f747446495a6a566d586f43374c6c54425a576c\
\597a425834515177666475467578436b4d485569733d7c78324d646738\
\49554a3232507235676531393575445a76583646552b7757395a6a6a2f\
\51303054356c654751794279732f7662753367526d726c316c657a7150\
\43676d364e6758476d4d2f4b6438343265304b4945773d3d"

fixturePassphraseEncryptedWrongHash :: PassphraseHash
fixturePassphraseEncryptedWrongHash = PassphraseHash $ BA.convert $ unsafeFromHex
fixturePassphraseEncryptedWrongHash = unsafeFromHex
"31347c387c317c2b6a6f747446495a6a566d586f43374c6c54425a576c\
\597a425834515177666475467578436b4d485569733d7c78324d646738\
\49554a3232507235676531393575445a76583646552b7757395a6a6a2f\
\51303054356c654751794279732f7662753367526d726c316c657a7150\
\43676d364e6758476d4d2f4b6438343265304b4945773d30"

{-------------------------------------------------------------------------------
Golden Tests
-------------------------------------------------------------------------------}

scryptGoldenSpec :: Spec
scryptGoldenSpec =
describe "golden tests comparing this implementation with cardano-sl" $ do
it "short password" $ do
let pwd = Passphrase "patate"
let hash = unsafeFromHex
"31347c387c317c574342652b796362417576356c2b4258676a344a314c\
\6343675375414c2f5653393661364e576a2b7550766655513d3d7c2f37\
\6738486c59723174734e394f6e4e753253302b6a65515a6b5437316b45\
\414941366a515867386539493d"
checkPassphrase EncryptWithScrypt pwd hash `shouldBe` Right ()

it "normal password" $ do
let pwd = Passphrase @"user" "Secure Passphrase"
let hash = unsafeFromHex
"31347c387c317c714968506842665966555a336f5156434c384449744b\
\677642417a6c584d62314d6d4267695433776a556f3d7c53672b436e30\
\4232766b4475682f704265335569694577633364385845756f55737661\
\42514e62464443353569474f4135736e453144326743346f47564c472b\
\524331385958326c6863552f36687a38432f496172773d3d"
checkPassphrase EncryptWithScrypt pwd hash `shouldBe` Right ()

it "empty password" $ do
let pwd = Passphrase @"user" ""
let hash = unsafeFromHex
"31347c387c317c5743424875746242496c6a66734d764934314a30727a\
\79663076657375724954796376766a793150554e377452673d3d7c5475\
\3434596d6e547957546c5759674a3164494f7974474a7842632b432f78\
\62507657382b5135356a38303d"
checkPassphrase EncryptWithScrypt pwd hash `shouldBe` Right ()

it "cardano-wallet password" $ do
let pwd = Passphrase @"user" "cardano-wallet"
let hash = unsafeFromHex
"31347c387c317c2b6a6f747446495a6a566d586f43374c6c54425a576c\
\597a425834515177666475467578436b4d485569733d7c78324d646738\
\49554a3232507235676531393575445a76583646552b7757395a6a6a2f\
\51303054356c654751794279732f7662753367526d726c316c657a7150\
\43676d364e6758476d4d2f4b6438343265304b4945773d3d"
checkPassphrase EncryptWithScrypt pwd hash `shouldBe` Right ()

{-------------------------------------------------------------------------------
Properties
-------------------------------------------------------------------------------}

scryptPropsSpec :: Spec
scryptPropsSpec = describe "Legacy scrypt password encryption" $ do
it "checkPassphrase p h(p) == Right ()" $
property prop_passphraseFromScryptRoundtrip
it "p /= p' => checkPassphrase p' h(p) == Left ErrWrongPassphrase" $
property prop_passphraseFromScryptRoundtripFail

prop_passphraseFromScryptRoundtrip
:: Passphrase "user"
-> Property
Expand Down
26 changes: 13 additions & 13 deletions lib/core/test/unit/Cardano/Wallet/Primitive/PassphraseSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import Cardano.Wallet.Primitive.Passphrase.Gen
)
import Cardano.Wallet.Primitive.Passphrase.Legacy
( haveScrypt )
import Cardano.Wallet.Unsafe
( unsafeFromHex )
import Control.Monad.IO.Class
( liftIO )
import Data.ByteString
Expand All @@ -51,11 +53,11 @@ import Test.Text.Roundtrip
import qualified Data.ByteArray as BA

spec :: Spec
spec = do
parallel $ describe "Text Roundtrip" $ do
spec = parallel $ do
describe "Text Roundtrip" $ do
textRoundtrip $ Proxy @(Passphrase "user")

parallel $ describe "Passphrases" $ do
describe "Passphrases" $ do
it "checkPassphrase p h(p) == Right ()" $
property prop_passphraseRoundtrip
it "p /= p' => checkPassphrase p' h(p) == Left ErrWrongPassphrase" $
Expand Down Expand Up @@ -156,20 +158,18 @@ passphraseGolden1 :: Golden
passphraseGolden1 = Golden
{ passphrase = Passphrase "passphrase"
, prepared = Passphrase "passphrase"
, hash = PassphraseHash
"\SI\133\128\ESC#\211\232\218\ESC\134>\216\216H\235\206\206\211\134'\
\\135\253\237\244\226\143\SYN\239!}\247\172\168\CAN\212\DC1\SI\255\235\
\\215\241\DC4\181\133\177\232=\190\154\249\ETB\EOTd\176\149\249\216\133\
\\141\188P\188s\159q\250\159^dj\STX\ACK$O@\208\138\236wp"
, hash = unsafeFromHex
"0f85801b23d3e8da1b863ed8d848ebceced3862787fdedf4e28f16ef217df7ac\
\a818d4110fffebd7f114b585b1e83dbe9af9170464b095f9d8858dbc50bc739f\
\71fa9f5e646a0206244f40d08aec7770"
}

passphraseGolden2 :: Golden
passphraseGolden2 = Golden
{ passphrase = Passphrase ""
, prepared = Passphrase ""
, hash = PassphraseHash
"\SI\133\128\ESC#\211\232\218\ESC\134>\216\216H\235\206\130\173|\250\
\\215\130\227^\155\216\176\NUL\145p\190P\CAN\254\155\190\140\DLE\208\
\\194\207)X\171p*Y\170\192eiZ\243\\\222Mr\174\av\NAK\238\183\DC2\156\
\\203\196\156,,\245X\161\242\160\148\217k\EM\234"
, hash = unsafeFromHex
"0f85801b23d3e8da1b863ed8d848ebce82ad7cfad782e35e9bd8b0009170be50\
\18fe9bbe8c10d0c2cf2958ab702a59aac065695af35cde4d72ae077615eeb712\
\9ccbc49c2c2cf558a1f2a094d96b19ea"
}

0 comments on commit cdf9a54

Please sign in to comment.