abstract

package
v0.0.0-...-0b3308b Latest Latest
Warning

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

Go to latest
Published: Jun 19, 2017 License: MPL-2.0 Imports: 11 Imported by: 35

Documentation

Overview

This package defines abstract interfaces for advanced cryptographic primitives. Implementations of these interfaces are provided in other packages.

Index

Constants

This section is empty.

Variables

View Source
var NoKey = []byte{}

Pass NoKey to a Cipher constructor to create an unkeyed Cipher.

View Source
var RandomKey []byte = nil

Pass RandomKey to a Cipher constructor to create a randomly seeded Cipher.

Functions

func SuiteNew

func SuiteNew(s Suite, t reflect.Type) interface{}

Default implementation of reflective constructor for ciphersuites

func SuiteRead

func SuiteRead(s Suite, r io.Reader, objs ...interface{}) error

Default implementation of Encoding interface Read for ciphersuites

func SuiteWrite

func SuiteWrite(s Suite, w io.Writer, objs ...interface{}) error

Default implementation of Encoding interface Write for ciphersuites

func Sum

func Sum(suite Suite, data ...[]byte) []byte

Sum uses a given ciphersuite's hash function to checksum a byte-slice.

Types

type BinaryEncoding

type BinaryEncoding struct {
	Constructor // Constructor for instantiating abstract types
	// contains filtered or unexported fields
}

BinaryEncoding represents a simple binary encoding suitable for reading and writing fixed-length cryptographic objects. The interface allows reading and writing composite types such as structs, arrays, and slices, but the encoded size of any object must be completely defined by the type and size of the object itself and the ciphersuite in use.

Slices must be instantiated to the correct length before either reading or writing: hence the reader must determine the correct length "out of band" (the encoding supports no transmission of length metadata).

XXX move this and Constructor to some other, more generic package

func (BinaryEncoding) Read

func (e BinaryEncoding) Read(r io.Reader, objs ...interface{}) error

Read a series of binary objects from an io.Reader. The objs must be a list of pointers.

func (BinaryEncoding) Write

func (e BinaryEncoding) Write(w io.Writer, objs ...interface{}) error

Write a data structure containing cryptographic objects, using their built-in binary serialization, to an io.Writer. Supports writing of Points, Scalars, basic fixed-length data types supported by encoding/binary/Write(), and structs, arrays, and slices containing all of these types.

XXX should this perhaps become a Suite method?

XXX now this code could/should be moved into a separate package relatively independent from this crypto code.

type Cipher

type Cipher struct {
	CipherState // underlying message cipher implementation
}

Cipher represents a general-purpose symmetric message cipher. A Cipher instance embodies a scalar that may be used to encrypt/decrypt data as well as to generate cryptographically random bits. The Cipher can also cryptographically absorb data or key material, updating its state to produce cryptographic hashes and authenticators. Using these encryption and absorption functions in combination, a Cipher may be used for authenticated encryption and decryption.

A Cipher is in fact simply a convenience/helper wrapper around the CipherState interface, which represents and abstracts over an underlying message cipher implementation. The underlying CipherState instance typically embodies both the specific message cipher algorithm in use, and the choice of security parameter with which the cipher is operated. This algorithm and security parameter is typically set when the Cipher (and its underlying CipherState) instance is constructed. The simplest way to get a Cipher instance is via a cipher Suite.

The standard function signature for a Cipher constructor is:

NewCipher(key []byte, options ...interface{}) Cipher

If key is nil, the Cipher constructor picks a fresh, random key. The key may be an empty but non-nil slice to create an unkeyed cipher. Key material may be of any length, but to ensure full security, secret keys should be at least the size returned by the KeySize method. The variable-length options argument may contain options whose interpretation is specific to the particular cipher. (XXX may reconsider the wisdom of this options convention; its lack of type-checking has led to accidental confusion at least once.)

func (Cipher) Clone

func (c Cipher) Clone() Cipher

Clone creates an initially identicial instance of a Cipher. Warning:: misuse of Clone can lead to replay or key-reuse vulnerabilities.

func (Cipher) EndMessage

func (c Cipher) EndMessage()

EndMessage inserts an explicit end-of-message boundary, finalizing the message currently being processed and starting a new one. The client should typically call EndMessage after a series of calls to streaming methods such as Partial, Read, or Write.

func (Cipher) Message

func (c Cipher) Message(dst, src, key []byte) Cipher

Message processes a complete message through the Cipher, XORing a src byte-slice with cryptographic random bits to yield dst bytes, and concurrently absorbing bytes from a key byte-slice into its state:

cipher.Message(dst, src, key) Cipher

A call always processes exactly max(len(dst),len(dst),len(key)) bytes. All slice arguments may be nil or of varying lengths. If the src or key slices are short, the missing bytes are taken to be zero. If the dst slice is short, the extra output bytes are discarded. The src and/or key slices may overlap with dst exactly or not at all.

The Cipher preserves and cryptographically accounts for message boundaries, so that the following sequence of two calls yields a result that is always cryptographically distinct from the above single call.

cipher.Message(dst[:div], src[:div], key[:div])
cipher.Message(dst[div:], src[div:], key[div:])

The Cipher guarantees that any key material absorbed during a given call will cryptographically affect every bit of all future messages processed, but makes no guarantees about whether key material absorbed in this call will affect some, all, or none of the cryptographic pseudorandom bits produced concurrently in the same call.

func (Cipher) Open

func (c Cipher) Open(dst, src []byte) ([]byte, error)

Open decrypts and authenticates a message encrypted using Seal. It decrypts sealed message src and appends it onto plaintext buffer dst, growing the dst buffer if it is too small (or nil), and returns the resulting destination buffer or an error.

func (Cipher) Partial

func (c Cipher) Partial(dst, src, key []byte) Cipher

Partial processes a partial (initial or continuing) portion of a message, allowing the Cipher to be used for byte-granularity streaming:

cipher.Partial(dst, src, key)

The above single call is thus equivalent to the following pair of calls:

cipher.Partial(dst[:div], src[:div], key[:div])
cipher.Partial(dst[div:], src[div:], key[div:])

One or more calls to Partial must be terminated with a call to Message, to complete the message and ensure that key-material bytes absorbed in the current message affect the pseudorandom bits the Cipher produces in the context of the next message. Key material absorbed in a given Partial call may, or may not, affect the pseudorandom bits generated in subsequent Partial calls if there are no intervening calls to Message.

func (Cipher) Read

func (c Cipher) Read(dst []byte) (n int, err error)

Read satisfies the standard io.Reader interface, yielding a stream of cryptographically pseudorandom bytes. Consistent with the streaming semantics of the io.Reader interface, two consecutive reads of length l1 and l2 produce the same bytes as a single read of length l1+l2.

func (Cipher) Seal

func (c Cipher) Seal(dst, src []byte) []byte

Seal uses a stateful message cipher to implement authenticated encryption. It encrypts the src message and appends it to the dst slice, growing or allocating the dst slice if it is too small or nil. Seal also absorbs the produced ciphertext into the Cipher's state, then uses that state to append a message authentication check (MAC) to the sealed message, to be verified by Open.

func (Cipher) Sum

func (c Cipher) Sum(dst []byte) []byte

Sum ends the current message and produces a cryptographic checksum or hash based on the Cipher's state after absorbing all previously-written data. The resulting hash is appended to the dst slice, which Sum will grow or allocate if dst is too small or nil. A Cipher may be used as a hash function by absorbing data via Write and then calling Sum to finalize the message and produce the hash. Unlike the hash.Hash interface, this Sum method affects the Cipher's state: two consecutive calls to Sum on the same Cipher will produce two different hashes, not the same one.

func (Cipher) Write

func (c Cipher) Write(key []byte) (n int, err error)

Write satisifies the standard io.Writer interface, cryptographically absorbing all written data into the Cipher's state. Consistent with the streaming semantics of the io.Writer interface, Write calls by themselves never produce message boundaries, and written data is NOT guaranteed to affect the Cipher's output until the next explicit message boundary. The caller should invoke EndMessage after a series of Write calls to ensure that all written data is fully absorbed into the Cipher, before reading Cipher output that is supposed to depend on the written data.

func (Cipher) XORKeyStream

func (c Cipher) XORKeyStream(dst, src []byte)

XORKeyStream satisfies the Go library's legacy cipher.Stream interface, enabling a Cipher to be used as a stream cipher. This method reads len(src) pseudorandom bytes from the Cipher, XORs them with the corresponding bytes from src, and writes the resulting stream-encrypted bytes to dst. The dst slice must be at least as long as src, and if it is longer, only the corresponding prefix of dst is affected.

Warning: stream ciphers inherently provide no authentication, and malicious bit-flipping attacks are trivial if the encrypted stream is not authenticated in some other way. For this reason, stream cipher operation is not recommended in common-case situations in which authenticated encryption methods (e.g., via Seal and Open) are applicable.

type CipherState

type CipherState interface {

	// Transform a message (or the final portion of one) from src to dst,
	// absorb key into the cipher state, and return the Cipher.
	Message(dst, src, key []byte)

	// Transform a partial, incomplete message from src to dst,
	// absorb key into the cipher state, and return the Cipher.
	Partial(dst, src, key []byte)

	// Return the minimum size in bytes of secret keys for full security
	// (although key material may be of any size).
	KeySize() int

	// Return recommended size in bytes of hashes for full security.
	// This is usually 2*KeySize() to account for birthday attacks.
	HashSize() int

	// Create an identical clone of this cryptographic state object.
	// Caution: misuse can lead to key-reuse vulnerabilities.
	Clone() CipherState
}

CipherState defines an interface to an abstract symmetric message cipher. The cipher embodies a scalar that may be used to encrypt/decrypt data as well as to generate cryptographically random bits. The Cipher can also cryptographically absorb data or key material, updating its state to produce cryptographic hashes and authenticators.

The main Message method processes a complete message through the Cipher, XORing a src byte-slice with cryptographic random bits to yield dst bytes, and concurrently absorbing bytes from a key byte-slice into its state:

cipher.Message(dst, src, key) Cipher

A call always processes exactly max(len(dst),len(dst),len(key)) bytes. All slice arguments may be nil or of varying lengths. If the src or key slices are short, the missing bytes are taken to be zero. If the dst slice is short, the extra output bytes are discarded. The src and/or key slices may overlap with dst exactly or not at all.

The cipher preserves and cryptographically accounts for message boundaries, so that the following sequence of two calls yields a result that is always cryptographically distinct from the above single call.

cipher.Message(dst[:div], src[:div], key[:div])
cipher.Message(dst[div:], src[div:], key[div:])

The cipher guarantees that any key material absorbed during a given call will cryptographically affect every bit of all future messages processed, but makes no guarantees about whether key material absorbed in this call will affect some, all, or none of the cryptographic pseudorandom bits produced concurrently in the same call.

A message cipher supports "full-duplex" operation, concurrently producing pseudorandom bits and absorbing data, supporting efficient use for authenticated encryption. This sequence of calls encrypts a plaintext msg to produce a ciphertext ctx and an associated message-authenticator mac:

cipher.Message(ctx, msg, ctx)	// Encrypt and absorb ciphertext
cipher.Message(mac, nil, nil)	// Produce MAC based on ciphertext

This encrypts msg into ctx by XORing it with bits generated by the cipher, while absorbing the output ciphertext into the cipher's state. The second Message call then uses the resulting state to produce a MAC.

The following sequence decrypts and verifies a received ciphertext and MAC encrypted in the above fashion:

cipher.Message(msg, ctx, ctx)	// Decrypt and absorb ciphertext
cipher.Message(mac, mac, nil)	// Compute MAC and XOR with received
valid := subtle.ConstantTimeAllEq(mac, 0)

This decrypts ctx into msg by XORing the same bits used during encryption, while similarly absorbing the ciphertext (which is the input this time). The second Message call recomputes the MAC based on the absorbed ciphertext, XORs the recomputed MAC onto the received MAC in-place, and verifies in constant time that the result is zero (i.e., that the received and recomputed MACs are equal).

The Cipher wrapper provides convenient Seal and Open functions performing the above authenticated encryption sequences.

A cipher may be operated as a cryptographic hash function taking messsage msg and producing cryptographic checksum in slice sum:

cipher.Message(nil, nil, msg)	// Absorb msg into Cipher state
cipher.Message(sum, nil, nil)	// Produce cryptographic hash in sum

Both the input msg and output sum may be of any length, and the Cipher guarantees that every bit of the output sum has a strong cryptographic dependency on every bit of the input msg. However, to achieve full security, the caller should ensure that the output sum is at least cipher.HashSize() bytes long.

The Partial method processes a partial (initial or continuing) portion of a message, allowing the Cipher to be used for byte-granularity streaming:

cipher.Partial(dst, src, key)

The above single call is thus equivalent to the following pair of calls:

cipher.Partial(dst[:div], src[:div], key[:div])
cipher.Partial(dst[div:], src[div:], key[div:])

One or more calls to Partial must be terminated with a call to Message, to complete the message and ensure that key-material bytes absorbed in the current message affect the pseudorandom bits the Cipher produces in the context of the next message. Key material absorbed in a given Partial call may, or may not, affect the pseudorandom bits generated in subsequent Partial calls if there are no intervening calls to Message.

A Cipher may be used to generate pseudorandom bits that depend only on the Cipher's initial state in the following fashion:

cipher.Partial(dst, nil, nil)

type Constructor

type Constructor interface {
	New(t reflect.Type) interface{}
}

Constructor represents a generic constructor that takes a reflect.Type, typically for an interface type, and constructs some suitable concrete instance of that type. The crypto library uses this capability to support dynamic instantiation of cryptographic objects of the concrete type appropriate for a given abstract.Suite.

type Encoding

type Encoding interface {

	// Encode and write objects to an io.Writer.
	Write(w io.Writer, objs ...interface{}) error

	// Read and decode objects from an io.Reader.
	Read(r io.Reader, objs ...interface{}) error
}

Encoding represents an abstract interface to an encoding/decoding that can be used to marshal/unmarshal objects to and from streams. Different Encodings will have different constraints, of course.

type Group

type Group interface {
	String() string

	ScalarLen() int // Max len of scalars in bytes
	Scalar() Scalar // Create new scalar

	PointLen() int // Max len of point in bytes
	Point() Point  // Create new point

	PrimeOrder() bool // Returns true if group is prime-order
}

This interface represents an abstract cryptographic group usable for Diffie-Hellman key exchange, ElGamal encryption, and the related body of public-key cryptographic algorithms and zero-knowledge proof methods. The Group interface is designed in particular to be a generic front-end to both traditional DSA-style modular arithmetic groups and ECDSA-style elliptic curves: the caller of this interface's methods need not know or care which specific mathematical construction underlies the interface.

The Group interface is essentially just a "constructor" interface enabling the caller to generate the two particular types of objects relevant to DSA-style public-key cryptography; we call these objects Points and Scalars. The caller must explicitly initialize or set a new Point or Scalar object to some value before using it as an input to some other operation involving Point and/or Scalar objects. For example, to compare a point P against the neutral (identity) element, you might use P.Equal(suite.Point().Null()), but not just P.Equal(suite.Point()).

It is expected that any implementation of this interface should satisfy suitable hardness assumptions for the applicable group: e.g., that it is cryptographically hard for an adversary to take an encrypted Point and the known generator it was based on, and derive the Scalar with which the Point was encrypted. Any implementation is also expected to satisfy the standard homomorphism properties that Diffie-Hellman and the associated body of public-key cryptography are based on.

XXX should probably delete the somewhat redundant ...Len() methods.

type Hiding

type Hiding interface {

	// Hiding-encoded length of this object in bytes.
	HideLen() int

	// Attempt to encode the content of this object into a slice,
	// whose length must be exactly HideLen(),
	// using a specified source of random bits.
	// Encoding may consistently fail on some curve points,
	// in which case this method returns nil,
	// and the caller must try again after re-randomizing the object.
	HideEncode(rand cipher.Stream) []byte

	// Decode a uniform representation of this object from a slice,
	// whose length must be exactly HideLen().
	// This method cannot fail on correctly-sized input:
	// it maps every HideLen()-byte string to some object.
	// This is a necessary security property,
	// since if some correctly-sized byte strings failed to decode,
	// an attacker could use decoding as a hidden object detection test.
	HideDecode(buf []byte)
}

Hiding is an alternative encoding interface to encode cryptographic objects such that their representation appears indistinguishable from a uniformly random byte-string.

Achieving uniformity in representation is challenging for elliptic curves. For this reason, the Hiding-encoding of an elliptic curve point is typically more costly to compute than the normal (non-hidden) encoding, may be less space efficient, and may not allow representation for all possible curve points. This interface allows the ciphersuite to determine the specific uniform encoding method and balance their tradeoffs. Since some uniform encodings cannot represent all possible points, the caller must be prepared to call HideEncode() in a loop with a freshly-chosen object (typically a fresh Diffie-Hellman public key).

For further background and technical details:

"Elligator: Elliptic-curve points indistinguishable from uniform random strings"
http://elligator.cr.yp.to/elligator-20130828.pdf
"Elligator Squared: Uniform Points on Elliptic Curves of Prime Order as Uniform Random Strings"
http://eprint.iacr.org/2014/043.pdf
"Binary Elligator squared"
http://eprint.iacr.org/2014/486.pdf

type Marshaling

type Marshaling interface {
	encoding.BinaryMarshaler
	encoding.BinaryUnmarshaler

	// XXX This may go away from the interface.
	String() string

	// Encoded length of this object in bytes.
	MarshalSize() int

	// Encode the contents of this object and write it to an io.Writer.
	MarshalTo(w io.Writer) (int, error)

	// Decode the content of this object by reading from an io.Reader.
	// If r is a Cipher, uses it to pick a valid object pseudo-randomly,
	// which may entail reading more than Len bytes due to retries.
	UnmarshalFrom(r io.Reader) (int, error)
}

Marshaling is a basic interface representing fixed-length (or known-length) cryptographic objects or structures having a built-in binary encoding.

type Point

type Point interface {
	Marshaling

	// Equality test for two Points derived from the same Group
	Equal(s2 Point) bool

	Null() Point // Set to neutral identity element

	// Set to this group's standard base point.
	Base() Point

	// Pick and set to a point that is at least partly [pseudo-]random,
	// and optionally so as to encode a limited amount of specified data.
	// If data is nil, the point is completely [pseudo]-random.
	// Returns this Point and a slice containing the remaining data
	// following the data that was successfully embedded in this point.
	Pick(data []byte, rand cipher.Stream) (Point, []byte)

	// Maximum number of bytes that can be reliably embedded
	// in a single group element via Pick().
	PickLen() int

	// Set equal to another Point p.
	Set(p Point) Point

	// Clone clones the underlying point.
	Clone() Point

	// Extract data embedded in a point chosen via Embed().
	// Returns an error if doesn't represent valid embedded data.
	Data() ([]byte, error)

	// Add points so that their scalars add homomorphically
	Add(a, b Point) Point

	// Subtract points so that their scalars subtract homomorphically
	Sub(a, b Point) Point

	// Set to the negation of point a
	Neg(a Point) Point

	// Encrypt point p by multiplying with scalar s.
	// If p == nil, encrypt the standard base point Base().
	Mul(p Point, s Scalar) Point
}

A Point abstractly represents an element of a public-key cryptographic Group. For example, this is a number modulo the prime P in a DSA-style Schnorr group, or an x,y point on an elliptic curve. A Point can contain a Diffie-Hellman public key, an ElGamal ciphertext, etc.

type Scalar

type Scalar interface {
	Marshaling

	// Equality test for two Scalars derived from the same Group
	Equal(s2 Scalar) bool

	// Set equal to another Scalar a
	Set(a Scalar) Scalar

	// Clone creates a new Scalar with same value
	Clone() Scalar

	// Set to a small integer value
	SetInt64(v int64) Scalar

	// Set to the additive identity (0)
	Zero() Scalar

	// Set to the modular sum of scalars a and b
	Add(a, b Scalar) Scalar

	// Set to the modular difference a - b
	Sub(a, b Scalar) Scalar

	// Set to the modular negation of scalar a
	Neg(a Scalar) Scalar

	// Set to the multiplicative identity (1)
	One() Scalar

	// Set to the modular product of scalars a and b
	Mul(a, b Scalar) Scalar

	// Set to the modular division of scalar a by scalar b
	Div(a, b Scalar) Scalar

	// Set to the modular inverse of scalar a
	Inv(a Scalar) Scalar

	// Set to a fresh random or pseudo-random scalar
	Pick(rand cipher.Stream) Scalar
	// SetBytes will take bytes and create a scalar out of it
	SetBytes([]byte) Scalar

	// Bytes returns the raw internal representation
	Bytes() []byte
}

A Scalar abstractly represents a scalar value by which a Point (group element) may be encrypted to produce another Point. This is an exponent in DSA-style groups, in which security is based on the Discrete Logarithm assumption, and a scalar multiplier in elliptic curve groups.

type Suite

type Suite interface {

	// Create a cryptographic Cipher with a given key and configuration.
	// If key is nil, creates a Cipher seeded with a fresh random key.
	Cipher(key []byte, options ...interface{}) Cipher

	// Symmetric-key hash function
	Hash() hash.Hash

	// Abstract group for public-key crypto
	Group

	// Fixed-length binary encoding for all crypto objects
	Encoding

	// Generic constructor to instantiate any abstract interface type
	// supported by this suite: at least Cipher, Hash, Point, Scalar.
	Constructor

	// NewKey returns a freshly generated private key from the cipher stream.
	// If cipher == nil, it uses random.Stream.
	NewKey(cipher.Stream) Scalar
}

Suite is an abstract interface to a full suite of public-key and symmetric-key crypto primitives chosen to be suited to each other and haver matching security parameters. A ciphersuite in this framework basically consists of three components: a hash function, a stream cipher, and an abstract group for public-key crypto.

This interface adopts hashes and stream ciphers as its fundamental symmetric-key crypto abstractions because they are conceptually simple and directly complementary in function: a hash takes any desired number of input bytes and produces a small fixed number of output bytes, whereas a stream cipher takes a small fixed number of input bytes and produces any desired number of output bytes. While stream ciphers can be and often are constructed from block ciphers, we treat block ciphers as an implementation detail hidden below the abstraction level of this ciphersuite interface.

Jump to

Keyboard shortcuts

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