2019-12-28 04:09:43 +08:00
|
|
|
package record
|
2019-11-07 01:24:34 +08:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
2019-12-28 04:07:28 +08:00
|
|
|
"fmt"
|
2019-11-22 00:00:38 +08:00
|
|
|
|
2019-12-28 04:07:28 +08:00
|
|
|
pool "github.com/libp2p/go-buffer-pool"
|
2019-12-28 04:09:43 +08:00
|
|
|
"github.com/libp2p/go-libp2p-core/crypto"
|
2019-12-28 04:26:08 +08:00
|
|
|
pb "github.com/libp2p/go-libp2p-core/record/pb"
|
2019-12-28 04:09:43 +08:00
|
|
|
|
|
|
|
"github.com/gogo/protobuf/proto"
|
2019-12-10 01:12:35 +08:00
|
|
|
"github.com/multiformats/go-varint"
|
2019-11-07 01:24:34 +08:00
|
|
|
)
|
|
|
|
|
2019-11-07 23:56:28 +08:00
|
|
|
// SignedEnvelope contains an arbitrary []byte payload, signed by a libp2p peer.
|
2019-12-28 04:07:28 +08:00
|
|
|
//
|
|
|
|
// Envelopes are signed in the context of a particular "domain", which is a
|
|
|
|
// string specified when creating and verifying the envelope. You must know the
|
|
|
|
// domain string used to produce the envelope in order to verify the signature
|
|
|
|
// and access the payload.
|
2019-11-07 23:37:30 +08:00
|
|
|
type SignedEnvelope struct {
|
2019-11-07 23:56:28 +08:00
|
|
|
// The public key that can be used to verify the signature and derive the peer id of the signer.
|
2019-12-28 04:09:43 +08:00
|
|
|
PublicKey crypto.PubKey
|
2019-11-07 23:56:28 +08:00
|
|
|
|
|
|
|
// A binary identifier that indicates what kind of data is contained in the payload.
|
|
|
|
// TODO(yusef): enforce multicodec prefix
|
2019-11-22 01:55:14 +08:00
|
|
|
PayloadType []byte
|
2019-11-07 23:56:28 +08:00
|
|
|
|
2019-11-16 02:11:04 +08:00
|
|
|
// The envelope payload.
|
2019-11-22 01:55:14 +08:00
|
|
|
Payload []byte
|
2019-11-07 23:56:28 +08:00
|
|
|
|
2019-12-28 04:07:28 +08:00
|
|
|
// The signature of the domain string :: type hint :: payload.
|
2019-11-07 23:37:30 +08:00
|
|
|
signature []byte
|
|
|
|
}
|
2019-11-07 01:24:34 +08:00
|
|
|
|
2019-12-28 04:07:28 +08:00
|
|
|
var ErrEmptyDomain = errors.New("envelope domain must not be empty")
|
|
|
|
var ErrInvalidSignature = errors.New("invalid signature or incorrect domain")
|
2019-11-07 23:56:28 +08:00
|
|
|
|
|
|
|
// MakeEnvelope constructs a new SignedEnvelope using the given privateKey.
|
|
|
|
//
|
|
|
|
// The required 'domain' string contextualizes the envelope for a particular purpose,
|
|
|
|
// and must be supplied when verifying the signature.
|
|
|
|
//
|
2019-11-22 01:55:14 +08:00
|
|
|
// The 'PayloadType' field indicates what kind of data is contained and may be empty.
|
2019-12-28 04:09:43 +08:00
|
|
|
func MakeEnvelope(privateKey crypto.PrivKey, domain string, payloadType []byte, payload []byte) (*SignedEnvelope, error) {
|
2019-12-28 04:07:28 +08:00
|
|
|
if domain == "" {
|
|
|
|
return nil, ErrEmptyDomain
|
2019-11-07 23:56:28 +08:00
|
|
|
}
|
2019-12-28 04:07:28 +08:00
|
|
|
|
|
|
|
unsigned, err := makeUnsigned(domain, payloadType, payload)
|
2019-11-18 22:18:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-12-28 04:07:28 +08:00
|
|
|
defer pool.Put(unsigned)
|
|
|
|
|
|
|
|
sig, err := privateKey.Sign(unsigned)
|
2019-11-07 01:24:34 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-11-07 23:37:30 +08:00
|
|
|
return &SignedEnvelope{
|
2019-11-22 01:55:14 +08:00
|
|
|
PublicKey: privateKey.GetPublic(),
|
|
|
|
PayloadType: payloadType,
|
|
|
|
Payload: payload,
|
2019-11-15 23:39:17 +08:00
|
|
|
signature: sig,
|
2019-11-07 01:24:34 +08:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-12-28 04:07:28 +08:00
|
|
|
// ConsumeEnvelope unmarshals a serialized SignedEnvelope, and validates its
|
|
|
|
// signature using the provided 'domain' string. If validation fails, an error
|
|
|
|
// is returned, along with the unmarshalled payload so it can be inspected.
|
|
|
|
func ConsumeEnvelope(data []byte, domain string) (*SignedEnvelope, error) {
|
|
|
|
e, err := UnmarshalEnvelope(data)
|
2019-11-16 02:11:04 +08:00
|
|
|
if err != nil {
|
2019-12-28 04:07:28 +08:00
|
|
|
return nil, fmt.Errorf("failed when unmarshalling the envelope: %w", err)
|
2019-11-16 02:11:04 +08:00
|
|
|
}
|
2019-12-28 04:07:28 +08:00
|
|
|
|
|
|
|
return e, e.validate(domain)
|
2019-11-16 02:11:04 +08:00
|
|
|
}
|
|
|
|
|
2019-12-28 04:07:28 +08:00
|
|
|
// UnmarshalEnvelope unmarshals a serialized SignedEnvelope protobuf message,
|
|
|
|
// without validating its contents. Most users should use ConsumeEnvelope.
|
|
|
|
func UnmarshalEnvelope(data []byte) (*SignedEnvelope, error) {
|
2019-11-21 23:09:31 +08:00
|
|
|
var e pb.SignedEnvelope
|
2019-12-28 04:07:28 +08:00
|
|
|
if err := proto.Unmarshal(data, &e); err != nil {
|
2019-11-07 23:37:30 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
2019-12-28 04:07:28 +08:00
|
|
|
|
2019-12-28 04:09:43 +08:00
|
|
|
key, err := crypto.PublicKeyFromProto(e.PublicKey)
|
2019-11-07 01:24:34 +08:00
|
|
|
if err != nil {
|
2019-11-07 23:37:30 +08:00
|
|
|
return nil, err
|
2019-11-07 01:24:34 +08:00
|
|
|
}
|
2019-12-28 04:07:28 +08:00
|
|
|
|
2019-11-07 23:37:30 +08:00
|
|
|
return &SignedEnvelope{
|
2019-11-22 01:55:14 +08:00
|
|
|
PublicKey: key,
|
|
|
|
PayloadType: e.PayloadType,
|
|
|
|
Payload: e.Payload,
|
2019-11-15 23:39:17 +08:00
|
|
|
signature: e.Signature,
|
2019-11-07 23:37:30 +08:00
|
|
|
}, nil
|
2019-11-07 01:24:34 +08:00
|
|
|
}
|
|
|
|
|
2019-12-28 04:07:28 +08:00
|
|
|
// Marshal returns a byte slice containing a serialized protobuf representation
|
|
|
|
// of a SignedEnvelope.
|
2019-11-07 23:37:30 +08:00
|
|
|
func (e *SignedEnvelope) Marshal() ([]byte, error) {
|
2019-12-28 04:09:43 +08:00
|
|
|
key, err := crypto.PublicKeyToProto(e.PublicKey)
|
2019-11-07 23:37:30 +08:00
|
|
|
if err != nil {
|
2019-11-07 01:24:34 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
2019-12-28 04:07:28 +08:00
|
|
|
|
2019-11-07 23:37:30 +08:00
|
|
|
msg := pb.SignedEnvelope{
|
2019-11-15 23:39:17 +08:00
|
|
|
PublicKey: key,
|
2019-11-22 01:55:14 +08:00
|
|
|
PayloadType: e.PayloadType,
|
|
|
|
Payload: e.Payload,
|
2019-11-15 23:39:17 +08:00
|
|
|
Signature: e.signature,
|
2019-11-07 23:37:30 +08:00
|
|
|
}
|
|
|
|
return proto.Marshal(&msg)
|
2019-11-07 01:24:34 +08:00
|
|
|
}
|
|
|
|
|
2019-12-28 04:07:28 +08:00
|
|
|
// Equal returns true if the other SignedEnvelope has the same public key,
|
|
|
|
// payload, payload type, and signature. This implies that they were also
|
|
|
|
// created with the same domain string.
|
2019-11-19 23:19:17 +08:00
|
|
|
func (e *SignedEnvelope) Equal(other *SignedEnvelope) bool {
|
2019-11-22 01:55:14 +08:00
|
|
|
return e.PublicKey.Equals(other.PublicKey) &&
|
|
|
|
bytes.Compare(e.PayloadType, other.PayloadType) == 0 &&
|
|
|
|
bytes.Compare(e.Payload, other.Payload) == 0 &&
|
2019-11-16 02:11:04 +08:00
|
|
|
bytes.Compare(e.signature, other.signature) == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// validate returns true if the envelope signature is valid for the given 'domain',
|
|
|
|
// or false if it is invalid. May return an error if signature validation fails.
|
|
|
|
func (e *SignedEnvelope) validate(domain string) error {
|
2019-12-28 04:07:28 +08:00
|
|
|
unsigned, err := makeUnsigned(domain, e.PayloadType, e.Payload)
|
2019-11-18 22:18:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-12-28 04:07:28 +08:00
|
|
|
defer pool.Put(unsigned)
|
|
|
|
|
|
|
|
valid, err := e.PublicKey.Verify(unsigned, e.signature)
|
2019-11-07 01:24:34 +08:00
|
|
|
if err != nil {
|
2019-12-28 04:07:28 +08:00
|
|
|
return fmt.Errorf("failed while verifying signature: %w", err)
|
2019-11-07 01:24:34 +08:00
|
|
|
}
|
|
|
|
if !valid {
|
2019-12-28 04:07:28 +08:00
|
|
|
return ErrInvalidSignature
|
2019-11-07 01:24:34 +08:00
|
|
|
}
|
2019-11-16 02:11:04 +08:00
|
|
|
return nil
|
2019-11-08 01:01:00 +08:00
|
|
|
}
|
|
|
|
|
2019-12-28 04:07:28 +08:00
|
|
|
// makeUnsigned is a helper function that prepares a buffer to sign or verify.
|
|
|
|
// It returns a byte slice from a pool. The caller MUST return this slice to the
|
|
|
|
// pool.
|
|
|
|
func makeUnsigned(domain string, payloadType []byte, payload []byte) ([]byte, error) {
|
|
|
|
var (
|
|
|
|
fields = [][]byte{[]byte(domain), payloadType, payload}
|
|
|
|
|
|
|
|
// fields are prefixed with their length as an unsigned varint. we
|
|
|
|
// compute the lengths before allocating the sig buffer so we know how
|
|
|
|
// much space to add for the lengths
|
|
|
|
flen = make([][]byte, len(fields))
|
|
|
|
size = 0
|
|
|
|
)
|
2019-11-18 22:18:06 +08:00
|
|
|
|
2019-12-10 01:12:35 +08:00
|
|
|
for i, f := range fields {
|
|
|
|
l := len(f)
|
2019-12-28 04:07:28 +08:00
|
|
|
flen[i] = varint.ToUvarint(uint64(l))
|
|
|
|
size += l + len(flen[i])
|
2019-11-18 22:18:06 +08:00
|
|
|
}
|
|
|
|
|
2019-12-28 04:07:28 +08:00
|
|
|
b := pool.Get(size)
|
2019-11-18 22:18:06 +08:00
|
|
|
|
2019-12-28 04:07:28 +08:00
|
|
|
var s int
|
2019-12-10 01:12:35 +08:00
|
|
|
for i, f := range fields {
|
2019-12-28 04:07:28 +08:00
|
|
|
s += copy(b[s:], flen[i])
|
|
|
|
s += copy(b[s:], f)
|
2019-11-18 22:18:06 +08:00
|
|
|
}
|
|
|
|
|
2019-12-28 04:07:28 +08:00
|
|
|
return b[:s], nil
|
2019-11-18 22:18:06 +08:00
|
|
|
}
|