Commit 4f82ae5e authored by Jeromy's avatar Jeromy
Browse files

move some deps out as gx packages

Showing with 4 additions and 1820 deletions
+4 -1820
// Package loggables includes a bunch of transaltor functions for commonplace/stdlib
// objects. This is boilerplate code that shouldn't change much, and not sprinkled
// all over the place (i.e. gather it here).
//
// Note: it may make sense to put all stdlib Loggable functions in the eventlog
// package. Putting it here for now in case we don't want to polute it.
package loggables
import (
"net"
peer "github.com/ipfs/go-libp2p/p2p/peer"
logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr"
)
// NetConn returns an eventlog.Metadata with the conn addresses
func NetConn(c net.Conn) logging.Loggable {
return logging.Metadata{
"localAddr": c.LocalAddr(),
"remoteAddr": c.RemoteAddr(),
}
}
// Error returns an eventlog.Metadata with an error
func Error(e error) logging.Loggable {
return logging.Metadata{
"error": e.Error(),
}
}
// Dial metadata is metadata for dial events
func Dial(sys string, lid, rid peer.ID, laddr, raddr ma.Multiaddr) DeferredMap {
m := DeferredMap{}
m["subsystem"] = sys
if lid != "" {
m["localPeer"] = func() interface{} { return lid.Pretty() }
}
if laddr != nil {
m["localAddr"] = func() interface{} { return laddr.String() }
}
if rid != "" {
m["remotePeer"] = func() interface{} { return rid.Pretty() }
}
if raddr != nil {
m["remoteAddr"] = func() interface{} { return raddr.String() }
}
return m
}
// DeferredMap is a Loggable which may contained deffered values.
type DeferredMap map[string]interface{}
// Loggable describes objects that can be marshalled into Metadata for logging
func (m DeferredMap) Loggable() map[string]interface{} {
m2 := map[string]interface{}{}
for k, v := range m {
if vf, ok := v.(func() interface{}); ok {
// if it's a DeferredVal, call it.
m2[k] = vf()
} else {
// else use the value as is.
m2[k] = v
}
}
return m2
}
package crypto
import "testing"
func BenchmarkSign1B(b *testing.B) { RunBenchmarkSign(b, 1) }
func BenchmarkSign10B(b *testing.B) { RunBenchmarkSign(b, 10) }
func BenchmarkSign100B(b *testing.B) { RunBenchmarkSign(b, 100) }
func BenchmarkSign1000B(b *testing.B) { RunBenchmarkSign(b, 1000) }
func BenchmarkSign10000B(b *testing.B) { RunBenchmarkSign(b, 10000) }
func BenchmarkSign100000B(b *testing.B) { RunBenchmarkSign(b, 100000) }
func BenchmarkVerify1B(b *testing.B) { RunBenchmarkVerify(b, 1) }
func BenchmarkVerify10B(b *testing.B) { RunBenchmarkVerify(b, 10) }
func BenchmarkVerify100B(b *testing.B) { RunBenchmarkVerify(b, 100) }
func BenchmarkVerify1000B(b *testing.B) { RunBenchmarkVerify(b, 1000) }
func BenchmarkVerify10000B(b *testing.B) { RunBenchmarkVerify(b, 10000) }
func BenchmarkVerify100000B(b *testing.B) { RunBenchmarkVerify(b, 100000) }
func RunBenchmarkSign(b *testing.B, numBytes int) {
secret, _, err := GenerateKeyPair(RSA, 1024)
if err != nil {
b.Fatal(err)
}
someData := make([]byte, numBytes)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := secret.Sign(someData)
if err != nil {
b.Fatal(err)
}
}
}
func RunBenchmarkVerify(b *testing.B, numBytes int) {
secret, public, err := GenerateKeyPair(RSA, 1024)
if err != nil {
b.Fatal(err)
}
someData := make([]byte, numBytes)
signature, err := secret.Sign(someData)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
valid, err := public.Verify(someData, signature)
if err != nil {
b.Fatal(err)
}
if !valid {
b.Fatal("signature should be valid")
}
}
}
// package crypto implements various cryptographic utilities used by ipfs.
// This includes a Public and Private key interface and an RSA key implementation
// that satisfies it.
package crypto
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"io"
"crypto/elliptic"
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"hash"
pb "github.com/ipfs/go-libp2p/p2p/crypto/pb"
proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
)
var log = logging.Logger("crypto")
var ErrBadKeyType = errors.New("invalid or unsupported key type")
const (
RSA = iota
)
// Key represents a crypto key that can be compared to another key
type Key interface {
// Bytes returns a serialized, storeable representation of this key
Bytes() ([]byte, error)
// Hash returns the hash of this key
Hash() ([]byte, error)
// Equals checks whether two PubKeys are the same
Equals(Key) bool
}
// PrivKey represents a private key that can be used to generate a public key,
// sign data, and decrypt data that was encrypted with a public key
type PrivKey interface {
Key
// Cryptographically sign the given bytes
Sign([]byte) ([]byte, error)
// Return a public key paired with this private key
GetPublic() PubKey
// Generate a secret string of bytes
GenSecret() []byte
Decrypt(b []byte) ([]byte, error)
}
type PubKey interface {
Key
// Verify that 'sig' is the signed hash of 'data'
Verify(data []byte, sig []byte) (bool, error)
// Encrypt data in a way that can be decrypted by a paired private key
Encrypt(data []byte) ([]byte, error)
}
// Given a public key, generates the shared key.
type GenSharedKey func([]byte) ([]byte, error)
func GenerateKeyPair(typ, bits int) (PrivKey, PubKey, error) {
return GenerateKeyPairWithReader(typ, bits, rand.Reader)
}
// Generates a keypair of the given type and bitsize
func GenerateKeyPairWithReader(typ, bits int, src io.Reader) (PrivKey, PubKey, error) {
switch typ {
case RSA:
priv, err := rsa.GenerateKey(src, bits)
if err != nil {
return nil, nil, err
}
pk := &priv.PublicKey
return &RsaPrivateKey{sk: priv}, &RsaPublicKey{pk}, nil
default:
return nil, nil, ErrBadKeyType
}
}
// Generates an ephemeral public key and returns a function that will compute
// the shared secret key. Used in the identify module.
//
// Focuses only on ECDH now, but can be made more general in the future.
func GenerateEKeyPair(curveName string) ([]byte, GenSharedKey, error) {
var curve elliptic.Curve
switch curveName {
case "P-256":
curve = elliptic.P256()
case "P-384":
curve = elliptic.P384()
case "P-521":
curve = elliptic.P521()
}
priv, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
if err != nil {
return nil, nil, err
}
pubKey := elliptic.Marshal(curve, x, y)
// log.Debug("GenerateEKeyPair %d", len(pubKey))
done := func(theirPub []byte) ([]byte, error) {
// Verify and unpack node's public key.
x, y := elliptic.Unmarshal(curve, theirPub)
if x == nil {
return nil, fmt.Errorf("Malformed public key: %d %v", len(theirPub), theirPub)
}
if !curve.IsOnCurve(x, y) {
return nil, errors.New("Invalid public key.")
}
// Generate shared secret.
secret, _ := curve.ScalarMult(x, y, priv)
return secret.Bytes(), nil
}
return pubKey, done, nil
}
type StretchedKeys struct {
IV []byte
MacKey []byte
CipherKey []byte
}
// Generates a set of keys for each party by stretching the shared key.
// (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey)
func KeyStretcher(cipherType string, hashType string, secret []byte) (StretchedKeys, StretchedKeys) {
var cipherKeySize int
var ivSize int
switch cipherType {
case "AES-128":
ivSize = 16
cipherKeySize = 16
case "AES-256":
ivSize = 16
cipherKeySize = 32
case "Blowfish":
ivSize = 8
// Note: 24 arbitrarily selected, needs more thought
cipherKeySize = 32
}
hmacKeySize := 20
seed := []byte("key expansion")
result := make([]byte, 2*(ivSize+cipherKeySize+hmacKeySize))
var h func() hash.Hash
switch hashType {
case "SHA1":
h = sha1.New
case "SHA256":
h = sha256.New
case "SHA512":
h = sha512.New
default:
panic("Unrecognized hash function, programmer error?")
}
m := hmac.New(h, secret)
m.Write(seed)
a := m.Sum(nil)
j := 0
for j < len(result) {
m.Reset()
m.Write(a)
m.Write(seed)
b := m.Sum(nil)
todo := len(b)
if j+todo > len(result) {
todo = len(result) - j
}
copy(result[j:j+todo], b)
j += todo
m.Reset()
m.Write(a)
a = m.Sum(nil)
}
half := len(result) / 2
r1 := result[:half]
r2 := result[half:]
var k1 StretchedKeys
var k2 StretchedKeys
k1.IV = r1[0:ivSize]
k1.CipherKey = r1[ivSize : ivSize+cipherKeySize]
k1.MacKey = r1[ivSize+cipherKeySize:]
k2.IV = r2[0:ivSize]
k2.CipherKey = r2[ivSize : ivSize+cipherKeySize]
k2.MacKey = r2[ivSize+cipherKeySize:]
return k1, k2
}
// UnmarshalPublicKey converts a protobuf serialized public key into its
// representative object
func UnmarshalPublicKey(data []byte) (PubKey, error) {
pmes := new(pb.PublicKey)
err := proto.Unmarshal(data, pmes)
if err != nil {
return nil, err
}
switch pmes.GetType() {
case pb.KeyType_RSA:
return UnmarshalRsaPublicKey(pmes.GetData())
default:
return nil, ErrBadKeyType
}
}
// MarshalPublicKey converts a public key object into a protobuf serialized
// public key
func MarshalPublicKey(k PubKey) ([]byte, error) {
b, err := MarshalRsaPublicKey(k.(*RsaPublicKey))
if err != nil {
return nil, err
}
pmes := new(pb.PublicKey)
typ := pb.KeyType_RSA // for now only type.
pmes.Type = &typ
pmes.Data = b
return proto.Marshal(pmes)
}
// UnmarshalPrivateKey converts a protobuf serialized private key into its
// representative object
func UnmarshalPrivateKey(data []byte) (PrivKey, error) {
pmes := new(pb.PrivateKey)
err := proto.Unmarshal(data, pmes)
if err != nil {
return nil, err
}
switch pmes.GetType() {
case pb.KeyType_RSA:
return UnmarshalRsaPrivateKey(pmes.GetData())
default:
return nil, ErrBadKeyType
}
}
// MarshalPrivateKey converts a key object into its protobuf serialized form.
func MarshalPrivateKey(k PrivKey) ([]byte, error) {
b := MarshalRsaPrivateKey(k.(*RsaPrivateKey))
pmes := new(pb.PrivateKey)
typ := pb.KeyType_RSA // for now only type.
pmes.Type = &typ
pmes.Data = b
return proto.Marshal(pmes)
}
// ConfigDecodeKey decodes from b64 (for config file), and unmarshals.
func ConfigDecodeKey(b string) ([]byte, error) {
return base64.StdEncoding.DecodeString(b)
}
// ConfigEncodeKey encodes to b64 (for config file), and marshals.
func ConfigEncodeKey(b []byte) string {
return base64.StdEncoding.EncodeToString(b)
}
// KeyEqual checks whether two
func KeyEqual(k1, k2 Key) bool {
if k1 == k2 {
return true
}
b1, err1 := k1.Bytes()
b2, err2 := k2.Bytes()
return bytes.Equal(b1, b2) && err1 == err2
}
// 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
}
package crypto_test
import (
. "github.com/ipfs/go-libp2p/p2p/crypto"
"bytes"
tu "github.com/ipfs/go-libp2p/testutil"
"testing"
)
func TestRsaKeys(t *testing.T) {
sk, pk, err := tu.RandTestKeyPair(512)
if err != nil {
t.Fatal(err)
}
testKeySignature(t, sk)
testKeyEncoding(t, sk)
testKeyEquals(t, sk)
testKeyEquals(t, pk)
}
func testKeySignature(t *testing.T, sk PrivKey) {
pk := sk.GetPublic()
text := sk.GenSecret()
sig, err := sk.Sign(text)
if err != nil {
t.Fatal(err)
}
valid, err := pk.Verify(text, sig)
if err != nil {
t.Fatal(err)
}
if !valid {
t.Fatal("Invalid signature.")
}
}
func testKeyEncoding(t *testing.T, sk PrivKey) {
skbm, err := MarshalPrivateKey(sk)
if err != nil {
t.Fatal(err)
}
sk2, err := UnmarshalPrivateKey(skbm)
if err != nil {
t.Fatal(err)
}
skbm2, err := MarshalPrivateKey(sk2)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(skbm, skbm2) {
t.Error("skb -> marshal -> unmarshal -> skb failed.\n", skbm, "\n", skbm2)
}
pk := sk.GetPublic()
pkbm, err := MarshalPublicKey(pk)
if err != nil {
t.Fatal(err)
}
_, err = UnmarshalPublicKey(pkbm)
if err != nil {
t.Fatal(err)
}
pkbm2, err := MarshalPublicKey(pk)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(pkbm, pkbm2) {
t.Error("skb -> marshal -> unmarshal -> skb failed.\n", pkbm, "\n", pkbm2)
}
}
func testKeyEquals(t *testing.T, k Key) {
kb, err := k.Bytes()
if err != nil {
t.Fatal(err)
}
if !KeyEqual(k, k) {
t.Fatal("Key not equal to itself.")
}
if !KeyEqual(k, testkey(kb)) {
t.Fatal("Key not equal to key with same bytes.")
}
sk, pk, err := tu.RandTestKeyPair(512)
if err != nil {
t.Fatal(err)
}
if KeyEqual(k, sk) {
t.Fatal("Keys should not equal.")
}
if KeyEqual(k, pk) {
t.Fatal("Keys should not equal.")
}
}
type testkey []byte
func (pk testkey) Bytes() ([]byte, error) {
return pk, nil
}
func (pk testkey) Equals(k Key) bool {
return KeyEqual(pk, k)
}
func (pk testkey) Hash() ([]byte, error) {
return KeyHash(pk)
}
PB = $(wildcard *.proto)
GO = $(PB:.proto=.pb.go)
all: $(GO)
%.pb.go: %.proto
protoc --gogo_out=. $<
clean:
rm *.pb.go
// Code generated by protoc-gen-gogo.
// source: crypto.proto
// DO NOT EDIT!
/*
Package crypto_pb is a generated protocol buffer package.
It is generated from these files:
crypto.proto
It has these top-level messages:
PublicKey
PrivateKey
*/
package crypto_pb
import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type KeyType int32
const (
KeyType_RSA KeyType = 0
)
var KeyType_name = map[int32]string{
0: "RSA",
}
var KeyType_value = map[string]int32{
"RSA": 0,
}
func (x KeyType) Enum() *KeyType {
p := new(KeyType)
*p = x
return p
}
func (x KeyType) String() string {
return proto.EnumName(KeyType_name, int32(x))
}
func (x *KeyType) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(KeyType_value, data, "KeyType")
if err != nil {
return err
}
*x = KeyType(value)
return nil
}
type PublicKey struct {
Type *KeyType `protobuf:"varint,1,req,enum=crypto.pb.KeyType" json:"Type,omitempty"`
Data []byte `protobuf:"bytes,2,req" json:"Data,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *PublicKey) Reset() { *m = PublicKey{} }
func (m *PublicKey) String() string { return proto.CompactTextString(m) }
func (*PublicKey) ProtoMessage() {}
func (m *PublicKey) GetType() KeyType {
if m != nil && m.Type != nil {
return *m.Type
}
return KeyType_RSA
}
func (m *PublicKey) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
type PrivateKey struct {
Type *KeyType `protobuf:"varint,1,req,enum=crypto.pb.KeyType" json:"Type,omitempty"`
Data []byte `protobuf:"bytes,2,req" json:"Data,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *PrivateKey) Reset() { *m = PrivateKey{} }
func (m *PrivateKey) String() string { return proto.CompactTextString(m) }
func (*PrivateKey) ProtoMessage() {}
func (m *PrivateKey) GetType() KeyType {
if m != nil && m.Type != nil {
return *m.Type
}
return KeyType_RSA
}
func (m *PrivateKey) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
func init() {
proto.RegisterEnum("crypto.pb.KeyType", KeyType_name, KeyType_value)
}
package crypto.pb;
enum KeyType {
RSA = 0;
}
message PublicKey {
required KeyType Type = 1;
required bytes Data = 2;
}
message PrivateKey {
required KeyType Type = 1;
required bytes Data = 2;
}
package crypto
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"errors"
pb "github.com/ipfs/go-libp2p/p2p/crypto/pb"
proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
)
type RsaPrivateKey struct {
sk *rsa.PrivateKey
pk *rsa.PublicKey
}
type RsaPublicKey struct {
k *rsa.PublicKey
}
func (pk *RsaPublicKey) Verify(data, sig []byte) (bool, error) {
hashed := sha256.Sum256(data)
err := rsa.VerifyPKCS1v15(pk.k, crypto.SHA256, hashed[:], sig)
if err != nil {
return false, err
}
return true, nil
}
func (pk *RsaPublicKey) Bytes() ([]byte, error) {
b, err := x509.MarshalPKIXPublicKey(pk.k)
if err != nil {
return nil, err
}
pbmes := new(pb.PublicKey)
typ := pb.KeyType_RSA
pbmes.Type = &typ
pbmes.Data = b
return proto.Marshal(pbmes)
}
func (pk *RsaPublicKey) Encrypt(b []byte) ([]byte, error) {
return rsa.EncryptPKCS1v15(rand.Reader, pk.k, b)
}
// Equals checks whether this key is equal to another
func (pk *RsaPublicKey) Equals(k Key) bool {
return KeyEqual(pk, k)
}
func (pk *RsaPublicKey) Hash() ([]byte, error) {
return KeyHash(pk)
}
func (sk *RsaPrivateKey) GenSecret() []byte {
buf := make([]byte, 16)
rand.Read(buf)
return buf
}
func (sk *RsaPrivateKey) Sign(message []byte) ([]byte, error) {
hashed := sha256.Sum256(message)
return rsa.SignPKCS1v15(rand.Reader, sk.sk, crypto.SHA256, hashed[:])
}
func (sk *RsaPrivateKey) GetPublic() PubKey {
if sk.pk == nil {
sk.pk = &sk.sk.PublicKey
}
return &RsaPublicKey{sk.pk}
}
func (sk *RsaPrivateKey) Decrypt(b []byte) ([]byte, error) {
return rsa.DecryptPKCS1v15(rand.Reader, sk.sk, b)
}
func (sk *RsaPrivateKey) Bytes() ([]byte, error) {
b := x509.MarshalPKCS1PrivateKey(sk.sk)
pbmes := new(pb.PrivateKey)
typ := pb.KeyType_RSA
pbmes.Type = &typ
pbmes.Data = b
return proto.Marshal(pbmes)
}
// Equals checks whether this key is equal to another
func (sk *RsaPrivateKey) Equals(k Key) bool {
return KeyEqual(sk, k)
}
func (sk *RsaPrivateKey) Hash() ([]byte, error) {
return KeyHash(sk)
}
func UnmarshalRsaPrivateKey(b []byte) (*RsaPrivateKey, error) {
sk, err := x509.ParsePKCS1PrivateKey(b)
if err != nil {
return nil, err
}
return &RsaPrivateKey{sk: sk}, nil
}
func MarshalRsaPrivateKey(k *RsaPrivateKey) []byte {
return x509.MarshalPKCS1PrivateKey(k.sk)
}
func UnmarshalRsaPublicKey(b []byte) (*RsaPublicKey, error) {
pub, err := x509.ParsePKIXPublicKey(b)
if err != nil {
return nil, err
}
pk, ok := pub.(*rsa.PublicKey)
if !ok {
return nil, errors.New("Not actually an rsa public key.")
}
return &RsaPublicKey{pk}, nil
}
func MarshalRsaPublicKey(k *RsaPublicKey) ([]byte, error) {
return x509.MarshalPKIXPublicKey(k.k)
}
package secio
import (
"errors"
"fmt"
"strings"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"hash"
ci "github.com/ipfs/go-libp2p/p2p/crypto"
bfish "gx/ipfs/Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2/go-crypto/blowfish"
)
// List of supported ECDH curves
var SupportedExchanges = "P-256,P-384,P-521"
// List of supported Ciphers
var SupportedCiphers = "AES-256,AES-128,Blowfish"
// List of supported Hashes
var SupportedHashes = "SHA256,SHA512"
type HMAC struct {
hash.Hash
size int
}
// encParams represent encryption parameters
type encParams struct {
// keys
permanentPubKey ci.PubKey
ephemeralPubKey []byte
keys ci.StretchedKeys
// selections
curveT string
cipherT string
hashT string
// cipher + mac
cipher cipher.Stream
mac HMAC
}
func (e *encParams) makeMacAndCipher() error {
m, err := newMac(e.hashT, e.keys.MacKey)
if err != nil {
return err
}
bc, err := newBlockCipher(e.cipherT, e.keys.CipherKey)
if err != nil {
return err
}
e.cipher = cipher.NewCTR(bc, e.keys.IV)
e.mac = m
return nil
}
func newMac(hashType string, key []byte) (HMAC, error) {
switch hashType {
case "SHA1":
return HMAC{hmac.New(sha1.New, key), sha1.Size}, nil
case "SHA512":
return HMAC{hmac.New(sha512.New, key), sha512.Size}, nil
case "SHA256":
return HMAC{hmac.New(sha256.New, key), sha256.Size}, nil
default:
return HMAC{}, fmt.Errorf("Unrecognized hash type: %s", hashType)
}
}
func newBlockCipher(cipherT string, key []byte) (cipher.Block, error) {
switch cipherT {
case "AES-128", "AES-256":
return aes.NewCipher(key)
case "Blowfish":
return bfish.NewCipher(key)
default:
return nil, fmt.Errorf("Unrecognized cipher type: %s", cipherT)
}
}
// Determines which algorithm to use. Note: f(a, b) = f(b, a)
func selectBest(order int, p1, p2 string) (string, error) {
var f, s []string
switch {
case order < 0:
f = strings.Split(p2, ",")
s = strings.Split(p1, ",")
case order > 0:
f = strings.Split(p1, ",")
s = strings.Split(p2, ",")
default: // Exact same preferences.
p := strings.Split(p1, ",")
return p[0], nil
}
for _, fc := range f {
for _, sc := range s {
if fc == sc {
return fc, nil
}
}
}
return "", errors.New("No algorithms in common!")
}
// package secio handles establishing secure communication between two peers.
package secio
import (
"io"
ci "github.com/ipfs/go-libp2p/p2p/crypto"
peer "github.com/ipfs/go-libp2p/p2p/peer"
msgio "gx/ipfs/QmRQhVisS8dmPbjBUthVkenn81pBxrx1GxE281csJhm2vL/go-msgio"
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
)
// SessionGenerator constructs secure communication sessions for a peer.
type SessionGenerator struct {
LocalID peer.ID
PrivateKey ci.PrivKey
}
// NewSession takes an insecure io.ReadWriter, sets up a TLS-like
// handshake with the other side, and returns a secure session.
// The handshake isn't run until the connection is read or written to.
// See the source for the protocol details and security implementation.
// The provided Context is only needed for the duration of this function.
func (sg *SessionGenerator) NewSession(ctx context.Context, insecure io.ReadWriteCloser) (Session, error) {
return newSecureSession(ctx, sg.LocalID, sg.PrivateKey, insecure)
}
type Session interface {
// ReadWriter returns the encrypted communication channel
ReadWriter() msgio.ReadWriteCloser
// LocalPeer retrieves the local peer.
LocalPeer() peer.ID
// LocalPrivateKey retrieves the local private key
LocalPrivateKey() ci.PrivKey
// RemotePeer retrieves the remote peer.
RemotePeer() peer.ID
// RemotePublicKey retrieves the remote's public key
// which was received during the handshake.
RemotePublicKey() ci.PubKey
// Close closes the secure session
Close() error
}
// SecureReadWriter returns the encrypted communication channel
func (s *secureSession) ReadWriter() msgio.ReadWriteCloser {
if err := s.Handshake(); err != nil {
return &closedRW{err}
}
return s.secure
}
// LocalPeer retrieves the local peer.
func (s *secureSession) LocalPeer() peer.ID {
return s.localPeer
}
// LocalPrivateKey retrieves the local peer's PrivateKey
func (s *secureSession) LocalPrivateKey() ci.PrivKey {
return s.localKey
}
// RemotePeer retrieves the remote peer.
func (s *secureSession) RemotePeer() peer.ID {
if err := s.Handshake(); err != nil {
return ""
}
return s.remotePeer
}
// RemotePeer retrieves the remote peer.
func (s *secureSession) RemotePublicKey() ci.PubKey {
if err := s.Handshake(); err != nil {
return nil
}
return s.remote.permanentPubKey
}
// Close closes the secure session
func (s *secureSession) Close() error {
s.cancel()
s.handshakeMu.Lock()
defer s.handshakeMu.Unlock()
if s.secure == nil {
return s.insecure.Close() // hadn't secured yet.
}
return s.secure.Close()
}
// closedRW implements a stub msgio interface that's already
// closed and errored.
type closedRW struct {
err error
}
func (c *closedRW) Read(buf []byte) (int, error) {
return 0, c.err
}
func (c *closedRW) Write(buf []byte) (int, error) {
return 0, c.err
}
func (c *closedRW) NextMsgLen() (int, error) {
return 0, c.err
}
func (c *closedRW) ReadMsg() ([]byte, error) {
return nil, c.err
}
func (c *closedRW) WriteMsg(buf []byte) error {
return c.err
}
func (c *closedRW) Close() error {
return c.err
}
func (c *closedRW) ReleaseMsg(m []byte) {
}
package secio
PB = $(wildcard *.proto)
GO = $(PB:.proto=.pb.go)
all: $(GO)
%.pb.go: %.proto
protoc --gogo_out=. --proto_path=../../../../../../:/usr/local/opt/protobuf/include:. $<
clean:
rm *.pb.go
// Code generated by protoc-gen-gogo.
// source: spipe.proto
// DO NOT EDIT!
/*
Package spipe_pb is a generated protocol buffer package.
It is generated from these files:
spipe.proto
It has these top-level messages:
Propose
Exchange
*/
package spipe_pb
import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type Propose struct {
Rand []byte `protobuf:"bytes,1,opt,name=rand" json:"rand,omitempty"`
Pubkey []byte `protobuf:"bytes,2,opt,name=pubkey" json:"pubkey,omitempty"`
Exchanges *string `protobuf:"bytes,3,opt,name=exchanges" json:"exchanges,omitempty"`
Ciphers *string `protobuf:"bytes,4,opt,name=ciphers" json:"ciphers,omitempty"`
Hashes *string `protobuf:"bytes,5,opt,name=hashes" json:"hashes,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Propose) Reset() { *m = Propose{} }
func (m *Propose) String() string { return proto.CompactTextString(m) }
func (*Propose) ProtoMessage() {}
func (m *Propose) GetRand() []byte {
if m != nil {
return m.Rand
}
return nil
}
func (m *Propose) GetPubkey() []byte {
if m != nil {
return m.Pubkey
}
return nil
}
func (m *Propose) GetExchanges() string {
if m != nil && m.Exchanges != nil {
return *m.Exchanges
}
return ""
}
func (m *Propose) GetCiphers() string {
if m != nil && m.Ciphers != nil {
return *m.Ciphers
}
return ""
}
func (m *Propose) GetHashes() string {
if m != nil && m.Hashes != nil {
return *m.Hashes
}
return ""
}
type Exchange struct {
Epubkey []byte `protobuf:"bytes,1,opt,name=epubkey" json:"epubkey,omitempty"`
Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Exchange) Reset() { *m = Exchange{} }
func (m *Exchange) String() string { return proto.CompactTextString(m) }
func (*Exchange) ProtoMessage() {}
func (m *Exchange) GetEpubkey() []byte {
if m != nil {
return m.Epubkey
}
return nil
}
func (m *Exchange) GetSignature() []byte {
if m != nil {
return m.Signature
}
return nil
}
func init() {
}
package spipe.pb;
message Propose {
optional bytes rand = 1;
optional bytes pubkey = 2;
optional string exchanges = 3;
optional string ciphers = 4;
optional string hashes = 5;
}
message Exchange {
optional bytes epubkey = 1;
optional bytes signature = 2;
}
package secio
import (
"bytes"
"crypto/rand"
"errors"
"fmt"
"io"
"sync"
"time"
ci "github.com/ipfs/go-libp2p/p2p/crypto"
pb "github.com/ipfs/go-libp2p/p2p/crypto/secio/pb"
peer "github.com/ipfs/go-libp2p/p2p/peer"
msgio "gx/ipfs/QmRQhVisS8dmPbjBUthVkenn81pBxrx1GxE281csJhm2vL/go-msgio"
u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
)
var log = logging.Logger("secio")
// ErrUnsupportedKeyType is returned when a private key cast/type switch fails.
var ErrUnsupportedKeyType = errors.New("unsupported key type")
// ErrClosed signals the closing of a connection.
var ErrClosed = errors.New("connection closed")
// ErrEcho is returned when we're attempting to handshake with the same keys and nonces.
var ErrEcho = errors.New("same keys and nonces. one side talking to self.")
// HandshakeTimeout governs how long the handshake will be allowed to take place for.
// Making this number large means there could be many bogus connections waiting to
// timeout in flight. Typical handshakes take ~3RTTs, so it should be completed within
// seconds across a typical planet in the solar system.
var HandshakeTimeout = time.Second * 30
// nonceSize is the size of our nonces (in bytes)
const nonceSize = 16
// secureSession encapsulates all the parameters needed for encrypting
// and decrypting traffic from an insecure channel.
type secureSession struct {
ctx context.Context
cancel context.CancelFunc
secure msgio.ReadWriteCloser
insecure io.ReadWriteCloser
insecureM msgio.ReadWriter
localKey ci.PrivKey
localPeer peer.ID
remotePeer peer.ID
local encParams
remote encParams
sharedSecret []byte
handshakeMu sync.Mutex // guards handshakeDone + handshakeErr
handshakeDone bool
handshakeErr error
}
func (s *secureSession) Loggable() map[string]interface{} {
m := make(map[string]interface{})
m["localPeer"] = s.localPeer.Pretty()
m["remotePeer"] = s.remotePeer.Pretty()
m["established"] = (s.secure != nil)
return m
}
func newSecureSession(ctx context.Context, local peer.ID, key ci.PrivKey, insecure io.ReadWriteCloser) (*secureSession, error) {
s := &secureSession{localPeer: local, localKey: key}
s.ctx, s.cancel = context.WithCancel(ctx)
switch {
case s.localPeer == "":
return nil, errors.New("no local id provided")
case s.localKey == nil:
return nil, errors.New("no local private key provided")
case !s.localPeer.MatchesPrivateKey(s.localKey):
return nil, fmt.Errorf("peer.ID does not match PrivateKey")
case insecure == nil:
return nil, fmt.Errorf("insecure ReadWriter is nil")
}
s.ctx = ctx
s.insecure = insecure
s.insecureM = msgio.NewReadWriter(insecure)
return s, nil
}
func (s *secureSession) Handshake() error {
s.handshakeMu.Lock()
defer s.handshakeMu.Unlock()
if s.handshakeErr != nil {
return s.handshakeErr
}
if !s.handshakeDone {
s.handshakeErr = s.runHandshake()
s.handshakeDone = true
}
return s.handshakeErr
}
// 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
}
package secio
import (
"crypto/cipher"
"errors"
"fmt"
"io"
"sync"
"crypto/hmac"
msgio "gx/ipfs/QmRQhVisS8dmPbjBUthVkenn81pBxrx1GxE281csJhm2vL/go-msgio"
mpool "gx/ipfs/QmRQhVisS8dmPbjBUthVkenn81pBxrx1GxE281csJhm2vL/go-msgio/mpool"
proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
)
// ErrMACInvalid signals that a MAC verification failed
var ErrMACInvalid = errors.New("MAC verification failed")
// bufPool is a ByteSlicePool for messages. we need buffers because (sadly)
// we cannot encrypt in place-- the user needs their buffer back.
var bufPool = mpool.ByteSlicePool
type etmWriter struct {
// params
pool mpool.Pool // for the buffers with encrypted data
msg msgio.WriteCloser // msgio for knowing where boundaries lie
str cipher.Stream // the stream cipher to encrypt with
mac HMAC // the mac to authenticate data with
sync.Mutex
}
// NewETMWriter Encrypt-Then-MAC
func NewETMWriter(w io.Writer, s cipher.Stream, mac HMAC) msgio.WriteCloser {
return &etmWriter{msg: msgio.NewWriter(w), str: s, mac: mac, pool: bufPool}
}
// Write writes passed in buffer as a single message.
func (w *etmWriter) Write(b []byte) (int, error) {
if err := w.WriteMsg(b); err != nil {
return 0, err
}
return len(b), nil
}
// WriteMsg writes the msg in the passed in buffer.
func (w *etmWriter) WriteMsg(b []byte) error {
w.Lock()
defer w.Unlock()
// encrypt.
data := w.pool.Get(uint32(len(b))).([]byte)
data = data[:len(b)] // the pool's buffer may be larger
w.str.XORKeyStream(data, b)
// log.Debugf("ENC plaintext (%d): %s %v", len(b), b, b)
// log.Debugf("ENC ciphertext (%d): %s %v", len(data), data, data)
// then, mac.
if _, err := w.mac.Write(data); err != nil {
return err
}
// Sum appends.
data = w.mac.Sum(data)
w.mac.Reset()
// it's sad to append here. our buffers are -- hopefully -- coming from
// a shared buffer pool, so the append may not actually cause allocation
// one can only hope. i guess we'll see.
return w.msg.WriteMsg(data)
}
func (w *etmWriter) Close() error {
return w.msg.Close()
}
type etmReader struct {
msgio.Reader
io.Closer
// internal buffer returned from the msgio
buf []byte
// low and high watermark for the buffered data
lowat int
hiwat int
// params
msg msgio.ReadCloser // msgio for knowing where boundaries lie
str cipher.Stream // the stream cipher to encrypt with
mac HMAC // the mac to authenticate data with
sync.Mutex
}
// NewETMReader Encrypt-Then-MAC
func NewETMReader(r io.Reader, s cipher.Stream, mac HMAC) msgio.ReadCloser {
return &etmReader{msg: msgio.NewReader(r), str: s, mac: mac}
}
func (r *etmReader) NextMsgLen() (int, error) {
return r.msg.NextMsgLen()
}
func (r *etmReader) drain(buf []byte) int {
// Return zero if there is no data remaining in the internal buffer.
if r.lowat == r.hiwat {
return 0
}
// Copy data to the output buffer.
n := copy(buf, r.buf[r.lowat:r.hiwat])
// Update the low watermark.
r.lowat += n
// Release the buffer and reset the watermarks if it has been fully read.
if r.lowat == r.hiwat {
r.msg.ReleaseMsg(r.buf)
r.buf = nil
r.lowat = 0
r.hiwat = 0
}
return n
}
func (r *etmReader) fill() error {
// Read a message from the underlying msgio.
msg, err := r.msg.ReadMsg()
if err != nil {
return err
}
// Check the MAC.
n, err := r.macCheckThenDecrypt(msg)
if err != nil {
r.msg.ReleaseMsg(msg)
return err
}
// Retain the buffer so it can be drained from and later released.
r.buf = msg
r.lowat = 0
r.hiwat = n
return nil
}
func (r *etmReader) Read(buf []byte) (int, error) {
r.Lock()
defer r.Unlock()
// Return buffered data without reading more, if possible.
copied := r.drain(buf)
if copied > 0 {
return copied, nil
}
// Check the length of the next message.
fullLen, err := r.msg.NextMsgLen()
if err != nil {
return 0, err
}
// If the destination buffer is too short, fill an internal buffer and then
// drain as much of that into the output buffer as will fit.
if cap(buf) < fullLen {
err := r.fill()
if err != nil {
return 0, err
}
copied := r.drain(buf)
return copied, nil
}
// Otherwise, read directly into the destination buffer.
n, err := io.ReadFull(r.msg, buf[:fullLen])
if err != nil {
return 0, err
}
m, err := r.macCheckThenDecrypt(buf[:n])
if err != nil {
return 0, err
}
return m, nil
}
func (r *etmReader) ReadMsg() ([]byte, error) {
r.Lock()
defer r.Unlock()
msg, err := r.msg.ReadMsg()
if err != nil {
return nil, err
}
n, err := r.macCheckThenDecrypt(msg)
if err != nil {
return nil, err
}
return msg[:n], nil
}
func (r *etmReader) macCheckThenDecrypt(m []byte) (int, error) {
l := len(m)
if l < r.mac.size {
return 0, fmt.Errorf("buffer (%d) shorter than MAC size (%d)", l, r.mac.size)
}
mark := l - r.mac.size
data := m[:mark]
macd := m[mark:]
r.mac.Write(data)
expected := r.mac.Sum(nil)
r.mac.Reset()
// check mac. if failed, return error.
if !hmac.Equal(macd, expected) {
log.Debug("MAC Invalid:", expected, "!=", macd)
return 0, ErrMACInvalid
}
// ok seems good. decrypt. (can decrypt in place, yay!)
// log.Debugf("DEC ciphertext (%d): %s %v", len(data), data, data)
r.str.XORKeyStream(data, data)
// log.Debugf("DEC plaintext (%d): %s %v", len(data), data, data)
return mark, nil
}
func (w *etmReader) Close() error {
return w.msg.Close()
}
// ReleaseMsg signals a buffer can be reused.
func (r *etmReader) ReleaseMsg(b []byte) {
r.msg.ReleaseMsg(b)
}
// writeMsgCtx is used by the
func writeMsgCtx(ctx context.Context, w msgio.Writer, msg proto.Message) ([]byte, error) {
enc, err := proto.Marshal(msg)
if err != nil {
return nil, err
}
// write in a goroutine so we can exit when our context is cancelled.
done := make(chan error)
go func(m []byte) {
err := w.WriteMsg(m)
select {
case done <- err:
case <-ctx.Done():
}
}(enc)
select {
case <-ctx.Done():
return nil, ctx.Err()
case e := <-done:
return enc, e
}
}
func readMsgCtx(ctx context.Context, r msgio.Reader, p proto.Message) ([]byte, error) {
var msg []byte
// read in a goroutine so we can exit when our context is cancelled.
done := make(chan error)
go func() {
var err error
msg, err = r.ReadMsg()
select {
case done <- err:
case <-ctx.Done():
}
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case e := <-done:
if e != nil {
return nil, e
}
}
return msg, proto.Unmarshal(msg, p)
}
......@@ -10,8 +10,8 @@ import (
"time"
"github.com/ipfs/go-libp2p/p2p/host"
"github.com/ipfs/go-libp2p/p2p/peer"
"gx/ipfs/QmSscYPCcE1H3UQr2tnsJ2a9dK9LsHTBGgP71VW6fz67e5/mdns"
"gx/ipfs/QmY1xNhBfF9xA1pmD8yejyQAyd77K68qNN6JPM1CN2eiRu/go-libp2p-peer"
manet "gx/ipfs/QmYVqhVfbK4BKvbW88Lhm26b3ud14sTBvcm1H7uWUx1Fkp/go-multiaddr-net"
logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr"
......
......@@ -6,11 +6,11 @@ import (
metrics "github.com/ipfs/go-libp2p/p2p/metrics"
mstream "github.com/ipfs/go-libp2p/p2p/metrics/stream"
inet "github.com/ipfs/go-libp2p/p2p/net"
peer "github.com/ipfs/go-libp2p/p2p/peer"
protocol "github.com/ipfs/go-libp2p/p2p/protocol"
identify "github.com/ipfs/go-libp2p/p2p/protocol/identify"
relay "github.com/ipfs/go-libp2p/p2p/protocol/relay"
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
peer "gx/ipfs/QmY1xNhBfF9xA1pmD8yejyQAyd77K68qNN6JPM1CN2eiRu/go-libp2p-peer"
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr"
......
......@@ -3,10 +3,10 @@ package basichost
import (
"sync"
lgbl "github.com/ipfs/go-libp2p/loggables"
inat "github.com/ipfs/go-libp2p/p2p/nat"
inet "github.com/ipfs/go-libp2p/p2p/net"
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
lgbl "gx/ipfs/QmSyBhZt2upyQ3NJmTpab1pX6hesA59vcYTGmgoDorZZbw/go-libp2p-loggables"
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr"
)
......
......@@ -3,9 +3,9 @@ package host
import (
metrics "github.com/ipfs/go-libp2p/p2p/metrics"
inet "github.com/ipfs/go-libp2p/p2p/net"
peer "github.com/ipfs/go-libp2p/p2p/peer"
protocol "github.com/ipfs/go-libp2p/p2p/protocol"
msmux "gx/ipfs/QmUeEcYJrzAEKdQXjzTxCgNZgc9sRuwharsvzzm5Gd2oGB/go-multistream"
peer "gx/ipfs/QmY1xNhBfF9xA1pmD8yejyQAyd77K68qNN6JPM1CN2eiRu/go-libp2p-peer"
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment