Conversation
585efde to
1d2315b
Compare
1d2315b to
c59307c
Compare
9dc8ae0 to
70e8a64
Compare
76a069d to
ecfeba5
Compare
src/Pact/Native.hs
Outdated
|
|
||
| -- With Simplified error messages, map every error to a single string. | ||
| Left _ | behavior == Simplified -> | ||
| return $ Left "Could not base64-decode string" |
There was a problem hiding this comment.
weird indentations here and below on some things
| Right bs -> case T.decodeUtf8' bs of | ||
| Right t -> return $ tStr t | ||
| Left _unicodeError -> evalError' i $ "invalid unicode" | ||
| Left base64Error -> evalError' i (pretty (T.pack base64Error)) |
There was a problem hiding this comment.
This is not aligned with line 1349:
...
Left e -> evalError' si (pretty e)
There was a problem hiding this comment.
Oh, you mean 1349 calls pretty on a String while 1422 calls pretty on a Text? That does seem like a discrepancy. I'll change both to call pretty on a Text, and recheck replay. Good catch!
| WebAuthnSignature | ||
| { authenticatorData | ||
| , signature | ||
| , clientDataJSON } <- A.eitherDecode (BSL.fromStrict sigBS) |
There was a problem hiding this comment.
Maybe
| WebAuthnSignature | |
| { authenticatorData | |
| , signature | |
| , clientDataJSON } <- A.eitherDecode (BSL.fromStrict sigBS) | |
| WebAuthnSignature{..} <- A.eitherDecode (BSL.fromStrict sigBS) |
using {-# LANGUAGE RecordWildCards #-}
There was a problem hiding this comment.
I like NamedFieldPuns because it makes the identifiers being brought into scope explicit. Meaning: if someone reading the code didn't know what authenticationData is, or where it comes from, NamedFieldPuns makes it greppable, while RecordWildCards doesn't.
There was a problem hiding this comment.
Alternatively, construct/destruct the thing directly since we're not even really punning as much as we are constructing directly.
There was a problem hiding this comment.
I like NamedFieldPuns even for vanilla record desructuring too, since it removes the possibility of accidentally naming fields out of order. Meaning, I could write:
WebAuthnSignature sig authData clientData <- decode sigBS
-- (Is this what you mean by destruct the thing directly?)
...
and it would typecheck but be subtly wrong, because I accidentally swapped the field names around for fields that have the same type.
| ``` | ||
|
|
||
|
|
||
| ### format-address {#format-address} |
There was a problem hiding this comment.
What does dropping this have to do with WebAuthn signatures?
There was a problem hiding this comment.
Dropping it was required after removing a method from the Scheme typeclass that format-address required.
We have to remove some methods and associated types from Scheme to accommodate webauthn. It's believed be to be unused or only used very rarely.
| Pact.Types.Command | ||
| Pact.Types.Continuation | ||
| Pact.Types.Crypto | ||
| Pact.Types.ECDSA |
There was a problem hiding this comment.
Are we dropping this module because the WebAuthn support supercedes the functionality?
There was a problem hiding this comment.
While we were modifying the Scheme typeclass to support webauthn, another instance of that typeclass, the ETH scheme (and the Pact.Types.ECDSA module that supported it) came up as a place to eliminate some effectively-dead code.
| Right sd -> Right sd | ||
|
|
||
| addSigToSigData :: SomeKeyPair -> SigData a -> IO (SigData a) | ||
| addSigToSigData :: Ed25519KeyPair -> SigData a -> IO (SigData a) |
There was a problem hiding this comment.
If we start integrating more with other changes, would we still want to monomorphize the signature scheme?
There was a problem hiding this comment.
There are some integrations we could make that would introduce new PPK schemes. Depending on the details of how those integrations work, they might not see the changes here in Pact.ApiReq, since Pact.ApiReq is only used client-side, e.g. by users signing requests and making API calls to a pact server. A rule of thumb I used in this PR was to be more cautious changing server-side code, but more liberal with client-side code as needed).
A new integration that merely needs signature verification in pact will not be bothered by the monomorphization happening in client code here. But an integration that also wanted client-side support would indeed be less convenient to write, after we remove SomeKeyPair.
| import Control.Monad | ||
| import Control.Monad.IO.Class | ||
| import qualified Data.Attoparsec.Text as AP | ||
| import Data.Bifunctor (first) |
There was a problem hiding this comment.
This doesn't conflict with the import of first from Control.Arrow just above?
There was a problem hiding this comment.
import Control.Arrow hiding (app, first)
Added it to the hiding so I could have my bifunctor first.
| --------- PPKSCHEME DATA TYPE --------- | ||
|
|
||
| data PPKScheme = ED25519 | ETH | ||
| data PPKScheme = ED25519 | WebAuthn |
There was a problem hiding this comment.
I feel like there are really 4 PRs hiding in this one:
- Monomorphizing the signature scheme
- Dropping the ETH PPKScheme
- Dropping format-address
- Adding the WebAuthn PPKScheme
I want to hear from @larskuhtz whether we'd like to keep the ETH scheme, if we're going to be doing bridging with Ethereum...
There was a problem hiding this comment.
Just 4? Don't forget bumping base64-bytestring! :) That's got to count as 3 all on its own.
jwiegley
left a comment
There was a problem hiding this comment.
This is looking good, just a few comments made!
There was a problem hiding this comment.
Great work! Main note is we should reconsider removing ETH, see comment in Pact.Types.Crypto.
EDIT scratch that, Eth ECDSA support for Pact payloads is a dead letter. ECDSA support in the context of ethereum compatibility (Solidity ABI, or EVM support) uses a different payload.
Co-authored-by: John Wiegley <johnw@newartisans.com>
a1bcd02 to
7c8fb49
Compare
| -- Extract the original challenge from client data. | ||
| ClientDataJSON { challenge } <- A.eitherDecode (BSL.fromStrict clientData) |
There was a problem hiding this comment.
I'm a bit worried by the amount of aeson instances here being used to decode things that are on-chain, but I don't suggest changing this at this point.
| publicKey <- first show $ | ||
| Serialise.deserialiseOrFail @WA.CosePublicKey | ||
| (BSL.fromStrict pubBS) |
There was a problem hiding this comment.
Should we restrict the algorithm here to just a few to avoid algorithm confusion attacks? I think @EnoF wanted the algorithm "ES256", which has COSE identifier -7. https://portswigger.net/web-security/jwt/algorithm-confusion
There was a problem hiding this comment.
I've added some code that restricts to -7 or -8. (ECDSA SHA-256, and EdDSA respectively).
I informally tested the restriction by setting the requirement to -1 or -8 temporarily and observing that the happy path signature failed.
I'll hopefully be able to generate a test case that we can check in to the repo soon. But it requires spinning up a server that. implements webauthn to create new sample keys with a different signing algorithm.
| -- This test uses example public keys, clientData and authenticatorData from a | ||
| -- real WebAuthn browser session from a test user. | ||
| verifyWebAuthnSignature :: Spec |
There was a problem hiding this comment.
If we do restrict the signing algorithm we should have a test for it
Co-authored-by: Edmund Noble <edmundnoble@users.noreply.github.com>
Co-authored-by: Lars Kuhtz <lars@kadena.io>
This PR adds
WebAuthnas aPPKScheme, allowing WebAuthn signatures to be used for user authentication in pact commands.It also removes some things that were hard to maintain alongside
WebAuthn:Schemeclass are removed.SomeKeyPairis removed.Schemeclass except_validare removed.ETHscheme and all its support code were removed.Removing those things has several consequences:
Schemeclass switch to monomorphic versions, assuming ED25519format-addressrepl builtin is removed (it relied on a deletedSchememethod)The removed methods in
Schemeare primarily used in client code, for instance when Pact generates a .yaml command file, or when we generate keys in tests. This helps justify the simplification ofScheme, since we're primarily impacting client code. The server-oriented code paths are relatively unchanged.Implementation
A signature is valid as a WebAuthn signature if
clientDataJSON,authenticatorData, andsignatureClients may issue transactions with webauthn signatures by stringifying a JSON blob according to the spec. An example of such a blob is included in the unit tests.
base64-bytestring upgrade
The
webauthnlibrary requiresbase64-bytestring > 1.0, and pact requiresbase64-bytestring < 1.0, so this PR also bumps thebase64-bytestringlibrary inpact(which will require a bump inchainweb-node, too).Bumping
base64-bytestringis difficult because the new version produces different error messages than the old version (and in some cases, the new version rejects base64-encoded messages that were accepted by old versions). These error messages end up on chain, so to get hash compatibility for historical blocks, we must emulate the oldbase64-bytestring's behavior. We encountered this issue only in thedecodeBase64native and thestr-to-intnative, so the behavior-shimming code is colocated with the native definitions (inNative.hs).