From cd4ea135b354762372758ea18e48dd1aef6befa8 Mon Sep 17 00:00:00 2001 From: Marcin Gorzynski Date: Tue, 6 Feb 2024 13:53:00 +0100 Subject: [PATCH] Return to having sessionId = pubKey/sessAdddr --- intents/intent.go | 72 ++++++++++++-------- intents/intent_send_transaction_test.go | 11 +-- intents/intent_sign_message_test.go | 13 +--- intents/intent_test.go | 89 +++++++++++++++++++++---- 4 files changed, 121 insertions(+), 64 deletions(-) diff --git a/intents/intent.go b/intents/intent.go index 6ca60dc..8d22fe6 100644 --- a/intents/intent.go +++ b/intents/intent.go @@ -22,7 +22,12 @@ var ( ErrInvalidSignature = fmt.Errorf("invalid signature") ) -type IntentVerifierGetter func(sessionId string) (string, error) +type KeyType int + +const ( + KeyTypeSECP256K1 KeyType = iota + KeyTypeSECP256R1 +) type Intent struct { Version string `json:"version"` @@ -78,24 +83,19 @@ func (intent *Intent) Hash() ([]byte, error) { return crypto.Keccak256(packetBytes), nil } -func (intent *Intent) Signers(verifierGetter IntentVerifierGetter) []string { +func (intent *Intent) Signers() []string { var signers []string for _, signature := range intent.signatures { - sessionVerifier, err := verifierGetter(signature.SessionId) - if err != nil { - continue - } - - if intent.isValidSignature(sessionVerifier, signature.Signature) { - signers = append(signers, sessionVerifier) + if intent.isValidSignature(signature.SessionId, signature.Signature) { + signers = append(signers, signature.SessionId) } } return signers } -func (intent *Intent) IsValid(verifierGetter IntentVerifierGetter) (bool, error) { +func (intent *Intent) IsValid() (bool, error) { // Check if the packet is valid var packet packets.BasePacket err := json.Unmarshal(intent.Packet, &packet) @@ -118,12 +118,7 @@ func (intent *Intent) IsValid(verifierGetter IntentVerifierGetter) (bool, error) // Check if all signatures are valid for _, signature := range intent.signatures { - sessionVerifier, err := verifierGetter(signature.SessionId) - if err != nil { - return false, fmt.Errorf("intent: %w", err) - } - - if !intent.isValidSignature(sessionVerifier, signature.Signature) { + if !intent.isValidSignature(signature.SessionId, signature.Signature) { return false, fmt.Errorf("intent: %w", ErrInvalidSignature) } } @@ -135,20 +130,40 @@ func (intent *Intent) IsValid(verifierGetter IntentVerifierGetter) (bool, error) return true, nil } -func (intent *Intent) isValidSECP256R1Session(session string, signature string) bool { - return strings.HasPrefix(session, "r1:") && strings.HasPrefix(signature, "r1:") +func (intent *Intent) isValidP256R1Session(sessionId string, signature string) bool { + // handle old session ids + if len(sessionId) <= 42 { + return false + } + + // handle key typed session ids + sessionIdBytes := common.FromHex(sessionId) + switch KeyType(sessionIdBytes[0]) { + case KeyTypeSECP256K1: + return false + case KeyTypeSECP256R1: + return true + default: + return false + } } func (intent *Intent) isValidSignature(session string, signature string) bool { - if intent.isValidSECP256R1Session(session, signature) { - return intent.isValidSignatureSECP256R1(session, signature) + if intent.isValidP256R1Session(session, signature) { + return intent.isValidSignatureP256R1(session, signature) } else { - return intent.isValidSignatureSPECP256K1(session, signature) + return intent.isValidSignatureP256K1(session, signature) } } -// isValidSignatureSPECP256K1 checks if the signature is valid for the given secp256k1 session -func (intent *Intent) isValidSignatureSPECP256K1(session string, signature string) bool { +// isValidSignatureP256K1 checks if the signature is valid for the given secp256k1 session +func (intent *Intent) isValidSignatureP256K1(sessionAddress string, signature string) bool { + // handle typed session address + if len(sessionAddress) > 42 { + sessionAddressBytes := common.FromHex(sessionAddress) + sessionAddress = fmt.Sprintf("0x%s", common.Bytes2Hex(sessionAddressBytes[1:])) + } + // Get hash of the packet hash, err := intent.Hash() if err != nil { @@ -173,18 +188,17 @@ func (intent *Intent) isValidSignatureSPECP256K1(session string, signature strin addr := common.BytesToAddress(crypto.Keccak256(pubKey[1:])[12:]) // Check if the recovered address matches the session address - return strings.ToLower(addr.Hex()) == strings.ToLower(session) + return strings.ToLower(addr.Hex()) == strings.ToLower(sessionAddress) } -// isValidSignatureSPECP256K1 checks if the signature is valid for the given secp256r1 session -func (intent *Intent) isValidSignatureSECP256R1(session string, signature string) bool { - // session - sessionBuff := common.FromHex(session[3:]) +// isValidSignatureP256R1 checks if the signature is valid for the given secp256r1 session +func (intent *Intent) isValidSignatureP256R1(publicKey string, signature string) bool { + publicKeyBuff := common.FromHex(publicKey)[1:] // public key // TODO: check if can use ecdh instead of unmarshal // NOTE: no way to convert ecdh pub key into elliptic pub key? - x, y := elliptic.Unmarshal(elliptic.P256(), sessionBuff) + x, y := elliptic.Unmarshal(elliptic.P256(), publicKeyBuff) if x == nil || y == nil { return false } diff --git a/intents/intent_send_transaction_test.go b/intents/intent_send_transaction_test.go index 6e2f0b8..de433f4 100644 --- a/intents/intent_send_transaction_test.go +++ b/intents/intent_send_transaction_test.go @@ -2,7 +2,6 @@ package intents import ( "encoding/json" - "fmt" "math/big" "testing" @@ -102,15 +101,7 @@ func TestRecoverTransactionIntent(t *testing.T) { assert.Nil(t, err) assert.Equal(t, common.Bytes2Hex(hash), "2feb22d5631075041c5aaafce98da8950d706a9eca8d9ea2b28ea95142d8e890") - getSessionVerifier := func(sessionId string) (string, error) { - if sessionId == "afaf60c0-67ba-4c9b-89ae-b115c78026a4" { - return "0x1111BD4F3233e7a7f552AdAf32C910fD30de598B", nil - } else { - return "", fmt.Errorf("invalid session id") - } - } - - signers := intent.Signers(getSessionVerifier) + signers := intent.Signers() assert.Equal(t, 1, len(signers)) assert.Equal(t, "0x1111BD4F3233e7a7f552AdAf32C910fD30de598B", signers[0]) diff --git a/intents/intent_sign_message_test.go b/intents/intent_sign_message_test.go index abf4b1f..71448b0 100644 --- a/intents/intent_sign_message_test.go +++ b/intents/intent_sign_message_test.go @@ -2,7 +2,6 @@ package intents import ( "encoding/json" - "fmt" "math/big" "testing" @@ -24,7 +23,7 @@ func TestRecoverMessageIntent(t *testing.T) { "message": "0xdeadbeef" }, "signatures": [{ - "sessionId": "afaf60c0-67ba-4c9b-89ae-b115c78026a4", + "sessionId": "0x1111BD4F3233e7a7f552AdAf32C910fD30de598B", "signature": "0x827b2a2afbf4a8a79e761fdb26e567b519a56a06e897dce5517b3ccfb408b55f20aaba276c1dade28112f51fe7262fbd0508da0019c0f8582c41b2be451ddede1b" }] }` @@ -40,15 +39,7 @@ func TestRecoverMessageIntent(t *testing.T) { assert.Nil(t, err) assert.Equal(t, common.Bytes2Hex(hash), "5b15538a25716e951630dde1cf38ae056d764976145d1134576461203a621ddb") - getSessionVerifier := func(sessionId string) (string, error) { - if sessionId == "afaf60c0-67ba-4c9b-89ae-b115c78026a4" { - return "0x1111BD4F3233e7a7f552AdAf32C910fD30de598B", nil - } else { - return "", fmt.Errorf("invalid session id") - } - } - - signers := intent.Signers(getSessionVerifier) + signers := intent.Signers() assert.Equal(t, 1, len(signers)) assert.Equal(t, "0x1111BD4F3233e7a7f552AdAf32C910fD30de598B", signers[0]) diff --git a/intents/intent_test.go b/intents/intent_test.go index 81a7885..8bff319 100644 --- a/intents/intent_test.go +++ b/intents/intent_test.go @@ -11,13 +11,10 @@ import ( "github.com/0xsequence/ethkit/go-ethereum/common" "github.com/davecgh/go-spew/spew" - "github.com/google/uuid" "github.com/stretchr/testify/assert" ) func TestParseAndRecoverIntent(t *testing.T) { - fmt.Println(uuid.New().String()) - data := `{ "version": "1.0.0", "packet": { @@ -35,7 +32,7 @@ func TestParseAndRecoverIntent(t *testing.T) { }] }, "signatures": [{ - "sessionId": "afaf60c0-67ba-4c9b-89ae-b115c78026a4", + "sessionId": "0x1111BD4F3233e7a7f552AdAf32C910fD30de598B", "signature": "0xcca6253c4fd281247ddd0fa487252ef91932eaec8d68b61f0901ccaa70345bf66fdbbd98ed3e3c9752f9e35ef2a7bc88dd9c8ae23c594241b476fe988824ab881c" }] }` @@ -51,15 +48,7 @@ func TestParseAndRecoverIntent(t *testing.T) { assert.NotNil(t, hash) assert.Equal(t, common.Bytes2Hex(hash), "893060f818437f8e3d9b4d8e103c5eb3c325fa25dd0221fb7b61cca6dd03a79e") - getSessionVerifier := func(sessionId string) (string, error) { - if sessionId == "afaf60c0-67ba-4c9b-89ae-b115c78026a4" { - return "0x1111BD4F3233e7a7f552AdAf32C910fD30de598B", nil - } else { - return "", fmt.Errorf("invalid session id") - } - } - - signers := intent.Signers(getSessionVerifier) + signers := intent.Signers() assert.Equal(t, 1, len(signers)) assert.Equal(t, "0x1111BD4F3233e7a7f552AdAf32C910fD30de598B", signers[0]) assert.Equal(t, intent.PacketCode(), "sendTransactions") @@ -79,7 +68,77 @@ func TestParseAndRecoverIntent(t *testing.T) { assert.NotEqual(t, common.Bytes2Hex(hash), "893060f818437f8e3d9b4d8e103c5eb3c325fa25dd0221fb7b61cca6dd03a79e") assert.Equal(t, intent.PacketCode(), "sendTransactions2") - signers = intent.Signers(getSessionVerifier) + signers = intent.Signers() + assert.Equal(t, 0, len(signers)) + + // Parsing the JSON without tabs, spaces, newlines, etc. should still work + // and produce the same hash + data2 := `{"signatures":[{"signature":"0xcca6253c4fd281247ddd0fa487252ef91932eaec8d68b61f0901ccaa70345bf66fdbbd98ed3e3c9752f9e35ef2a7bc88dd9c8ae23c594241b476fe988824ab881c","session":"0x1111BD4F3233e7a7f552AdAf32C910fD30de598B"}],"version":"1.0.0","packet":{"transactions":[{"token":"0x0000000000000000000000000000000000000000","value":"0","type":"erc20send","to":"0x0dc9603d4da53841C1C83f3B550C6143e60e0425"}],"wallet":"0xD67FC48b298B09Ed3D03403d930769C527186c4e","expires":1600086400,"code":"sendTransactions","network":"1","identifier":"test-identifier","issued":1600000000}}` + intent2 := &Intent{} + err = json.Unmarshal([]byte(data2), intent2) + assert.Nil(t, err) + + hash2, err := intent2.Hash() + assert.Nil(t, err) + assert.NotNil(t, hash2) + assert.Equal(t, common.Bytes2Hex(hash2), "893060f818437f8e3d9b4d8e103c5eb3c325fa25dd0221fb7b61cca6dd03a79e") +} + +func TestParseAndRecoverIntent_SessionKeyP256K1Typed(t *testing.T) { + data := `{ + "version": "1.0.0", + "packet": { + "code": "sendTransactions", + "identifier": "test-identifier", + "issued": 1600000000, + "expires": 1600086400, + "wallet": "0xD67FC48b298B09Ed3D03403d930769C527186c4e", + "network": "1", + "transactions": [{ + "type": "erc20send", + "token": "0x0000000000000000000000000000000000000000", + "to": "0x0dc9603d4da53841C1C83f3B550C6143e60e0425", + "value": "0" + }] + }, + "signatures": [{ + "sessionId": "0x001111BD4F3233e7a7f552AdAf32C910fD30de598B", + "signature": "0xcca6253c4fd281247ddd0fa487252ef91932eaec8d68b61f0901ccaa70345bf66fdbbd98ed3e3c9752f9e35ef2a7bc88dd9c8ae23c594241b476fe988824ab881c" + }] + }` + + intent := &Intent{} + err := json.Unmarshal([]byte(data), intent) + assert.Nil(t, err) + + assert.Equal(t, "1.0.0", intent.Version) + + hash, err := intent.Hash() + assert.Nil(t, err) + assert.NotNil(t, hash) + assert.Equal(t, common.Bytes2Hex(hash), "893060f818437f8e3d9b4d8e103c5eb3c325fa25dd0221fb7b61cca6dd03a79e") + + signers := intent.Signers() + assert.Equal(t, 1, len(signers)) + assert.Equal(t, "0x001111BD4F3233e7a7f552AdAf32C910fD30de598B", signers[0]) + assert.Equal(t, intent.PacketCode(), "sendTransactions") + + // Changing the version should not affect the hash + intent.Version = "2.0.0" + hash, err = intent.Hash() + assert.Nil(t, err) + assert.NotNil(t, hash) + assert.Equal(t, common.Bytes2Hex(hash), "893060f818437f8e3d9b4d8e103c5eb3c325fa25dd0221fb7b61cca6dd03a79e") + + // Changing the packet code SHOULD affect the hash (and make Signers() return empty) + intent.Packet = json.RawMessage(`{"code": "sendTransactions2"}`) + hash, err = intent.Hash() + assert.Nil(t, err) + assert.NotNil(t, hash) + assert.NotEqual(t, common.Bytes2Hex(hash), "893060f818437f8e3d9b4d8e103c5eb3c325fa25dd0221fb7b61cca6dd03a79e") + assert.Equal(t, intent.PacketCode(), "sendTransactions2") + + signers = intent.Signers() assert.Equal(t, 0, len(signers)) // Parsing the JSON without tabs, spaces, newlines, etc. should still work @@ -104,6 +163,8 @@ func TestECDSAP256SessionSig(t *testing.T) { message := "0x7a7e5a0913e63cac5886afcafedba93b17baae3eb4066534ffdd5e3da3e8c714" signature := "r1:0x4038376385b045c19754bb69fa6cde925674778e6a1a78b8fa3135ec96b695aef5b8126c78dc17a2cc0be522a4e6154bf5152c908d763fb1c28e47cf419a3ea5" + fmt.Println(len(sessionId)) + // get public key from sessionId sessionIdBuff := common.FromHex(sessionId[3:])