diff --git a/src/chacha.js b/src/chacha.js --- a/src/chacha.js +++ b/src/chacha.js @@ -1,8 +1,8 @@ // https://tools.ietf.org/html/rfc7539 -import {MASK,int32s2bytes,bytes2int32s,zeroPad,createRandomNonce} from "./util.js"; +import {MASK, int32s2bytes, bytes2int32s, zeroPad, createRandomNonce} from "./util.js"; -function lrot(x,shift){ - return (x<>>(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()); }