lotus/chain/types/signature.go
2019-08-26 17:46:39 -07:00

182 lines
3.6 KiB
Go

package types
import (
"bytes"
"encoding/binary"
"fmt"
"io"
bls "github.com/filecoin-project/go-bls-sigs"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/lib/crypto"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/minio/blake2b-simd"
"github.com/polydawn/refmt/obj/atlas"
cbg "github.com/whyrusleeping/cbor-gen"
)
const SignatureMaxLength = 200
const (
KTSecp256k1 = "secp256k1"
KTBLS = "bls"
)
const (
IKTUnknown = -1
IKTSecp256k1 = iota
IKTBLS
)
func init() {
cbor.RegisterCborType(atlas.BuildEntry(Signature{}).Transform().
TransformMarshal(atlas.MakeMarshalTransformFunc(
func(s Signature) ([]byte, error) {
buf := make([]byte, 4)
n := binary.PutUvarint(buf, uint64(s.TypeCode()))
return append(buf[:n], s.Data...), nil
})).
TransformUnmarshal(atlas.MakeUnmarshalTransformFunc(
func(x []byte) (Signature, error) {
return SignatureFromBytes(x)
})).
Complete())
}
type Signature struct {
Type string
Data []byte
}
func SignatureFromBytes(x []byte) (Signature, error) {
val, nr := binary.Uvarint(x)
if nr != 1 {
return Signature{}, fmt.Errorf("signatures with type field longer than one byte are invalid")
}
var ts string
switch val {
case IKTSecp256k1:
ts = KTSecp256k1
case IKTBLS:
ts = KTBLS
default:
return Signature{}, fmt.Errorf("unsupported signature type: %d", val)
}
return Signature{
Type: ts,
Data: x[1:],
}, nil
}
func (s *Signature) Verify(addr address.Address, msg []byte) error {
if addr.Protocol() == address.ID {
return fmt.Errorf("must resolve ID addresses before using them to verify a signature")
}
b2sum := blake2b.Sum256(msg)
switch s.Type {
case KTSecp256k1:
pubk, err := crypto.EcRecover(b2sum[:], s.Data)
if err != nil {
return err
}
maybeaddr, err := address.NewSecp256k1Address(pubk)
if err != nil {
return err
}
if addr != maybeaddr {
return fmt.Errorf("signature did not match")
}
return nil
case KTBLS:
digests := []bls.Digest{bls.Hash(bls.Message(msg))}
var pubk bls.PublicKey
copy(pubk[:], addr.Payload())
pubkeys := []bls.PublicKey{pubk}
var sig bls.Signature
copy(sig[:], s.Data)
if !bls.Verify(sig, digests, pubkeys) {
return fmt.Errorf("bls signature failed to verify")
}
return nil
default:
return fmt.Errorf("cannot verify signature of unsupported type: %s", s.Type)
}
}
func (s *Signature) TypeCode() int {
switch s.Type {
case KTSecp256k1:
return IKTSecp256k1
case KTBLS:
return IKTBLS
case "":
return IKTUnknown
default:
panic("unsupported signature type")
}
}
func (s *Signature) MarshalCBOR(w io.Writer) error {
header := cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(s.Data)+1))
if _, err := w.Write(header); err != nil {
return err
}
if _, err := w.Write([]byte{byte(s.TypeCode())}); err != nil {
return err
}
if _, err := w.Write(s.Data); err != nil {
return err
}
return nil
}
func (s *Signature) UnmarshalCBOR(br io.Reader) error {
maj, l, err := cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajByteString {
return fmt.Errorf("cbor input for signature was not a byte string")
}
if l > SignatureMaxLength {
return fmt.Errorf("cbor byte array for signature was too long")
}
buf := make([]byte, l)
if _, err := io.ReadFull(br, buf); err != nil {
return err
}
switch buf[0] {
default:
return fmt.Errorf("invalid signature type in cbor input: %d", buf[0])
case IKTSecp256k1:
s.Type = KTSecp256k1
case IKTBLS:
s.Type = KTBLS
}
s.Data = buf[1:]
return nil
}
func (s *Signature) Equals(o *Signature) bool {
return s.Type == o.Type && bytes.Equal(s.Data, o.Data)
}