dr

package module
v0.0.0-...-0e86e3a Latest Latest
Warning

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

Go to latest
Published: Feb 15, 2022 License: BSD-3-Clause Imports: 16 Imported by: 0

README

dr

Go Reference

The Double Ratchet algorithm.

Security

Disclosure

This project uses full disclosure. If you find a security bug in an implementation, please e-mail me or create a GitHub issue.

Disclaimer

You should only use cryptography libraries that have been reviewed by cryptographers or cryptography engineers. While I am a cryptography engineer, I'm not your cryptography engineer, and I have not had this project reviewed by any other cryptographers.

Documentation

Overview

Package dr implements the Double Ratchet scheme.

Overview

What follows is a high-level overview of the Double Ratchet scheme, mostly paraphrased from the whitepaper [signal].

Double Ratchet Algorithm

The Double Ratchet Algorithm is comprised of two "ratchets" over three KDF chains. A ratchet is a construction where each step forward is constructed with a one-way function, making it impossible to recover previous keys (forward secrecy).

KDF Chains

KDF chains are the core construction of the Double Ratchet Algorithm.

A KDF chain is a construction where part of the output of the KDF is used to key the next invocation of the KDF, and the rest is used for some other purpose (like message encryption).

          key
           v
        ┌─────┐
input > │ KDF │
        └──┬──┘
           ├─> output key
           v
          key
           v
        ┌─────┐
input > │ KDF │
        └──┬──┘
           ├─> output key
           v
          key

This construction has some desirable properties, including forward security and resilience against attackers that can manipulate the KDF inputs.

In a Double Ratchet session both parties have three chains:

  1. root chain
  2. sending chain
  3. receiving chain

Each party's sending chain will match the other's receiving chain and vice versa. The root chain is the same for both parties.

Diffie-Hellman Ratchet

Both parties have their own ephemeral ratchet key pair. Each time a message is sent the sender generates a new key pair and attaches the new public key to the message. The sender then uses the shared Diffie-Hellman value as input to the sending chain, advancing it one step. Likewise, when the recipient receives the message (and is informed of the sender's new public key), the recipient uses the shared Diffie-Hellman value as input to the receiving chain, advancing it one step and keeping it in sync with the other party's sending chain.

In other words, when Alice sends Bob a message she creates a new Diffie-Hellman key pair and uses her private key and Bob's public key to compute the shared Diffie-Hellman value. When Bob receives the message, he uses Alice's new public key and his private key to also compute the shared Diffie-Hellman value.

Symmetric-Key Ratchet

As each message is sent and received the sending and receiving chains are advanced. The output of advancing each chain is used as a message key to encrypt each individual message.

Notes

This package does not implement encrypted headers.

References

More information can be found in the following links.

[signal]: https://signal.org/docs/specifications/doubleratchet/doubleratchet.pdf

Index

Constants

This section is empty.

Variables

View Source
var ErrNotFound = errors.New("dr: key not found")

ErrNotFound is returned by Store when a message key is not found in the Store.

Functions

func Concat

func Concat(additionalData []byte, h Header) []byte

Concat is a default implementation of Ratchet.Concat.

Types

type ChainKey

type ChainKey []byte

ChainKey is an ephemeral key used to key the KDF used to generate message keys.

          chain key
              v
           ┌─────┐
constant > │ kdf │
           └──┬──┘
              ├─> message key
              v
           chain key
              v
           ┌─────┐
constant > │ kdf │
           └──┬──┘
              ├─> message key
              v
           chain key

ChainKeys are always 32 bytes.

type Header struct {
	// PublicKey is the sender's new public key.
	PublicKey []byte
	// PN is the previous chain length.
	PN int
	// N is the current message number.
	N int
}

Header is generated alongside each message.

func (Header) Append

func (h Header) Append(buf []byte) []byte

Append serializes the Header and appends it to buf.

func (*Header) Decode

func (h *Header) Decode(data []byte) error

Decode deserializes a Header from data.

type Message

type Message struct {
	Header     Header
	Ciphertext []byte
}

Message is a messages encrypted with the Double Ratchet Algorithm.

type MessageKey

type MessageKey []byte

MessageKey is an ephemeral key used to encrypt a single message.

MessageKeys are output from the sending and receiving KDF chains.

MessageKeys are always 32 bytes.

type Option

type Option func(*Session)

Option configures a Session.

func WithStore

func WithStore(t Store) Option

WithStore configures some backing store for saving state and skipped messages.

Saving session state allows the session to be paused and resumed at a later time.

Messages are skipped and queued when they arrive out of order.

By default, skipped messages are stored in memory and sessions are ephemeral.

type PrivateKey

type PrivateKey []byte

PrivateKey is a complete (private, public) key pair.

type PublicKey

type PublicKey []byte

PublicKey is a peer's public.

type Ratchet

type Ratchet interface {
	// Generate creates a new Diffie-Hellman pair.
	//
	// Generate might use entropy from the provided Reader.
	Generate(io.Reader) (PrivateKey, error)
	// Public returns a copy of the public key portion of the key
	// pair.
	Public(PrivateKey) PublicKey
	// DH returns the Diffie-Hellman value computed with the key
	// pair and public key.
	DH(PrivateKey, PublicKey) ([]byte, error)
	// KDFrk applies a KDF keyed by the root key to the
	// Diffie-Hellman value and returns a (root key, chain key)
	// pair.
	KDFrk(RootKey, []byte) (RootKey, ChainKey)
	// KDFck applies a KDF keyed by the chain key to some
	// constant value and returns a (root key, chain key) pair.
	KDFck(ChainKey) (ChainKey, MessageKey)
	// Seal encrypts and authenticates plaintext, authenticates
	// additionalData, and appends the ciphertext to dst.
	//
	// Because each message key is only used once the nonce can
	// be handled in one of several ways:
	//
	//    1. fixed to a constant
	//    2. derived from mk alongside an independent AEAD
	//       encryption key
	//    3. derived as additional output of KDFck
	//    4. chosen randomly and transmitted
	//
	Seal(key MessageKey, plaintext, additionalData []byte) []byte
	// Open decrypts and authenticates ciphertext, authenticates
	// additionalData, and appends the plaintext to dst.
	Open(key MessageKey, ciphertext, additionalData []byte) ([]byte, error)
	// Header creates a message header from the key pair,
	// previous chain length, and current message number.
	//
	// The header contains the Diffie-Hellman public ratchet key.
	Header(priv PrivateKey, prevChainLength, messageNum int) Header
	// Concat encodes a message header and prepends the
	// additional data.
	//
	// Concact should ensure that the additional data and header
	// can be differentiated.
	//
	// See the Concat function for a default implementation.
	Concat(additionalData []byte, h Header) []byte
}

Ratchet implements the Double Ratchet scheme.

Ratchet should be safe for concurrent use by multiple distinct goroutines.

func DJB

func DJB(namespace string) Ratchet

DJB creates a Ratchet using X25519, 256-bit XChaCha20-Poly1305, HKDF with BLAKE2b, and HMAC-BLAKE2b.

The namespace is used to bind keys to a particular application or context.

func NIST

func NIST(curve elliptic.Curve, hash func() hash.Hash, namespace string) Ratchet

NIST creates a Ratchet using NIST curves, 256-bit AES-GCM, and HKDF and HMAC with the provided hash function.

The namespace is used to bind keys to a particular application or context.

type RootKey

type RootKey []byte

RootKey is a key generated by each step in the root chain.

RootKeys are always 32 bytes.

type Session

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

Session encapsulates an asynchronous conversation between two parties.

func NewRecv

func NewRecv(r Ratchet, SK []byte, priv PrivateKey, opts ...Option) (*Session, error)

NewRecv creates a new Session for receiving communication initiated by some peer.

The shared key SK must be negotiated with the peer ahead of time.

func NewSend

func NewSend(r Ratchet, SK []byte, peer PublicKey, opts ...Option) (*Session, error)

NewSend creates a new Session for initiating communication with some peer.

The shared key SK must be negotiated with the peer ahead of time.

func Resume

func Resume(r Ratchet, state *State, opts ...Option) (*Session, error)

Resume continues an existing Session.

func (*Session) Open

func (s *Session) Open(msg Message, additionalData []byte) ([]byte, error)

Open decrypts and authenticates ciphertext, authenticates additionalData, and returns the resulting plaintext.

func (*Session) Seal

func (s *Session) Seal(plaintext, additionalData []byte) Message

Seal encrypts and authenticates plaintext, authenticates additionalData, and returns the resulting message.

type State

type State struct {
	// DHs is the sending (self) ratchet key pair.
	DHs PrivateKey
	// DHr is the peer's ratchet public key.
	DHr PublicKey
	// RK is the current root key.
	RK RootKey
	// CKs is the sending chain key.
	CKs ChainKey
	// CKr is the receivinb chain key.
	CKr ChainKey
	// NS is the sending message number.
	Ns int
	// Nr is the receiving message number.
	Nr int
	// PN is the number of messages in the previous sending
	// chain.
	PN int
}

State is the current state of a session.

func (*State) Clone

func (s *State) Clone() *State

Clone performs a deep copy of the session state.

type Store

type Store interface {
	// Save saves the state.
	Save(s *State) error
	// StoreKey stores a skipped message's key under the (Nr,
	// PublicKey) tuple.
	//
	// StoreKey must return an error if too many messages have
	// been Skipped.
	StoreKey(Nr int, pub PublicKey, key MessageKey) error
	// LoadKey retrieves a message key using the (Nr, PublicKey)
	// tuple.
	//
	// If the message key is not found LoadKey returns
	// ErrNotFound.
	LoadKey(Nr int, pub PublicKey) (MessageKey, error)
	// DeleteKey removes a message key using the (Nr, PublicKey)
	// tuple.
	DeleteKey(Nr int, pub PublicKey) error
}

Store saves session state.

Jump to

Keyboard shortcuts

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