Skip to content

Latest commit

 

History

History
178 lines (120 loc) · 6.17 KB

SecretStream.md

File metadata and controls

178 lines (120 loc) · 6.17 KB

Secret Stream

Stream/file encryption is made available through a high-level API that encrypts a sequence of messages, or a single message split up into an arbitary number of chunks, using a secret key with the following properties:

  • Messages cannot be truncated, removed, reordered, duplicated or modified without this being detected by the decryption methods.
  • Non-deterministic - the same sequence encrypted twice will produce different ciphertexts.
  • An authentication tag is added to each encrypted message, allowing streaming corruption to be detected without having to read the stream until the end.
  • Each message can incldue additional data in computation of the message tag.
  • Messages can be of arbirary size and length.
  • There is no practical limit to the total length of the stream, or the total number of individual messages.
  • Ratcheting: at any point in the stream, it's possible to forget the key used to encrypt previous messages and to switch to a new key.
  • Nonce and key rotation is automatically handled.

This API can be used to securely send an ordered sequence of messages to a peer. Since the length of the stream is not limited, it can also be used to encrypt files regardless of their size.

Please reference the libsodium documentation for more information on secret streams.

Example

The following is a rough example of how to use the API.

// Starting on the sender
// Create a new key
var key = Sodium.SecretStream.GenerateKey();
// Create a new header
var header = Sodium.SecretStream.GenerateHeader();

// Create a new stream in PUSH mode to push new messages onto the stream.
var encrypter = new Sodium.SecretStream(key, header, Sodium.SecretStream.MODE_PUSH);

var message1 = "Hello, World!";
var message2 = "{ \"json\": \"data\" }";

var ciphertext1 = encrypter.Push(message1);
var ciphertext2 = encrypter.Push(message2, Sodium.SecretStream.TAG_FINAL);

// On the peer, create a PULL stream, and pull in the stream as it comes in from the peer and decrypt it
var decrypter = new Sodium.SecretStream(key, header, Sodium.SecretStream.MODE_PULL);
// System.Text.Encoding.UTF8.GetBytes(message1) == d1
var d1 = decrypter.Pull(ciphertext1);
// System.Text.Encoding.UTF8.GetBytes(message2) == d2
var d2 = decrypter.Pull(ciphertext2, Sodium.SecretStream.TAG_FINAL);

Stream Modes

Namespace: Sodium.SecretStream

SecretStream.MODE_PUSH
SecretStream.MODE_PULL

The streaming API exposes two distinct modes, one for encrypting, and the other for decrypting a stream.

Tags

Namespace: Sodium.SecretStream

A tag is attached to each message, and may be one of the following:

SecretStream.TAG_MESSAGE

TAG_MESSAGE is the default tag that is added. It does not contain any additional information about the nature of the message.

SecretStream.TAG_PUSH

TAG_PUSH indicates that the message marks the end of a set of messages, but not the end of a stream. More messages may follow.

SecretStream.TAG_REKEY

TAG_REKEY "forgets" the key used to encrypt this message and the previous one, and derives a new secret key.

SecretStream.TAG_FINAL

TAG_FINAL indicates that the message marks the end of the stream, and erases the secret key used to encrypt the previous sequence.

Key Generation

Secret streams require both a key and a header to encrypt and decrypt the header. Both components are needed to encrypt and decrypt a given stream.

Namespace: Sodium.SecretStream

public static byte[] Sodium.PublicKeyBox.GenerateKey()

This method returns a 32 byte key. Within the same application you can use this method to generate a 32 byte key for encrypting and decrypting. When working with remote peers however, use a key exchange method such as Sodium::ScalarMult::Mult to create a 32 byte shared key that can be safely transmitted to the remote peer.

Internally this method uses crypto_secretstream_xchacha20poly1305_keygen.

public static byte[] Sodium.PublicKeyBox.GenerateHeader()

This method returns a 24 byte header.

Stream Handling

Namespace: Sodium.SecretStream

public SecretStream SecretStream(byte[] key, byte[] header, int mode);

A new stream can be created either in encrypt or decrypt mode.

Internally this method uses crypto_secretstream_xchacha20poly1305_init_push or crypto_secretstream_xchacha20poly1305_init_pull, depending upon the mode selected.

Encrypting a Stream

Namespace: Sodium.SecretStream

public byte[] Push(String message);
public byte[] Push(byte[] message);

Several methods are exposed to encrypt a new message in the stream. By default, TAG_MESSAGE will be used for the tag.

public byte[] Push(String message, int tag);
public byte[] Push(byte[] message, int tag);

One of the aforementioned tags may be defined. This is useful for rekeying the stream or indicating that the stream is final.

public byte[] Push(String message, int tag, String additionalData);
public byte[] Push(byte[] message, int tag, byte[] additionalData);

Additional data may also be included with the stream either as a String or as a byte array.

Internally this method uses crypto_secretstream_xchacha20poly1305_push.

Decrypting a Stream

Namespace: Sodium.SecretStream

public byte[] Pull(byte[] ciphertext);

Streams can be decrypted with the default TAG_MESSAGE.

public byte[] Pull(byte[] ciphertext, int tag);

Tags may be specified in the decryption.

public byte[] Pull(byte[] ciphertext, int tag, byte[] additionalData);
public byte[] Pull(byte[] ciphertext, int tag, String additionalData);

Additional data may also be specified

Internally this method uses crypto_secretstream_xchacha20poly1305_pull.

Rekeying

Namespace: Sodium.SecretStream

public void Rekey();

Rekeying happens automatically, and transparently. If you want to manually rekey, you can either use the TAG_REKEY tag with your message, or explicitly call Rekey().

Note that rekeying must occur at the same point on both the sender and reciever.

Internally this method uses crypto_secretstream_xchacha20poly1305_rekey.