Skip to content

Latest commit

 

History

History
162 lines (119 loc) · 6.66 KB

3a.md

File metadata and controls

162 lines (119 loc) · 6.66 KB

Cipher Set 3a

Cipher Set 3a is based on Daniel J. Bernstein's NaCl: Networking and Cryptography library. The cipher set leverages the public-key and secret-key portions of NaCl. Implementations will need to support the crypto_box, crypto_secretbox, and crypto_onetimeauth related functions.

Switch Key Pair and Fingerprint

When a switch is first initialized, it must generate a key pair and fingerprint for each cipher set. For cs3a, this key pair is generated using NaCl's crypto_box from the components for public-key cryptography.

Here is some example code:

var sodium = require("sodium").api;
var keys = sodium.crypto_box_keypair();
console.log(keys.publicKey); // binary public key, 32 bytes
console.log(crypto.createHash("sha256").update(keys.publicKey).digest("hex")); // fingerprint

Open

In order to create a new line, a switch must generate a new, temporary key pair for that line. This key pair is also created using crypto_box. The public key for the line is called the LINE KEY. After a successful open handshake, both parties will be in posession of a shared secret (agreedKey).

The BODY of the open packet is binary and defined as the following byte sections in sequential order:

  • AUTH - 16 bytes, the calculated onetimeauth(line key, inner ciphertext)
  • LINE KEY - 32 bytes, the sender's line level public key
  • INNER CIPHERTEXT - the secretbox() encrypted inner packet

Line

After a line has been opened, the line encryption keys are generated by performing a SHA-256 hash of the shared secret (agreedKey) and the line ids from each switch:

  • line encryption key: SHA256(secret, my-line-id, their-line-id)
  • line decryption key: SHA256(secret, their-line-id, my-line-id)

The enclosing line packet binary is defined as the following byte sections in sequential order:

  • NONCE - 24 bytes, randomly generated
  • CHANNEL CIPHERTEXT - the secretbox() output representing the encrypted inner packet

Example Code For Discussion (open handshake)

The following example illustrates the usage of cs3a for the sending and receiving sides of an open request. Warning: pseudo code interspersed with real code.

Sender (initiating an open request):

var sodium = require("sodium").api;

// Generate Switch Key Pair
// Upon switch initialization, an instance level key pair is generated for
// the switch.
var senderKeys = sodium.crypto_box_keypair();

// Generate Line Key Pair
// When the sender is ready to create an open request, a line specific key
// pair is generated.
var senderLineKeys = sodium.crypto_box_keypair();

// Generate a 24 byte nonce of 0
// Both sender and receiver must use the same initial nonce.
var nonce = new Buffer(24);
for (var i = 0; i < 24; ++i) {
  nonce[i] = 0;
}

// Generate the shared secret (agreedKey)
// Before any specific message handling, this "message independent pre-computaion"
// is performed, generating the agreedKey.  This shared secret is later used as a
// part of the encryption process for all packets sent over the line.
var agreedKey = sodium.crypto_box_beforenm(receiverKeys.publicKey, senderLineKeys.secretKey);

// Sample inner packet
// (The json schema can be found in the spec for open)
var plainText = JSON.stringify("This is a test chunk of data for the inner packet. it would have JSON and a payload of the sender publicKey");

// Encrypt the inner packet
// Note that this step uses NaCl's crypto_secretbox from the components for
// secret-key cryptography.
var innerPacketData = sodium.crypto_secretbox(new Buffer(plainText), nonce, agreedKey);

// Take the encrypted inner packet and the line level public key, and build the
// data section of the outer packet.
var openPacketData = Buffer.concat([senderLineKeys.publicKey, innerPacketData]);

// Generate the macKey
// The macKey uses the switch level private key.
var macKey = sodium.crypto_box_beforenm(receiverKeys.publicKey, senderKeys.secretKey);

// Generate the open MAC
// Note that this uses the NaCl's crypto_onetimeauth from the components for
// secret-key cryptography.
var openMAC = sodium.crypto_onetimeauth(openPacketData, macKey);

// Generate the outer packet BODY
// <open-MAC><sender-line-public-key><encrypted-inner-packet-data>
var openPacketBody = Buffer.concat([openMAC, openPacketData]);

Receiver (accepting an open request):

var sodium = require("sodium").api;

// Generate Switch Key Pair
// Upon switch initialization, an instance level key pair is generated for
// the switch.
var receiverKeys = sodium.crypto_box_keypair();

// Generate Line Key Pair
// Upon receiving an open request, a line specific key pair is generated.
var receiverLineKeys = sodium.crypto_box_keypair();

// Unpack and authenticate the outer packet
//
// At this point, the open packet has been received.  Remember the following
// format:
// <open-MAC><sender-line-public-key><encrypted-inner-packet-data>
//
var openMAC = ...                  // the leading 16 bytes 
var senderLineKeys.publicKey = ...  // the next 32 bytes
var innerPacketData = ...           // the remaining bytes are the encrypted inner packet data
var openPacketData = ...            // Buffer.concat([senderLineKeys.publicKey, innerPacketData]);

// Generate the macKey
// This macKey will match the macKey generated by the sender, due to the corresponding
// public/private keys.  Here is the sender version for comparison:
//  macKey = sodium.crypto_box_beforenm(receiverKeys.publicKey, senderKeys.secretKey);
//
var macKey = sodium.crypto_box_beforenm(senderKeys.publicKey, receiverKeys.secretKey);

// Authenticate the open packet
// With all the pieces available, a onetimeauth verification can be completed.  If
// successful, this step completes the authentication of the open request.
var authed = sodium.crypto_onetimeauth_verify(openMAC, openPacketData, macKey) === 0 ;
console.log("Open mac verify:", authed);


// Auth successful?  The decryption can proceed.


// Generate a 24 byte nonce of 0
// (same as sender for open)
var nonce = new Buffer(24);
for (var i = 0; i < 24; ++i) {
  nonce[i] = 0;
}

// Generate the shared secret (agreedKey)
// This agreedKey will match the agreedKey generated by the sender, due to the
// corresponding public/private keys.  Here is the sender version for comparison:
//  agreedKey = sodium.crypto_box_beforenm(receiverKeys.publicKey, senderLineKeys.secretKey);
//
var agreedKey = sodium.crypto_box_beforenm(senderLineKeys.publicKey, receiverKeys.secretKey);

// Decrypt the inner packet
// Using the agreedKey, the inner packet can now be decrypted. This should match
// the sample plaintext from the sender example.
var decryptedPacketData = sodium.crypto_secretbox_open(innerPacketData, nonce, agreedKey);