From bc129ac5c786d55c2e1725c2195dd50cbfdfe87a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 5 Jun 2018 02:01:18 -0700 Subject: [PATCH] embed public keys inside ipns records, use for validation License: MIT Signed-off-by: Jeromy --- namesys/pb/namesys.pb.go | 33 ++++++++++++++++++++++++--------- namesys/pb/namesys.proto | 6 ++++++ namesys/publisher.go | 13 +++++++++++++ namesys/validator.go | 38 ++++++++++++++++++++++++++++++++++---- 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/namesys/pb/namesys.pb.go b/namesys/pb/namesys.pb.go index 31e6355d7..66626ca7d 100644 --- a/namesys/pb/namesys.pb.go +++ b/namesys/pb/namesys.pb.go @@ -1,12 +1,12 @@ // Code generated by protoc-gen-gogo. -// source: namesys.proto +// source: namesys/pb/namesys.proto // DO NOT EDIT! /* Package namesys_pb is a generated protocol buffer package. It is generated from these files: - namesys.proto + namesys/pb/namesys.proto It has these top-level messages: IpnsEntry @@ -14,10 +14,12 @@ It has these top-level messages: package namesys_pb import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +import fmt "fmt" import math "math" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal +var _ = fmt.Errorf var _ = math.Inf type IpnsEntry_ValidityType int32 @@ -52,13 +54,18 @@ func (x *IpnsEntry_ValidityType) UnmarshalJSON(data []byte) error { } type IpnsEntry struct { - Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` - Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` - ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=namesys.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"` - Validity []byte `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"` - Sequence *uint64 `protobuf:"varint,5,opt,name=sequence" json:"sequence,omitempty"` - Ttl *uint64 `protobuf:"varint,6,opt,name=ttl" json:"ttl,omitempty"` - XXX_unrecognized []byte `json:"-"` + Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` + Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` + ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=namesys.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"` + Validity []byte `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"` + Sequence *uint64 `protobuf:"varint,5,opt,name=sequence" json:"sequence,omitempty"` + Ttl *uint64 `protobuf:"varint,6,opt,name=ttl" json:"ttl,omitempty"` + // in order for nodes to properly validate a record upon receipt, they need the public + // key associated with it. For old RSA keys, its easiest if we just send this as part of + // the record itself. For newer ed25519 keys, the public key can be embedded in the + // peerID, making this field unnecessary. + PubKey []byte `protobuf:"bytes,7,opt,name=pubKey" json:"pubKey,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *IpnsEntry) Reset() { *m = IpnsEntry{} } @@ -107,6 +114,14 @@ func (m *IpnsEntry) GetTtl() uint64 { return 0 } +func (m *IpnsEntry) GetPubKey() []byte { + if m != nil { + return m.PubKey + } + return nil +} + func init() { + proto.RegisterType((*IpnsEntry)(nil), "namesys.pb.IpnsEntry") proto.RegisterEnum("namesys.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value) } diff --git a/namesys/pb/namesys.proto b/namesys/pb/namesys.proto index d6eaf3243..b72d49843 100644 --- a/namesys/pb/namesys.proto +++ b/namesys/pb/namesys.proto @@ -14,4 +14,10 @@ message IpnsEntry { optional uint64 sequence = 5; optional uint64 ttl = 6; + + // in order for nodes to properly validate a record upon receipt, they need the public + // key associated with it. For old RSA keys, its easiest if we just send this as part of + // the record itself. For newer ed25519 keys, the public key can be embedded in the + // peerID, making this field unnecessary. + optional bytes pubKey = 7; } diff --git a/namesys/publisher.go b/namesys/publisher.go index f5f7d3695..80bc10d47 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -240,6 +240,17 @@ func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, return err } + // if we can't derive the public key from the peerID, embed the entire pubkey in + // the record to make the verifiers job easier + if extractedPublicKey == nil { + pubkeyBytes, err := k.Bytes() + if err != nil { + return err + } + + entry.PubKey = pubkeyBytes + } + namekey, ipnskey := IpnsKeysForID(id) go func() { @@ -247,6 +258,8 @@ func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, }() // Publish the public key if a public key cannot be extracted from the ID + // TODO: once v0.4.16 is widespread enough, we can stop doing this + // and at that point we can even deprecate the /pk/ namespace in the dht if extractedPublicKey == nil { go func() { errs <- PublishPublicKey(ctx, r, namekey, k) diff --git a/namesys/validator.go b/namesys/validator.go index 941d6a667..852276f17 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -3,11 +3,13 @@ package namesys import ( "bytes" "errors" + "fmt" "time" pb "github.com/ipfs/go-ipfs/namesys/pb" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" + ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" record "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record" @@ -65,10 +67,10 @@ func (v IpnsValidator) Validate(key string, value []byte) error { log.Debugf("failed to parse ipns record key %s into peer ID", pidString) return ErrKeyFormat } - pubk := v.KeyBook.PubKey(pid) - if pubk == nil { - log.Debugf("public key with hash %s not found in peer store", pid) - return ErrPublicKeyNotFound + + pubk, err := v.getPublicKey(pid, entry) + if err != nil { + return fmt.Errorf("getting public key failed: %s", err) } // Check the ipns record signature with the public key @@ -94,6 +96,34 @@ func (v IpnsValidator) Validate(key string, value []byte) error { return nil } +func (v IpnsValidator) getPublicKey(pid peer.ID, entry *pb.IpnsEntry) (ic.PubKey, error) { + if entry.PubKey != nil { + pk, err := ic.UnmarshalPublicKey(entry.PubKey) + if err != nil { + // TODO: i think this counts as a 'malformed record' and should be discarded + log.Debugf("public key in ipns record failed to parse: ", err) + return nil, err + } + expPid, err := peer.IDFromPublicKey(pk) + if err != nil { + return nil, fmt.Errorf("could not regenerate peerID from pubkey: %s", err) + } + + if pid != expPid { + return nil, fmt.Errorf("pubkey in record did not match expected pubkey") + } + + return pk, nil + } + + pubk := v.KeyBook.PubKey(pid) + if pubk == nil { + log.Debugf("public key with hash %s not found in peer store", pid) + return nil, ErrPublicKeyNotFound + } + return pubk, nil +} + // IpnsSelectorFunc selects the best record by checking which has the highest // sequence number and latest EOL func (v IpnsValidator) Select(k string, vals [][]byte) (int, error) {