embed public keys inside ipns records, use for validation

License: MIT
Signed-off-by: Jeromy <jeromyj@gmail.com>
This commit is contained in:
Jeromy 2018-06-05 02:01:18 -07:00
parent f7a980926b
commit bc129ac5c7
4 changed files with 77 additions and 13 deletions

View File

@ -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)
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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) {