From ac248d3ed537fcd8acbd88b905ed8ce98d93974c Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 10 Jan 2023 17:13:52 -0700 Subject: [PATCH 1/4] Added SanitizeChecksums to handler package, and similar SanitizeChecksum to common package --- api/handler/common.go | 31 +++++++++++++++++++++++++++++++ api/handler/login.go | 4 +++- api/handler/quotes.go | 8 ++++++++ api/handler/transact.go | 7 +++++++ pkg/internal/common/evm.go | 11 +++++++---- pkg/internal/common/evm_test.go | 22 ++++++++++++++++++++++ 6 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 pkg/internal/common/evm_test.go diff --git a/api/handler/common.go b/api/handler/common.go index 93f11417..d1f710c3 100644 --- a/api/handler/common.go +++ b/api/handler/common.go @@ -4,10 +4,12 @@ import ( "fmt" "net/http" "os" + "regexp" "strings" "time" service "github.com/String-xyz/string-api/pkg/service" + "golang.org/x/crypto/sha3" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" "github.com/labstack/echo/v4" @@ -116,3 +118,32 @@ func DeleteAuthCookies(c echo.Context) error { func IsLocalEnv() bool { return os.Getenv("ENV") == "local" } + +func validAddress(addr string) bool { + re := regexp.MustCompile("^0x[0-9a-fA-F]{40}$") + return re.MatchString(addr) +} + +func SanitizeChecksums(addrs ...*string) { + for _, addr := range addrs { + if !validAddress(*addr) { + continue + } + lowerCase := strings.ToLower(*addr)[2:] + hash := sha3.NewLegacyKeccak256() + hash.Write([]byte(lowerCase)) + hashBytes := hash.Sum(nil) + + valid := "0x" + for i, b := range lowerCase { + c := string(b) + if b < '0' || b > '9' { + if hashBytes[i/2]&byte(128-i%2*120) != 0 { + c = string(b - 32) + } + } + valid += c + } + *addr = valid + } +} diff --git a/api/handler/login.go b/api/handler/login.go index 63524802..be08c07a 100644 --- a/api/handler/login.go +++ b/api/handler/login.go @@ -34,7 +34,7 @@ func (l login) NoncePayload(c echo.Context) error { if walletAddress == "" { return BadRequestError(c, "WalletAddress must be provided") } - + SanitizeChecksums(&walletAddress) payload, err := l.Service.PayloadToSign(walletAddress) if err != nil { LogStringError(c, err, "login: request wallet login") @@ -85,6 +85,8 @@ func (l login) RefreshToken(c echo.Context) error { return InvalidPayloadError(c, err) } + SanitizeChecksums(&body.WalletAddress) + cookie, err := c.Cookie("refresh_token") if err != nil { LogStringError(c, err, "RefreshToken: unable to get refresh_token cookie") diff --git a/api/handler/quotes.go b/api/handler/quotes.go index 8c5a180a..973831d4 100644 --- a/api/handler/quotes.go +++ b/api/handler/quotes.go @@ -30,6 +30,14 @@ func (q quote) Quote(c echo.Context) error { LogStringError(c, err, "quote: quote bind") return BadRequestError(c) } + SanitizeChecksums(&body.CxAddr, &body.UserAddress) + // Sanitize Checksum for body.CxParams? It might look like this: + var cxParams []*string + for _, p := range body.CxParams { + cxParams = append(cxParams, &p) + } + SanitizeChecksums(cxParams...) + // userId := c.Get("userId").(string) res, err := q.Service.Quote(body) // TODO: pass in userId and use it if err != nil && errors.Cause(err).Error() == "w3: response handling failed: execution reverted" { diff --git a/api/handler/transact.go b/api/handler/transact.go index 071df638..d4a4da3c 100644 --- a/api/handler/transact.go +++ b/api/handler/transact.go @@ -29,6 +29,13 @@ func (t transaction) Transact(c echo.Context) error { LogStringError(c, err, "transact: execute bind") return BadRequestError(c) } + SanitizeChecksums(&body.CxAddr, &body.UserAddress) + // Sanitize Checksum for body.CxParams? It might look like this: + var cxParams []*string + for _, p := range body.CxParams { + cxParams = append(cxParams, &p) + } + SanitizeChecksums(cxParams...) userId := c.Get("userId").(string) deviceId := c.Get("deviceId").(string) res, err := t.Service.Execute(body, userId, deviceId) diff --git a/pkg/internal/common/evm.go b/pkg/internal/common/evm.go index 15835ddb..c62acc0f 100644 --- a/pkg/internal/common/evm.go +++ b/pkg/internal/common/evm.go @@ -90,9 +90,7 @@ func IsWallet(addr string) bool { if !validAddress(addr) { return false } - if !validChecksum(addr) { - return false - } + addr = SanitizeChecksum(addr) // Copy correct checksum, although endpoint handlers are doing this already address := common.HexToAddress(addr) bytecode, err := geth.CodeAt(context.Background(), address, nil) @@ -104,6 +102,11 @@ func IsWallet(addr string) bool { } func validChecksum(addr string) bool { + valid := SanitizeChecksum(addr) + return addr == valid +} + +func SanitizeChecksum(addr string) string { lowerCase := strings.ToLower(addr)[2:] hash := sha3.NewLegacyKeccak256() hash.Write([]byte(lowerCase)) @@ -119,7 +122,7 @@ func validChecksum(addr string) bool { } valid += c } - return addr == valid + return valid } func validAddress(addr string) bool { diff --git a/pkg/internal/common/evm_test.go b/pkg/internal/common/evm_test.go new file mode 100644 index 00000000..c541948a --- /dev/null +++ b/pkg/internal/common/evm_test.go @@ -0,0 +1,22 @@ +package common + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestChecksumValid(t *testing.T) { + valid := validChecksum("0x44A4b9E2A69d86BA382a511f845CbF2E31286770") + assert.Equal(t, valid, true) +} + +func TestChecksumInvalid(t *testing.T) { + valid := validChecksum("0x44a4b9E2A69d86BA382a511f845CbF2E31286770") + assert.Equal(t, valid, false) +} + +func TestSanitizeChecksum(t *testing.T) { + valid := SanitizeChecksum("0x44a4b9E2A69d86BA382a511f845CbF2E31286770") + assert.Equal(t, valid, "0x44A4b9E2A69d86BA382a511f845CbF2E31286770") +} From 8c4500991b29e7c9d1f040283a291f1278b9b52e Mon Sep 17 00:00:00 2001 From: Sean Date: Wed, 11 Jan 2023 14:25:48 -0700 Subject: [PATCH 2/4] Added empty wallet authentication prefix for testing locally --- pkg/service/auth.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/service/auth.go b/pkg/service/auth.go index 668b9b0c..48f256e3 100644 --- a/pkg/service/auth.go +++ b/pkg/service/auth.go @@ -22,6 +22,8 @@ type SignablePayload struct { var hexRegex *regexp.Regexp = regexp.MustCompile(`^0x[a-fA-F0-9]{40}$`) +// var walletAuthenticationPrefix string = "" // For testing locally + var walletAuthenticationPrefix string = "Thank you for using String! By signing this message you are:\n\n1) Authorizing String to initiate off-chain transactions on your behalf, including your bank account, credit card, or debit card.\n\n2) Confirming that this wallet is owned by you.\n\nThis request will not trigger any blockchain transaction or cost any gas.\n\nNonce: " type RefreshTokenResponse struct { From b707e3db862b3a126c8518f4c486d7f8eb9688f0 Mon Sep 17 00:00:00 2001 From: Sean Date: Wed, 11 Jan 2023 15:31:40 -0700 Subject: [PATCH 3/4] Fixed wrong pointer bug --- api/handler/checksum_test.go | 17 +++++++++++++++++ api/handler/quotes.go | 6 ++---- api/handler/transact.go | 6 ++---- 3 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 api/handler/checksum_test.go diff --git a/api/handler/checksum_test.go b/api/handler/checksum_test.go new file mode 100644 index 00000000..1c708c3a --- /dev/null +++ b/api/handler/checksum_test.go @@ -0,0 +1,17 @@ +package handler + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSanitizeChecksumArray(t *testing.T) { + arrayByValue := []string{"0x44a4b9E2A69d86BA382a511f845CbF2E31286770"} + var cxParams []*string + for _, p := range arrayByValue { + cxParams = append(cxParams, &p) + } + SanitizeChecksums(cxParams...) + assert.Equal(t, *cxParams[0], "0x44A4b9E2A69d86BA382a511f845CbF2E31286770") +} diff --git a/api/handler/quotes.go b/api/handler/quotes.go index 973831d4..e736a28b 100644 --- a/api/handler/quotes.go +++ b/api/handler/quotes.go @@ -32,11 +32,9 @@ func (q quote) Quote(c echo.Context) error { } SanitizeChecksums(&body.CxAddr, &body.UserAddress) // Sanitize Checksum for body.CxParams? It might look like this: - var cxParams []*string - for _, p := range body.CxParams { - cxParams = append(cxParams, &p) + for i := range body.CxParams { + SanitizeChecksums(&body.CxParams[i]) } - SanitizeChecksums(cxParams...) // userId := c.Get("userId").(string) res, err := q.Service.Quote(body) // TODO: pass in userId and use it diff --git a/api/handler/transact.go b/api/handler/transact.go index d4a4da3c..0178fb3d 100644 --- a/api/handler/transact.go +++ b/api/handler/transact.go @@ -31,11 +31,9 @@ func (t transaction) Transact(c echo.Context) error { } SanitizeChecksums(&body.CxAddr, &body.UserAddress) // Sanitize Checksum for body.CxParams? It might look like this: - var cxParams []*string - for _, p := range body.CxParams { - cxParams = append(cxParams, &p) + for i := range body.CxParams { + SanitizeChecksums(&body.CxParams[i]) } - SanitizeChecksums(cxParams...) userId := c.Get("userId").(string) deviceId := c.Get("deviceId").(string) res, err := t.Service.Execute(body, userId, deviceId) From 1f79804bd6e02235f19d22b51f8d6f6fe0f45103 Mon Sep 17 00:00:00 2001 From: Sean Date: Wed, 11 Jan 2023 16:30:50 -0700 Subject: [PATCH 4/4] Only generate fake checkout card token if ENV is LOCAL --- pkg/service/checkout.go | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/pkg/service/checkout.go b/pkg/service/checkout.go index d98195b2..fd7db0c6 100644 --- a/pkg/service/checkout.go +++ b/pkg/service/checkout.go @@ -56,22 +56,27 @@ func AuthorizeCharge(amount float64, userWallet string, tokenId string) (auth Au } client := payments.NewClient(*config) - // Generate a payment token ID in case we don't yet have one in the front end - // For testing purposes only - card := tokens.Card{ - Type: checkoutCommon.Card, - Number: "4242424242424242", - ExpiryMonth: 2, - ExpiryYear: 2024, - Name: "Customer Name", - CVV: "100", - } - paymentToken, err := CreateToken(&card) - if err != nil { - return auth, common.StringError(err) - } - paymentTokenID := paymentToken.Created.Token - if tokenId != "" { + var paymentTokenID string + if common.IsLocalEnv() { + // Generate a payment token ID in case we don't yet have one in the front end + // For testing purposes only + card := tokens.Card{ + Type: checkoutCommon.Card, + Number: "4242424242424242", + ExpiryMonth: 2, + ExpiryYear: 2024, + Name: "Customer Name", + CVV: "100", + } + paymentToken, err := CreateToken(&card) + if err != nil { + return auth, common.StringError(err) + } + paymentTokenID = paymentToken.Created.Token + if tokenId != "" { + paymentTokenID = tokenId + } + } else { paymentTokenID = tokenId }