noise

package module
v1.1.1-0...-2151071 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2019 License: MIT Imports: 18 Imported by: 0

README

Noise

GoDoc Discord MIT licensed Build Status Go Report Card Coverage Statusd

noise is an opinionated, easy-to-use P2P network stack for decentralized applications, and cryptographic protocols written in Go by the Perlin team.

noise is made to be robust, developer-friendly, performant, secure, and cross-platform across multitudes of devices by making use of well-tested, production-grade dependencies.


By itself, noise is a low-level, stateless, concurrent networking library that easily allows you to incorporate fundamental features any modern p2p application needs such as:

  1. cryptographic primitives (Ed25519, PoW, AES-256),
  2. message serialization/deserialization schemes (byte-order little endian, protobuf, msgpack),
  3. network timeout/error management (on dial, on receive message, on send buffer full),
  4. network-level atomic operations (receive-then-lock),
  5. and NAT traversal support (NAT-PMP, UPnP).

Out of its own low-level constructs, noise additionally comes bundled with a high-level protocol package comprised of a large number of production-ready, high-level protocol building blocks such as:

  1. handshake protocol implementations (Elliptic-Curve Diffie Hellman),
  2. peer routing/discovery protocol implementations (S/Kademlia),
  3. message broadcasting protocol implementations (S/Kademlia),
  4. overlay network protocol implementations (S/Kademlia),
  5. cryptographic identity schemes (Ed25519 w/ EdDSA signatures),
  6. and authenticated encryption schemes (AES-256 GCM AEAD).

Every single building block is easily configurable, and may be mixed and matched together to help you kickstart your journey on developing secure, debuggable, and highly-performant p2p applications.

package main

import (
    "fmt"
	
    "github.com/Yayg/noise"
    "github.com/Yayg/noise/cipher/aead"
    "github.com/Yayg/noise/handshake/ecdh"
    "github.com/Yayg/noise/identity/ed25519"
    "github.com/Yayg/noise/protocol"
    "github.com/Yayg/noise/rpc"
    "github.com/Yayg/noise/skademlia"
)

type chatMessage struct {
	text string
}

func (chatMessage) Read(reader payload.Reader) (noise.Message, error) {
	text, err := reader.ReadString()
	if err != nil {
		return nil, errors.Wrap(err, "failed to read chat msg")
	}

	return chatMessage{text: text}, nil
}

func (m chatMessage) Write() []byte {
	return payload.NewWriter(nil).WriteString(m.text).Bytes()
}

func main() {
    // Register message type to Noise.
    opcodeChatMessage := noise.RegisterMessage(noise.NextAvailableOpcode(), (*chatMessage)(nil))
    
    params := noise.DefaultParams()
    params.Keys = ed25519.Random()
    params.Port = uint16(3000)
    
    node, err := noise.NewNode(params)
    if err != nil {
        panic(err)
    }
    
    protocol.New().
    	Register(ecdh.New()).
    	Register(aead.New()).
    	Register(skademlia.New()).
    	Enforce(node)
    
    fmt.Printf("Listening for peers on port %d.\n", node.ExternalPort())
    
    go node.Listen()
    
    // Dial peer via TCP located at address 127.0.0.1:3001.
    peer, err := node.Dial("127.0.0.1:3001")
    if err != nil {
        panic(err)
    }
    
    // Wait until the peer has finished all cryptographic handshake procedures.
    skademlia.WaitUntilAuthenticated(peer)
    
    // Send a single chat message over the peer knowing that it's encrypted over the wire.
    err = peer.SendMessage(chatMessage{text: "Hello peer!"})
    if err != nil {
        panic(err)
    }
    
    // Receive and print out a single chat message back from our peer.
    fmt.Println(<-peer.Receive(opcodeChatMessage))
}

Setup

Make sure to have at the bare minimum Go 1.11 installed before incorporating noise into your project.

After installing Go, you may choose to either:

  1. directly incorporate noise as a library dependency to your project,
# Be sure to have Go modules enabled: https://github.com/golang/go/wiki/Modules
export GO111MODULE=on

# Run this inside your projects directory.
go get github.com/Yayg/noise
  1. or checkout the source code on Github and run any of the following commands below.
# Be sure to have Go modules enabled: https://github.com/golang/go/wiki/Modules
export GO111MODULE=on

# Run an example creating a cluster of 3 peers automatically
# discovering one another.
[terminal 1] go run examples/chat/main.go -p 3000
[terminal 2] go run examples/chat/main.go -p 3001 127.0.0.1:3000
[terminal 3] go run examples/chat/main.go -p 3002 127.0.0.1:3001

# Optionally run test cases.
go test -v -count=1 -race ./...

We're hiring!

Here at Perlin, we spend days and weeks debating, tinkering, and researching what is out there in academia to bring to industries truly resilient, open-source, secure, economic, and decentralized software to empower companies, startups, and users.

Our doors are open to academics that have a knack for distributed systems, engineers that want to explore unknown waters, frontend developers that want to make and evangelize the next generation of customer-facing applications, and graphics designers that yearn to instrument together greater user experiences for decentralized applications.

Contributions

First of all, thank you so much for taking part in our efforts for creating a p2p networking stack that can meet everyones needs without sacrificing developer productivity!

All code contributions to noise should comply with all idiomatic Go standards listed here.

All commit messages should be in the format:

module_name_1, module_name_2: description of the changes you made to the two
    modules here as a sentence

Be sure to use only imperative, present tense within your commit messages and optionally include motivation for your changes two lines breaks away from your commit message.

This allows other maintainers and contributors to know which modules you are modifying/creating within the code/docs repository.

Lastly, be sure to consider backwards compatibility.

New modules/methods are perfectly fine, but changing code living in noise.Node or noise.Peer radically for example would break a lot of existing projects utilizing noise.

Additionally, if you'd like to talk to us or any of the team in real-time, be sure to join our Discord server!

We are heavily active, ready to answer any questions/assist you with any code/doc contributions at almost any time.

License

noise, and all of its source code is released under the MIT License.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DebugOpcodes

func DebugOpcodes()

DebugOpcodes prints out all opcodes registered to Noise thus far.

func DefaultParams

func DefaultParams() parameters

Types

type AfterMessageEncodedCallback

type AfterMessageEncodedCallback func(node *Node, peer *Peer, header, msg []byte) ([]byte, error)

type AfterMessageReceivedCallback

type AfterMessageReceivedCallback func(node *Node, peer *Peer) error

type AfterMessageSentCallback

type AfterMessageSentCallback func(node *Node, peer *Peer) error

type BeforeMessageReceivedCallback

type BeforeMessageReceivedCallback func(node *Node, peer *Peer, msg []byte) ([]byte, error)

type BeforeMessageSentCallback

type BeforeMessageSentCallback func(node *Node, peer *Peer, msg []byte) ([]byte, error)

type EmptyMessage

type EmptyMessage struct{}

func (EmptyMessage) Read

func (EmptyMessage) Read(reader payload.Reader) (Message, error)

func (EmptyMessage) Write

func (EmptyMessage) Write() []byte

type Message

type Message interface {
	Read(reader payload.Reader) (Message, error)
	Write() []byte
}

To have Noise send/receive messages of a given type, said type must implement the following Message interface.

Noise by default encodes messages as bytes in little-endian order, and provides utility classes to assist with serializing/deserializing arbitrary Go types into bytes efficiently.

By exposing raw network packets as bytes to users, any additional form of serialization or message packing or compression scheme or cipher scheme may be bootstrapped on top of any particular message type registered to Noise.

func MessageFromOpcode

func MessageFromOpcode(opcode Opcode) (Message, error)

MessageFromOpcode returns an empty message representation associated to a registered message opcode.

It errors if the specified message opcode is not registered to Noise.

type Node

type Node struct {
	Keys identity.Keypair
	// contains filtered or unexported fields
}

func NewNode

func NewNode(params parameters) (*Node, error)

func (*Node) Delete

func (n *Node) Delete(key string)

func (*Node) Dial

func (n *Node) Dial(address string) (*Peer, error)

Dial has our node attempt to dial and establish a connection with a remote peer.

func (*Node) ExternalAddress

func (n *Node) ExternalAddress() string

func (*Node) ExternalPort

func (n *Node) ExternalPort() uint16

func (*Node) Fence

func (n *Node) Fence()

Fence blocks the current goroutine until the node stops listening for peers.

func (*Node) Get

func (n *Node) Get(key string) interface{}

Get returns the value to a metadata key from our node, or otherwise returns nil should there be no corresponding value to a provided key.

func (*Node) Has

func (n *Node) Has(key string) bool

func (*Node) InternalPort

func (n *Node) InternalPort() uint16

func (*Node) Kill

func (n *Node) Kill()

func (*Node) Listen

func (n *Node) Listen()

Listen makes our node start listening for peers.

func (*Node) LoadOrStore

func (n *Node) LoadOrStore(key string, val interface{}) interface{}

func (*Node) OnListenerError

func (n *Node) OnListenerError(c OnErrorCallback)

OnListenerError registers a callback for whenever our nodes listener fails to accept an incoming peer.

func (*Node) OnPeerConnected

func (n *Node) OnPeerConnected(c OnPeerInitCallback)

OnPeerConnected registers a callback for whenever a peer has successfully been accepted by our node.

func (*Node) OnPeerDialed

func (n *Node) OnPeerDialed(c OnPeerInitCallback)

OnPeerDialed registers a callback for whenever a peer has been successfully dialed.

func (*Node) OnPeerDisconnected

func (n *Node) OnPeerDisconnected(srcCallbacks ...OnPeerDisconnectCallback)

OnPeerDisconnected registers a callback whenever a peer has been disconnected.

func (*Node) OnPeerInit

func (n *Node) OnPeerInit(srcCallbacks ...OnPeerInitCallback)

OnPeerInit registers a callback for whenever a peer has either been successfully dialed, or otherwise accepted by our node.

In essence a helper function that registers callbacks for both `OnPeerConnected` and `OnPeerDialed` at once.

func (*Node) Set

func (n *Node) Set(key string, val interface{})

Set sets a metadata entry given a key-value pair on our node.

type OnErrorCallback

type OnErrorCallback func(node *Node, err error) error

type OnMessageReceivedCallback

type OnMessageReceivedCallback func(node *Node, opcode Opcode, peer *Peer, message Message) error

type OnPeerDecodeFooterCallback

type OnPeerDecodeFooterCallback func(node *Node, peer *Peer, msg []byte, reader payload.Reader) error

type OnPeerDecodeHeaderCallback

type OnPeerDecodeHeaderCallback func(node *Node, peer *Peer, reader payload.Reader) error

type OnPeerDisconnectCallback

type OnPeerDisconnectCallback func(node *Node, peer *Peer) error

type OnPeerErrorCallback

type OnPeerErrorCallback func(node *Node, peer *Peer, err error) error

type OnPeerInitCallback

type OnPeerInitCallback func(node *Node, peer *Peer) error

type Opcode

type Opcode byte
const (
	OpcodeNil Opcode = 0
)

func NextAvailableOpcode

func NextAvailableOpcode() Opcode

NextAvailableOpcode returns the next available unregistered message opcode registered to Noise.

func OpcodeFromMessage

func OpcodeFromMessage(msg Message) (Opcode, error)

OpcodeFromMessage uses reflection to extract and return the opcode associated to a message value type.

It errors if the specified message value type is not registered to Noise.

func RegisterMessage

func RegisterMessage(o Opcode, m interface{}) Opcode

func (Opcode) Bytes

func (o Opcode) Bytes() (buf [1]byte)

Bytes returns this opcodes' byte representation.

type Peer

type Peer struct {
	// contains filtered or unexported fields
}

func (*Peer) AfterMessageReceived

func (p *Peer) AfterMessageReceived(c AfterMessageReceivedCallback)

AfterMessageReceived registers a callback to be called after a message is to be received from a specified peer.

func (*Peer) AfterMessageSent

func (p *Peer) AfterMessageSent(c AfterMessageSentCallback)

AfterMessageSent registers a callback to be called after a message is sent to a specified peer.

func (*Peer) BeforeMessageReceived

func (p *Peer) BeforeMessageReceived(c BeforeMessageReceivedCallback)

BeforeMessageReceived registers a callback to be called before a message is to be received from a specified peer.

func (*Peer) BeforeMessageSent

func (p *Peer) BeforeMessageSent(c BeforeMessageSentCallback)

BeforeMessageSent registers a callback to be called before a message is sent to a specified peer.

func (*Peer) DecodeMessage

func (p *Peer) DecodeMessage(buf []byte) (Opcode, Message, error)

func (*Peer) Delete

func (p *Peer) Delete(key string)

func (*Peer) Disconnect

func (p *Peer) Disconnect()

func (*Peer) DisconnectAsync

func (p *Peer) DisconnectAsync() <-chan struct{}

func (*Peer) EncodeMessage

func (p *Peer) EncodeMessage(message Message) ([]byte, error)

EncodeMessage serializes a message body into its byte representation, and prefixes said byte representation with the messages opcode for the purpose of sending said bytes over the wire.

Additional header/footer bytes is prepended/appended accordingly.

Refer to the functions `OnEncodeHeader` and `OnEncodeFooter` available in `noise.Peer` to prepend/append additional information on every single message sent over the wire.

func (*Peer) Get

func (p *Peer) Get(key string) interface{}

Get returns the value to a metadata key from our node, or otherwise returns nil should there be no corresponding value to a provided key.

func (*Peer) Has

func (p *Peer) Has(key string) bool

func (*Peer) LoadOrStore

func (p *Peer) LoadOrStore(key string, val interface{}) interface{}

func (*Peer) LocalIP

func (p *Peer) LocalIP() net.IP

func (*Peer) LocalPort

func (p *Peer) LocalPort() uint16

func (*Peer) LockOnReceive

func (p *Peer) LockOnReceive(opcode Opcode) receiveHandle

func (*Peer) Node

func (p *Peer) Node() *Node

func (*Peer) OnConnError

func (p *Peer) OnConnError(c OnPeerErrorCallback)

OnConnError registers a callback for whenever something goes wrong with the connection to our peer.

func (*Peer) OnDecodeFooter

func (p *Peer) OnDecodeFooter(c OnPeerDecodeFooterCallback)

OnDecodeFooter registers a callback that is fed in the contents of the footer portion of an incoming message from a specified peer.

func (*Peer) OnDecodeHeader

func (p *Peer) OnDecodeHeader(c OnPeerDecodeHeaderCallback)

OnDecodeHeader registers a callback that is fed in the contents of the header portion of an incoming message from a specified peer.

func (*Peer) OnDisconnect

func (p *Peer) OnDisconnect(srcCallbacks ...OnPeerDisconnectCallback)

OnDisconnect registers a callback for whenever the peer disconnects.

func (*Peer) OnEncodeFooter

func (p *Peer) OnEncodeFooter(c AfterMessageEncodedCallback)

OnEncodeFooter registers a callback that is fed in the raw contents of a message to be sent, which then outputs bytes that are to be appended to the footer of an outgoing message.

func (*Peer) OnEncodeHeader

func (p *Peer) OnEncodeHeader(c AfterMessageEncodedCallback)

OnEncodeHeader registers a callback that is fed in the raw contents of a message to be sent, which then outputs bytes that are to be appended to the header of an outgoing message.

func (*Peer) Receive

func (p *Peer) Receive(o Opcode) <-chan Message

func (*Peer) RemoteIP

func (p *Peer) RemoteIP() net.IP

func (*Peer) RemotePort

func (p *Peer) RemotePort() uint16

func (*Peer) SendMessage

func (p *Peer) SendMessage(message Message) error

SendMessage sends a message whose type is registered with Noise to a specified peer. Calling this function will block the current goroutine until the message is successfully sent. In order to not block, refer to `SendMessageAsync(message Message) <-chan error`.

It is guaranteed that all messages are sent in a linearized order.

It returns an error should it take too long to send a message, the message is not registered with Noise, or there are message that are blocking the peers send worker.

func (*Peer) SendMessageAsync

func (p *Peer) SendMessageAsync(message Message) <-chan error

SendMessageAsync sends a message whose type is registered with Noise to a specified peer. Calling this function will not block the current goroutine until the message is successfully sent. In order to block, refer to `SendMessage(message Message) error`.

It is guaranteed that all messages are sent in a linearized order.

It returns an error should the message not be registered with Noise, or there are message that are blocking the peers send worker.

func (*Peer) Set

func (p *Peer) Set(key string, val interface{})

Set sets a metadata entry given a key-value pair on our node.

func (*Peer) SetNode

func (p *Peer) SetNode(node *Node)

Directories

Path Synopsis
examples
e2e
handshake
internal
bufconn
Package bufconn provides a net.Conn implemented by a buffer and related dialing and listening functionality.
Package bufconn provides a net.Conn implemented by a buffer and related dialing and listening functionality.
edwards25519
Package ed25519 implements the Ed25519 signature algorithm.
Package ed25519 implements the Ed25519 signature algorithm.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL