summaryrefslogtreecommitdiffstats
path: root/core/ccmArrayBuffer.js
diff options
context:
space:
mode:
authorNils Kenneweg <nk@ovt.me>2014-09-28 12:20:59 +0200
committerNils Kenneweg <nk@ovt.me>2014-09-28 12:20:59 +0200
commitbf05b20df52d6749d96d65e46b3717e6d9b94e45 (patch)
tree0d5403847b6e23c8abd00d95514a5c7e80ee6761 /core/ccmArrayBuffer.js
parent52bc7c51056f05c90da302a66d95a0121232d031 (diff)
downloadsjcl-bf05b20df52d6749d96d65e46b3717e6d9b94e45.zip
sjcl-bf05b20df52d6749d96d65e46b3717e6d9b94e45.tar.gz
sjcl-bf05b20df52d6749d96d65e46b3717e6d9b94e45.tar.bz2
reuse parts of original ccm mode. (compatibility for all ivs in sight)
Diffstat (limited to 'core/ccmArrayBuffer.js')
-rw-r--r--core/ccmArrayBuffer.js146
1 files changed, 33 insertions, 113 deletions
diff --git a/core/ccmArrayBuffer.js b/core/ccmArrayBuffer.js
index 6f71e97..551d514 100644
--- a/core/ccmArrayBuffer.js
+++ b/core/ccmArrayBuffer.js
@@ -21,7 +21,6 @@ sjcl.arrayBuffer.ccm = {
mode: "ccm",
defaults: {
- adata_buffer: new ArrayBuffer(),
tlen:128 //this is M in the NIST paper
},
@@ -36,8 +35,7 @@ sjcl.arrayBuffer.ccm = {
*/
compat_encrypt: function(prf, plaintext, iv, adata, tlen){
var plaintext_buffer = sjcl.codec.arrayBuffer.fromBits(plaintext, true, 16),
- l_m = sjcl.bitArray.bitLength(plaintext)/8,
- adata_buffer = sjcl.codec.arrayBuffer.fromBits(adata, false),
+ ol = sjcl.bitArray.bitLength(plaintext)/8,
encrypted_obj,
ct,
tag;
@@ -45,10 +43,10 @@ sjcl.arrayBuffer.ccm = {
tlen = tlen || 64;
adata = adata || [];
- encrypted_obj = sjcl.arrayBuffer.ccm.encrypt(prf, plaintext_buffer, iv, adata_buffer, tlen, l_m);
+ encrypted_obj = sjcl.arrayBuffer.ccm.encrypt(prf, plaintext_buffer, iv, adata, tlen, ol);
ct = sjcl.codec.arrayBuffer.toBits(encrypted_obj.ciphertext_buffer);
- ct = sjcl.bitArray.clamp(ct, l_m*8);
+ ct = sjcl.bitArray.clamp(ct, ol*8);
return sjcl.bitArray.concat(ct, encrypted_obj.tag);
@@ -68,14 +66,12 @@ sjcl.arrayBuffer.ccm = {
adata = adata || [];
var L, i,
w=sjcl.bitArray,
- ivl = w.bitLength(iv) / 8,
ol = w.bitLength(ciphertext),
out = w.clamp(ciphertext, ol - tlen),
tag = w.bitSlice(ciphertext, ol - tlen), tag2,
- adata_buffer = sjcl.codec.arrayBuffer.fromBits(adata, false),
ciphertext_buffer = sjcl.codec.arrayBuffer.fromBits(out, true, 16);
- var plaintext_buffer = sjcl.arrayBuffer.ccm.decrypt(prf, ciphertext_buffer, iv, tag, adata_buffer, tlen, (ol-tlen)/8);
+ var plaintext_buffer = sjcl.arrayBuffer.ccm.decrypt(prf, ciphertext_buffer, iv, tag, adata, tlen, (ol-tlen)/8);
return sjcl.bitArray.clamp(sjcl.codec.arrayBuffer.toBits(plaintext_buffer), ol-tlen);
},
@@ -89,25 +85,30 @@ sjcl.arrayBuffer.ccm = {
* @param {Number} [tlen=128] the desired tag length, in bits.
* @return {ArrayBuffer} The encrypted data, in the same array buffer as the given plaintext, but given back anyways
*/
- encrypt: function(prf, plaintext_buffer, iv, adata_buffer, tlen, l_m){
- var auth_blocks, mac, plaintext, ctr, keyblock, word0, word1, word2, word3, i;
+ encrypt: function(prf, plaintext_buffer, iv, adata, tlen, ol){
+ var auth_blocks, mac, L, w = sjcl.bitArray,
+ ivl = w.bitLength(iv) / 8;
//set up defaults
- adata_buffer = adata_buffer || sjcl.arrayBuffer.ccm.defaults.adata_buffer;
+ adata = adata || [];
tlen = tlen || sjcl.arrayBuffer.ccm.defaults.tlen;
- l_m = l_m || plaintext_buffer.byteLength;
+ ol = ol || plaintext_buffer.byteLength;
tlen = Math.ceil(tlen/8);
+ for (L=2; L<4 && ol >>> 8*L; L++) {}
+ if (L < 15 - ivl) { L = 15-ivl; }
+ iv = w.clamp(iv,8*(15-L));
+
//prf should use a 256 bit key to make precomputation attacks infeasible
//the iv should be 8bytes
//assume the iv is set to an array of 32 ints, so we need a length of 2 to get 8 bytes
if (iv.length !== 2) throw new sjcl.exception.invalid("Invalid IV length, it should be 8 bytes");
- mac = sjcl.arrayBuffer.ccm.compute_tag(prf, plaintext_buffer, iv, adata_buffer, tlen, l_m);
+ mac = sjcl.arrayBuffer.ccm._computeTag(prf, plaintext_buffer, iv, adata, tlen, ol, L);
//encrypt the plaintext and the mac
//returns the mac since the plaintext will be left encrypted inside the buffer
- mac = sjcl.arrayBuffer.ccm.ctrCipher(prf, plaintext_buffer, iv, mac, tlen);
+ mac = sjcl.arrayBuffer.ccm._ctrMode(prf, plaintext_buffer, iv, mac, tlen);
//the plaintext_buffer has been modified so it is now the ciphertext_buffer
@@ -124,14 +125,19 @@ sjcl.arrayBuffer.ccm = {
* @param {Number} [tlen=128] the desired tag length, in bits.
* @return {ArrayBuffer} The decrypted data, in the same array buffer as the given buffer, but given back anyways
*/
- decrypt: function(prf, ciphertext_buffer, iv, tag, adata_buffer, tlen, l_m){
- var mac, mac2, i;
+ decrypt: function(prf, ciphertext_buffer, iv, tag, adata, tlen, ol){
+ var mac, mac2, i, L, w = sjcl.bitArray,
+ ivl = w.bitLength(iv) / 8;
//set up defaults
- adata_buffer = adata_buffer || sjcl.arrayBuffer.ccm.defaults.adata_buffer;
+ adata = adata || [];
tlen = tlen || sjcl.arrayBuffer.ccm.defaults.tlen;
- l_m = l_m || ciphertext_buffer.byteLength;
+ ol = ol || ciphertext_buffer.byteLength;
tlen = Math.ceil(tlen/8) ;
+
+ for (L=2; L<4 && ol >>> 8*L; L++) {}
+ if (L < 15 - ivl) { L = 15-ivl; }
+ iv = w.clamp(iv,8*(15-L));
//prf should use a 256 bit key to make precomputation attacks infeasible
//the iv should be 8bytes
@@ -139,9 +145,9 @@ sjcl.arrayBuffer.ccm = {
if (iv.length !== 2) throw new sjcl.exception.invalid("Invalid IV length, it should be 8 bytes");
//decrypt the buffer
- mac = sjcl.arrayBuffer.ccm.ctrCipher(prf, ciphertext_buffer, iv, tag, tlen);
+ mac = sjcl.arrayBuffer.ccm._ctrMode(prf, ciphertext_buffer, iv, tag, tlen);
- mac2 = sjcl.arrayBuffer.ccm.compute_tag(prf, ciphertext_buffer, iv, adata_buffer, tlen, l_m);
+ mac2 = sjcl.arrayBuffer.ccm._computeTag(prf, ciphertext_buffer, iv, adata, tlen, ol, L);
//check the tag
if (!sjcl.bitArray.equal(mac, mac2)){
@@ -156,107 +162,21 @@ sjcl.arrayBuffer.ccm = {
* @param {Object} prf The pseudorandom function.
* @param {ArrayBuffer} data_buffer The plaintext data in an arraybuffer.
* @param {bitArray} iv The initialization value.
- * @param {ArrayBuffer} adata_buffer The authenticated data in an arraybuffer.
+ * @param {bitArray} adata The authenticated data.
* @param {Number} tlen the desired tag length, in bits.
* @return {bitArray} The tag, but not yet encrypted.
* @private
*/
- compute_tag: function(prf, data_buffer, iv, adata_buffer, tlen, l_m){
- var i, l_a, l_a_encoded_length, l_a_encoded_prefix, data_blocks, data_blocks_size, flags, offset, adata, plaintext, mac, data;
-
- //Let calculate l(a), the length of the authenticated data
- l_a = adata_buffer.byteLength;
-
- //now we need to calculate how many bytes we will need to encode l(a)
- if (l_a === 0){
- l_a_encoded_length = 0;
- l_a_encoded_prefix = [];
- }
- else if (l_a < 0xFF00){ //0xff00 === 2^16-2^8
- l_a_encoded_length = 2;
- l_a_encoded_prefix = [];
- }else if (l_a < 0x100000000){
- l_a_encoded_length = 6;
- l_a_encoded_prefix = [0xff, 0xfe];
- }else{
- //the length is greater than 4GB... no way, not supported
- //we really shouldn't end up here
- throw("your authenticated data is too big: "+l_a+" "+adata_buffer);
- //l_a_encoded_length = 10
- //l_a_encoded_prefix = [0xff, 0xff]
- }
-
- //calculate the amount of blocks we will need, then add one for the first block
- data_blocks_size = Math.ceil((l_a_encoded_length+l_a+16)/16) ;
+ _computeTag: function(prf, data_buffer, iv, adata, tlen, ol, L){
+ var i, plaintext, mac, data, data_blocks_size, data_blocks,
+ w = sjcl.bitArray, tmp, macData;
- data_blocks = new DataView(new ArrayBuffer(data_blocks_size*16));
-
- //set the first block
- //the format is flag (1 byte) (IV 8 bytes) (l(m) 7 bytes)
- //flag looks like [<bit>:content] [7:reserved 6:Adata 5-3:M(aka tlen), 2-0 L(hardcoded to 6]
- flags = l_a_encoded_length === 0 ? 0x06 : 0x46; //depending on the length of adata we vary the second MSB
- //flip the tlen bits
- flags |= ((tlen-2)/2) << 3;
- data_blocks.setUint8(0, flags);
-
- //Copy the IV over to the first block
- data_blocks.setUint32(1,iv[0]);
- data_blocks.setUint32(5,iv[1]);
-
- //copy the message length
- data_blocks.setUint32(12, l_m);
- //The higher three bytes are going to be 0 since we never expect to encrypt > 4GB file with this
-
- //now we add the bytes encoding the length of the adata
- //start at 16 because that's the start of the next block
- offset = 16;
- //if there was a prefix we need to but before the length lets do that
- if ( l_a_encoded_prefix.length > 0){
- data_blocks.setUint8(offset++,l_a_encoded_prefix[0]);
- data_blocks.setUint8(offset++,l_a_encoded_prefix[1]);
- }
-
- //place the length
- if (l_a_encoded_length === 0){
- }else if(l_a_encoded_length === 2){
- data_blocks.setUint16(offset,l_a);
- offset += 2;
- }else if(l_a_encoded_length === 6){
- //setting the bottom 4bytes, the top two(prefix, were already set
- data_blocks.setUint32(offset,l_a);
- offset += 4;
- }else{
- throw("Error in encoding length into Authorized blocks");
- }
-
- //now copy the adata over to the blocks
- //This will implicitly add 0 padding
- if (adata_buffer.byteLength !== 0) {
- adata = new DataView(adata_buffer);
- for (i = 0; i < adata.byteLength; i++){
- data_blocks.setUint8(offset, adata.getUint8(i));
- offset++;
- }
- }
-
-
- //now lets do the cbc-mac
-
- mac = [0,0,0,0];
-
- for (i=0; i < data_blocks.byteLength; i+=16){
- mac[0] ^= data_blocks.getUint32(i);
- mac[1] ^= data_blocks.getUint32(i+4);
- mac[2] ^= data_blocks.getUint32(i+8);
- mac[3] ^= data_blocks.getUint32(i+12);
-
- mac = prf.encrypt(mac);
- }
+ mac = sjcl.mode.ccm._macAdditionalData(prf, adata, iv, tlen, ol, L);
if (data_buffer.byteLength !== 0) {
data = new DataView(data_buffer);
//set padding bytes to 0
- for (i=l_m; i< data_buffer.byteLength; i++){
+ for (i=ol; i< data_buffer.byteLength; i++){
data.setUint8(i,0);
}
@@ -285,7 +205,7 @@ sjcl.arrayBuffer.ccm = {
* @return {Object} An object with data and tag, the en/decryption of data and tag values.
* @private
*/
- ctrCipher: function(prf, data_buffer, iv, mac, tlen){
+ _ctrMode: function(prf, data_buffer, iv, mac, tlen){
var data, ctr, word0, word1, word2, word3, keyblock, i;
ctr = new DataView(new ArrayBuffer(16)); //create the first block for the counter