// FIXME(brian): is this method meant to simulate putting a value into the network? func (c *client) PutValue(ctx context.Context, key key.Key, val []byte) error { log.Debugf("PutValue: %s", key) rec := new(dhtpb.Record) rec.Value = val rec.Key = proto.String(string(key)) rec.TimeReceived = proto.String(u.FormatRFC3339(time.Now())) data, err := proto.Marshal(rec) if err != nil { return err } return c.datastore.Put(key.DsKey(), data) }
func verify(ps peer.Peerstore, r *dhtpb.Record) error { v := make(record.Validator) v["pk"] = record.PublicKeyValidator p := peer.ID(r.GetAuthor()) pk := ps.PubKey(p) if pk == nil { return fmt.Errorf("do not have public key for %s", p) } if err := record.CheckRecordSig(r, pk); err != nil { return err } if err := v.VerifyRecord(r); err != nil { return err } return nil }
// verifyRecordLocally attempts to verify a record. if we do not have the public // key, we fail. we do not search the dht. func (dht *IpfsDHT) verifyRecordLocally(r *pb.Record) error { if len(r.Signature) > 0 { // First, validate the signature p := peer.ID(r.GetAuthor()) pk := dht.peerstore.PubKey(p) if pk == nil { return fmt.Errorf("do not have public key for %s", p) } if err := record.CheckRecordSig(r, pk); err != nil { return err } } return dht.Validator.VerifyRecord(r) }
func (c *offlineRouting) GetValue(ctx context.Context, key key.Key) ([]byte, error) { v, err := c.datastore.Get(key.DsKey()) if err != nil { return nil, err } byt, ok := v.([]byte) if !ok { return nil, errors.New("value stored in datastore not []byte") } rec := new(pb.Record) err = proto.Unmarshal(byt, rec) if err != nil { return nil, err } return rec.GetValue(), nil }
// verifyRecordOnline verifies a record, searching the DHT for the public key // if necessary. The reason there is a distinction in the functions is that // retrieving arbitrary public keys from the DHT as a result of passively // receiving records (e.g. through a PUT_VALUE or ADD_PROVIDER) can cause a // massive amplification attack on the dht. Use with care. func (dht *IpfsDHT) verifyRecordOnline(ctx context.Context, r *pb.Record) error { if len(r.Signature) > 0 { // get the public key, search for it if necessary. p := peer.ID(r.GetAuthor()) pk, err := dht.GetPublicKey(ctx, p) if err != nil { return err } err = record.CheckRecordSig(r, pk) if err != nil { return err } } return dht.Validator.VerifyRecord(r) }
// FIXME(brian): is this method meant to simulate getting a value from the network? func (c *client) GetValue(ctx context.Context, key key.Key) ([]byte, error) { log.Debugf("GetValue: %s", key) v, err := c.datastore.Get(key.DsKey()) if err != nil { return nil, err } data, ok := v.([]byte) if !ok { return nil, errors.New("could not cast value from datastore") } rec := new(dhtpb.Record) err = proto.Unmarshal(data, rec) if err != nil { return nil, err } return rec.GetValue(), nil }
func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey key.Key) (uint64, error) { prevrec, err := p.ds.Get(ipnskey.DsKey()) if err != nil && err != ds.ErrNotFound { // None found, lets start at zero! return 0, err } var val []byte if err == nil { prbytes, ok := prevrec.([]byte) if !ok { return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec) } dhtrec := new(dhtpb.Record) err := proto.Unmarshal(prbytes, dhtrec) if err != nil { return 0, err } val = dhtrec.GetValue() } else { // try and check the dht for a record ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() rv, err := p.routing.GetValue(ctx, ipnskey) if err != nil { // no such record found, start at zero! return 0, nil } val = rv } e := new(pb.IpnsEntry) err = proto.Unmarshal(val, e) if err != nil { return 0, err } return e.GetSequence(), nil }
func (rp *Republisher) getLastVal(k key.Key) (path.Path, uint64, error) { ival, err := rp.ds.Get(k.DsKey()) if err != nil { // not found means we dont have a previously published entry return "", 0, errNoEntry } val := ival.([]byte) dhtrec := new(dhtpb.Record) err = proto.Unmarshal(val, dhtrec) if err != nil { return "", 0, err } // extract published data from record e := new(pb.IpnsEntry) err = proto.Unmarshal(dhtrec.GetValue(), e) if err != nil { return "", 0, err } return path.Path(e.Value), e.GetSequence(), nil }
// VerifyRecord checks a record and ensures it is still valid. // It runs needed validators func (v Validator) VerifyRecord(r *pb.Record) error { // Now, check validity func parts := strings.Split(r.GetKey(), "/") if len(parts) < 3 { log.Infof("Record key does not have validator: %s", key.Key(r.GetKey())) return nil } val, ok := v[parts[1]] if !ok { log.Infof("Unrecognized key prefix: %s", parts[1]) return ErrInvalidRecordType } return val.Func(key.Key(r.GetKey()), r.GetValue()) }
// MakePutRecord creates and signs a dht record for the given key/value pair func MakePutRecord(sk ci.PrivKey, key key.Key, value []byte, sign bool) (*pb.Record, error) { record := new(pb.Record) record.Key = proto.String(string(key)) record.Value = value pkh, err := sk.GetPublic().Hash() if err != nil { return nil, err } record.Author = proto.String(string(pkh)) if sign { blob := RecordBlobForSig(record) sig, err := sk.Sign(blob) if err != nil { return nil, err } record.Signature = sig } return record, nil }
func (dht *IpfsDHT) checkLocalDatastore(k key.Key) (*pb.Record, error) { log.Debugf("%s handleGetValue looking into ds", dht.self) dskey := k.DsKey() iVal, err := dht.datastore.Get(dskey) log.Debugf("%s handleGetValue looking into ds GOT %v", dht.self, iVal) if err == ds.ErrNotFound { return nil, nil } // if we got an unexpected error, bail. if err != nil { return nil, err } // if we have the value, send it back log.Debugf("%s handleGetValue success!", dht.self) byts, ok := iVal.([]byte) if !ok { return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey) } rec := new(pb.Record) err = proto.Unmarshal(byts, rec) if err != nil { log.Debug("Failed to unmarshal dht record from datastore") return nil, err } // if its our record, dont bother checking the times on it if peer.ID(rec.GetAuthor()) == dht.self { return rec, nil } var recordIsBad bool recvtime, err := u.ParseRFC3339(rec.GetTimeReceived()) if err != nil { log.Info("either no receive time set on record, or it was invalid: ", err) recordIsBad = true } if time.Now().Sub(recvtime) > MaxRecordAge { log.Debug("old record found, tossing.") recordIsBad = true } // NOTE: we do not verify the record here beyond checking these timestamps. // we put the burden of checking the records on the requester as checking a record // may be computationally expensive if recordIsBad { err := dht.datastore.Delete(dskey) if err != nil { log.Error("Failed to delete bad record from datastore: ", err) } return nil, nil // can treat this as not having the record at all } return rec, nil }
// RecordBlobForSig returns the blob protected by the record signature func RecordBlobForSig(r *pb.Record) []byte { k := []byte(r.GetKey()) v := []byte(r.GetValue()) a := []byte(r.GetAuthor()) return bytes.Join([][]byte{k, v, a}, []byte{}) }