Changeset - 7eec13103f80
[Not reviewed]
default
0 3 0
Laman - 5 years ago 2019-07-03 16:49:43

encrypt/decrypt wrappers
3 files changed with 37 insertions and 5 deletions:
0 comments (0 inline, 0 general)
dist/crypto.html
Show inline comments
 
<!DOCTYPE html>
 
<html>
 
<head>
 
	<meta charset="utf-8" />
 
	<title>Chacha20</title>
 
	<script type="text/javascript" src="main.js"></script>
 
</head>
 
<body>
 
	<form name="chacha">
 
		<textarea name="input"></textarea>
 
		<input type="password" name="password" />
 
		<button type="button" name="encrypt">zašifrovat</button>
 
		<button type="button" name="decrypt">dešifrovat</button>
 
		<textarea name="output"></textarea>
 
	</form>
 
	<script type="text/javascript">
 
		console.log(cryptoJS.util.str2utf8("abc"));
 
		></i>
 
	</script>
 
</body>
 
</html>
src/chacha.js
Show inline comments
 
// https://tools.ietf.org/html/rfc7539
 
import {MASK,int32s2bytes,bytes2int32s,zeroPad} from "./util.js";
 

	
 
function lrot(x,shift){
 
	return (x<<shift|x>>>(32-shift))&MASK;
 
}
 

	
 
function createNonce(){
 
	let nonce=new Uint8Array(12);
 
	window.crypto.getRandomValues(nonce);
 
	return 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){
 
	if(nonce===undefined){
 
		nonce=createNonce();
 
	}
 
	this._nonce=zeroPad(nonce,12);
 
	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=[];
 
}
 

	
 
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());
 
		this._incrementPos();
 
	}
 
	return this._buffer.shift();
 
};
 

	
 
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._computeBlock=function(){
 
	let state=this._state.slice();
 

	
 
	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);
 
		// 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);
 
	}
 

	
 
	return state.map((xi,i)=>(xi+this._state[i])&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.concat(data.map(b=>b^cipher.getByte()));
 
}
 

	
 
export function decrypt(data,key){
 
	let nonce=data.slice(0,12);
 
	let ciphertext=data.slice(12);
 
	let cipher=new Chacha20(key,nonce);
 
	return ciphertext.map(b=>b^cipher.getByte());
 
}
src/main.js
Show inline comments
 
import * as util from "./util.js";
 
import {blake2s} from "./blake.js";
 
import {pbkdf2} from "./pbkdf2.js";
 
import {Chacha20,encrypt,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 noncedCiphertext=_encrypt(bs,pass);
 
	let signature=blake2s([VERSION].concat(noncedCiphertext),16,pass);
 
	let arr=[VERSION].concat(signature,noncedCiphertext);
 
	return util.bytes2base64(arr);
 
}
 

	
 
function decrypt(s,password){
 
	let pass=util.str2utf8(password);
 
	let arr=util.base642bytes(s);
 
	let version=arr[0];
 
	let signature=arr.slice(1,17);
 
	let noncedCiphertext=arr.slice(17);
 
	let check=blake2s([version].concat(noncedCiphertext),16,pass);
 
	if(!signature.every((b,i)=>b===check[i])){return false;}
 
	if(version>VERSION){return false;}
 
	let plainbytes=_decrypt(noncedCiphertext,pass);
 
	return util.utf82str(plainbytes);
 
}
 

	
 
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;
 
	module.exports.encrypt=_encrypt;
 
	module.exports.decrypt=_decrypt;
 
}
0 comments (0 inline, 0 general)