// ID returns the ID of a given Conn. func ID(c Conn) string { l := fmt.Sprintf("%s/%s", c.LocalMultiaddr(), c.LocalPeer().Pretty()) r := fmt.Sprintf("%s/%s", c.RemoteMultiaddr(), c.RemotePeer().Pretty()) lh := u.Hash([]byte(l)) rh := u.Hash([]byte(r)) ch := u.XOR(lh, rh) return peer.ID(ch).Pretty() }
// KeyHash hashes a key. func KeyHash(k Key) ([]byte, error) { kb, err := k.Bytes() if err != nil { return nil, err } return u.Hash(kb), nil }
// IDFromPublicKey returns the Peer ID corresponding to pk func IDFromPublicKey(pk ic.PubKey) (ID, error) { b, err := pk.Bytes() if err != nil { return "", err } hash := u.Hash(b) return ID(hash), nil }
// RandPeerID generates random "valid" peer IDs. it does not NEED to generate // keys because it is as if we lost the key right away. fine to read randomness // and hash it. to generate proper keys and corresponding PeerID, use: // sk, pk, _ := testutil.RandKeyPair() // id, _ := peer.IDFromPublicKey(pk) func RandPeerID() (peer.ID, error) { buf := make([]byte, 16) if _, err := io.ReadFull(u.NewTimeSeededRand(), buf); err != nil { return "", err } h := u.Hash(buf) return peer.ID(h), nil }
// NewBlockWithHash creates a new block when the hash of the data // is already known, this is used to save time in situations where // we are able to be confident that the data is correct func NewBlockWithHash(data []byte, h mh.Multihash) (*Block, error) { if u.Debug { chk := u.Hash(data) if string(chk) != string(h) { return nil, errors.New("Data did not match given hash!") } } return &Block{Data: data, Multihash: h}, nil }
// ValidatePublicKeyRecord implements ValidatorFunc and // verifies that the passed in record value is the PublicKey // that matches the passed in key. func ValidatePublicKeyRecord(k key.Key, val []byte) error { keyparts := bytes.Split([]byte(k), []byte("/")) if len(keyparts) < 3 { return errors.New("invalid key") } pkh := u.Hash(val) if !bytes.Equal(keyparts[2], pkh) { return errors.New("public key does not match storage key") } return nil }
func (ks *keyset) generate() error { var err error ks.sk, ks.pk, err = tu.RandTestKeyPair(512) if err != nil { return err } bpk, err := ks.pk.Bytes() if err != nil { return err } ks.hpk = string(u.Hash(bpk)) ks.hpkp = b58.Encode([]byte(ks.hpk)) return nil }
func TestBlocks(t *testing.T) { bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) bs, err := New(bstore, offline.Exchange(bstore)) if err != nil { t.Error("failed to construct block service", err) return } defer bs.Close() b := blocks.NewBlock([]byte("beep boop")) h := u.Hash([]byte("beep boop")) if !bytes.Equal(b.Multihash, h) { t.Error("Block Multihash and data multihash not equal") } if b.Key() != key.Key(h) { t.Error("Block key and data multihash key not equal") } k, err := bs.AddBlock(b) if err != nil { t.Error("failed to add block to BlockService", err) return } if k != b.Key() { t.Error("returned key is not equal to block key", err) } ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) b2, err := bs.GetBlock(ctx, b.Key()) if err != nil { t.Error("failed to retrieve block from BlockService", err) return } if b.Key() != b2.Key() { t.Error("Block keys not equal.") } if !bytes.Equal(b.Data, b2.Data) { t.Error("Block data is not equal.") } }
func (ks *keyset) load(hpkp, skBytesStr string) error { skBytes, err := base64.StdEncoding.DecodeString(skBytesStr) if err != nil { return err } ks.sk, err = ic.UnmarshalPrivateKey(skBytes) if err != nil { return err } ks.pk = ks.sk.GetPublic() bpk, err := ks.pk.Bytes() if err != nil { return err } ks.hpk = string(u.Hash(bpk)) ks.hpkp = b58.Encode([]byte(ks.hpk)) if ks.hpkp != hpkp { return fmt.Errorf("hpkp doesn't match key. %s", hpkp) } return nil }
// runHandshake performs initial communication over insecure channel to share // keys, IDs, and initiate communication, assigning all necessary params. // requires the duplex channel to be a msgio.ReadWriter (for framed messaging) func (s *secureSession) runHandshake() error { ctx, cancel := context.WithTimeout(s.ctx, HandshakeTimeout) // remove defer cancel() // ============================================================================= // step 1. Propose -- propose cipher suite + send pubkeys + nonce // Generate and send Hello packet. // Hello = (rand, PublicKey, Supported) nonceOut := make([]byte, nonceSize) _, err := rand.Read(nonceOut) if err != nil { return err } defer log.EventBegin(ctx, "secureHandshake", s).Done() s.local.permanentPubKey = s.localKey.GetPublic() myPubKeyBytes, err := s.local.permanentPubKey.Bytes() if err != nil { return err } proposeOut := new(pb.Propose) proposeOut.Rand = nonceOut proposeOut.Pubkey = myPubKeyBytes proposeOut.Exchanges = &SupportedExchanges proposeOut.Ciphers = &SupportedCiphers proposeOut.Hashes = &SupportedHashes // log.Debugf("1.0 Propose: nonce:%s exchanges:%s ciphers:%s hashes:%s", // nonceOut, SupportedExchanges, SupportedCiphers, SupportedHashes) // Send Propose packet (respects ctx) proposeOutBytes, err := writeMsgCtx(ctx, s.insecureM, proposeOut) if err != nil { return err } // Receive + Parse their Propose packet and generate an Exchange packet. proposeIn := new(pb.Propose) proposeInBytes, err := readMsgCtx(ctx, s.insecureM, proposeIn) if err != nil { return err } // log.Debugf("1.0.1 Propose recv: nonce:%s exchanges:%s ciphers:%s hashes:%s", // proposeIn.GetRand(), proposeIn.GetExchanges(), proposeIn.GetCiphers(), proposeIn.GetHashes()) // ============================================================================= // step 1.1 Identify -- get identity from their key // get remote identity s.remote.permanentPubKey, err = ci.UnmarshalPublicKey(proposeIn.GetPubkey()) if err != nil { return err } // get peer id s.remotePeer, err = peer.IDFromPublicKey(s.remote.permanentPubKey) if err != nil { return err } log.Debugf("1.1 Identify: %s Remote Peer Identified as %s", s.localPeer, s.remotePeer) // ============================================================================= // step 1.2 Selection -- select/agree on best encryption parameters // to determine order, use cmp(H(remote_pubkey||local_rand), H(local_pubkey||remote_rand)). oh1 := u.Hash(append(proposeIn.GetPubkey(), nonceOut...)) oh2 := u.Hash(append(myPubKeyBytes, proposeIn.GetRand()...)) order := bytes.Compare(oh1, oh2) if order == 0 { return ErrEcho // talking to self (same socket. must be reuseport + dialing self) } s.local.curveT, err = selectBest(order, SupportedExchanges, proposeIn.GetExchanges()) if err != nil { return err } s.local.cipherT, err = selectBest(order, SupportedCiphers, proposeIn.GetCiphers()) if err != nil { return err } s.local.hashT, err = selectBest(order, SupportedHashes, proposeIn.GetHashes()) if err != nil { return err } // we use the same params for both directions (must choose same curve) // WARNING: if they dont SelectBest the same way, this won't work... s.remote.curveT = s.local.curveT s.remote.cipherT = s.local.cipherT s.remote.hashT = s.local.hashT // log.Debugf("1.2 selection: exchange:%s cipher:%s hash:%s", // s.local.curveT, s.local.cipherT, s.local.hashT) // ============================================================================= // step 2. Exchange -- exchange (signed) ephemeral keys. verify signatures. // Generate EphemeralPubKey var genSharedKey ci.GenSharedKey s.local.ephemeralPubKey, genSharedKey, err = ci.GenerateEKeyPair(s.local.curveT) // Gather corpus to sign. selectionOut := new(bytes.Buffer) selectionOut.Write(proposeOutBytes) selectionOut.Write(proposeInBytes) selectionOut.Write(s.local.ephemeralPubKey) selectionOutBytes := selectionOut.Bytes() // log.Debugf("2.0 exchange: %v", selectionOutBytes) exchangeOut := new(pb.Exchange) exchangeOut.Epubkey = s.local.ephemeralPubKey exchangeOut.Signature, err = s.localKey.Sign(selectionOutBytes) if err != nil { return err } // Send Propose packet (respects ctx) if _, err := writeMsgCtx(ctx, s.insecureM, exchangeOut); err != nil { return err } // Receive + Parse their Exchange packet. exchangeIn := new(pb.Exchange) if _, err := readMsgCtx(ctx, s.insecureM, exchangeIn); err != nil { return err } // ============================================================================= // step 2.1. Verify -- verify their exchange packet is good. // get their ephemeral pub key s.remote.ephemeralPubKey = exchangeIn.GetEpubkey() selectionIn := new(bytes.Buffer) selectionIn.Write(proposeInBytes) selectionIn.Write(proposeOutBytes) selectionIn.Write(s.remote.ephemeralPubKey) selectionInBytes := selectionIn.Bytes() // log.Debugf("2.0.1 exchange recv: %v", selectionInBytes) // u.POut("Remote Peer Identified as %s\n", s.remote) sigOK, err := s.remote.permanentPubKey.Verify(selectionInBytes, exchangeIn.GetSignature()) if err != nil { // log.Error("2.1 Verify: failed: %s", err) return err } if !sigOK { err := errors.New("Bad signature!") // log.Error("2.1 Verify: failed: %s", err) return err } // log.Debugf("2.1 Verify: signature verified.") // ============================================================================= // step 2.2. Keys -- generate keys for mac + encryption // OK! seems like we're good to go. s.sharedSecret, err = genSharedKey(exchangeIn.GetEpubkey()) if err != nil { return err } // generate two sets of keys (stretching) k1, k2 := ci.KeyStretcher(s.local.cipherT, s.local.hashT, s.sharedSecret) // use random nonces to decide order. switch { case order > 0: // just break case order < 0: k1, k2 = k2, k1 // swap default: // we should've bailed before this. but if not, bail here. return ErrEcho } s.local.keys = k1 s.remote.keys = k2 // log.Debug("2.2 keys:\n\tshared: %v\n\tk1: %v\n\tk2: %v", // s.sharedSecret, s.local.keys, s.remote.keys) // ============================================================================= // step 2.3. MAC + Cipher -- prepare MAC + cipher if err := s.local.makeMacAndCipher(); err != nil { return err } if err := s.remote.makeMacAndCipher(); err != nil { return err } // log.Debug("2.3 mac + cipher.") // ============================================================================= // step 3. Finish -- send expected message to verify encryption works (send local nonce) // setup ETM ReadWriter w := NewETMWriter(s.insecure, s.local.cipher, s.local.mac) r := NewETMReader(s.insecure, s.remote.cipher, s.remote.mac) s.secure = msgio.Combine(w, r).(msgio.ReadWriteCloser) // log.Debug("3.0 finish. sending: %v", proposeIn.GetRand()) // send their Nonce. if _, err := s.secure.Write(proposeIn.GetRand()); err != nil { return fmt.Errorf("Failed to write Finish nonce: %s", err) } // read our Nonce nonceOut2 := make([]byte, len(nonceOut)) if _, err := io.ReadFull(s.secure, nonceOut2); err != nil { return fmt.Errorf("Failed to read Finish nonce: %s", err) } // log.Debug("3.0 finish.\n\texpect: %v\n\tactual: %v", nonceOut, nonceOut2) if !bytes.Equal(nonceOut, nonceOut2) { return fmt.Errorf("Failed to read our encrypted nonce: %s != %s", nonceOut2, nonceOut) } // Whew! ok, that's all folks. return nil }
func newPeerTime(t time.Time) peer.ID { s := fmt.Sprintf("hmmm time: %v", t) h := u.Hash([]byte(s)) return peer.ID(h) }
// NewBlock creates a Block object from opaque data. It will hash the data. func NewBlock(data []byte) *Block { return &Block{Data: data, Multihash: u.Hash(data)} }
// runBootstrap builds up list of peers by requesting random peer IDs func (dht *IpfsDHT) runBootstrap(ctx context.Context, cfg BootstrapConfig) error { bslog := func(msg string) { log.Debugf("DHT %s dhtRunBootstrap %s -- routing table size: %d", dht.self, msg, dht.routingTable.Size()) } bslog("start") defer bslog("end") defer log.EventBegin(ctx, "dhtRunBootstrap").Done() var merr u.MultiErr randomID := func() peer.ID { // 16 random bytes is not a valid peer id. it may be fine becuase // the dht will rehash to its own keyspace anyway. id := make([]byte, 16) rand.Read(id) id = u.Hash(id) return peer.ID(id) } // bootstrap sequentially, as results will compound ctx, cancel := context.WithTimeout(ctx, cfg.Timeout) defer cancel() runQuery := func(ctx context.Context, id peer.ID) { p, err := dht.FindPeer(ctx, id) if err == routing.ErrNotFound { // this isn't an error. this is precisely what we expect. } else if err != nil { merr = append(merr, err) } else { // woah, actually found a peer with that ID? this shouldn't happen normally // (as the ID we use is not a real ID). this is an odd error worth logging. err := fmt.Errorf("Bootstrap peer error: Actually FOUND peer. (%s, %s)", id, p) log.Warningf("%s", err) merr = append(merr, err) } } sequential := true if sequential { // these should be parallel normally. but can make them sequential for debugging. // note that the core/bootstrap context deadline should be extended too for that. for i := 0; i < cfg.Queries; i++ { id := randomID() log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, cfg.Queries, id) runQuery(ctx, id) } } else { // note on parallelism here: the context is passed in to the queries, so they // **should** exit when it exceeds, making this function exit on ctx cancel. // normally, we should be selecting on ctx.Done() here too, but this gets // complicated to do with WaitGroup, and doesnt wait for the children to exit. var wg sync.WaitGroup for i := 0; i < cfg.Queries; i++ { wg.Add(1) go func() { defer wg.Done() id := randomID() log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, cfg.Queries, id) runQuery(ctx, id) }() } wg.Wait() } if len(merr) > 0 { return merr } return nil }