diff --git a/chain/actors/actor_storagemarket.go b/chain/actors/actor_storagemarket.go index 8fa4386f7..57911a021 100644 --- a/chain/actors/actor_storagemarket.go +++ b/chain/actors/actor_storagemarket.go @@ -18,6 +18,7 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/aerrors" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sigs" ) type StorageMarketActor struct{} @@ -131,7 +132,7 @@ func (sdp *StorageDealProposal) Verify(worker address.Address) error { return err } - if err := sdp.ProposerSignature.Verify(sdp.Client, buf.Bytes()); err != nil { + if err := sigs.Verify(sdp.ProposerSignature, sdp.Client, buf.Bytes()); err != nil { return err } } diff --git a/chain/actors/actor_storagepower.go b/chain/actors/actor_storagepower.go index af56e0deb..1a2491aa7 100644 --- a/chain/actors/actor_storagepower.go +++ b/chain/actors/actor_storagepower.go @@ -17,6 +17,7 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/aerrors" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sigs" ) type StoragePowerActor struct{} @@ -170,11 +171,11 @@ func (spa StoragePowerActor) ArbitrateConsensusFault(act *types.Actor, vmctx typ return nil, aerrors.Absorb(oerr, 3, "response from 'GetWorkerAddr' was not a valid address") } - if err := params.Block1.CheckBlockSignature(vmctx.Context(), worker); err != nil { + if err := sigs.CheckBlockSignature(params.Block1, vmctx.Context(), worker); err != nil { return nil, aerrors.Absorb(err, 4, "block1 did not have valid signature") } - if err := params.Block2.CheckBlockSignature(vmctx.Context(), worker); err != nil { + if err := sigs.CheckBlockSignature(params.Block2, vmctx.Context(), worker); err != nil { return nil, aerrors.Absorb(err, 5, "block2 did not have valid signature") } diff --git a/chain/actors/actor_storagepower_test.go b/chain/actors/actor_storagepower_test.go index c84492eae..51c3e0247 100644 --- a/chain/actors/actor_storagepower_test.go +++ b/chain/actors/actor_storagepower_test.go @@ -13,6 +13,8 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/wallet" + _ "github.com/filecoin-project/lotus/lib/sigs/bls" + _ "github.com/filecoin-project/lotus/lib/sigs/secp" cid "github.com/ipfs/go-cid" hamt "github.com/ipfs/go-hamt-ipld" diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 066ec918a..8486056ff 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -9,34 +9,33 @@ import ( "io/ioutil" "sync/atomic" - "github.com/filecoin-project/lotus/chain/vm" - - ffi "github.com/filecoin-project/filecoin-ffi" - - sectorbuilder "github.com/filecoin-project/go-sectorbuilder" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-car" + "github.com/ipfs/go-cid" + blockstore "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" + logging "github.com/ipfs/go-log/v2" "github.com/ipfs/go-merkledag" peer "github.com/libp2p/go-libp2p-core/peer" - "go.opencensus.io/trace" - "golang.org/x/xerrors" + ffi "github.com/filecoin-project/filecoin-ffi" "github.com/filecoin-project/go-address" + sectorbuilder "github.com/filecoin-project/go-sectorbuilder" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/wallet" "github.com/filecoin-project/lotus/cmd/lotus-seed/seed" "github.com/filecoin-project/lotus/genesis" + "github.com/filecoin-project/lotus/lib/sigs" "github.com/filecoin-project/lotus/node/repo" - block "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - blockstore "github.com/ipfs/go-ipfs-blockstore" - logging "github.com/ipfs/go-log/v2" + "go.opencensus.io/trace" + "golang.org/x/xerrors" ) var log = logging.Logger("gen") @@ -631,7 +630,7 @@ func VerifyVRF(ctx context.Context, worker, miner address.Address, p uint64, inp Data: vrfproof, } - if err := sig.Verify(worker, vrfBase); err != nil { + if err := sigs.Verify(sig, worker, vrfBase); err != nil { return xerrors.Errorf("vrf was invalid: %w", err) } diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 8a5672dc6..5a9deb569 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -25,6 +25,7 @@ import ( "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sigs" "github.com/filecoin-project/lotus/node/modules/dtypes" ) @@ -303,7 +304,7 @@ func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error return ErrMessageValueTooHigh } - if err := m.Signature.Verify(m.Message.From, m.Message.Cid().Bytes()); err != nil { + if err := sigs.Verify(&m.Signature, m.Message.From, m.Message.Cid().Bytes()); err != nil { log.Warnf("mpooladd signature verification failed: %s", err) return err } diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index 514806eb1..e0b1022ab 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -9,6 +9,8 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/mock" "github.com/filecoin-project/lotus/chain/wallet" + _ "github.com/filecoin-project/lotus/lib/sigs/bls" + _ "github.com/filecoin-project/lotus/lib/sigs/secp" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" ) diff --git a/chain/sync.go b/chain/sync.go index 7c8e13b36..95ff9cd42 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -37,6 +37,7 @@ import ( "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sigs" ) var log = logging.Logger("chain") @@ -604,7 +605,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err } blockSigCheck := async.Err(func() error { - if err := h.CheckBlockSignature(ctx, waddr); err != nil { + if err := sigs.CheckBlockSignature(h, ctx, waddr); err != nil { return xerrors.Errorf("check block signature failed: %w", err) } return nil @@ -787,7 +788,7 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock return xerrors.Errorf("failed to resolve key addr: %w", err) } - if err := m.Signature.Verify(kaddr, m.Message.Cid().Bytes()); err != nil { + if err := sigs.Verify(&m.Signature, kaddr, m.Message.Cid().Bytes()); err != nil { return xerrors.Errorf("secpk message %s has invalid signature: %w", m.Cid(), err) } diff --git a/chain/types/blockheader_cgo.go b/chain/types/blockheader_cgo.go deleted file mode 100644 index 653935cbc..000000000 --- a/chain/types/blockheader_cgo.go +++ /dev/null @@ -1,27 +0,0 @@ -//+build cgo - -package types - -import ( - "context" - - "github.com/filecoin-project/go-address" - "go.opencensus.io/trace" - "golang.org/x/xerrors" -) - -func (blk *BlockHeader) CheckBlockSignature(ctx context.Context, worker address.Address) error { - _, span := trace.StartSpan(ctx, "checkBlockSignature") - defer span.End() - - if blk.BlockSig == nil { - return xerrors.New("block signature not present") - } - - sigb, err := blk.SigningBytes() - if err != nil { - return xerrors.Errorf("failed to get block signing bytes: %w", err) - } - - return blk.BlockSig.Verify(worker, sigb) -} diff --git a/chain/types/signature_cgo.go b/chain/types/signature_cgo.go deleted file mode 100644 index a7423d946..000000000 --- a/chain/types/signature_cgo.go +++ /dev/null @@ -1,60 +0,0 @@ -//+build cgo - -package types - -import ( - "fmt" - - bls "github.com/filecoin-project/filecoin-ffi" - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-crypto" - "github.com/minio/blake2b-simd" - "golang.org/x/xerrors" -) - -func (s *Signature) Verify(addr address.Address, msg []byte) error { - if s == nil { - return xerrors.Errorf("signature is nil") - } - - 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) - } -} diff --git a/chain/vm/vm.go b/chain/vm/vm.go index cfb9c02e2..e7c09ad04 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -22,6 +22,7 @@ import ( "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/bufbstore" + "github.com/filecoin-project/lotus/lib/sigs" ) var log = logging.Logger("vm") @@ -196,7 +197,7 @@ func (vmctx *VMContext) VerifySignature(sig *types.Signature, act address.Addres act = kaddr } - if err := sig.Verify(act, data); err != nil { + if err := sigs.Verify(sig, act, data); err != nil { return aerrors.New(2, "signature verification failed") } diff --git a/chain/wallet/wallet.go b/chain/wallet/wallet.go index 3a70b506a..3e5d506a8 100644 --- a/chain/wallet/wallet.go +++ b/chain/wallet/wallet.go @@ -2,20 +2,16 @@ package wallet import ( "context" - "fmt" "sort" "strings" "sync" - bls "github.com/filecoin-project/filecoin-ffi" - logging "github.com/ipfs/go-log/v2" - "github.com/minio/blake2b-simd" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-crypto" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sigs" ) var log = logging.Logger("wallet") @@ -61,31 +57,7 @@ func (w *Wallet) Sign(ctx context.Context, addr address.Address, msg []byte) (*t return nil, xerrors.Errorf("signing using key '%s': %w", addr.String(), types.ErrKeyInfoNotFound) } - switch ki.Type { - case types.KTSecp256k1: - b2sum := blake2b.Sum256(msg) - sig, err := crypto.Sign(ki.PrivateKey, b2sum[:]) - if err != nil { - return nil, err - } - - return &types.Signature{ - Type: types.KTSecp256k1, - Data: sig, - }, nil - case types.KTBLS: - var pk bls.PrivateKey - copy(pk[:], ki.PrivateKey) - sig := bls.PrivateKeySign(pk, msg) - - return &types.Signature{ - Type: types.KTBLS, - Data: sig[:], - }, nil - - default: - return nil, fmt.Errorf("cannot sign with unsupported key type: %q", ki.Type) - } + return sigs.Sign(ki.Type, ki.PrivateKey, msg) } func (w *Wallet) findKey(addr address.Address) (*Key, error) { @@ -204,29 +176,15 @@ func (w *Wallet) SetDefault(a address.Address) error { } func GenerateKey(typ string) (*Key, error) { - switch typ { - case types.KTSecp256k1: - priv, err := crypto.GenerateKey() - if err != nil { - return nil, err - } - ki := types.KeyInfo{ - Type: typ, - PrivateKey: priv, - } - - return NewKey(ki) - case types.KTBLS: - priv := bls.PrivateKeyGenerate() - ki := types.KeyInfo{ - Type: typ, - PrivateKey: priv[:], - } - - return NewKey(ki) - default: - return nil, xerrors.Errorf("invalid key type: %s", typ) + pk, err := sigs.Generate(typ) + if err != nil { + return nil, err } + ki := types.KeyInfo{ + Type: typ, + PrivateKey: pk, + } + return NewKey(ki) } func (w *Wallet) GenerateKey(typ string) (address.Address, error) { @@ -277,28 +235,23 @@ func NewKey(keyinfo types.KeyInfo) (*Key, error) { KeyInfo: keyinfo, } + var err error + k.PublicKey, err = sigs.ToPublic(k.Type, k.PrivateKey) + if err != nil { + return nil, err + } + switch k.Type { case types.KTSecp256k1: - k.PublicKey = crypto.PublicKey(k.PrivateKey) - - var err error k.Address, err = address.NewSecp256k1Address(k.PublicKey) if err != nil { return nil, xerrors.Errorf("converting Secp256k1 to address: %w", err) } - case types.KTBLS: - var pk bls.PrivateKey - copy(pk[:], k.PrivateKey) - pub := bls.PrivateKeyPublicKey(pk) - k.PublicKey = pub[:] - - var err error k.Address, err = address.NewBLSAddress(k.PublicKey) if err != nil { return nil, xerrors.Errorf("converting BLS to address: %w", err) } - default: return nil, xerrors.Errorf("unknown key type") } diff --git a/lib/sigs/bls/init.go b/lib/sigs/bls/init.go new file mode 100644 index 000000000..1c22202c8 --- /dev/null +++ b/lib/sigs/bls/init.go @@ -0,0 +1,52 @@ +package bls + +import ( + "fmt" + + ffi "github.com/filecoin-project/filecoin-ffi" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sigs" +) + +type blsSigner struct{} + +func (blsSigner) GenPrivate() ([]byte, error) { + pk := ffi.PrivateKeyGenerate() + return pk[:], nil +} + +func (blsSigner) ToPublic(priv []byte) ([]byte, error) { + var pk ffi.PrivateKey + copy(pk[:], priv) + pub := ffi.PrivateKeyPublicKey(pk) + return pub[:], nil +} + +func (blsSigner) Sign(p []byte, msg []byte) ([]byte, error) { + var pk ffi.PrivateKey + copy(pk[:], p) + sig := ffi.PrivateKeySign(pk, msg) + return sig[:], nil +} + +func (blsSigner) Verify(sig []byte, a address.Address, msg []byte) error { + digests := []ffi.Digest{ffi.Hash(ffi.Message(msg))} + + var pubk ffi.PublicKey + copy(pubk[:], a.Payload()) + pubkeys := []ffi.PublicKey{pubk} + + var s ffi.Signature + copy(s[:], sig) + + if !ffi.Verify(&s, digests, pubkeys) { + return fmt.Errorf("bls signature failed to verify") + } + + return nil +} + +func init() { + sigs.RegisterSignature(types.KTBLS, blsSigner{}) +} diff --git a/lib/sigs/doc.go b/lib/sigs/doc.go new file mode 100644 index 000000000..637cd2bcd --- /dev/null +++ b/lib/sigs/doc.go @@ -0,0 +1,9 @@ +// Sigs package allows for signing, verifying signatures and key generation +// using key types selected by package user. +// +// For support of secp256k1 import: +// _ "github.com/filecoin-project/lotus/lib/sigs/secp" +// +// For support of Filecoin BLS import: +// _ "github.com/filecoin-project/lotus/lib/sigs/bls" +package sigs diff --git a/lib/sigs/secp/init.go b/lib/sigs/secp/init.go new file mode 100644 index 000000000..84680ac81 --- /dev/null +++ b/lib/sigs/secp/init.go @@ -0,0 +1,58 @@ +package secp + +import ( + "fmt" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-crypto" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sigs" + "github.com/minio/blake2b-simd" +) + +type secpSigner struct{} + +func (secpSigner) GenPrivate() ([]byte, error) { + priv, err := crypto.GenerateKey() + if err != nil { + return nil, err + } + return priv, nil +} + +func (secpSigner) ToPublic(pk []byte) ([]byte, error) { + return crypto.PublicKey(pk), nil +} + +func (secpSigner) Sign(pk []byte, msg []byte) ([]byte, error) { + b2sum := blake2b.Sum256(msg) + sig, err := crypto.Sign(pk, b2sum[:]) + if err != nil { + return nil, err + } + + return sig, nil +} + +func (secpSigner) Verify(sig []byte, a address.Address, msg []byte) error { + b2sum := blake2b.Sum256(msg) + pubk, err := crypto.EcRecover(b2sum[:], sig) + if err != nil { + return err + } + + maybeaddr, err := address.NewSecp256k1Address(pubk) + if err != nil { + return err + } + + if a != maybeaddr { + return fmt.Errorf("signature did not match") + } + + return nil +} + +func init() { + sigs.RegisterSignature(types.KTSecp256k1, secpSigner{}) +} diff --git a/lib/sigs/sigs.go b/lib/sigs/sigs.go new file mode 100644 index 000000000..9d1bc4b6d --- /dev/null +++ b/lib/sigs/sigs.go @@ -0,0 +1,102 @@ +package sigs + +import ( + "context" + "fmt" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/chain/types" + "go.opencensus.io/trace" + "golang.org/x/xerrors" +) + +// Sign takes in signature type, private key and message. Returns a signature for that message. +// Valid sigTypes are: "secp256k1" and "bls" +func Sign(sigType string, privkey []byte, msg []byte) (*types.Signature, error) { + sv, ok := sigs[sigType] + if !ok { + return nil, fmt.Errorf("cannot sign message with signature of unsupported type: %s", sigType) + } + + sb, err := sv.Sign(privkey, msg) + if err != nil { + return nil, err + } + return &types.Signature{ + Type: sigType, + Data: sb, + }, nil +} + +// Verify verifies signatures +func Verify(sig *types.Signature, addr address.Address, msg []byte) error { + if sig == nil { + return xerrors.Errorf("signature is nil") + } + + if addr.Protocol() == address.ID { + return fmt.Errorf("must resolve ID addresses before using them to verify a signature") + } + + sv, ok := sigs[sig.Type] + if !ok { + return fmt.Errorf("cannot verify signature of unsupported type: %s", sig.Type) + } + + return sv.Verify(sig.Data, addr, msg) +} + +// Generate generates private key of given type +func Generate(sigType string) ([]byte, error) { + sv, ok := sigs[sigType] + if !ok { + return nil, fmt.Errorf("cannot generate private key of unsupported type: %s", sigType) + } + + return sv.GenPrivate() +} + +// ToPublic converts private key to public key +func ToPublic(sigType string, pk []byte) ([]byte, error) { + sv, ok := sigs[sigType] + if !ok { + return nil, fmt.Errorf("cannot generate public key of unsupported type: %s", sigType) + } + + return sv.ToPublic(pk) +} + +func CheckBlockSignature(blk *types.BlockHeader, ctx context.Context, worker address.Address) error { + _, span := trace.StartSpan(ctx, "checkBlockSignature") + defer span.End() + + if blk.BlockSig == nil { + return xerrors.New("block signature not present") + } + + sigb, err := blk.SigningBytes() + if err != nil { + return xerrors.Errorf("failed to get block signing bytes: %w", err) + } + + _ = sigb + return Verify(blk.BlockSig, worker, sigb) +} + +// SigShim is used for introducing signature functions +type SigShim interface { + GenPrivate() ([]byte, error) + ToPublic(pk []byte) ([]byte, error) + Sign(pk []byte, msg []byte) ([]byte, error) + Verify(sig []byte, a address.Address, msg []byte) error +} + +var sigs map[string]SigShim + +// RegisterSig should be only used during init +func RegisterSignature(name string, vs SigShim) { + if sigs == nil { + sigs = make(map[string]SigShim) + } + sigs[name] = vs +} diff --git a/node/builder.go b/node/builder.go index 028778c09..d7fe6b1be 100644 --- a/node/builder.go +++ b/node/builder.go @@ -37,6 +37,8 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/wallet" + _ "github.com/filecoin-project/lotus/lib/sigs/bls" + _ "github.com/filecoin-project/lotus/lib/sigs/secp" "github.com/filecoin-project/lotus/markets/storageadapter" "github.com/filecoin-project/lotus/miner" "github.com/filecoin-project/lotus/node/config" diff --git a/paych/paych.go b/paych/paych.go index 28f4c0fca..375fe7395 100644 --- a/paych/paych.go +++ b/paych/paych.go @@ -16,6 +16,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sigs" "github.com/filecoin-project/lotus/node/impl/full" ) @@ -138,7 +139,7 @@ func (pm *Manager) CheckVoucherValid(ctx context.Context, ch address.Address, sv // TODO: technically, either party may create and sign a voucher. // However, for now, we only accept them from the channel creator. // More complex handling logic can be added later - if err := sv.Signature.Verify(pca.From, vb); err != nil { + if err := sigs.Verify(sv.Signature, pca.From, vb); err != nil { return err }