136 lines
2.3 KiB
Go
136 lines
2.3 KiB
Go
package types
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/status-im/keycard-go/apdu"
|
|
)
|
|
|
|
var (
|
|
TagSignatureTemplate = uint8(0xA0)
|
|
TagRawSignature = uint8(0x80)
|
|
)
|
|
|
|
type Signature struct {
|
|
pubKey []byte
|
|
r []byte
|
|
s []byte
|
|
v byte
|
|
}
|
|
|
|
func ParseSignature(message, resp []byte) (*Signature, error) {
|
|
// check for old template first because TagRawSignature matches the pubkey tag
|
|
template, err := apdu.FindTag(resp, apdu.Tag{TagSignatureTemplate})
|
|
if err == nil {
|
|
return parseLegacySignature(message, template)
|
|
}
|
|
|
|
sig, err := apdu.FindTag(resp, apdu.Tag{TagRawSignature})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ParseRecoverableSignature(message, sig)
|
|
}
|
|
|
|
func ParseRecoverableSignature(message, sig []byte) (*Signature, error) {
|
|
if len(sig) != 65 {
|
|
return nil, errors.New("invalid signature")
|
|
}
|
|
|
|
pubKey, err := crypto.Ecrecover(message, sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Signature{
|
|
pubKey: pubKey,
|
|
r: sig[0:32],
|
|
s: sig[32:64],
|
|
v: sig[64],
|
|
}, nil
|
|
}
|
|
|
|
func DERSignatureToRS(tlv []byte) ([]byte, []byte, error) {
|
|
r, err := apdu.FindTagN(tlv, 0, apdu.Tag{0x30}, apdu.Tag{0x02})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if len(r) > 32 {
|
|
r = r[len(r)-32:]
|
|
}
|
|
|
|
s, err := apdu.FindTagN(tlv, 1, apdu.Tag{0x30}, apdu.Tag{0x02})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if len(s) > 32 {
|
|
s = s[len(s)-32:]
|
|
}
|
|
|
|
return r, s, nil
|
|
}
|
|
|
|
func (s *Signature) PubKey() []byte {
|
|
return s.pubKey
|
|
}
|
|
|
|
func (s *Signature) R() []byte {
|
|
return s.r
|
|
}
|
|
|
|
func (s *Signature) S() []byte {
|
|
return s.s
|
|
}
|
|
|
|
func (s *Signature) V() byte {
|
|
return s.v
|
|
}
|
|
|
|
func parseLegacySignature(message, template []byte) (*Signature, error) {
|
|
pubKey, err := apdu.FindTag(template, apdu.Tag{0x80})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r, s, err := DERSignatureToRS(template)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
v, err := calculateV(message, pubKey, r, s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Signature{
|
|
pubKey: pubKey,
|
|
r: r,
|
|
s: s,
|
|
v: v,
|
|
}, nil
|
|
}
|
|
|
|
func calculateV(message, pubKey, r, s []byte) (v byte, err error) {
|
|
rs := append(r, s...)
|
|
for i := 0; i < 4; i++ {
|
|
v = byte(i)
|
|
sig := append(rs, v)
|
|
rec, err := crypto.Ecrecover(message, sig)
|
|
if err != nil {
|
|
return v, err
|
|
}
|
|
|
|
if bytes.Equal(pubKey, rec) {
|
|
return v, nil
|
|
}
|
|
}
|
|
|
|
return v, err
|
|
}
|