# HG changeset patch # User Laman # Date 2022-04-01 13:09:18 # Node ID e2095e3881bf09c15bdc0273eabc7aea432e1efe # Parent abd03aa8b1630cc4c853e75f363c7dedcfd7d496 reformated with more whitespace diff --git a/spec/test/blakeSpec.js b/spec/test/blakeSpec.js --- a/spec/test/blakeSpec.js +++ b/spec/test/blakeSpec.js @@ -1,37 +1,37 @@ /* global expect */ -describe("Blake2",function(){ - let cryptoJS=require("../../dist/main.js"); - let util=cryptoJS.util; - let blake2s=cryptoJS.blake2s; - let str2utf8=util.str2utf8; - - let msg=str2utf8("abc"); - let longMsg=str2utf8("0123456789.10.456789.20.456789.30.456789.40.456789.50.456789.60.456789.70.456789.80.456789.90.456789"); - let key=str2utf8("zoqpiz"); - let longKey=str2utf8("zoqpizjutyclcmkamzhhmhvchxjtefjy"); - - describe("blake2s",function(){ - it("should return a correct variable size output",function(){ - expect(util.bytes2hex(blake2s(msg,16))).toEqual("aa4938119b1dc7b87cbad0ffd200d0ae"); +describe("Blake2", function () { + let cryptoJS = require("../../dist/main.js"); + let util = cryptoJS.util; + let blake2s = cryptoJS.blake2s; + let str2utf8 = util.str2utf8; + + let msg = str2utf8("abc"); + let longMsg = str2utf8("0123456789.10.456789.20.456789.30.456789.40.456789.50.456789.60.456789.70.456789.80.456789.90.456789"); + let key = str2utf8("zoqpiz"); + let longKey = str2utf8("zoqpizjutyclcmkamzhhmhvchxjtefjy"); + + describe("blake2s", function() { + it("should return a correct variable size output", function() { + expect(util.bytes2hex(blake2s(msg, 16))).toEqual("aa4938119b1dc7b87cbad0ffd200d0ae"); }); - it("should work with just a message",function(){ + it("should work with just a message", function() { expect(util.bytes2hex(blake2s(msg))).toEqual("508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"); }); - it("should work for multi block messages",function(){ - expect(util.bytes2hex(blake2s(longMsg.slice(0,64)))).toEqual("70484f89974551454d596350dda8af2aa6f0811b527549b9ecfe7adede063753"); - expect(util.bytes2hex(blake2s(longMsg.slice(0,65)))).toEqual("af14d4f74947bbde734d0e3015c667cc80676efe4349be235be8046e9e45e0ae"); + it("should work for multi block messages", function() { + expect(util.bytes2hex(blake2s(longMsg.slice(0, 64)))).toEqual("70484f89974551454d596350dda8af2aa6f0811b527549b9ecfe7adede063753"); + expect(util.bytes2hex(blake2s(longMsg.slice(0, 65)))).toEqual("af14d4f74947bbde734d0e3015c667cc80676efe4349be235be8046e9e45e0ae"); expect(util.bytes2hex(blake2s(longMsg))).toEqual("59a44e5e417d07fb382505ee7e67c23e0d476d354abc81899960bcab677beee1"); }); - it("should work for keyed messages",function(){ - expect(util.bytes2hex(blake2s(msg,32,key))).toEqual("0da0b6a54e8f294b60bb25c572700166ddb9d124257ff36f9f43f18b844adf9f"); + it("should work for keyed messages", function() { + expect(util.bytes2hex(blake2s(msg, 32, key))).toEqual("0da0b6a54e8f294b60bb25c572700166ddb9d124257ff36f9f43f18b844adf9f"); }); - it("should work with long keyes",function(){ - expect(util.bytes2hex(blake2s(msg,32,longKey))).toEqual("09ef85c9942bebdeb866c6ade769220fd9b851aead642017f6d59bf7e2a32037"); + it("should work with long keyes", function() { + expect(util.bytes2hex(blake2s(msg, 32, longKey))).toEqual("09ef85c9942bebdeb866c6ade769220fd9b851aead642017f6d59bf7e2a32037"); }); - it("should work with an empty input",function(){ + it("should work with an empty input", function() { expect(util.bytes2hex(blake2s([]))).toEqual("69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"); }); }); - + }); diff --git a/spec/test/chachaSpec.js b/spec/test/chachaSpec.js --- a/spec/test/chachaSpec.js +++ b/spec/test/chachaSpec.js @@ -1,63 +1,63 @@ /* global expect */ -function getRandomValues(arr){ - for(let i=0;ix>>>0); - let expected=[0xe4e7f110,0x15593bd1,0x1fdd0f50,0xc47120a3,0xc7f4d1c7,0x0368c033,0x9aaa2204,0x4e6cd4c3,0x466482d2,0x09aa9f07,0x05d7c214,0xa2028bd9,0xd19c12b5,0xb94e16de,0xe883d0cb,0x4e3c50a2]; +describe("Chacha", function() { + let cryptoJS = require("../../dist/main.js"); + let util = cryptoJS.util; + let Chacha20 = cryptoJS.Chacha20; + let encrypt = cryptoJS.encrypt; + let decrypt = cryptoJS.decrypt; + let str2utf8 = util.str2utf8; + + let key = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]; + let nonce1 = [0, 0, 0, 9, 0, 0, 0, 74, 0, 0, 0, 0]; + let nonce2 = [0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0]; + let plaintext = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; + + describe("Chacha20", function() { + it("should return a correct test vector", function() { + let cipher = new Chacha20(key, nonce1); + let output = cipher._computeBlock().map(x => x >>> 0); + let expected = [0xe4e7f110, 0x15593bd1, 0x1fdd0f50, 0xc47120a3, 0xc7f4d1c7, 0x0368c033, 0x9aaa2204, 0x4e6cd4c3, 0x466482d2, 0x09aa9f07, 0x05d7c214, 0xa2028bd9, 0xd19c12b5, 0xb94e16de, 0xe883d0cb, 0x4e3c50a2]; expect(output).toEqual(expected); }); - - it("should output a correct key stream",function(){ - let cipher=new Chacha20(key,nonce2); - let expected=[34,79,81,243,64,27,217,225,47,222,39,111,184,99,29,237,140,19,31,130,61,44,6, 226,126,79,202,236,158,243,207,120,138,59,10,163,114,96,10,146,181,121,116,205,237,43,147,52,121, 76,186,64,198,62,52,205,234,33,44,76,240,125,65,183,105,166,116,159,63,99,15,65,34,202,254,40,236, 77,196,126,38,212,52,109,112,185,140,115,243,233,197,58,196,12,89,69,57,139,110,218,26,131,44,137, 193,103,234,205,144,29,126,43,243,99]; - expected.forEach(b=>{ + + it("should output a correct key stream", function() { + let cipher = new Chacha20(key, nonce2); + let expected = [34, 79, 81, 243, 64, 27, 217, 225, 47, 222, 39, 111, 184, 99, 29, 237, 140, 19, 31, 130, 61, 44, 6, 226, 126, 79, 202, 236, 158, 243, 207, 120, 138, 59, 10, 163, 114, 96, 10, 146, 181, 121, 116, 205, 237, 43, 147, 52, 121, 76, 186, 64, 198, 62, 52, 205, 234, 33, 44, 76, 240, 125, 65, 183, 105, 166, 116, 159, 63, 99, 15, 65, 34, 202, 254, 40, 236, 77, 196, 126, 38, 212, 52, 109, 112, 185, 140, 115, 243, 233, 197, 58, 196, 12, 89, 69, 57, 139, 110, 218, 26, 131, 44, 137, 193, 103, 234, 205, 144, 29, 126, 43, 243, 99]; + expected.forEach(b => { expect(cipher.getByte()).toEqual(b); }); }); - + }); - - describe("encrypt",function(){ - it("should correctly encrypt an example text",function(){ - let ciphertext=[110,46,53,154,37,104,249,128,65,186,7,40,221,13,105,129,233,126,122,236,29, 67,96,194,10,39,175,204,253,159,174,11,249,27,101,197,82,71,51,171,143,89,61,171,205,98,179,87,22, 57,214,36,230,81,82,171,143,83,12,53,159,8,97,216,7,202,13,191,80,13,106,97,86,163,142,8,138,34, 182,94,82,188,81,77,22,204,248,6,129,140,233,26,183,121,55,54,90,249,11,191,116,163,91,230,180,11, 142,237,242,120,94,66,135,77]; - expect(encrypt(str2utf8(plaintext),key,nonce2)).toEqual([nonce2,ciphertext]); + + describe("encrypt", function() { + it("should correctly encrypt an example text", function() { + let ciphertext = [110, 46, 53, 154, 37, 104, 249, 128, 65, 186, 7, 40, 221, 13, 105, 129, 233, 126, 122, 236, 29, 67, 96, 194, 10, 39, 175, 204, 253, 159, 174, 11, 249, 27, 101, 197, 82, 71, 51, 171, 143, 89, 61, 171, 205, 98, 179, 87, 22, 57, 214, 36, 230, 81, 82, 171, 143, 83, 12, 53, 159, 8, 97, 216, 7, 202, 13, 191, 80, 13, 106, 97, 86, 163, 142, 8, 138, 34, 182, 94, 82, 188, 81, 77, 22, 204, 248, 6, 129, 140, 233, 26, 183, 121, 55, 54, 90, 249, 11, 191, 116, 163, 91, 230, 180, 11, 142, 237, 242, 120, 94, 66, 135, 77]; + expect(encrypt(str2utf8(plaintext), key, nonce2)).toEqual([nonce2, ciphertext]); }); }); - - describe("decrypt",function(){ - it("should be able to decrypt a Chacha20 encrypted text",function(){ - let text=str2utf8(plaintext); - let key=[]; - for(let i=0;i<16;i++){key.push(Math.floor(Math.random()*256));} - let [nonce,ciphertext]=encrypt(text,key); - expect(decrypt(ciphertext,key,nonce)).toEqual(text); + + describe("decrypt", function() { + it("should be able to decrypt a Chacha20 encrypted text", function() { + let text = str2utf8(plaintext); + let key = []; + for (let i = 0; i < 16; i++) {key.push(Math.floor(Math.random() * 256));} + let [nonce, ciphertext] = encrypt(text, key); + expect(decrypt(ciphertext, key, nonce)).toEqual(text); }); }); - + }); diff --git a/spec/test/pbkdf2Spec.js b/spec/test/pbkdf2Spec.js --- a/spec/test/pbkdf2Spec.js +++ b/spec/test/pbkdf2Spec.js @@ -1,17 +1,17 @@ /* global expect */ -describe("pbkdf2",function(){ - let cryptoJS=require("../../dist/main.js"); - let pbkdf2=cryptoJS.pbkdf2; - +describe("pbkdf2", function() { + let cryptoJS = require("../../dist/main.js"); + let pbkdf2 = cryptoJS.pbkdf2; + // it is difficult to find an independent implementation or an example of PBKDF2-BLAKE2s with no HMAC. // so we test against our own values to test at least against bugs introduced later - it("should return a correct test key",function(){ - expect(pbkdf2([1,2,3],[4,5,6,7,8,9,10,11],1024,32)).toEqual([2,3,212,35,227,5,152,88,48,219,42, 238,177,51,111,73,233,136,19,10,10,108,89,230,77,23,104,152,86,23,255,14]); - - let longPass="1234567890=10=567890=20=567890=30=56".split("").map(c=>c.charCodeAt(0)); - let salt="1234567890_10_56".split("").map(c=>c.charCodeAt(0)); - expect(pbkdf2(longPass,salt,1337,48)).toEqual([244,203,226,107,154,99,73,33,255,175,82,97,85, 137,201,104,158,129,12,200,193,48,71,149,117,184,161,189,247,120,62,9,20,30,109,140,190,224,78, 217,133,51,168,25,25,52,247,40]); + it("should return a correct test key", function() { + expect(pbkdf2([1, 2, 3], [4, 5, 6, 7, 8, 9, 10, 11], 1024, 32)).toEqual([2, 3, 212, 35, 227, 5, 152, 88, 48, 219, 42, 238, 177, 51, 111, 73, 233, 136, 19, 10, 10, 108, 89, 230, 77, 23, 104, 152, 86, 23, 255, 14]); + + let longPass = "1234567890=10=567890=20=567890=30=56".split("").map(c => c.charCodeAt(0)); + let salt = "1234567890_10_56".split("").map(c => c.charCodeAt(0)); + expect(pbkdf2(longPass, salt, 1337, 48)).toEqual([244, 203, 226, 107, 154, 99, 73, 33, 255, 175, 82, 97, 85, 137, 201, 104, 158, 129, 12, 200, 193, 48, 71, 149, 117, 184, 161, 189, 247, 120, 62, 9, 20, 30, 109, 140, 190, 224, 78, 217, 133, 51, 168, 25, 25, 52, 247, 40]); }); - + }); diff --git a/spec/test/utilSpec.js b/spec/test/utilSpec.js --- a/spec/test/utilSpec.js +++ b/spec/test/utilSpec.js @@ -1,59 +1,59 @@ /* global expect */ -describe("Util",function(){ - let cryptoJS=require("../../dist/main.js"); - let util=cryptoJS.util; - let utf=[ // https://tools.ietf.org/html/rfc3629#page-8 - ["",[]], - ["abc",[97,98,99]], - ["å",[195,165]], - ["🚅",[240,159,154,133]], - ["žír",[0xc5,0xbe,0xc3,0xad,114]], - ["A\u2262\u0391.",[0x41,0xE2,0x89,0xA2,0xCE,0x91,0x2E]], - ["\uD55C\uAD6D\uC5B4",[0xED,0x95,0x9C,0xEA,0xB5,0xAD,0xEC,0x96,0xB4]], - ["\ud84c\udfb4",[0xf0,0xa3,0x8e,0xb4]] +describe("Util", function() { + let cryptoJS = require("../../dist/main.js"); + let util = cryptoJS.util; + let utf = [ // https://tools.ietf.org/html/rfc3629#page-8 + ["", []], + ["abc", [97, 98, 99]], + ["å", [195, 165]], + ["🚅", [240, 159, 154, 133]], + ["žír", [0xc5, 0xbe, 0xc3, 0xad, 114]], + ["A\u2262\u0391.", [0x41, 0xE2, 0x89, 0xA2, 0xCE, 0x91, 0x2E]], + ["\uD55C\uAD6D\uC5B4", [0xED, 0x95, 0x9C, 0xEA, 0xB5, 0xAD, 0xEC, 0x96, 0xB4]], + ["\ud84c\udfb4", [0xf0, 0xa3, 0x8e, 0xb4]] ]; - let base64=[ - [[],""], - [[102],"Zg=="], - [[102,111],"Zm8="], - [[102,111,111],"Zm9v"], - [[102,111,111,98],"Zm9vYg=="], - [[102,111,111,98,97],"Zm9vYmE="], - [[102,111,111,98,97,114],"Zm9vYmFy"] + let base64 = [ + [[], ""], + [[102], "Zg=="], + [[102, 111], "Zm8="], + [[102, 111, 111], "Zm9v"], + [[102, 111, 111, 98], "Zm9vYg=="], + [[102, 111, 111, 98, 97], "Zm9vYmE="], + [[102, 111, 111, 98, 97, 114], "Zm9vYmFy"] ]; - - describe("bytes2int32s",function(){ - it("should pack bytes into 32b integers",function(){ + + describe("bytes2int32s", function() { + it("should pack bytes into 32b integers", function() { expect(util.bytes2int32s([])).toEqual([]); expect(util.bytes2int32s([0])).toEqual([0]); expect(util.bytes2int32s([1])).toEqual([1]); - expect(util.bytes2int32s([0x12,0x34,0x56,0x78,0x9a])).toEqual([0x78563412,0x9a]); + expect(util.bytes2int32s([0x12, 0x34, 0x56, 0x78, 0x9a])).toEqual([0x78563412, 0x9a]); }); }); - - describe("str2utf8",function(){ - it("should encode a String into bytes in UTF-8",function(){ - utf.forEach(couple=>expect(util.str2utf8(couple[0])).toEqual(couple[1])); + + describe("str2utf8", function() { + it("should encode a String into bytes in UTF-8", function() { + utf.forEach(couple => expect(util.str2utf8(couple[0])).toEqual(couple[1])); }); }); - - describe("utf82str",function(){ - it("should decode a String from UTF-8 bytes",function(){ - utf.forEach(couple=>expect(util.utf82str(couple[1])).toEqual(couple[0])); + + describe("utf82str", function() { + it("should decode a String from UTF-8 bytes", function() { + utf.forEach(couple => expect(util.utf82str(couple[1])).toEqual(couple[0])); }); }); - - - describe("bytes2base64",function(){ - it("should correctly encode bytes into base64",function(){ - base64.forEach(couple=>expect(util.bytes2base64(couple[0])).toEqual(couple[1])); + + + describe("bytes2base64", function() { + it("should correctly encode bytes into base64", function() { + base64.forEach(couple => expect(util.bytes2base64(couple[0])).toEqual(couple[1])); }); }); - - describe("base642bytes",function(){ - it("should correctly decode bytes from base64",function(){ - base64.forEach(couple=>expect(util.base642bytes(couple[1])).toEqual(couple[0])); + + describe("base642bytes", function() { + it("should correctly decode bytes from base64", function() { + base64.forEach(couple => expect(util.base642bytes(couple[1])).toEqual(couple[0])); }); }); }); diff --git a/src/blake.js b/src/blake.js --- a/src/blake.js +++ b/src/blake.js @@ -1,12 +1,12 @@ // https://tools.ietf.org/html/rfc7693 -import {MASK,int32s2bytes,bytes2int32s,zeroPad} from "./util.js"; +import {MASK, int32s2bytes, bytes2int32s, zeroPad} from "./util.js"; -const BLOCK_LEN=64; +const BLOCK_LEN = 64; -const IV=[0x6A09E667,0xBB67AE85,0x3C6EF372,0xA54FF53A,0x510E527F,0x9B05688C,0x1F83D9AB,0x5BE0CD19]; +const IV = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19]; -function rrot(x,shift){ - return ((x>>>shift)|(x<<(32-shift)))&MASK; +function rrot(x, shift) { + return ((x >>> shift) | (x << (32-shift))) & MASK; } /** @@ -14,81 +14,83 @@ function rrot(x,shift){ * @param {Array} key byte array, 0<=key.length<=32 * @returns {BLAKE2S} */ -export function BLAKE2S(outputLen=32,key=[]){ - this._buffer=[]; - this._dataLen=[0,0]; // low, high - this._outputLen=outputLen; - - this._state=IV.slice(); - this._state[0]^=0x01010000^(key.length<<8)^this._outputLen; - - if(key.length>0){this.update(zeroPad(key,BLOCK_LEN));} +export function BLAKE2S(outputLen = 32, key = []) { + this._buffer = []; + this._dataLen = [0, 0]; // low, high + this._outputLen = outputLen; + + this._state = IV.slice(); + this._state[0] ^= 0x01010000 ^ (key.length << 8) ^ this._outputLen; + + if (key.length > 0) {this.update(zeroPad(key, BLOCK_LEN));} } -BLAKE2S.prototype.update=function(data){ - for(let i=0;ix^v[i]^v[i+8]); + this._state = this._state.map((x, i) => x ^ v[i] ^ v[i+8]); }; -BLAKE2S.prototype._mix=function(arr,ia,ib,ic,id,x,y){ - let a=arr[ia], b=arr[ib], c=arr[ic], d=arr[id]; - a=(a+b+x)&MASK; d=rrot(d^a,16); - c=(c+d)&MASK; b=rrot(b^c,12); - a=(a+b+y)&MASK; d=rrot(d^a,8); - c=(c+d)&MASK; b=rrot(b^c,7); - arr[ia]=a; arr[ib]=b; arr[ic]=c; arr[id]=d; +BLAKE2S.prototype._mix = function (arr, ia, ib, ic, id, x, y) { + let a = arr[ia], b = arr[ib], c = arr[ic], d = arr[id]; + a = (a + b + x) & MASK; d = rrot(d ^ a, 16); + c = (c + d) & MASK; b = rrot(b ^ c, 12); + a = (a + b + y) & MASK; d = rrot(d ^ a, 8); + c = (c + d) & MASK; b = rrot(b ^ c, 7); + arr[ia] = a; arr[ib] = b; arr[ic] = c; arr[id] = d; }; -export function blake2s(data,outputLen=32,key=[]){ - let h=new BLAKE2S(outputLen,key); - for(let i=0;i>>(32-shift))&MASK; +function lrot(x, shift) { + return (x << shift | x >>> (32-shift)) & MASK; } /** @@ -10,80 +10,80 @@ function lrot(x,shift){ * @param {Array} key Array of bytes (integers: 0<=x<256). Short keys are padded to 32B, long keys are silently truncated. * @param {Array} nonce optional. If present, it must be an Array of bytes (integers: 0<=x<256). Short nonces are padded to 12B, long nonces are silently truncated. */ -export function Chacha20(key,nonce){ - const NONCE_LEN=12; - if(nonce===undefined){ - nonce=createRandomNonce(NONCE_LEN); +export function Chacha20(key, nonce) { + const NONCE_LEN = 12; + if (nonce === undefined) { + nonce = createRandomNonce(NONCE_LEN); } - this._nonce=zeroPad(nonce,NONCE_LEN); - nonce=bytes2int32s(this._nonce); - key=bytes2int32s(zeroPad(key,32)); - - this._state=[ - 0x61707865,0x3320646e,0x79622d32,0x6b206574, - key[0],key[1],key[2],key[3], - key[4],key[5],key[6],key[7], - 1,nonce[0],nonce[1],nonce[2] + this._nonce = zeroPad(nonce, NONCE_LEN); + nonce = bytes2int32s(this._nonce); + key = bytes2int32s(zeroPad(key, 32)); + + this._state = [ + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, + key[0], key[1], key[2], key[3], + key[4], key[5], key[6], key[7], + 1, nonce[0], nonce[1], nonce[2] ]; - this._buffer=[]; + this._buffer = []; } -Chacha20.prototype.setPos=function(pos){ - this._state[12]=pos; - this._buffer=[]; +Chacha20.prototype.setPos = function(pos) { + this._state[12] = pos; + this._buffer = []; } -Chacha20.prototype.getByte=function(){ - if(this._buffer.length==0){ - this._buffer=int32s2bytes(this._computeBlock()); +Chacha20.prototype.getByte = function() { + if (this._buffer.length == 0) { + this._buffer = int32s2bytes(this._computeBlock()); this._incrementPos(); } return this._buffer.shift(); }; -Chacha20.prototype.getNonce=function(){ +Chacha20.prototype.getNonce = function() { return this._nonce.concat(); }; -Chacha20.prototype._quarterRound=function(arr,ia,ib,ic,id){ - let a=arr[ia], b=arr[ib], c=arr[ic], d=arr[id]; - a=(a+b)&MASK; d=lrot(d^a,16); - c=(c+d)&MASK; b=lrot(b^c,12); - a=(a+b)&MASK; d=lrot(d^a,8); - c=(c+d)&MASK; b=lrot(b^c,7); - arr[ia]=a; arr[ib]=b; arr[ic]=c; arr[id]=d; +Chacha20.prototype._quarterRound = function(arr, ia, ib, ic, id) { + let a = arr[ia], b = arr[ib], c = arr[ic], d = arr[id]; + a = (a+b) & MASK; d = lrot(d^a, 16); + c = (c+d) & MASK; b = lrot(b^c, 12); + a = (a+b) & MASK; d = lrot(d^a, 8); + c = (c+d) & MASK; b = lrot(b^c, 7); + arr[ia] = a; arr[ib] = b; arr[ic] = c; arr[id] = d; }; -Chacha20.prototype._computeBlock=function(){ - let state=this._state.slice(); +Chacha20.prototype._computeBlock = function() { + let state = this._state.slice(); - for(let i=0;i<10;i++){ // 10 double rounds + for (let i = 0; i < 10; i++) { // 10 double rounds // column round - this._quarterRound(state,0,4,8,12); - this._quarterRound(state,1,5,9,13); - this._quarterRound(state,2,6,10,14); - this._quarterRound(state,3,7,11,15); + this._quarterRound(state, 0, 4, 8, 12); + this._quarterRound(state, 1, 5, 9, 13); + this._quarterRound(state, 2, 6, 10, 14); + this._quarterRound(state, 3, 7, 11, 15); // diagonal round - this._quarterRound(state,0,5,10,15); - this._quarterRound(state,1,6,11,12); - this._quarterRound(state,2,7,8,13); - this._quarterRound(state,3,4,9,14); + this._quarterRound(state, 0, 5, 10, 15); + this._quarterRound(state, 1, 6, 11, 12); + this._quarterRound(state, 2, 7, 8, 13); + this._quarterRound(state, 3, 4, 9, 14); } - return state.map((xi,i)=>(xi+this._state[i])&MASK); + return state.map((xi, i) => (xi + this._state[i]) & MASK); }; -Chacha20.prototype._incrementPos=function(){ - this._state[12]=(this._state[12]+1)&MASK; +Chacha20.prototype._incrementPos = function() { + this._state[12] = (this._state[12]+1) & MASK; }; -export function encrypt(data,key,nonce){ - let cipher=new Chacha20(key,nonce); - nonce=cipher.getNonce(); - return [nonce,data.map(b=>b^cipher.getByte())]; +export function encrypt(data, key, nonce) { + let cipher = new Chacha20(key, nonce); + nonce = cipher.getNonce(); + return [nonce, data.map(b => b ^ cipher.getByte())]; } -export function decrypt(ciphertext,key,nonce){ - let cipher=new Chacha20(key,nonce); - return ciphertext.map(b=>b^cipher.getByte()); +export function decrypt(ciphertext, key, nonce) { + let cipher = new Chacha20(key, nonce); + return ciphertext.map(b => b ^ cipher.getByte()); } diff --git a/src/main.js b/src/main.js --- a/src/main.js +++ b/src/main.js @@ -1,59 +1,59 @@ import * as util from "./util.js"; import {blake2s} from "./blake.js"; import {pbkdf2} from "./pbkdf2.js"; -import {Chacha20,encrypt as _encrypt,decrypt as _decrypt} from "./chacha.js"; +import {Chacha20, encrypt as _encrypt, decrypt as _decrypt} from "./chacha.js"; -const VERSION=1; -const MAX_ITERS=24; +const VERSION = 1; +const MAX_ITERS = 24; -function encrypt(s,password){ - let bs=util.str2utf8(s); - let pass=util.str2utf8(password); - let salt=util.createRandomNonce(12); - let [iters,key]=stretchKey(pass,salt); - let [_,ciphertext]=_encrypt(bs,key,salt); - let signature=blake2s([VERSION,iters].concat(salt,ciphertext),16,key); - let arr=[VERSION,iters].concat(signature,salt,ciphertext); +function encrypt(s, password) { + let bs = util.str2utf8(s); + let pass = util.str2utf8(password); + let salt = util.createRandomNonce(12); + let [iters, key] = stretchKey(pass, salt); + let [_, ciphertext] = _encrypt(bs, key, salt); + let signature = blake2s([VERSION, iters].concat(salt, ciphertext), 16, key); + let arr = [VERSION, iters].concat(signature, salt, ciphertext); return util.bytes2base64(arr); } -function decrypt(s,password){ - let pass=util.str2utf8(password); - let arr=util.base642bytes(s); - let version=arr[0]; - let iters=arr[1]; - let signature=arr.slice(2,18); - let salt=arr.slice(18,30); - let ciphertext=arr.slice(30); - - if(ciphertext.length==0){return 4;} - if(version>VERSION){return 2;} - if(iters>MAX_ITERS){return 3;} - let key=pbkdf2(pass,salt,1<b===check[i])){return 1;} - let plainbytes=_decrypt(ciphertext,key,salt); +function decrypt(s, password) { + let pass = util.str2utf8(password); + let arr = util.base642bytes(s); + let version = arr[0]; + let iters = arr[1]; + let signature = arr.slice(2, 18); + let salt = arr.slice(18, 30); + let ciphertext = arr.slice(30); + + if (ciphertext.length == 0) {return 4;} + if (version > VERSION) {return 2;} + if (iters > MAX_ITERS) {return 3;} + let key = pbkdf2(pass, salt, 1 << iters, 32); + let check = blake2s([version, iters].concat(salt, ciphertext), 16, key); + if (!signature.every((b, i) => b === check[i])) {return 1;} + let plainbytes = _decrypt(ciphertext, key, salt); return util.utf82str(plainbytes); } -function stretchKey(password,salt){ - let start=Date.now(); // ms - let i,key; - for(i=0;i<=MAX_ITERS;i++){ - key=pbkdf2(password,salt,1<=500){break;} +function stretchKey(password, salt) { + let start = Date.now(); // ms + let i, key; + for (i = 0; i <= MAX_ITERS; i++) { + key = pbkdf2(password, salt, 1<= 500) {break;} } - return [i,key]; + return [i, key]; } -export default {util,blake2s,pbkdf2,Chacha20,encrypt,decrypt}; +export default {util, blake2s, pbkdf2, Chacha20, encrypt, decrypt}; // export for tests running on Node -if(typeof module!=='undefined'&&module.hasOwnProperty('exports')){ - module.exports.util=util; - module.exports.blake2s=blake2s; - module.exports.pbkdf2=pbkdf2; - module.exports.Chacha20=Chacha20; - module.exports.encrypt=_encrypt; - module.exports.decrypt=_decrypt; +if (typeof module !== 'undefined' && module.hasOwnProperty('exports')) { + module.exports.util = util; + module.exports.blake2s = blake2s; + module.exports.pbkdf2 = pbkdf2; + module.exports.Chacha20 = Chacha20; + module.exports.encrypt = _encrypt; + module.exports.decrypt = _decrypt; } diff --git a/src/pbkdf2.js b/src/pbkdf2.js --- a/src/pbkdf2.js +++ b/src/pbkdf2.js @@ -2,27 +2,27 @@ import {int322bytesBE} from "./util.js"; import {blake2s} from "./blake.js"; -const HASH_LENGTH=32; +const HASH_LENGTH = 32; -export function pbkdf2(password,salt,iterationCount,outLength){ +export function pbkdf2(password, salt, iterationCount, outLength) { // max BLAKE2 key length is 32B - if(password.length>32){password=blake2s(password);} - - let blockCount=Math.ceil(outLength/HASH_LENGTH); - let result=[]; - - for(let i=0;i 32) {password = blake2s(password);} + + let blockCount = Math.ceil(outLength / HASH_LENGTH); + let result = []; + + for (let i = 0; i < blockCount; i++) { + let block = _computeBlock(password, salt, iterationCount, i+1); + result = result.concat(block); } - return result.slice(0,outLength); + return result.slice(0, outLength); } -function _computeBlock(password,salt,iterationCount,blockIndex){ - let u=blake2s(salt.concat(int322bytesBE(blockIndex)),HASH_LENGTH,password); - for(let i=1;ib^ui[j]); +function _computeBlock(password, salt, iterationCount, blockIndex) { + let u = blake2s(salt.concat(int322bytesBE(blockIndex)), HASH_LENGTH, password); + for (let i = 1; i < iterationCount; i++) { + let ui = blake2s(u, HASH_LENGTH, password); + u = u.map((b, j) => b ^ ui[j]); } return u; } diff --git a/src/util.js b/src/util.js --- a/src/util.js +++ b/src/util.js @@ -1,17 +1,17 @@ -export const MASK=0xffffffff; +export const MASK = 0xffffffff; -export function zeroPad(arr,length){ - return arr.concat((new Array(length)).fill(0)).slice(0,length); +export function zeroPad(arr, length) { + return arr.concat((new Array(length)).fill(0)).slice(0, length); } -export function bytes2int32(arr){ - return arr.reduce((acc,b,i)=>acc|b<<(i*8)); +export function bytes2int32(arr) { + return arr.reduce((acc, b, i) => acc | b << (i*8)); } -export function bytes2int32s(arr){ - let res=[]; - for(let i=0;i>>=8; +export function int322bytes(x) { + let res = []; + for (let i = 0; i < 4; i++) { + res.push(x & 0xff); + x >>>= 8; } return res; } @@ -31,116 +31,116 @@ export function int322bytes(x){ /** * Converts a 32 bit integer into 4 bytes in big endian order. */ -export function int322bytesBE(x){ - let res=int322bytes(x); +export function int322bytesBE(x) { + let res = int322bytes(x); res.reverse(); return res; } -export function int32s2bytes(arr){ - return arr.map(int322bytes).reduce((acc,bytes)=>acc.concat(bytes)); +export function int32s2bytes(arr) { + return arr.map(int322bytes).reduce((acc, bytes) => acc.concat(bytes)); } -export function bytes2hex(arr){ - return arr.map(x=>x.toString(16).padStart(2,"0")).join(""); +export function bytes2hex(arr) { + return arr.map(x => x.toString(16).padStart(2, "0")).join(""); } -export function str2utf8(s){ - let res=[]; - let c=s.codePointAt(0); - for(let i=0; c!==undefined; i++,c=s.codePointAt(i)){ - if(c<0x80){res.push(c);} - else if(c<0x800){ - res.push(0b11000000|(c>>>6)); - res.push(0b10000000|(c&0b111111)); +export function str2utf8(s) { + let res = []; + let c = s.codePointAt(0); + for (let i=0; c!==undefined; i++, c=s.codePointAt(i)) { + if (c < 0x80) {res.push(c);} + else if (c < 0x800) { + res.push(0b11000000 | (c >>> 6)); + res.push(0b10000000 | (c & 0b111111)); } - else if(c<0x10000){ - res.push(0b11100000|(c>>>12)); - res.push(0b10000000|((c>>>6)&0b111111)); - res.push(0b10000000|(c&0b111111)); + else if (c < 0x10000) { + res.push(0b11100000 | (c >>> 12)); + res.push(0b10000000 | ((c >>> 6) & 0b111111)); + res.push(0b10000000 | (c & 0b111111)); } - else{ - res.push(0b11110000|(c>>>18)); - res.push(0b10000000|((c>>>12)&0b111111)); - res.push(0b10000000|((c>>>6)&0b111111)); - res.push(0b10000000|(c&0b111111)); + else { + res.push(0b11110000 | (c >>> 18)); + res.push(0b10000000 | ((c >>> 12) & 0b111111)); + res.push(0b10000000 | ((c >>> 6) & 0b111111)); + res.push(0b10000000 | (c & 0b111111)); } - if(c>0xffff){i++;} // skip surrogate + if (c > 0xffff) {i++;} // skip surrogate } return res; } -export function utf82str(arr){ - let res=[]; - for(let i=0;iString.fromCodePoint(x)).join(""); + return res.map(x => String.fromCodePoint(x)).join(""); } -const mapping="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""); -const remapping=new Array(128); -mapping.forEach((c,i)=>{remapping[c.charCodeAt(0)]=i;}); +const mapping = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""); +const remapping = new Array(128); +mapping.forEach((c, i) => {remapping[c.charCodeAt(0)] = i;}); + +export function bytes2base64(byteArr) { + let arr = byteArr.concat(); + let out = []; + let rem = (3 - arr.length%3) % 3; + for (let i = 0; i < rem; i++) {arr.push(0);} // pad array to a multiple of 3 -export function bytes2base64(byteArr){ - let arr=byteArr.concat(); - let out=[]; - let rem=(3-arr.length%3)%3; - for(let i=0;i>>2&63]); - out.push(mapping[((arr[i]&3)<<4)+(arr[i+1]>>>4&15)]); - out.push(mapping[((arr[i+1]&15)<<2)+(arr[i+2]>>>6&3)]); - out.push(mapping[arr[i+2]&63]); + for (let i = 0; i < arr.length; i += 3) { // encode 3 bytes into 4 characters + out.push(mapping[arr[i]>>>2 & 63]); + out.push(mapping[((arr[i]&3) << 4) + (arr[i+1]>>>4 & 15)]); + out.push(mapping[((arr[i+1]&15) << 2) + (arr[i+2]>>>6 & 3)]); + out.push(mapping[arr[i+2] & 63]); } - - for(let i=0;i>4&3)); - out.push(((b2&15)<<4)+(b3>>2&15)); - out.push(((b3&3)<<6)+b4); +export function base642bytes(str) { + let out = []; + + for (let i = 0; i < str.length; i += 4) { + let b1 = remapping[str.charCodeAt(i)]; + let b2 = remapping[str.charCodeAt(i+1)]; + let b3 = remapping[str.charCodeAt(i+2)]; + let b4 = remapping[str.charCodeAt(i+3)]; + + out.push((b1<<2) + (b2>>4 & 3)); + out.push(((b2&15) << 4) + (b3>>2 & 15)); + out.push(((b3&3) << 6) + b4); } - - for(let i=1; i<3&&str[str.length-i]=="="; i++){out.pop();} + + for (let i=1; i<3 && str[str.length-i]=="="; i++) {out.pop();} return out; } -export function createRandomNonce(n){ - let nonce=new Uint8Array(n); +export function createRandomNonce(n) { + let nonce = new Uint8Array(n); window.crypto.getRandomValues(nonce); return Array.from(nonce); }