Skip to content

Commit

Permalink
Recover signer from client envelopes
Browse files Browse the repository at this point in the history
  • Loading branch information
neekolas committed Oct 17, 2024
1 parent b58a961 commit 1e9a07a
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 11 deletions.
18 changes: 7 additions & 11 deletions pkg/authn/signingMethod.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import (

ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/golang-jwt/jwt/v5"
"github.com/xmtp/xmtpd/pkg/utils"
)

const (
ALGORITHM = "ES256K"
SIG_LENGTH = 65
R_LENGTH = 32
S_LENGTH = 32
DOMAIN_SEPARATION_LABEL = "jwt"
ALGORITHM = "ES256K"
SIG_LENGTH = 65
R_LENGTH = 32
S_LENGTH = 32
)

var (
Expand All @@ -36,7 +36,7 @@ func (sm *SigningMethodSecp256k1) Verify(signingString string, sig []byte, key i
return ErrWrongKeyFormat
}

hashedString := hashStringWithDomainSeparation(signingString)
hashedString := utils.HashJWTSignatureInput([]byte(signingString))

if len(sig) != SIG_LENGTH {
return ErrBadSignature
Expand All @@ -58,7 +58,7 @@ func (sm *SigningMethodSecp256k1) Sign(signingString string, key interface{}) ([
return nil, ErrWrongKeyFormat
}

hashedString := hashStringWithDomainSeparation(signingString)
hashedString := utils.HashJWTSignatureInput([]byte(signingString))

sig, err := ethcrypto.Sign(hashedString, priv)
if err != nil {
Expand All @@ -76,10 +76,6 @@ func (sm *SigningMethodSecp256k1) Alg() string {
return ALGORITHM
}

func hashStringWithDomainSeparation(signingString string) []byte {
return ethcrypto.Keccak256([]byte(DOMAIN_SEPARATION_LABEL + signingString))
}

func init() {
method := &SigningMethodSecp256k1{}
jwt.RegisterSigningMethod(ALGORITHM, func() jwt.SigningMethod {
Expand Down
6 changes: 6 additions & 0 deletions pkg/constants/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package constants

const (
JWT_DOMAIN_SEPARATION_LABEL = "jwt|"
PAYER_DOMAIN_SEPARATION_LABEL = "payer|"
)
43 changes: 43 additions & 0 deletions pkg/envelopes/envelopes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ package envelopes
import (
"testing"

ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
"github.com/xmtp/xmtpd/pkg/proto/identity/associations"
"github.com/xmtp/xmtpd/pkg/proto/xmtpv4/message_api"
"github.com/xmtp/xmtpd/pkg/testutils"
envelopeTestUtils "github.com/xmtp/xmtpd/pkg/testutils/envelopes"
"github.com/xmtp/xmtpd/pkg/topic"
"github.com/xmtp/xmtpd/pkg/utils"
"google.golang.org/protobuf/proto"
)

Expand Down Expand Up @@ -132,3 +136,42 @@ func TestPayloadType(t *testing.T) {
require.False(t, clientEnvelope.TopicMatchesPayload())

}

func TestRecoverSigner(t *testing.T) {
payerPrivateKey := testutils.RandomPrivateKey(t)
rawPayerEnv := envelopeTestUtils.CreatePayerEnvelope(t)

payerSignature, err := utils.SignPayerEnvelope(
rawPayerEnv.UnsignedClientEnvelope,
payerPrivateKey,
)
require.NoError(t, err)
rawPayerEnv.PayerSignature = &associations.RecoverableEcdsaSignature{
Bytes: payerSignature,
}

payerEnv, err := NewPayerEnvelope(rawPayerEnv)
require.NoError(t, err)

signer, err := payerEnv.RecoverSigner()
require.NoError(t, err)
require.Equal(t, ethcrypto.PubkeyToAddress(payerPrivateKey.PublicKey).Hex(), signer.Hex())

// Now test with an incorrect signature
wrongPayerSignature, err := utils.SignPayerEnvelope(
testutils.RandomBytes(128),
payerPrivateKey,
)
require.NoError(t, err)
rawPayerEnv.PayerSignature = &associations.RecoverableEcdsaSignature{
Bytes: wrongPayerSignature,
}
payerEnv, err = NewPayerEnvelope(rawPayerEnv)
require.NoError(t, err)

// This will recover an incorrect signer address because the inputs to the signature
// do not match the unsigned client envelope
newSigner, err := payerEnv.RecoverSigner()
require.NoError(t, err)
require.NotEqual(t, signer.Hex(), newSigner.Hex())
}
20 changes: 20 additions & 0 deletions pkg/envelopes/payer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package envelopes
import (
"errors"

"github.com/ethereum/go-ethereum/common"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/xmtp/xmtpd/pkg/proto/xmtpv4/message_api"
"github.com/xmtp/xmtpd/pkg/utils"
"google.golang.org/protobuf/proto"
)

Expand Down Expand Up @@ -35,3 +38,20 @@ func (p *PayerEnvelope) Bytes() ([]byte, error) {
}
return bytes, nil
}

func (p *PayerEnvelope) RecoverSigner() (*common.Address, error) {
payerSignature := p.proto.PayerSignature
if payerSignature == nil {
return nil, errors.New("payer signature is missing")
}

hash := utils.HashPayerSignatureInput(p.proto.UnsignedClientEnvelope)
signer, err := ethcrypto.SigToPub(hash, payerSignature.Bytes)
if err != nil {
return nil, err
}

address := ethcrypto.PubkeyToAddress(*signer)

return &address, nil
}
20 changes: 20 additions & 0 deletions pkg/utils/hash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package utils

import (
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/xmtp/xmtpd/pkg/constants"
)

func HashPayerSignatureInput(unsignedClientEnvelope []byte) []byte {
return ethcrypto.Keccak256(
[]byte(constants.PAYER_DOMAIN_SEPARATION_LABEL),
unsignedClientEnvelope,
)
}

func HashJWTSignatureInput(textToSign []byte) []byte {
return ethcrypto.Keccak256(
[]byte(constants.JWT_DOMAIN_SEPARATION_LABEL),
textToSign,
)
}
20 changes: 20 additions & 0 deletions pkg/utils/signature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package utils

import (
"crypto/ecdsa"

ethcrypto "github.com/ethereum/go-ethereum/crypto"
)

func SignPayerEnvelope(
unsignedClientEnvelope []byte,
payerPrivateKey *ecdsa.PrivateKey,
) ([]byte, error) {
hash := HashPayerSignatureInput(unsignedClientEnvelope)
signature, err := ethcrypto.Sign(hash, payerPrivateKey)
if err != nil {
return nil, err
}

return signature, nil
}

0 comments on commit 1e9a07a

Please sign in to comment.