From f21ec1923a7ea2ffeb82a87f6f08324adacb9f7b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Nov 2014 17:58:08 -0800 Subject: [PATCH] verify ipns records --- core/core.go | 2 ++ namesys/internal/pb/namesys.pb.go | 58 ++++++++++++++++++++++++++++--- namesys/internal/pb/namesys.proto | 7 ++++ namesys/publisher.go | 49 ++++++++++++++++++++++++-- namesys/routing.go | 4 ++- routing/dht/dht.go | 1 - routing/dht/records.go | 5 --- 7 files changed, 112 insertions(+), 14 deletions(-) diff --git a/core/core.go b/core/core.go index 5157263d2..12205544f 100644 --- a/core/core.go +++ b/core/core.go @@ -156,6 +156,8 @@ func NewIpfsNode(cfg *config.Config, online bool) (n *IpfsNode, err error) { // setup routing service dhtRouting := dht.NewDHT(ctx, n.Identity, n.Peerstore, n.Network, dhtService, n.Datastore) + dhtRouting.Validators["ipns"] = namesys.ValidateIpnsRecord + // TODO(brian): perform this inside NewDHT factory method dhtService.SetHandler(dhtRouting) // wire the handler to the service. n.Routing = dhtRouting diff --git a/namesys/internal/pb/namesys.pb.go b/namesys/internal/pb/namesys.pb.go index b5d8885a2..81021b818 100644 --- a/namesys/internal/pb/namesys.pb.go +++ b/namesys/internal/pb/namesys.pb.go @@ -13,17 +13,50 @@ It has these top-level messages: */ package namesys_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = math.Inf +type IpnsEntry_ValidityType int32 + +const ( + // setting an EOL says "this record is valid until..." + IpnsEntry_EOL IpnsEntry_ValidityType = 0 +) + +var IpnsEntry_ValidityType_name = map[int32]string{ + 0: "EOL", +} +var IpnsEntry_ValidityType_value = map[string]int32{ + "EOL": 0, +} + +func (x IpnsEntry_ValidityType) Enum() *IpnsEntry_ValidityType { + p := new(IpnsEntry_ValidityType) + *p = x + return p +} +func (x IpnsEntry_ValidityType) String() string { + return proto.EnumName(IpnsEntry_ValidityType_name, int32(x)) +} +func (x *IpnsEntry_ValidityType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(IpnsEntry_ValidityType_value, data, "IpnsEntry_ValidityType") + if err != nil { + return err + } + *x = IpnsEntry_ValidityType(value) + return nil +} + 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"` - 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"` + XXX_unrecognized []byte `json:"-"` } func (m *IpnsEntry) Reset() { *m = IpnsEntry{} } @@ -44,5 +77,20 @@ func (m *IpnsEntry) GetSignature() []byte { return nil } -func init() { +func (m *IpnsEntry) GetValidityType() IpnsEntry_ValidityType { + if m != nil && m.ValidityType != nil { + return *m.ValidityType + } + return IpnsEntry_EOL +} + +func (m *IpnsEntry) GetValidity() []byte { + if m != nil { + return m.Validity + } + return nil +} + +func init() { + proto.RegisterEnum("namesys.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value) } diff --git a/namesys/internal/pb/namesys.proto b/namesys/internal/pb/namesys.proto index ac8a78da3..4219af6bb 100644 --- a/namesys/internal/pb/namesys.proto +++ b/namesys/internal/pb/namesys.proto @@ -1,6 +1,13 @@ package namesys.pb; message IpnsEntry { + enum ValidityType { + // setting an EOL says "this record is valid until..." + EOL = 0; + } required bytes value = 1; required bytes signature = 2; + + optional ValidityType validityType = 3; + optional bytes validity = 4; } diff --git a/namesys/publisher.go b/namesys/publisher.go index 9aaaa1cc3..7123264db 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -1,6 +1,8 @@ package namesys import ( + "bytes" + "errors" "fmt" "time" @@ -14,6 +16,12 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +// ErrExpiredRecord should be returned when an ipns record is +// invalid due to being too old +var ErrExpiredRecord = errors.New("expired record") + +var ErrUnrecognizedValidity = errors.New("unrecognized validity type") + // ipnsPublisher is capable of publishing and resolving names to the IPFS // routing system. type ipnsPublisher struct { @@ -76,11 +84,48 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { func createRoutingEntryData(pk ci.PrivKey, val string) ([]byte, error) { entry := new(pb.IpnsEntry) - sig, err := pk.Sign([]byte(val)) + + entry.Value = []byte(val) + typ := pb.IpnsEntry_EOL + entry.ValidityType = &typ + entry.Validity = []byte(time.Now().Add(time.Hour * 24).String()) + + sig, err := pk.Sign(ipnsEntryDataForSig(entry)) if err != nil { return nil, err } entry.Signature = sig - entry.Value = []byte(val) return proto.Marshal(entry) } + +func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { + return bytes.Join([][]byte{ + e.Value, + e.Validity, + []byte(fmt.Sprint(e.GetValidityType())), + }, + []byte{}) +} + +func ValidateIpnsRecord(k u.Key, val []byte) error { + entry := new(pb.IpnsEntry) + err := proto.Unmarshal(val, entry) + if err != nil { + return err + } + switch entry.GetValidityType() { + case pb.IpnsEntry_EOL: + defaultTimeFormat := "2006-01-02 15:04:05.999999999 -0700 MST" + t, err := time.Parse(defaultTimeFormat, string(entry.GetValue())) + if err != nil { + log.Error("Failed parsing time for ipns record EOL") + return err + } + if time.Now().After(t) { + return ErrExpiredRecord + } + default: + return ErrUnrecognizedValidity + } + return nil +} diff --git a/namesys/routing.go b/namesys/routing.go index c956f4c44..c990b492b 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -75,9 +75,11 @@ func (r *routingResolver) Resolve(name string) (string, error) { if err != nil { return "", err } + hsh, _ := pk.Hash() + log.Debugf("pk hash = %s", u.Key(hsh)) // check sig with pk - if ok, err := pk.Verify(entry.GetValue(), entry.GetSignature()); err != nil || !ok { + if ok, err := pk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { return "", fmt.Errorf("Invalid value. Not signed by PrivateKey corresponding to %v", pk) } diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6b2d3f5bd..db17f9e7e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -86,7 +86,6 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dia dht.birth = time.Now() dht.Validators = make(map[string]ValidatorFunc) - dht.Validators["ipns"] = ValidateIpnsRecord dht.Validators["pk"] = ValidatePublicKeyRecord if doPinging { diff --git a/routing/dht/records.go b/routing/dht/records.go index c8397c960..ee1257e24 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -96,11 +96,6 @@ func (dht *IpfsDHT) verifyRecord(r *pb.Record) error { return fnc(u.Key(r.GetKey()), r.GetValue()) } -func ValidateIpnsRecord(k u.Key, val []byte) error { - // TODO: - return nil -} - func ValidatePublicKeyRecord(k u.Key, val []byte) error { keyparts := bytes.Split([]byte(k), []byte("/")) if len(keyparts) < 3 {