diff --git a/spec/test/chachaSpec.js b/spec/test/chachaSpec.js --- a/spec/test/chachaSpec.js +++ b/spec/test/chachaSpec.js @@ -34,7 +34,7 @@ describe("Chacha",function(){ 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.concat(ciphertext)); + expect(encrypt(str2utf8(plaintext),key,nonce2)).toEqual([nonce2,ciphertext]); }); }); @@ -45,7 +45,8 @@ describe("Chacha",function(){ for(let i=0;i<16;i++){key.push(Math.floor(Math.random()*256));} let nonce=[]; for(let i=0;i<8;i++){nonce.push(Math.floor(Math.random()*256));} - expect(decrypt(encrypt(text,key,nonce),key)).toEqual(text); + let [_,ciphertext]=encrypt(text,key,nonce); + expect(decrypt(ciphertext,key,nonce)).toEqual(text); }); }); diff --git a/src/chacha.js b/src/chacha.js --- a/src/chacha.js +++ b/src/chacha.js @@ -1,26 +1,21 @@ // https://tools.ietf.org/html/rfc7539 -import {MASK,int32s2bytes,bytes2int32s,zeroPad} from "./util.js"; +import {MASK,int32s2bytes,bytes2int32s,zeroPad,createRandomNonce} from "./util.js"; function lrot(x,shift){ return (x<>>(32-shift))&MASK; } -export function createNonce(){ - let nonce=new Uint8Array(12); - window.crypto.getRandomValues(nonce); - return Array.from(nonce); -} - /** * A Chacha20 cipher class. * @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=createNonce(); + nonce=createRandomNonce(NONCE_LEN); } - this._nonce=zeroPad(nonce,12); + this._nonce=zeroPad(nonce,NONCE_LEN); nonce=bytes2int32s(this._nonce); key=bytes2int32s(zeroPad(key,32)); @@ -85,12 +80,10 @@ Chacha20.prototype._incrementPos=functio export function encrypt(data,key,nonce){ let cipher=new Chacha20(key,nonce); nonce=cipher.getNonce(); - return nonce.concat(data.map(b=>b^cipher.getByte())); + return [nonce,data.map(b=>b^cipher.getByte())]; } -export function decrypt(data,key){ - let nonce=data.slice(0,12); - let ciphertext=data.slice(12); +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,17 +1,17 @@ import * as util from "./util.js"; import {blake2s} from "./blake.js"; import {pbkdf2} from "./pbkdf2.js"; -import {createNonce,Chacha20,encrypt as _encrypt,decrypt as _decrypt} from "./chacha.js"; +import {Chacha20,encrypt as _encrypt,decrypt as _decrypt} from "./chacha.js"; const VERSION=1; function encrypt(s,password){ let bs=util.str2utf8(s); let pass=util.str2utf8(password); - let salt=createNonce(); + let salt=util.createRandomNonce(12); let [iters,key]=stretchKey(pass,salt); - let noncedCiphertext=_encrypt(bs,key,salt); - let payload=[iters].concat(noncedCiphertext); + let [_,ciphertext]=_encrypt(bs,key,salt); + let payload=[iters].concat(salt,ciphertext); let signature=blake2s([VERSION].concat(payload),16,pass); let arr=[VERSION].concat(signature,payload); return util.bytes2base64(arr); @@ -24,12 +24,12 @@ function decrypt(s,password){ let signature=arr.slice(1,17); let iters=arr[17]; let salt=arr.slice(18,30); - let noncedCiphertext=arr.slice(18); - let check=blake2s([version].concat([iters],noncedCiphertext),16,pass); + let ciphertext=arr.slice(30); + let check=blake2s([version,iters].concat(salt,ciphertext),16,pass); if(!signature.every((b,i)=>b===check[i])){return false;} if(version>VERSION){return false;} let key=pbkdf2(pass,salt,1<