dht: split out online record verification

Online record verification -- meaning record verification
that might cause messages to other peers -- presents a
way to perform an attack on a dht node: forge a record and
make the node attempt to fetch the public key to verify.
This becomes a very powerful amplification attack if
online verification is done for records _received passively_.
This means records that were received as the result of a
PUT_VALUE or ADD_PROVIDER. Thus we only accept records we
can verify offline (whose public keys we already have). In
practice this is not at all a problem for us, because
typical connections are encrypted: we've already exchanged
public keys.
This commit is contained in:
Juan Batiz-Benet 2014-12-20 23:41:04 -08:00
parent cb2e560c32
commit 0cdc2e0adf

View File

@ -147,8 +147,8 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID,
// Success! We were given the value
log.Debug("getValueOrPeers: got value")
// make sure record is still valid
err = dht.verifyRecord(record)
// make sure record is valid.
err = dht.verifyRecordOnline(ctx, record)
if err != nil {
log.Error("Received invalid record!")
return nil, nil, err
@ -156,19 +156,20 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID,
return record.GetValue(), nil, nil
}
// TODO decide on providers. This probably shouldn't be happening.
// TODO what did "this" mean? i think it's the notion of receiving
// a list of providers and then immediately requesting that value
// from them. providers a higher level construct for dht clients:
// the dht shouldn't be _using_ providers... -@jbenet 2014-12-20
if prv := pmes.GetProviderPeers(); prv != nil && len(prv) > 0 {
val, err := dht.getFromPeerList(ctx, key, prv)
if err != nil {
return nil, nil, err
}
log.Debug("getValueOrPeers: get from providers")
return val, nil, nil
}
// commenting out for now. i'm not sure this is correct -jbenet
// // TODO decide on providers. This probably shouldn't be happening.
// // TODO what did "this" mean? i think it's the notion of receiving
// // a list of providers and then immediately requesting that value
// // from them. providers a higher level construct for dht clients:
// // the dht shouldn't be _using_ providers... -@jbenet 2014-12-20
// if prv := pmes.GetProviderPeers(); prv != nil && len(prv) > 0 {
// val, err := dht.getFromPeerList(ctx, key, prv)
// if err != nil {
// return nil, nil, err
// }
// log.Debug("getValueOrPeers: get from providers")
// return val, nil, nil
// }
// Perhaps we were given closer peers
peers := pb.PBPeersToPeerInfos(pmes.GetCloserPeers())
@ -189,43 +190,44 @@ func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.ID,
return dht.sendRequest(ctx, p, pmes)
}
// TODO: Im not certain on this implementation, we get a list of peers/providers
// from someone what do we do with it? Connect to each of them? randomly pick
// one to get the value from? Or just connect to one at a time until we get a
// successful connection and request the value from it?
func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key,
peerlist []*pb.Message_Peer) ([]byte, error) {
// commenting out until i figure out whether this is needed -jbenet
// // TODO: Im not certain on this implementation, we get a list of peers/providers
// // from someone what do we do with it? Connect to each of them? randomly pick
// // one to get the value from? Or just connect to one at a time until we get a
// // successful connection and request the value from it?
// func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key,
// peerlist []*pb.Message_Peer) ([]byte, error) {
peerinfos := pb.PBPeersToPeerInfos(peerlist)
for _, pinfo := range peerinfos {
p := pinfo.ID
if err := dht.ensureConnectedToPeer(ctx, p); err != nil {
log.Errorf("getFromPeers error: %s", err)
continue
}
// peerinfos := pb.PBPeersToPeerInfos(peerlist)
// for _, pinfo := range peerinfos {
// p := pinfo.ID
// if err := dht.ensureConnectedToPeer(ctx, p); err != nil {
// log.Errorf("getFromPeers error: %s", err)
// continue
// }
pmes, err := dht.getValueSingle(ctx, p, key)
if err != nil {
log.Errorf("getFromPeers error: %s\n", err)
continue
}
// pmes, err := dht.getValueSingle(ctx, p, key)
// if err != nil {
// log.Errorf("getFromPeers error: %s\n", err)
// continue
// }
if record := pmes.GetRecord(); record != nil {
// Success! We were given the value
// if record := pmes.GetRecord(); record != nil {
// // Success! We were given the value
err := dht.verifyRecord(record)
if err != nil {
return nil, err
}
dht.providers.AddProvider(key, p)
return record.GetValue(), nil
}
}
return nil, routing.ErrNotFound
}
// err := dht.verifyRecord(ctx, record)
// if err != nil {
// return nil, err
// }
// dht.providers.AddProvider(key, p)
// return record.GetValue(), nil
// }
// }
// return nil, routing.ErrNotFound
// }
// getLocal attempts to retrieve the value from the datastore
func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) {
func (dht *IpfsDHT) getLocal(ctx context.Context, key u.Key) ([]byte, error) {
log.Debug("getLocal %s", key)
v, err := dht.datastore.Get(key.DsKey())
@ -246,7 +248,7 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) {
// TODO: 'if paranoid'
if u.Debug {
err = dht.verifyRecord(rec)
err = dht.verifyRecordLocally(rec)
if err != nil {
log.Errorf("local record verify failed: %s", err)
return nil, err