mirror of
https://github.com/balzack/databag.git
synced 2025-02-19 06:29:23 +00:00
218 lines
7.0 KiB
JavaScript
218 lines
7.0 KiB
JavaScript
import CryptoJS from 'crypto-js';
|
|
import { RSA } from 'react-native-rsa-native';
|
|
import { JSEncrypt } from 'jsencrypt'
|
|
|
|
export function getChannelSeals(subject) {
|
|
const { seals } = JSON.parse(subject);
|
|
return seals;
|
|
}
|
|
|
|
export function isUnsealed(seals, sealKey) {
|
|
for (let i = 0; i < seals?.length; i++) {
|
|
if (seals[i].publicKey === sealKey?.public) {
|
|
return sealKey?.private != null;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function getContentKey(seals, sealKey) {
|
|
for (let i = 0; i < seals?.length; i++) {
|
|
if (seals[i].publicKey === sealKey.public) {
|
|
const seal = seals[i];
|
|
const begin = '-----BEGIN RSA PRIVATE KEY-----\n';
|
|
const end = '\n-----END RSA PRIVATE KEY-----';
|
|
const key = `${begin}${sealKey.private}${end}`;
|
|
return await RSA.decrypt(seal.sealedKey, key);
|
|
}
|
|
}
|
|
throw new Error("unsealKey not available");
|
|
}
|
|
|
|
export function encryptChannelSubject(subject, publicKeys) {
|
|
const key = CryptoJS.lib.WordArray.random(256 / 8);
|
|
const iv = CryptoJS.lib.WordArray.random(128 / 8);
|
|
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ subject }), key, { iv: iv });
|
|
const subjectEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
|
|
const subjectIv = iv.toString();
|
|
const keyHex = key.toString();
|
|
|
|
let seals = [];
|
|
let crypto = new JSEncrypt();
|
|
publicKeys.forEach(publicKey => {
|
|
crypto.setPublicKey(publicKey);
|
|
const sealedKey = crypto.encrypt(keyHex);
|
|
seals.push({ publicKey, sealedKey });
|
|
});
|
|
|
|
return { subjectEncrypted, subjectIv, seals };
|
|
}
|
|
|
|
export function updateChannelSubject(subject, contentKey) {
|
|
const key = CryptoJS.enc.Hex.parse(contentKey);
|
|
const iv = CryptoJS.lib.WordArray.random(128 / 8);
|
|
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ subject }), key, { iv: iv });
|
|
const subjectEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
|
|
const subjectIv = iv.toString();
|
|
return { subjectEncrypted, subjectIv };
|
|
}
|
|
|
|
export function encryptBlock(block, contentKey) {
|
|
const key = CryptoJS.enc.Hex.parse(contentKey);
|
|
const iv = CryptoJS.lib.WordArray.random(128 / 8);
|
|
const encrypted = CryptoJS.AES.encrypt(block, key, { iv: iv });
|
|
const blockEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
|
|
const blockIv = iv.toString();
|
|
|
|
return { blockEncrypted, blockIv };
|
|
}
|
|
|
|
export function decryptBlock(blockEncrypted, blockIv, contentKey) {
|
|
const iv = CryptoJS.enc.Hex.parse(blockIv);
|
|
const key = CryptoJS.enc.Hex.parse(contentKey);
|
|
const enc = CryptoJS.enc.Base64.parse(blockEncrypted);
|
|
const cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
|
|
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
|
|
const block = dec.toString(CryptoJS.enc.Utf8);
|
|
|
|
return block;
|
|
}
|
|
|
|
export function decryptChannelSubject(subject, contentKey) {
|
|
const { subjectEncrypted, subjectIv } = JSON.parse(subject);
|
|
const iv = CryptoJS.enc.Hex.parse(subjectIv);
|
|
const key = CryptoJS.enc.Hex.parse(contentKey);
|
|
const enc = CryptoJS.enc.Base64.parse(subjectEncrypted);
|
|
const cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
|
|
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
|
|
const str = dec.toString(CryptoJS.enc.Utf8);
|
|
if (!str) {
|
|
return null;
|
|
}
|
|
return JSON.parse(str);
|
|
}
|
|
|
|
export function encryptTopicSubject(subject, contentKey) {
|
|
const iv = CryptoJS.lib.WordArray.random(128 / 8);
|
|
const key = CryptoJS.enc.Hex.parse(contentKey);
|
|
const encrypted = CryptoJS.AES.encrypt(JSON.stringify(subject), key, { iv: iv });
|
|
const messageEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
|
|
const messageIv = iv.toString();
|
|
return { messageEncrypted, messageIv };
|
|
}
|
|
|
|
export function decryptTopicSubject(subject, contentKey) {
|
|
const { messageEncrypted, messageIv } = JSON.parse(subject);
|
|
const iv = CryptoJS.enc.Hex.parse(messageIv);
|
|
const key = CryptoJS.enc.Hex.parse(contentKey);
|
|
const enc = CryptoJS.enc.Base64.parse(messageEncrypted);
|
|
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
|
|
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
|
|
return JSON.parse(dec.toString(CryptoJS.enc.Utf8));
|
|
}
|
|
|
|
function convertPem(pem) {
|
|
var lines = pem.split('\n');
|
|
var encoded = '';
|
|
for(var i = 0;i < lines.length;i++){
|
|
if (lines[i].trim().length > 0 &&
|
|
lines[i].indexOf('-BEGIN RSA PRIVATE KEY-') < 0 &&
|
|
lines[i].indexOf('-BEGIN RSA PUBLIC KEY-') < 0 &&
|
|
lines[i].indexOf('-BEGIN PUBLIC KEY-') < 0 &&
|
|
lines[i].indexOf('-END PUBLIC KEY-') < 0 &&
|
|
lines[i].indexOf('-END RSA PRIVATE KEY-') < 0 &&
|
|
lines[i].indexOf('-END RSA PUBLIC KEY-') < 0) {
|
|
encoded += lines[i].trim();
|
|
}
|
|
}
|
|
return encoded
|
|
};
|
|
|
|
export async function generateSeal(password) {
|
|
|
|
// generate key to encrypt private key
|
|
const salt = CryptoJS.lib.WordArray.random(128 / 8);
|
|
const aes = CryptoJS.PBKDF2(password, salt, {
|
|
keySize: 256 / 32,
|
|
iterations: 1024,
|
|
});
|
|
|
|
// generate rsa key for sealing channel, delay for activity indicators
|
|
await new Promise(r => setTimeout(r, 1000));
|
|
const crypto = new JSEncrypt({ default_key_size: 2048 });
|
|
crypto.getKey();
|
|
|
|
// encrypt private key
|
|
const iv = CryptoJS.lib.WordArray.random(128 / 8);
|
|
const privateKey = convertPem(crypto.getPrivateKey());
|
|
const enc = CryptoJS.AES.encrypt(privateKey, aes, { iv: iv });
|
|
const publicKey = convertPem(crypto.getPublicKey());
|
|
|
|
// update account
|
|
const seal = {
|
|
passwordSalt: salt.toString(),
|
|
privateKeyIv: iv.toString(),
|
|
privateKeyEncrypted: enc.ciphertext.toString(CryptoJS.enc.Base64),
|
|
publicKey: publicKey,
|
|
}
|
|
const sealKey = {
|
|
public: publicKey,
|
|
private: privateKey,
|
|
}
|
|
|
|
return { seal, sealKey };
|
|
}
|
|
|
|
export function unlockSeal(seal, password) {
|
|
|
|
// generate key to encrypt private key
|
|
const salt = CryptoJS.enc.Hex.parse(seal.passwordSalt);
|
|
const aes = CryptoJS.PBKDF2(password, salt, {
|
|
keySize: 256 / 32,
|
|
iterations: 1024,
|
|
});
|
|
|
|
// decrypt private key
|
|
const iv = CryptoJS.enc.Hex.parse(seal.privateKeyIv);
|
|
const enc = CryptoJS.enc.Base64.parse(seal.privateKeyEncrypted)
|
|
|
|
let cipherParams = CryptoJS.lib.CipherParams.create({
|
|
ciphertext: enc,
|
|
iv: iv
|
|
});
|
|
const dec = CryptoJS.AES.decrypt(cipherParams, aes, { iv: iv });
|
|
const privateKey = dec.toString(CryptoJS.enc.Utf8)
|
|
|
|
// store ke
|
|
const sealKey = {
|
|
public: seal.publicKey,
|
|
private: privateKey,
|
|
}
|
|
|
|
return sealKey;
|
|
}
|
|
|
|
export function updateSeal(seal, sealKey, password) {
|
|
|
|
// generate key to encrypt private key
|
|
const salt = CryptoJS.lib.WordArray.random(128 / 8);
|
|
const aes = CryptoJS.PBKDF2(password, salt, {
|
|
keySize: 256 / 32,
|
|
iterations: 1024,
|
|
});
|
|
|
|
// encrypt private key
|
|
const iv = CryptoJS.lib.WordArray.random(128 / 8);
|
|
const enc = CryptoJS.AES.encrypt(sealKey.private, aes, { iv: iv });
|
|
|
|
// update account
|
|
const updated = {
|
|
passwordSalt: salt.toString(),
|
|
privateKeyIv: iv.toString(),
|
|
privateKeyEncrypted: enc.ciphertext.toString(CryptoJS.enc.Base64),
|
|
publicKey: seal.publicKey,
|
|
}
|
|
|
|
return { seal: updated, sealKey };
|
|
}
|