ilmk/crypto.js

354 lines
12 KiB
JavaScript
Raw Normal View History

2021-10-22 18:12:48 +02:00
var node = false;
2021-10-22 19:51:31 +02:00
var crypto;
2021-10-22 18:12:48 +02:00
2021-10-22 19:51:31 +02:00
if (typeof process != "undefined") {
crypto = await import('crypto');
2021-10-22 18:12:48 +02:00
if(typeof crypto.webcrypto != "undefined"){
crypto = crypto.webcrypto;
2021-10-22 22:57:31 +02:00
console.log("ilmk: using subtle");
2021-10-22 18:12:48 +02:00
}else{
node = true;
2021-10-22 22:57:31 +02:00
console.log("ilmk: using node crypto");
2021-10-22 18:12:48 +02:00
}
2021-10-22 19:51:31 +02:00
}else{
2021-10-22 18:12:48 +02:00
crypto = window.crypto;
2021-10-22 19:51:31 +02:00
}
2021-10-22 18:12:48 +02:00
var asciiList = [];
for (var i = 0; i < 256; i++) {
asciiList[i]=String.fromCharCode(i);
}
2021-10-22 19:51:31 +02:00
2021-10-22 18:12:48 +02:00
const C = {
sha256: async (msgBuffer) => {
return node ? new Uint8Array(crypto.createHash("sha256").update(msgBuffer).digest()) :
new Uint8Array(await crypto.subtle.digest('SHA-256', msgBuffer));
},
random: async (length) => {
return node ? crypto.randomFillSync(new Uint8Array(length)) :
crypto.getRandomValues(new Uint8Array(length));
},
randomKey: async (length) => {
return C.decode(await C.random(length));
},
encode : (data)=>{
2021-10-22 19:51:31 +02:00
console.log(data);
2021-10-22 18:12:48 +02:00
if(node){
return new Uint8Array(Buffer.from(data,"binary"));
}else{
var out = new Uint8Array(data.length);
for (var i = 0; i < data.length; i++) {
out[i] = data.charCodeAt(i);
}
return out;//new Uint8Array(data.split("").map(d => d.charCodeAt(0)));
}
},
decode : (data)=>{
if(node){
return Buffer.from(data).toString("binary");
}else{
var out = [];
var i = 0;
var limit = i - (i%16);
while (i < limit) {
out.push(asciiList[data[i]]+asciiList[data[i+1]]+asciiList[data[i+2]]+asciiList[data[i+3]]+asciiList[data[i+4]]+asciiList[data[i+5]]+asciiList[data[i+6]]+asciiList[data[i+7]]+asciiList[data[i+8]]+asciiList[data[i+9]]+asciiList[data[i+10]]+asciiList[data[i+11]]+asciiList[data[i+12]]+asciiList[data[i+13]]+asciiList[data[i+14]]+asciiList[data[i+15]]);
i+=16;
}
while (i < data.length) {
out.push(asciiList[data[i]]);
i++;
}
return out.join("");
}
},
atob: (data)=>node?Buffer.from(data,"base64").toString("binary"):atob(data), //base to ascii
btoa: (data)=>node?Buffer.from(data,"binary").toString("base64"):btoa(data), //ascii to base
pad: (data) => {
var length = 16 - (data.length % 16);
var mergedArray = new Uint8Array(data.length+length);
mergedArray.set(data);
mergedArray.set(new Uint8Array(length).fill(length), data.length);
return mergedArray;
},
unpad: (data) => {
return data.slice(0, -data[data.length - 1]);
},
AES:{
bytes_to_key: async (bytes, salt, output = 48) => {
var data = new Uint8Array([...bytes, ...salt]);
var key = new Uint8Array();
var final_key = new Uint8Array();
while (final_key.length < output) {
key = await C.sha256(new Uint8Array([...key, ...data]));
final_key = new Uint8Array([...final_key, ...key]);
}
return final_key.slice(0, output);
},
encryptData: async (rawdata, rawkey) => {
var salt = await C.random(8);
var key_iv = await C.AES.bytes_to_key(rawkey, salt, 32 + 16);
var key = key_iv.slice(0, 32);
var iv = key_iv.slice(32);
var aes;
if(node){
let cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(C.pad(rawdata));
aes = new Uint8Array(Buffer.concat([encrypted, cipher.final()]));
}else{
aes = new Uint8Array(await crypto.subtle.encrypt({name: "AES-CBC",iv},await crypto.subtle.importKey("raw",key,"AES-CBC",true,["encrypt", "decrypt"]),C.pad(rawdata)));
}
var mergedArray = new Uint8Array(aes.length+16);
mergedArray.set([83, 97, 108, 116, 95, 50, 53, 54]);
mergedArray.set(salt,8);
mergedArray.set(aes,16);
return mergedArray;
},
encrypt: async (message, passphrase) => {
var rawdata = C.encode(message);
var rawkey = C.encode(passphrase);
var step = await C.AES.encryptData(rawdata,rawkey);
return C.decode(step);
},
decryptData: async (rawdata, rawkey) => {
var check = [83, 97, 108, 116, 95, 50, 53, 54];
for (var i = 0; i < check.length; i++) {
if(check[i]!=rawdata[i])return null;
}
var salt = rawdata.slice(8,16);
var key_iv = await C.AES.bytes_to_key(rawkey, salt, 32 + 16);
var key = key_iv.slice(0, 32);
var iv = key_iv.slice(32);
if(node){
let cipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key), iv);
let encrypted = cipher.update(rawdata.slice(16));
return C.unpad(new Uint8Array(Buffer.concat([encrypted, cipher.final()])));
}else{
return C.unpad(
new Uint8Array (await crypto.subtle.decrypt(
{name: "AES-CBC",iv},
await crypto.subtle.importKey("raw",key,"AES-CBC",true,["encrypt", "decrypt"]),
rawdata.slice(16)
))
);
}
},
decrypt: async (message, passphrase) => {
var rawdata = C.encode(message);
var rawkey = C.encode(passphrase);
try {
var step = await C.AES.decryptData(rawdata,rawkey);
return C.decode(step);
} catch (e) {
return null;
}
},
},
RSA:{
generateEncryptionKey: async () =>{
if(node){
var { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {modulusLength: 4096,publicExponent:0x010001,hashAlgorithm:"sha256"});
return {publicKey: C.decode(new Uint8Array( publicKey.export({type:"spki", format:"der"}))),
privateKey:C.decode(new Uint8Array(privateKey.export({type:"pkcs8",format:"der"})))};
}else{
var keyPair = await crypto.subtle.generateKey(
{name: "RSA-OAEP",modulusLength: 4096,publicExponent: new Uint8Array([1, 0, 1]),hash: "SHA-256"},
true,
["encrypt", "decrypt"]
);
return {privateKey:C.decode(new Uint8Array(await crypto.subtle.exportKey("pkcs8",keyPair.privateKey))),
publicKey: C.decode(new Uint8Array(await crypto.subtle.exportKey("spki", keyPair.publicKey )))};
}
},
generateSigningKey: async () =>{
if(node){
var { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {modulusLength: 4096,publicExponent:0x010001,hashAlgorithm:"sha256"});
return {publicKey: C.decode(new Uint8Array( publicKey.export({type:"spki", format:"der"}))),
privateKey:C.decode(new Uint8Array(privateKey.export({type:"pkcs8",format:"der"})))};
}else{
var keyPair = await crypto.subtle.generateKey(
{name: "RSA-PSS",modulusLength: 4096,publicExponent: new Uint8Array([1, 0, 1]),hash: "SHA-256"},
true,
["sign", "verify"]
);
return {privateKey:C.decode(new Uint8Array(await crypto.subtle.exportKey("pkcs8",keyPair.privateKey))),
publicKey: C.decode(new Uint8Array(await crypto.subtle.exportKey("spki", keyPair.publicKey )))};
}
},
encrypt: async (message, publicKey) => {
var rawdata = C.encode(message);
var step;
if(node){
var rawkey = crypto.createPublicKey({key:C.encode(publicKey),format:"der",type:"spki"});
//console.log("d",rawkey);
step = crypto.publicEncrypt({
key: rawkey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
}, Buffer.from(rawdata));
}else{
var rawkey = await crypto.subtle.importKey("spki",C.encode(publicKey),{name: "RSA-OAEP",hash: "SHA-256"},true,["encrypt"]);
step = await crypto.subtle.encrypt({name: "RSA-OAEP"},rawkey,rawdata);
}
return C.decode(new Uint8Array(step));
},
decrypt: async (message, privateKey) => {
var rawdata = C.encode(message);
if(node){
var rawkey = crypto.createPrivateKey({key:C.encode(privateKey),format:"der",type:"pkcs8"});
try {
var step = crypto.privateDecrypt({
key: rawkey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
}, Buffer.from(rawdata));
return C.decode(new Uint8Array(step));
} catch (e) {
return null;
}
}else{
var rawkey = await crypto.subtle.importKey("pkcs8",C.encode(privateKey),{name: "RSA-OAEP",hash: "SHA-256"},true,["decrypt"]);
try {
var step = await crypto.subtle.decrypt({name: "RSA-OAEP"},rawkey,rawdata);
return C.decode(new Uint8Array(step));
} catch (e) {
console.log(e);
return null;
}
}
},
sign:async (message, privateKey) => {
var rawdata = C.encode(message);
var step;
if(node){
var rawkey = crypto.createPrivateKey({key:C.encode(privateKey),format:"der",type:"pkcs8"});
step = crypto.sign("sha256", Buffer.from(rawdata), {
key: rawkey,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength:32
});
}else{
var rawkey = await crypto.subtle.importKey("pkcs8",C.encode(privateKey),{name: "RSA-PSS",hash: "SHA-256"},true,["sign"]);
step = await crypto.subtle.sign(
{
name: "RSA-PSS",
saltLength: 32,
},
rawkey,
rawdata
);
}
return C.decode(new Uint8Array(step));
},
verify:async (message, signature, publicKey) => {
var rawdata = C.encode(message);
var rawsig = C.encode(signature);
var step;
if(node){
var rawkey = crypto.createPublicKey({key:C.encode(publicKey),format:"der",type:"spki"});
step = crypto.verify(
"sha256",
Buffer.from(rawdata),
{
key: rawkey,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength:32
},
Buffer.from(rawsig)
);
}else{
var rawkey = await crypto.subtle.importKey("spki",C.encode(publicKey),{name: "RSA-PSS",hash: "SHA-256"},true,["verify"]);
step = await crypto.subtle.verify(
{
name: "RSA-PSS",
saltLength: 32,
},
rawkey,
rawsig,
rawdata
);
}
return step;// ? "valid" : "invalid";
}
},
communicate:{
encrypt: async (message,publicKey)=>{
var randKey = await C.randomKey(32);
var send = {
d: await C.AES.encrypt(message,randKey),
k: await C.RSA.encrypt(randKey,publicKey)
};
2021-10-22 19:51:31 +02:00
return {send:JSON.stringify(send),key:randKey};
2021-10-22 18:12:48 +02:00
},
decrypt: async (data,privateKey)=>{
try {
let {d,k} = JSON.parse(data);
if(typeof d != "string" || typeof k != "string")throw "invalid Data";
var randKey = await C.RSA.decrypt(k,privateKey);
if(randKey == null) throw "invalid async Key";
var out = await C.AES.decrypt(d,randKey);
if(out == null) throw "invalid sync Key";
2021-10-22 19:51:31 +02:00
return {data:out,key:randKey};
2021-10-22 18:12:48 +02:00
} catch (e) {
return null;
}
},
//Message to send, reomte publicKey, own privateKey, my id so that the other one knows how i am (can be empty)
encryptSign: async (message,publicKey,privateKey,id)=>{
return await C.communicate.encrypt(JSON.stringify({
d:message,
i:id,
s:await C.RSA.sign(message,privateKey)
}),publicKey);
},
//Data to decrypt, my privateKey, callback to get reomtes publicKey with id
decryptSign: async (data,privateKey,publicKeyCall)=>{
try {
var inn = await C.communicate.decrypt(data,privateKey);
if(inn == null)throw "invalid privateKey";
inn = JSON.parse(inn);
if(typeof inn.d == "undefined"||typeof inn.i != "string"||typeof inn.s != "string") throw "invalid Data";
let {d,i,s} = inn;
var publicKey = publicKeyCall(i);
var okay = await C.RSA.verify(d,s,publicKey);
if(!okay) throw "wrong Public Key";
return d;
} catch (e) {
console.log(e);
return null;
}
},
}
};
2021-10-22 19:51:31 +02:00
var a = C.sha256;
var b = C.random;
var c = C.randomKey;
var d = C.encode;
var e = C.decode;
var f = C.atob;
var g = C.btoa;
var h = C.pad;
var i = C.unpad;
var j = C.AES;
var k = C.RSA;
var l = C.communicate;
export {
a as sha256,
b as random,
c as randomKey,
d as encode,
e as decode,
f as atob,
g as btoa,
h as pad,
i as unpad,
j as AES,
k as RSA,
l as communicate
};
2021-10-22 18:12:48 +02:00
export default C;