Skip to content

Commit

Permalink
Merge pull request #1629 from Plutonomicon/marcusbfs/refactor-cip30-mock
Browse files Browse the repository at this point in the history
CTL F11 M4: Footprint reduction: use external Cip30Mock package
  • Loading branch information
klntsky authored Jul 9, 2024
2 parents 77db77f + ecc2308 commit 2c8e8fb
Show file tree
Hide file tree
Showing 29 changed files with 473 additions and 654 deletions.
90 changes: 30 additions & 60 deletions test/Wallet/Cip30/SignData.js → examples/SignData.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import * as csl from "@mlabs-haskell/cardano-serialization-lib-gc";
"use strict";

// eslint-disable-next-line no-unused-vars
import * as lib from "@mlabs-haskell/cardano-message-signing";
import * as CSL from "@mlabs-haskell/cardano-serialization-lib-gc";

function opt_chain(maybe, obj) {
const isNothing = x => x === null || x === undefined;
Expand All @@ -14,42 +17,10 @@ function opt_chain(maybe, obj) {
return isNothing(result) ? maybe.nothing : maybe.just(result);
}

const fromBytes = name => bytes => () => {
return lib[name].from_bytes(bytes);
};

// -----------------------------------------------------------------------------
// PublicKey
// -----------------------------------------------------------------------------

// verifySignature :: COSESign1 -> PublicKey -> CborBytes -> Effect Boolean
export function verifySignature(coseSign1) {
return publicKey => sigStructBytes => () => {
const signature = csl.Ed25519Signature.from_bytes(coseSign1.signature());
return publicKey.verify(sigStructBytes, signature);
};
}

// -----------------------------------------------------------------------------
// COSESign1
// -----------------------------------------------------------------------------

// _fromBytesCoseSign1 :: CborBytes -> Effect COSESign1
export const fromBytesCoseSign1 = fromBytes("COSESign1");

// getSignedData :: COSESign1 -> Effect CborBytes
export function getSignedData(coseSign1) {
return () => {
return coseSign1.signed_data(null, null).to_bytes();
};
}

// getCoseSign1ProtectedHeaders :: COSESign1 -> HeaderMap
const getCoseSign1ProtectedHeaders = coseSign1 => {
return coseSign1.headers().protected().deserialized_headers();
};

// getCoseSign1ProtectedHeaderAlg :: MaybeFfiHelper -> COSESign1 -> Maybe Int
export function _getCoseSign1ProtectedHeaderAlg(maybe) {
return coseSign1 => {
const protectedHeaders = getCoseSign1ProtectedHeaders(coseSign1);
Expand All @@ -63,8 +34,6 @@ export function _getCoseSign1ProtectedHeaderAlg(maybe) {
};
}

// _getCoseSign1ProtectedHeaderAddress
// :: MaybeFfiHelper -> COSESign1 -> Maybe CborBytes
export function _getCoseSign1ProtectedHeaderAddress(maybe) {
return coseSign1 => {
const protectedHeaders = getCoseSign1ProtectedHeaders(coseSign1);
Expand All @@ -73,37 +42,18 @@ export function _getCoseSign1ProtectedHeaderAddress(maybe) {
};
}

// _getCoseSign1ProtectedHeaderKid
// :: MaybeFfiHelper -> COSESign1 -> Maybe RawBytes
export function _getCoseSign1ProtectedHeaderKid(maybe) {
return coseSign1 => {
const protectedHeaders = getCoseSign1ProtectedHeaders(coseSign1);
return opt_chain(maybe, protectedHeaders, "key_id");
};
}

// -----------------------------------------------------------------------------
// COSEKey
// -----------------------------------------------------------------------------

// _fromBytesCoseKey :: CborBytes -> Effect COSEKey
export const fromBytesCoseKey = fromBytes("COSEKey");

// _getCoseKeyHeaderKty :: MaybeFfiHelper -> COSEKey -> Maybe Int
export function _getCoseKeyHeaderKty(maybe) {
return coseKey => {
return opt_chain(maybe, coseKey.key_type(), "as_int", "as_i32");
};
}

// _getCoseKeyHeaderAlg :: MaybeFfiHelper -> COSEKey -> Maybe Int
export function _getCoseKeyHeaderAlg(maybe) {
return coseKey => {
return opt_chain(maybe, coseKey, "algorithm_id", "as_int", "as_i32");
};
}

// _getCoseKeyHeaderCrv :: MaybeFfiHelper -> COSEKey -> Maybe Int
export function _getCoseKeyHeaderCrv(maybe) {
return coseKey => {
const cborValue = coseKey.header(
Expand All @@ -115,7 +65,18 @@ export function _getCoseKeyHeaderCrv(maybe) {
};
}

// _getCoseKeyHeaderX :: MaybeFfiHelper -> COSEKey -> Maybe RawBytes
export function _getCoseSign1ProtectedHeaderKid(maybe) {
return coseSign1 => {
const protectedHeaders = getCoseSign1ProtectedHeaders(coseSign1);
return opt_chain(maybe, protectedHeaders, "key_id");
};
}

export function _getCoseKeyHeaderKid(maybe) {
return coseKey => {
return opt_chain(maybe, coseKey, "key_id");
};
}
export function _getCoseKeyHeaderX(maybe) {
return coseKey => {
const cborValue = coseKey.header(
Expand All @@ -126,10 +87,19 @@ export function _getCoseKeyHeaderX(maybe) {
return opt_chain(maybe, cborValue, "as_bytes");
};
}

// _getCoseKeyHeaderKid :: MaybeFfiHelper -> COSESign1 -> Maybe RawBytes
export function _getCoseKeyHeaderKid(maybe) {
return coseKey => {
return opt_chain(maybe, coseKey, "key_id");
export function getSignedData(coseSign1) {
return () => {
return coseSign1.signed_data(null, null).to_bytes();
};
}
export function verifySignature(coseSign1) {
return publicKey => sigStructBytes => () => {
const signature = CSL.Ed25519Signature.from_bytes(coseSign1.signature());
return publicKey.verify(sigStructBytes, signature);
};
}
const fromBytes = name => bytes => () => {
return lib[name].from_bytes(bytes);
};
export const fromBytesCoseKey = fromBytes("COSEKey");
export const fromBytesCoseSign1 = fromBytes("COSESign1");
144 changes: 142 additions & 2 deletions examples/SignData.purs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ module Ctl.Examples.SignData (main, example, contract) where

import Contract.Prelude

import Cardano.Types (RawBytes)
import Cardano.AsCbor (encodeCbor)
import Cardano.MessageSigning (DataSignature)
import Cardano.Types (CborBytes, PublicKey, RawBytes)
import Cardano.Types.PublicKey as PublicKey
import Cardano.Wallet.Cip30.SignData (COSEKey, COSESign1)
import Contract.Address (Address)
import Contract.Config (ContractParams, testnetNamiConfig)
import Contract.Log (logInfo')
Expand All @@ -11,8 +15,10 @@ import Contract.Wallet (getChangeAddress, getRewardAddresses, signData)
import Data.Array (head) as Array
import Data.ByteArray (byteArrayFromAscii)
import Data.Maybe (fromJust)
import Effect.Aff (error)
import Effect.Class (class MonadEffect)
import Effect.Exception (throw, throwException)
import Partial.Unsafe (unsafePartial)
import Test.Ctl.Wallet.Cip30.SignData (checkCip30SignDataResponse)

main :: Effect Unit
main = example testnetNamiConfig
Expand Down Expand Up @@ -41,3 +47,137 @@ contract = do
dataSignature <- signData address payload
logInfo' $ "signData " <> addressLabel <> ": " <> show dataSignature
void $ liftAff $ checkCip30SignDataResponse address dataSignature

type DeserializedDataSignature =
{ coseKey :: COSEKey
, coseSign1 :: COSESign1
}

checkCip30SignDataResponse
:: Address -> DataSignature -> Aff DeserializedDataSignature
checkCip30SignDataResponse address { key, signature } = do
coseSign1 <- liftEffect $ fromBytesCoseSign1 signature
coseKey <- liftEffect $ fromBytesCoseKey key

checkCoseSign1ProtectedHeaders coseSign1
checkCoseKeyHeaders coseKey
checkKidHeaders coseSign1 coseKey
liftEffect $ checkVerification coseSign1 coseKey
pure { coseKey, coseSign1 }
where
checkCoseSign1ProtectedHeaders :: COSESign1 -> Aff Unit
checkCoseSign1ProtectedHeaders coseSign1 = do
assertTrue "COSE_Sign1's alg (1) header must be set to EdDSA (-8)"
(getCoseSign1ProtectedHeaderAlg coseSign1 == Just (-8))

assertTrue "COSE_Sign1's \"address\" header must be set to address bytes"
( getCoseSign1ProtectedHeaderAddress coseSign1
== Just (encodeCbor address)
)

checkCoseKeyHeaders :: COSEKey -> Aff Unit
checkCoseKeyHeaders coseKey = do
assertTrue "COSE_Key's kty (1) header must be set to OKP (1)"
(getCoseKeyHeaderKty coseKey == Just 1)

assertTrue "COSE_Key's alg (3) header must be set to EdDSA (-8)"
(getCoseKeyHeaderAlg coseKey == Just (-8))

assertTrue "COSE_Key's crv (-1) header must be set to Ed25519 (6)"
(getCoseKeyHeaderCrv coseKey == Just (6))

checkKidHeaders :: COSESign1 -> COSEKey -> Aff Unit
checkKidHeaders coseSign1 coseKey =
assertTrue
"COSE_Sign1's kid (4) and COSE_Key's kid (2) headers, if present, must \
\be set to the same value"
(getCoseSign1ProtectedHeaderKid coseSign1 == getCoseKeyHeaderKid coseKey)

checkVerification :: COSESign1 -> COSEKey -> Effect Unit
checkVerification coseSign1 coseKey = do
publicKey <-
errMaybe "COSE_Key's x (-2) header must be set to public key bytes"
$ getCoseKeyHeaderX coseKey
>>= PublicKey.fromRawBytes
sigStructBytes <- getSignedData coseSign1
assertTrue "Signature verification failed"
=<< verifySignature coseSign1 publicKey sigStructBytes

getCoseSign1ProtectedHeaderAlg :: COSESign1 -> Maybe Int
getCoseSign1ProtectedHeaderAlg = _getCoseSign1ProtectedHeaderAlg maybeFfiHelper

getCoseSign1ProtectedHeaderAddress :: COSESign1 -> Maybe CborBytes
getCoseSign1ProtectedHeaderAddress =
_getCoseSign1ProtectedHeaderAddress maybeFfiHelper

type MaybeFfiHelper =
{ nothing :: forall (x :: Type). Maybe x
, just :: forall (x :: Type). x -> Maybe x
, from :: forall (x :: Type). x -> Maybe x -> x
}

maybeFfiHelper :: MaybeFfiHelper
maybeFfiHelper = { nothing: Nothing, just: Just, from: fromMaybe }

getCoseKeyHeaderKty :: COSEKey -> Maybe Int
getCoseKeyHeaderKty = _getCoseKeyHeaderKty maybeFfiHelper

getCoseKeyHeaderAlg :: COSEKey -> Maybe Int
getCoseKeyHeaderAlg = _getCoseKeyHeaderAlg maybeFfiHelper

getCoseKeyHeaderCrv :: COSEKey -> Maybe Int
getCoseKeyHeaderCrv = _getCoseKeyHeaderCrv maybeFfiHelper

getCoseSign1ProtectedHeaderKid :: COSESign1 -> Maybe RawBytes
getCoseSign1ProtectedHeaderKid = _getCoseSign1ProtectedHeaderKid maybeFfiHelper

getCoseKeyHeaderKid :: COSEKey -> Maybe RawBytes
getCoseKeyHeaderKid = _getCoseKeyHeaderKid maybeFfiHelper

assertTrue
:: forall (m :: Type -> Type)
. Applicative m
=> MonadEffect m
=> String
-> Boolean
-> m Unit
assertTrue msg b = unless b $ liftEffect $ throwException $ error msg

errMaybe
:: forall (m :: Type -> Type) (a :: Type)
. MonadEffect m
=> String
-> Maybe a
-> m a
errMaybe msg = maybe (liftEffect $ throw msg) pure

getCoseKeyHeaderX :: COSEKey -> Maybe RawBytes
getCoseKeyHeaderX = _getCoseKeyHeaderX maybeFfiHelper

--------------------------------------------------------------------------------
-- Foreign functions
--------------------------------------------------------------------------------

foreign import _getCoseSign1ProtectedHeaderAlg
:: MaybeFfiHelper -> COSESign1 -> Maybe Int

foreign import _getCoseSign1ProtectedHeaderAddress
:: MaybeFfiHelper -> COSESign1 -> Maybe CborBytes

foreign import _getCoseKeyHeaderX :: MaybeFfiHelper -> COSEKey -> Maybe RawBytes
foreign import _getCoseKeyHeaderKty :: MaybeFfiHelper -> COSEKey -> Maybe Int
foreign import _getCoseKeyHeaderAlg :: MaybeFfiHelper -> COSEKey -> Maybe Int
foreign import _getCoseKeyHeaderCrv :: MaybeFfiHelper -> COSEKey -> Maybe Int
foreign import _getCoseSign1ProtectedHeaderKid
:: MaybeFfiHelper -> COSESign1 -> Maybe RawBytes

foreign import _getCoseKeyHeaderKid
:: MaybeFfiHelper -> COSEKey -> Maybe RawBytes

foreign import getSignedData :: COSESign1 -> Effect CborBytes

foreign import verifySignature
:: COSESign1 -> PublicKey -> CborBytes -> Effect Boolean

foreign import fromBytesCoseSign1 :: CborBytes -> Effect COSESign1
foreign import fromBytesCoseKey :: CborBytes -> Effect COSEKey
48 changes: 48 additions & 0 deletions packages.dhall
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,54 @@ let additions =
, repo = "https://github.com/mlabs-haskell/purescript-plutus-types"
, version = "v1.0.1"
}
, cip30-mock =
{ dependencies =
[ "aff-promise", "console", "effect", "functions", "prelude" ]
, repo = "https://github.com/mlabs-haskell/purescript-cip30-mock"
, version = "v1.0.0"
}
, cardano-collateral-select =
{ dependencies =
[ "arrays"
, "cardano-types"
, "console"
, "effect"
, "exceptions"
, "foldable-traversable"
, "lists"
, "maybe"
, "newtype"
, "ordered-collections"
, "partial"
, "prelude"
, "tuples"
]
, repo =
"https://github.com/mlabs-haskell/purescript-cardano-collateral-select"
, version = "v1.0.0"
}
, cardano-key-wallet =
{ dependencies =
[ "aeson"
, "aff"
, "arrays"
, "cardano-collateral-select"
, "cardano-message-signing"
, "cardano-types"
, "console"
, "effect"
, "either"
, "foldable-traversable"
, "maybe"
, "newtype"
, "prelude"
, "profunctor-lenses"
, "typelevel-prelude"
]
, repo =
"https://github.com/mlabs-haskell/purescript-cardano-key-wallet"
, version = "v1.0.0"
}
, uplc-apply-args =
{ dependencies =
[ "aff"
Expand Down
Loading

0 comments on commit 2c8e8fb

Please sign in to comment.