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
- Variables
- type Conn
- func (c *Conn) ChanQueueSubscribe(subject, group string, ch chan *Msg, signers ...string) (*Subscription, error)
- func (c *Conn) ChanSubscribe(subject string, ch chan *Msg, signers ...string) (*Subscription, error)
- func (c *Conn) Close()
- func (c *Conn) CloseAll()
- func (c *Conn) GetRecipients(subject string) []string
- func (c *Conn) Publish(subject string, data []byte) error
- func (c *Conn) PublishFor(subject string, data []byte, recipients ...string) error
- func (c *Conn) PublishMsg(m *Msg) error
- func (c *Conn) PublishRequest(subj, reply string, data []byte) error
- func (c *Conn) PublishRequestFor(subj, reply string, data []byte, recipients ...string) error
- func (c *Conn) QueueSubscribe(subject, queue string, cb MsgHandler, signers ...string) (*Subscription, error)
- func (c *Conn) Request(subj string, data []byte, timeout time.Duration) (*Msg, error)
- func (c *Conn) RequestFor(subj string, data []byte, timeout time.Duration, recipients ...string) (*Msg, error)
- func (c *Conn) SetDefaultDecryptErrorHandler(handler DecryptErrorHandler)
- func (c *Conn) SetMultiSubjectRecipients(recipients map[string][]string)
- func (c *Conn) SetSubjectRecipients(subject string, recipients []string)
- func (c *Conn) Subscribe(subject string, cb MsgHandler, signers ...string) (*Subscription, error)
- func (c *Conn) SubscribeSync(subj string, signers ...string) (*Subscription, error)
- type DecryptErrorHandler
- type EncodedConn
- func (c *EncodedConn) Publish(subject string, v interface{}) error
- func (c *EncodedConn) PublishFor(subject string, v interface{}, recipients ...string) error
- func (c *EncodedConn) PublishRequest(subject, reply string, v interface{}) error
- func (c *EncodedConn) PublishRequestFor(subject, reply string, v interface{}, recipients ...string) error
- func (c *EncodedConn) PublishRequestUnencrypted(subject, reply string, v interface{}) error
- func (c *EncodedConn) PublishUnencrypted(subject string, v interface{}) error
- func (c *EncodedConn) QueueSubscribe(subject, queue string, cb nats.Handler) (*EncodedSubscription, error)
- func (c *EncodedConn) Request(subject string, v interface{}, vPtr interface{}, timeout time.Duration) error
- func (c *EncodedConn) RequestFor(subject string, v interface{}, vPtr interface{}, timeout time.Duration, ...) error
- func (c *EncodedConn) RequestUnencrypted(subject string, v interface{}, vPtr interface{}, timeout time.Duration) (encrypted bool, err error)
- func (c *EncodedConn) RequestUnsafe(subject string, v interface{}, vPtr interface{}, timeout time.Duration) (encrypted bool, err error)
- func (c *EncodedConn) Subscribe(subject string, cb nats.Handler) (*EncodedSubscription, error)
- func (c *EncodedConn) SubscribeSync(subj string) (*EncodedSubscription, error)
- type EncodedSubscription
- func (s EncodedSubscription) Next(vPtr interface{}, timeout time.Duration) error
- func (s EncodedSubscription) NextSubject(subject *string, vPtr interface{}, timeout time.Duration) error
- func (s EncodedSubscription) NextSubjectReply(subject, reply *string, vPtr interface{}, timeout time.Duration) error
- type Encrypter
- type Msg
- type MsgHandler
- type PGPEncrypter
- func (e *PGPEncrypter) AddEntity(entities ...*openpgp.Entity)
- func (e *PGPEncrypter) AddOneShotEntity(entity *openpgp.Entity) string
- func (e *PGPEncrypter) DecryptData(data []byte) (cleardata []byte, recipients []string, signer string, err error)
- func (e *PGPEncrypter) EncryptData(data []byte, recipients []string, signer string) ([]byte, error)
- func (e *PGPEncrypter) RemoveID(id string)
- type Subscription
Constants ¶
const NoReply = ""
NoReply can be used as the "reply" argument when no reply is needed
Variables ¶
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") )
var ( // ErrNilEntity is returned or panicked by functions expecting a non-nil // *openpgp.Entity ErrNilEntity = errors.New("Nil Entity") )
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 ¶
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 ¶
GetRecipients returns the default recipients for a given subject.
func (*Conn) Publish ¶
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 ¶
PublishFor publishes to a subject for specific recipients
func (*Conn) PublishMsg ¶
PublishMsg publishes the Msg structure, which includes the Subject, an optional Reply and an optional Data field.
func (*Conn) PublishRequest ¶
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 ¶
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 ¶
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 ¶
SetMultiSubjectRecipients associates recipients to subjects
func (*Conn) SetSubjectRecipients ¶
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 ¶
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 ¶
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
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 ¶
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.