diff --git a/crypto/spipe/internal/pb/spipe.proto b/crypto/spipe/internal/pb/spipe.proto index 5f263e7ddc1278019afa4942e1a15bcf3669b5ac..a7a46773729aeb4d946e712cb7df8b51ea64a391 100644 --- a/crypto/spipe/internal/pb/spipe.proto +++ b/crypto/spipe/internal/pb/spipe.proto @@ -12,9 +12,3 @@ message Exchange { optional bytes epubkey = 1; optional bytes signature = 2; } - -message DataSig { - optional bytes data = 1; - optional bytes signature = 2; - optional uint64 id = 3; -} diff --git a/crypto/spipe/signedpipe.go b/crypto/spipe/signedpipe.go deleted file mode 100644 index 3bc7b1d6f8676d2b85ac392245fa08344b691eb1..0000000000000000000000000000000000000000 --- a/crypto/spipe/signedpipe.go +++ /dev/null @@ -1,305 +0,0 @@ -package spipe - -import ( - "bytes" - "crypto/rand" - "errors" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ci "github.com/jbenet/go-ipfs/crypto" - pb "github.com/jbenet/go-ipfs/crypto/spipe/internal/pb" - "github.com/jbenet/go-ipfs/peer" - "github.com/jbenet/go-ipfs/util/pipes" -) - -type SignedPipe struct { - pipes.Duplex - insecure pipes.Duplex - - local peer.Peer - remote peer.Peer - peers peer.Peerstore - - ctx context.Context - cancel context.CancelFunc - - localMsgID uint64 - removeMsgID uint64 -} - -// secureChallengeSize is a constant that determines the initial challenge, and every subsequent -// sequence number. It should be large enough to be unguessable by adversaries (128+ bits). -// (SECURITY WARNING) -const secureChallengeSize = (256 / 32) - -func NewSignedPipe(parctx context.Context, bufsize int, local peer.Peer, - peers peer.Peerstore, insecure pipes.Duplex) (*SignedPipe, error) { - - ctx, cancel := context.WithCancel(parctx) - - sp := &SignedPipe{ - Duplex: pipes.NewDuplex(bufsize), - local: local, - peers: peers, - insecure: insecure, - - ctx: ctx, - cancel: cancel, - } - - if err := sp.handshake(); err != nil { - sp.Close() - return nil, err - } - return sp, nil -} - -func (sp *SignedPipe) trySend(b []byte) bool { - select { - case <-sp.ctx.Done(): - return false - case sp.insecure.Out <- b: - return true - } -} - -func (sp *SignedPipe) tryRecv() ([]byte, bool) { - select { - case <-sp.ctx.Done(): - return nil, false - case data, ok := <-sp.insecure.In: - if !ok { - return nil, false - } - return data, true - } -} - -// reduceChallenge reduces a series of bytes into a -// single uint64 we can use as a seed for message IDs -func reduceChallenge(cha []byte) uint64 { - var out uint64 - for _, b := range cha { - out ^= uint64(b) - out = out << 1 - } - return out -} - -func (sp *SignedPipe) handshake() error { - // Send them our public key - pubk := sp.local.PubKey() - pkb, err := pubk.Bytes() - if err != nil { - return err - } - - // Exchange public keys with remote peer - if !sp.trySend(pkb) { - return context.Canceled - } - theirPkb := <-sp.insecure.In - - theirPubKey, err := ci.UnmarshalPublicKey(theirPkb) - if err != nil { - return err - } - - challenge := make([]byte, secureChallengeSize) - rand.Read(challenge) - - enc, err := theirPubKey.Encrypt(challenge) - if err != nil { - return err - } - - chsig, err := sp.local.PrivKey().Sign(challenge) - if err != nil { - return err - } - - if !sp.trySend(enc) { - return context.Canceled - } - if !sp.trySend(chsig) { - return context.Canceled - } - - theirEnc, ok := sp.tryRecv() - if !ok { - return context.Canceled - } - theirChSig, ok := sp.tryRecv() - if !ok { - return context.Canceled - } - - // Decrypt and verify their challenge - unenc, err := sp.local.PrivKey().Decrypt(theirEnc) - if err != nil { - return err - } - ok, err = theirPubKey.Verify(unenc, theirChSig) - if err != nil { - return err - } - if !ok { - return errors.New("Invalid signature!") - } - - // Sign the unencrypted challenge, and send it back - sig, err := sp.local.PrivKey().Sign(unenc) - if err != nil { - return err - } - - if !sp.trySend(unenc) { - return context.Canceled - } - if !sp.trySend(sig) { - return context.Canceled - } - theirUnenc, ok := sp.tryRecv() - if !ok { - return context.Canceled - } - theirSig, ok := sp.tryRecv() - if !ok { - return context.Canceled - } - - // Verify that they correctly unecrypted the challenge - if !bytes.Equal(theirUnenc, challenge) { - return errors.New("received bad challenge response") - } - - correct, err := theirPubKey.Verify(theirUnenc, theirSig) - if err != nil { - return err - } - - if !correct { - return errors.New("Incorrect signature on challenge") - } - - sp.removeMsgID = reduceChallenge(challenge) - sp.localMsgID = reduceChallenge(unenc) - - go sp.handleIn(theirPubKey) - go sp.handleOut(sp.local.PrivKey()) - - finished := []byte("finished") - - select { - case <-sp.ctx.Done(): - return context.Canceled - case sp.Out <- finished: - } - - var resp []byte - select { - case <-sp.ctx.Done(): - return context.Canceled - case resp, ok = <-sp.In: - if !ok { - return errors.New("Channel closed before handshake finished.") - } - } - if !bytes.Equal(resp, finished) { - return errors.New("Handshake failed!") - } - - return nil -} - -func (sp *SignedPipe) handleOut(pk ci.PrivKey) { - for { - var data []byte - var ok bool - select { - case <-sp.ctx.Done(): - return - case data, ok = <-sp.Out: - if !ok { - log.Warning("pipe closed!") - return - } - } - - sdata := new(pb.DataSig) - - sig, err := pk.Sign(data) - if err != nil { - log.Error("Error signing outgoing data: %s", err) - return - } - - sdata.Data = data - sdata.Signature = sig - sdata.Id = proto.Uint64(sp.localMsgID) - b, err := proto.Marshal(sdata) - if err != nil { - log.Error("Error marshaling signed data object: %s", err) - return - } - sp.localMsgID++ - - select { - case sp.insecure.Out <- b: - case <-sp.ctx.Done(): - log.Debug("Context finished before send could occur") - return - } - } -} - -func (sp *SignedPipe) handleIn(theirPubkey ci.PubKey) { - for { - var data []byte - var ok bool - select { - case <-sp.ctx.Done(): - return - case data, ok = <-sp.insecure.In: - if !ok { - log.Debug("Signed pipe closed") - return - } - } - - sdata := new(pb.DataSig) - err := proto.Unmarshal(data, sdata) - if err != nil { - log.Error("Failed to unmarshal sigdata object") - continue - } - correct, err := theirPubkey.Verify(sdata.GetData(), sdata.GetSignature()) - if err != nil { - log.Error(err) - continue - } - if !correct { - log.Error("Received data with invalid signature!") - continue - } - - if sdata.GetId() != sp.removeMsgID { - log.Critical("Out of order message id!") - return - } - sp.removeMsgID++ - - select { - case <-sp.ctx.Done(): - return - case sp.In <- sdata.GetData(): - } - } -} - -func (sp *SignedPipe) Close() error { - sp.cancel() - return nil -} diff --git a/crypto/spipe/spipe_test.go b/crypto/spipe/spipe_test.go index c0f15ac85587aae3aaa6ff7594431593646a0144..716b73311b3d7b2cfd035ec6742a557a14fd3203 100644 --- a/crypto/spipe/spipe_test.go +++ b/crypto/spipe/spipe_test.go @@ -130,58 +130,6 @@ func runEncryptBenchmark(b *testing.B) { } -func BenchmarkSignedChannel(b *testing.B) { - pstore := peer.NewPeerstore() - ctx := context.TODO() - bufsize := 1024 * 1024 - - pa := getPeer(b) - pb := getPeer(b) - duplexa := pipes.NewDuplex(16) - duplexb := pipes.NewDuplex(16) - - go bindDuplexNoCopy(duplexa, duplexb) - - var spb *SignedPipe - done := make(chan struct{}) - go func() { - var err error - spb, err = NewSignedPipe(ctx, bufsize, pb, pstore, duplexb) - if err != nil { - b.Fatal(err) - } - done <- struct{}{} - }() - - spa, err := NewSignedPipe(ctx, bufsize, pa, pstore, duplexa) - if err != nil { - b.Fatal(err) - } - - <-done - - go func() { - for _ = range spa.In { - // Throw it all away, - // all of your hopes and dreams - // piped out to /dev/null... - done <- struct{}{} - } - }() - - data := make([]byte, 1024*512) - util.NewTimeSeededRand().Read(data) - // Begin actual benchmarking - b.ResetTimer() - - for i := 0; i < b.N; i++ { - b.SetBytes(int64(len(data))) - spb.Out <- data - <-done - } - -} - func BenchmarkDataTransfer(b *testing.B) { duplexa := pipes.NewDuplex(16) duplexb := pipes.NewDuplex(16)