natscrypto

package module
v0.0.0-...-1957bea Latest Latest
Warning

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

Go to latest
Published: Oct 6, 2016 License: MIT Imports: 9 Imported by: 0

README

Nats Crypto

Build Status Go Report Card Coverage Status License (MIT)

natscrypto is a golang package that wraps nats.Conn to provide transparent PKI encryption to nats messages

The encryption part is pluggable and a PGP implemention is provided

The complete documentation is available here:

https://godoc.org/gopkg.in/orus-io/natscrypto.v0

Get it

go get -u gopkg.in/orus.io/natscrypto.v0

Documentation

Overview

Package natscrypto provides a PKI encryption layer on top of nats.Conn, as well a port of nats.EncodedConn on top of it.

Introduction

Once connected to a nats server, any client can subscribe and publish to any subject. When the nats server is shared among entities that should not be able to eavesdrop on each other, it can be a problem.

Our approach is to encrypt and sign each message with openpgp, so the actors can keep information private and certify the origin of the message.

This package is our implementation of this approach. The openpgp encrytion is the only provided one, but adding one would be pretty straighforward.

Basic usage

Once a connection is established with the nats server, we can wrap it in a natscrypto connection. Any message sent through this wrapper will be encrypted for the desired recipients, and any incoming message will be first decrypted and its signer verified

First, we need to setup an encrypter that hold our keyring:

var (
	publicEntities openpgp.EntityList
	privateEntity *openpgp.Entity
)

// Init the encrypter
encrypter := natscrypto.NewPGPEncrypter(publicEntities...)
encrypter.AddEntity(privateEntity)

// myidentity is my private key fingerprint. Only the encrypter
// needs to handle the actual keys, the rest of natscrypto only
// manipulates string ids. Their exact signification depends on
// the encrypter
myidentity := string(privateEntity.PrimaryKey.FingerPrint[:20])

// Get the identity of the potential recipients for future use
rec1 := string(publicEntities[0].PrimaryKey.FingerPrint[:20])
rec2 := string(publicEntities[1].PrimaryKey.FingerPrint[:20])
rec3 := string(publicEntities[2].PrimaryKey.FingerPrint[:20])

Then we can wrap the connection:

conn := nats.Connect(...)
eConn := natscrypto.NewConn(conn, myidentity, encrypter)

Post a message:

// Declare for which identities messages sent to "test" should be
// encrypted
eConn.SetSubjectRecipients("test", rec1)

// The message ("hello") will be signed using privateEntity
eConn.Publish("test", []byte("hello"))

// We can publish for arbitrary recipients on a single call
eConn.PublishFor("test", []byte("hello"), rec2, rec3)

Subscribe:

// any known emitter will be accepted on this subscription
sub, err := eConn.SubscribeSync("incoming")

// only rec1 will be accepted on this one
sub, err := eConn.Subscribe("other", func(*natscrypto Msg) {}, rec1)

The received messages have 3 extra attributes in addition to a classic nats.Msg:

- Signer: the id of the verified signer, or empty. - Recipients: the ids of the recipients (only one when receiving, but could be more when emitting). - Error: in some cases we can get messages that could be decrypted but have a signer problem. In a error handler, the 'Error' attribute of the message will be set so we can handle unknown signers gracefully (for example)

Encoding

EncodedConn are the preferred way to interface with NATS. They wrap a bare connection to a nats server and have an extendable encoder system that will encode and decode messages from raw Go types.

Since nats.EncodedConn cannot work on top of a natscrypto.Conn, we ported it. natscrypto.EncodedConn has both the features of natscrypto.Conn and nats.EncodedConn.

Index

Constants

View Source
const NoReply = ""

NoReply can be used as the "reply" argument when no reply is needed

Variables

View Source
var (
	// ErrNilConnection A nil connection was passed to a function expecting a non-nil one
	ErrNilConnection = errors.New("Nil Connection")
	// ErrNilEncrypter A nil encrypter was passed to a function expecting a non-nil one
	ErrNilEncrypter = errors.New("Nil Encrypter")
	// ErrNoRecipient An empty list of recipients was passed.
	ErrNoRecipient = errors.New("No Recipient. An empty list of recipient was passed.")

	// ErrUnknownSigner is returned by DecryptData is the message is signed by an
	// unknown identity
	ErrUnknownSigner = errors.New("Unknown Signer")
	// ErrUnsignedMessage is returned by DecryptData if the message is not pgp signed
	ErrUnsignedMessage = errors.New("Unsigned Message")

	// ErrSignerNotAuthorized is set on Msg when the signer is not authorized on a
	// subscription
	ErrSignerNotAuthorized = errors.New("natscrypto: Signer not authorized")
)
View Source
var (
	// ErrNilEntity is returned or panicked by functions expecting a non-nil
	// *openpgp.Entity
	ErrNilEntity = errors.New("Nil Entity")
)
View Source
var ErrNonEncryptedResponse = errors.New("Non encrypted Response")

ErrNonEncryptedResponse is returned by PublishUnencrypted if the response is not encrypted.

Functions

This section is empty.

Types

type Conn

type Conn struct {
	*nats.Conn
	Encrypter         Encrypter
	Identity          string
	SubjectRecipients map[string][]string
	ReplyRecipients   map[string]replyRecipient
	// contains filtered or unexported fields
}

Conn is a nats connection on which every message sent is encrypted for recipients of the subject, and every message received is decrypted automatically

func NewConn

func NewConn(c *nats.Conn, identity string, encrypter Encrypter) (*Conn, error)

NewConn wraps a nats.Conn in a Conn that uses the passed encrypter A same nats.Conn can be share among several natscrypto.Conn.

func (*Conn) ChanQueueSubscribe

func (c *Conn) ChanQueueSubscribe(subject, group string, ch chan *Msg, signers ...string) (*Subscription, error)

ChanQueueSubscribe will place all messages received on the channel. You should not close the channel until sub.Unsubscribe() has been called.

func (*Conn) ChanSubscribe

func (c *Conn) ChanSubscribe(subject string, ch chan *Msg, signers ...string) (*Subscription, error)

ChanSubscribe will place all messages received on the channel. You should not close the channel until sub.Unsubscribe() has been called.

func (*Conn) Close

func (c *Conn) Close()

Close closes the encrypted connection, _not_ the underlying nats.Conn. To close both the encryption layer and the actual nats.Conn, use CloseAll()

func (*Conn) CloseAll

func (c *Conn) CloseAll()

CloseAll closes the encrypted connection _and_ the underlying nats.Conn

func (*Conn) GetRecipients

func (c *Conn) GetRecipients(subject string) []string

GetRecipients returns the default recipients for a given subject.

func (*Conn) Publish

func (c *Conn) Publish(subject string, data []byte) error

Publish publishes the data argument to the given subject. The data argument will be encrypted for the destination identities of the subject

func (*Conn) PublishFor

func (c *Conn) PublishFor(subject string, data []byte, recipients ...string) error

PublishFor publishes to a subject for specific recipients

func (*Conn) PublishMsg

func (c *Conn) PublishMsg(m *Msg) error

PublishMsg publishes the Msg structure, which includes the Subject, an optional Reply and an optional Data field.

func (*Conn) PublishRequest

func (c *Conn) PublishRequest(subj, reply string, data []byte) error

PublishRequest will perform a Publish() excpecting a response on the reply subject. Use Request() for automatically waiting for a response inline. In this specific version of PublishRequest, the 'reply' gets encrypted too

func (*Conn) PublishRequestFor

func (c *Conn) PublishRequestFor(subj, reply string, data []byte, recipients ...string) error

PublishRequestFor is PublishRequest with explicit recipients

func (*Conn) QueueSubscribe

func (c *Conn) QueueSubscribe(subject, queue string, cb MsgHandler, signers ...string) (*Subscription, error)

QueueSubscribe will create a queue subscription on the given subject and process incoming messages using the specified Handler.

func (*Conn) Request

func (c *Conn) Request(subj string, data []byte, timeout time.Duration) (*Msg, error)

Request will create an Inbox and perform a Request() call with the Inbox reply for the data v. A response will be decrypted. This implementation is copied from nats.Conn.Request, but using our own PublishRequest that will encrypt the reply, and our own Subscription that will decrypt the incoming message

func (*Conn) RequestFor

func (c *Conn) RequestFor(subj string, data []byte, timeout time.Duration, recipients ...string) (*Msg, error)

RequestFor is Request with explicit recipients

func (*Conn) SetDefaultDecryptErrorHandler

func (c *Conn) SetDefaultDecryptErrorHandler(handler DecryptErrorHandler)

SetDefaultDecryptErrorHandler sets the default decrypt error handler of all the subscriptions to come. Already created subscriptions will be untouched

func (*Conn) SetMultiSubjectRecipients

func (c *Conn) SetMultiSubjectRecipients(recipients map[string][]string)

SetMultiSubjectRecipients associates recipients to subjects

func (*Conn) SetSubjectRecipients

func (c *Conn) SetSubjectRecipients(subject string, recipients []string)

SetSubjectRecipients associates a list of recipients to a subject if subject is "", the recipients are used as default for subjects having no explicit recipients

func (*Conn) Subscribe

func (c *Conn) Subscribe(subject string, cb MsgHandler, signers ...string) (*Subscription, error)

Subscribe will create a subscription on the given subject and process incoming messages using the specified Handler. The Handler should be a func that matches a signature from the description of Handler from above. signers is an optional list of authorized signers

func (*Conn) SubscribeSync

func (c *Conn) SubscribeSync(subj string, signers ...string) (*Subscription, error)

SubscribeSync is syntactic sugar for Subscribe(subject, nil).

type DecryptErrorHandler

type DecryptErrorHandler func(sub *Subscription, msg *Msg) *Msg

DecryptErrorHandler are callbacks for decryption errors if the function returns a nil Msg, the message will stop its course and never make it down the to final handler.

type EncodedConn

type EncodedConn struct {
	*Conn
	Enc nats.Encoder
}

EncodedConn is a Conn with encoding/decoding capabilities

func NewEncodedConn

func NewEncodedConn(c *Conn, encType string) (*EncodedConn, error)

NewEncodedConn wraps a Conn with encoding/decoding utilities

func (*EncodedConn) Publish

func (c *EncodedConn) Publish(subject string, v interface{}) error

Publish publishes the data argument to the given subject. The data argument will be encoded using the associated encoder.

func (*EncodedConn) PublishFor

func (c *EncodedConn) PublishFor(subject string, v interface{}, recipients ...string) error

PublishFor same as Publish for a specific recipient

func (*EncodedConn) PublishRequest

func (c *EncodedConn) PublishRequest(subject, reply string, v interface{}) error

PublishRequest will perform a Publish() expecting a response on the reply subject. Use Request() for automatically waiting for a response inline.

func (*EncodedConn) PublishRequestFor

func (c *EncodedConn) PublishRequestFor(subject, reply string, v interface{}, recipients ...string) error

PublishRequestFor same as PublishRequest for specific recipients

func (*EncodedConn) PublishRequestUnencrypted

func (c *EncodedConn) PublishRequestUnencrypted(subject, reply string, v interface{}) error

PublishRequestUnencrypted publishes the data encoded only, not encrypted.

func (*EncodedConn) PublishUnencrypted

func (c *EncodedConn) PublishUnencrypted(subject string, v interface{}) error

PublishUnencrypted publishes the data encoded only, not encrypted

func (*EncodedConn) QueueSubscribe

func (c *EncodedConn) QueueSubscribe(subject, queue string, cb nats.Handler) (*EncodedSubscription, error)

QueueSubscribe will create a queue subscription on the given subject and process incoming messages using the specified Handler. The Handler should be a func that matches a signature from the description of Handler from above.

func (*EncodedConn) Request

func (c *EncodedConn) Request(subject string, v interface{}, vPtr interface{}, timeout time.Duration) error

Request will create an Inbox and perform a Request() call with the Inbox reply for the data v. A response will be decoded into the vPtrResponse.

func (*EncodedConn) RequestFor

func (c *EncodedConn) RequestFor(subject string, v interface{}, vPtr interface{}, timeout time.Duration, recipients ...string) error

RequestFor same as Request for specific recipients

func (*EncodedConn) RequestUnencrypted

func (c *EncodedConn) RequestUnencrypted(subject string, v interface{}, vPtr interface{}, timeout time.Duration) (encrypted bool, err error)

RequestUnencrypted same as Request but the emitted message will _not_ be encrypted. The reponse may be encrypted though, in which case it is transparenly decrypted, and the returned bool is 'true'

func (*EncodedConn) RequestUnsafe

func (c *EncodedConn) RequestUnsafe(subject string, v interface{}, vPtr interface{}, timeout time.Duration) (encrypted bool, err error)

RequestUnsafe same as Request but if the response is not encryted or signed, the message will be decoded anyway

func (*EncodedConn) Subscribe

func (c *EncodedConn) Subscribe(subject string, cb nats.Handler) (*EncodedSubscription, error)

Subscribe will create a subscription on the given subject and process incoming messages using the specified Handler. The Handler should be a func that matches a signature from the description of Handler from above.

func (*EncodedConn) SubscribeSync

func (c *EncodedConn) SubscribeSync(subj string) (*EncodedSubscription, error)

SubscribeSync is syntactic sugar for Subscribe(subject, nil).

type EncodedSubscription

type EncodedSubscription struct {
	*Subscription
	Enc nats.Encoder
}

EncodedSubscription wraps a Conn and add a Next() function That decode incoming messages

func (EncodedSubscription) Next

func (s EncodedSubscription) Next(vPtr interface{}, timeout time.Duration) error

Next decodes the next message available to a synchronous subscriber or block until one is available.

func (EncodedSubscription) NextSubject

func (s EncodedSubscription) NextSubject(subject *string, vPtr interface{}, timeout time.Duration) error

NextSubject decodes the next message available to a synchronous subscriber or block until one is available.

func (EncodedSubscription) NextSubjectReply

func (s EncodedSubscription) NextSubjectReply(subject, reply *string, vPtr interface{}, timeout time.Duration) error

NextSubjectReply decodes the next message available to a synchronous subscriber or block until one is available.

type Encrypter

type Encrypter interface {
	EncryptData(data []byte, recipients []string, signer string) ([]byte, error)
	DecryptData(data []byte) (cleardata []byte, recipients []string, signer string, err error)
}

Encrypter is implemented by message encrypters Both function should be routine-safe as they may be called in parallel routines

type Msg

type Msg struct {
	*nats.Msg
	Signer     string
	Recipients []string
	Error      error
}

Msg is a wrapper for nats.Msg with added Signer and Recipients There fields are filled by decryption or by the user for proper encryption The identities can be any string that the encoder will recognize as a unique identity, generally a fingerprint

func NewMsg

func NewMsg(subject string, data []byte, sender string, recipients ...string) *Msg

NewMsg initialize a Msg

type MsgHandler

type MsgHandler func(msg *Msg)

MsgHandler is a callback function that processes messages delived to asynchronous subscribers

type PGPEncrypter

type PGPEncrypter struct {
	Identities      map[string]*openpgp.Entity
	AllEntities     openpgp.EntityList
	OneShotEntities map[string]*openpgp.Entity
	// contains filtered or unexported fields
}

PGPEncrypter is a openpgp based Encrypter for EncryptedConn

func NewPGPEncrypter

func NewPGPEncrypter(entities ...*openpgp.Entity) *PGPEncrypter

NewPGPEncrypter initialize a PGPEncrypter

func (*PGPEncrypter) AddEntity

func (e *PGPEncrypter) AddEntity(entities ...*openpgp.Entity)

AddEntity add one of more openpgp entities to the encrypter. If the entity contains a private key, it is added to the PrivateIdentities too, which means the PGPEncrypter will be able to decrypt messages to it Panics if the entity is nil or has no PrimaryKey

func (*PGPEncrypter) AddOneShotEntity

func (e *PGPEncrypter) AddOneShotEntity(entity *openpgp.Entity) string

AddOneShotEntity add an entity that can be used only once for encrypting only (not for verification)

func (*PGPEncrypter) DecryptData

func (e *PGPEncrypter) DecryptData(data []byte) (cleardata []byte, recipients []string, signer string, err error)

DecryptData decrypt the data and extract the recipients and signer

func (*PGPEncrypter) EncryptData

func (e *PGPEncrypter) EncryptData(data []byte, recipients []string, signer string) ([]byte, error)

EncryptData encrypt the data with the recipients public keys and sign it sith signer private key

func (*PGPEncrypter) RemoveID

func (e *PGPEncrypter) RemoveID(id string)

RemoveID removes an entity from the encrypter given its fingerprint

type Subscription

type Subscription struct {
	*nats.Subscription
	Conn *Conn
	// contains filtered or unexported fields
}

Subscription wraps nats.Subscription and override its 'NextMsg' function it also provides callbacks on decryption errors, so a subscriber can handle such errors or even reply to badly or unsigned requests. The default error handler will drop the message so the final handler never sees it.

func (*Subscription) NextMsg

func (s *Subscription) NextMsg(timeout time.Duration) (*Msg, error)

NextMsg returns the next message available to a synchronous subscriber of block until one is available. Badly encrypted incoming messages will return an error

func (*Subscription) SetAuthorizedSigners

func (s *Subscription) SetAuthorizedSigners(signers ...string)

SetAuthorizedSigners changes the list of signers allowed on this subscription. Any message received from a signer outside this list will be stopped and handled as error

func (*Subscription) SetDecryptErrorHandler

func (s *Subscription) SetDecryptErrorHandler(handler DecryptErrorHandler)

SetDecryptErrorHandler sets a callback that is called when a decryption error occurs. The handler can:

  • return a Msg, possibly the original one. In this case, the message will be passed down to the final subscriptor (cb, sync or chan)
  • return nil, which will make the message disappear and never reach the final handler. NextMsg() will however return the original Msg.Error as an error

func (*Subscription) Unsubscribe

func (s *Subscription) Unsubscribe() error

Unsubscribe will remove interest in the given subject.

Jump to

Keyboard shortcuts

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