Giter Club home page Giter Club logo

siwe-go's Introduction

Sign-In with Ethereum

This package provides a pure Go implementation of EIP-4361: Sign In With Ethereum.

Installation

SIWE can be easily installed in any Go project by running:

go get -u github.com/spruceid/siwe-go

Usage

SIWE exposes a Message struct which implements EIP-4361.

Parsing a SIWE Message

Parsing is done via the siwe.ParseMessage function:

var message *siwe.Message
var err error

message, err = siwe.ParseMessage(messageStr)

The function will return a nil pointer and an error if there was an issue while parsing.

Verifying and Authenticating a SIWE Message

Verification and Authentication is performed via EIP-191, using the address field of the Message as the expected signer. This returns the Ethereum public key of the signer:

var publicKey *ecdsa.PublicKey
var err error

publicKey, err = message.VerifyEIP191(signature)

The time constraints (expiry and not-before) can also be validated, at current or particular times:

var message *siwe.Message

if message.ValidNow() {
  // ...
}

// equivalent to

if message.ValidAt(time.Now().UTC()) {
  // ...
}

Combined verification of time constraints and authentication can be done in a single call with verify:

var publicKey *ecdsa.PublicKey
var err error

// Optional domain and nonce variable to be matched against the
// built message struct being verified
var optionalDomain *string
var optionalNonce *string

// Optional timestamp variable to verify at any point
// in time, by default it will use `time.Now()`
var optionalTimestamp *time.Time

publicKey, err = message.Verify(signature, optionalDomain, optionalNonce, optionalTimestamp)

// If you won't be using domain and nonce matching and want
// to verify the message at current time, it's
// safe to pass `nil` in these arguments
publicKey, err = message.Verify(signature, nil, nil, nil)

Serialization of a SIWE Message

Message instances can also be serialized as their EIP-4361 string representations via the String method:

fmt.Printf("%s", message.String())

Signing Messages from Go code

To sign messages directly from Go code, you will need to do it like shown below to correctly follow the personal_sign format.

func signHash(data []byte) common.Hash {
	msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
	return crypto.Keccak256Hash([]byte(msg))
}

func signMessage(message string, privateKey *ecdsa.PrivateKey) ([]byte, error) {
	sign := signHash([]byte(message))
	signature, err := crypto.Sign(sign.Bytes(), privateKey)

	if err != nil {
		return nil, err
	}

	signature[64] += 27
	return signature, nil
}

Disclaimer

Our Go library for Sign-In with Ethereum has not yet undergone a formal security audit. We welcome continued feedback on the usability, architecture, and security of this implementation.

See Also

siwe-go's People

Contributors

theosirian avatar dependabot[bot] avatar harryet avatar sbihel avatar a00012025 avatar jsign avatar nachobonilla avatar

Stargazers

Chunghoon Im avatar Yao avatar Glitch avatar iceymoss avatar aaron avatar  avatar kevin.xu avatar gargs avatar  avatar  avatar yosg avatar Nguyễn Ngọc Hùng avatar Lisandro avatar Bo avatar Alexander Blanc avatar Lil'Horse avatar Crossle Song avatar toby avatar Randy Chang avatar Bethuel Mmbaga avatar  avatar Joe Netti avatar YSTYLE-L.X.Y avatar Amnot avatar Michael Demarais avatar jiangplus avatar funjoke avatar smithereens avatar 0xshanks avatar Tomoyasu Ishikawa avatar Wondong Shin avatar sfter avatar Javed Khan avatar Tarrence van As avatar Nikita avatar  avatar  avatar Alexandr Vorobev avatar 卫晓栋 avatar Biplav avatar  avatar Nikki Abarca avatar Rafael Escrich avatar Elise Ng avatar Stéphane Busso avatar rb avatar AlexCrypto avatar midget crabapple avatar Albert Putra Purnama avatar kt avatar 0xZensh avatar Sander Pick avatar

Watchers

Charles E. Lehner avatar wyc avatar Em avatar Jakub Szerszen avatar  avatar Sølst1c3 avatar Sam Gbafa avatar Gregório Granado Magalhães avatar  avatar  avatar  avatar Gregory Rocco avatar Juliano Cézar Chagas Tavares avatar K avatar  avatar

siwe-go's Issues

parse message not work

It is very funny that the parse message function cannot parse internally built messages I also tried the run test case swie_test.go, but it cannot pass. This repo is open to the public. werid.

	message, err := siwe.InitMessage("example.com", "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", "https://example.com", "2", map[string]interface{}{})
	assert.Nil(t, err)
	assert.Equal(t, "2", message.GetNonce())
	//fmt.Print(message.String())
	// verify nonce
	resMessage, err := siwe.ParseMessage(message.String())
	assert.Nil(t, err)
	assert.Nil(t, message, resMessage)

The above code get an error, really interesting.

Edge case , that works on siwe-js and does not work on siwe-go

Hi,

Arthur from tally here. we use 'siwe-go' in the backend to authenticate SIWE requests. We noticed that it did not seem to work for users signing with Ledger+Metamask. I've put together some unit tests that duplicate the error.

You'll see that the javascript tests validate the signature, but the go tests for the same signature do not. We're not super familiar with how the cryptography works here. Do you know what the problem might be? We're looking for some help to fix the issue.

Happy to provide more context if that's helpful!

Tldr: I'm trying to sign (siwe) with my ledger at the tally website and got some errors. I was able to narrow down that it works on the JS code, (worked on https://login.xyz ) but it's not working with .GO

I put all replicable steps here:
https://github.com/afa7789/siwe-go

A more comprehensive error message

General Description

I have a question regarding siwe-go's error implementation. I am having difficulty debugging when there's some parsing/serialization error when using siwe.ParseMessage this is mainly because the Error() implementation hides the specific error message.

Specific Example

Specifically if I initialize using something like:

msg, err := siwe.InitMessage(
		challengeReq.Domain,
		challengeReq.Identifier,
		challengeReq.URI,
		"some-random-version", // incorrect siwe message version
		msgOptions,
	)

Then try to parse it using siwe.ParseMessage, it will return an error Invalid Message without explicitly specifying which part of the message is invalid. In this case, I think I should have a way to get Message could not be parsed message from the error since the regex validation failed.

Proposed Solution

I want to try to open a discussion here on how to handle this. But there's generally 2 things that I think we need to address:

  1. Have some way for devs to get a more specific error message outside of the default Error() implementation.
  2. A more granular regular expression validation, resulting in a better error handling in addition to (1).

For (1), I think since siwe already have Error types such as ExpiredMessage, InvalidMessage, InvalidSignature, we can just make the error string public so that developers can type cast the error into one of these types and see the string message for themselves.
For (2), A way that I think should work is to break apart the regexp.Regexp validation into smaller regexp.Regexp so that we can find out exactly where the expression fail.

ambiguous import: found package github.com/btcsuite/btcd/chaincfg/chainhash in multiple modules

I get below error while importing library using go get -u github.com/spruceid/siwe-go

github.com/spruceid/siwe-go imports
	github.com/ethereum/go-ethereum/crypto imports
	github.com/btcsuite/btcd/btcec/v2/ecdsa tested by
	github.com/btcsuite/btcd/btcec/v2/ecdsa.test imports
	github.com/btcsuite/btcd/chaincfg/chainhash: ambiguous import: found package github.com/btcsuite/btcd/chaincfg/chainhash in multiple modules:
	github.com/btcsuite/btcd v0.20.1-beta (/Users/aniket/go/pkg/mod/github.com/btcsuite/[email protected]/chaincfg/chainhash)
	github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 (/Users/aniket/go/pkg/mod/github.com/btcsuite/btcd/chaincfg/[email protected])

Can someone suggest fix for this?

Parsing/Validation and Verification

We should make sure the terms validation and verification are used consistently. We should validate a SIWE message when it is parsed or created. Validation includes schema validation and making sure the message complies with EIP-4361 spec. Verification means the EIP-191 signature is correct and is verified against a given optional domain, timestamp, nonce etc.

For this I propose, we do the following for validation:

  • SIWE message complies with the SIWE ABNF in general
  • SIWE message contains valid types such as for address (EIP-55), authority (see RFC/EIP), Resources (should be URIs) etc.
  • SIWE has all mandatory parameters (e.g., domain, address, issued at)
  • SIWE only uses accepted white spaces (LF)
  • Parameters in SIWE message are correctly ordered (this is required by the ABNF)

As a result of the validation above, it should not be possible to get a SIWE message that is invalid. Then verification includes the following:

  • verifying the EIP-191 signature
  • verifying that expiration time is < date.now() + some minimal clock skew
  • verifying that expected nonce and domain are in the message (for this, a server would need to have a configuration for domain and has to remember issued nonces which should also expire individually -> note, if a server issues a nonce it is still up to the frontend WHEN to create the SIWE message which can use a different issued at and a longer expiration time).
  • verifying not before date < date.now()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.