From 9cbaf22270c95c0de439228e9f6174ed96a6c4b4 Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Sat, 16 Mar 2019 01:11:20 +0700 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20sjcl=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D1=88=D0=B8=D1=84=D1=80=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20AES,=20=D1=82.=D0=BA.=20WebCrypto=20API=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=B5=D1=82=20?= =?UTF-8?q?=D1=81=20http,=20=D0=B0=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE?= =?UTF-8?q?=20=D1=81=20https?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reader/ServerStorage/ServerStorage.vue | 4 +- client/share/cryptoUtils.js | 75 ++------- client/share/sjclWrapper.js | 145 ++++++++++++++++++ package-lock.json | 5 + package.json | 1 + 5 files changed, 168 insertions(+), 62 deletions(-) create mode 100644 client/share/sjclWrapper.js diff --git a/client/components/Reader/ServerStorage/ServerStorage.vue b/client/components/Reader/ServerStorage/ServerStorage.vue index cdfc32a0..66d2547a 100644 --- a/client/components/Reader/ServerStorage/ServerStorage.vue +++ b/client/components/Reader/ServerStorage/ServerStorage.vue @@ -32,8 +32,8 @@ class ServerStorage extends Vue { //генерируем новый ключ this.generateNewServerStorageKey(); } - this.hashedStorageKey = utils.toBase58(await cryptoUtils.sha256(this.serverStorageKey)); - + this.hashedStorageKey = utils.toBase58(cryptoUtils.sha256(this.serverStorageKey)); + await this.loadProfiles(); } diff --git a/client/share/cryptoUtils.js b/client/share/cryptoUtils.js index b9d28d8a..a22eb8f5 100644 --- a/client/share/cryptoUtils.js +++ b/client/share/cryptoUtils.js @@ -1,70 +1,25 @@ -import {Buffer} from 'safe-buffer'; +import sjclWrapper from './sjclWrapper'; //не менять -const iv = Buffer.from('B6E2XejNh2dS'); -let aesKeys = {}; +const iv = 'B6E2XejNh2dS'; +const salt = 'Liberama project is awesome'; -export async function aesKeyFromPassword(password) { - return await window.crypto.subtle.importKey( - "raw", //only "raw" is allowed - Buffer.from(password), //your password - { - name: "PBKDF2", - }, - false, //whether the key is extractable (i.e. can be used in exportKey) - ["deriveKey"] //can be any combination of "deriveKey" and "deriveBits" - ).then((key) => { - return window.crypto.subtle.deriveKey( - { - "name": "PBKDF2", - salt: Buffer.from('Liberama project is awesome'),//не менять - iterations: 1000, - hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512" - }, - key, //your key from generateKey or importKey - { //the key type you want to create based on the derived bits - name: "AES-GCM", //can be any AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH", or "HMAC") - //the generateKey parameters for that type of algorithm - length: 256, //can be 128, 192, or 256 - }, - false, //whether the derived key is extractable (i.e. can be used in exportKey) - ["encrypt", "decrypt"] //limited to the options in that algorithm's importKey - ); - }); -} - -export async function aesEncrypt(data, password) { - if (!aesKeys[password]) - aesKeys[password] = await aesKeyFromPassword(password); - - const key = aesKeys[password]; - - return await window.crypto.subtle.encrypt( - { - name: "AES-GCM", - iv - }, - key, //from generateKey or importKey above - data //ArrayBuffer of data you want to encrypt +export function aesEncrypt(data, password) { + return sjclWrapper.codec.bytes.fromBits( + sjclWrapper.encryptArray( + password, sjclWrapper.codec.bytes.toBits(data), {iv, salt} + ).ct ); } -export async function aesDecrypt(data, password) { - if (!aesKeys[password]) - aesKeys[password] = await aesKeyFromPassword(password); - - const key = aesKeys[password]; - - return await window.crypto.subtle.decrypt( - { - name: "AES-GCM", - iv - }, - key, //from generateKey or importKey above - data //ArrayBuffer of the data +export function aesDecrypt(data, password) { + return sjclWrapper.codec.bytes.fromBits( + sjclWrapper.decryptArray( + password, {ct: sjclWrapper.codec.bytes.toBits(data)}, {iv, salt} + ) ); } -export async function sha256(data) { - return await crypto.subtle.digest("SHA-256", Buffer.from(data)); +export function sha256(str) { + return sjclWrapper.codec.bytes.fromBits(sjclWrapper.hash.sha256.hash(str)); } diff --git a/client/share/sjclWrapper.js b/client/share/sjclWrapper.js new file mode 100644 index 00000000..643c2f73 --- /dev/null +++ b/client/share/sjclWrapper.js @@ -0,0 +1,145 @@ +import sjcl from 'sjcl'; + +//везде недоработки... + +sjcl.codec.bytes = { + fromBits: function(arr) { + var out = [], bl = sjcl.bitArray.bitLength(arr), i, tmp; + for (i=0; i>> 24); + tmp <<= 8; + } + return out; + }, + toBits: function(bytes) { + var out = [], i, tmp=0; + for (i=0; i 4)) { + throw new sjcl.exception.invalid("json encrypt: invalid parameters"); + } + + if (typeof password === "string") { + tmp = sjcl.misc.cachedPbkdf2(password, p); + password = tmp.key.slice(0,p.ks/32); + p.salt = tmp.salt; + } else if (sjcl.ecc && password instanceof sjcl.ecc.elGamal.publicKey) { + tmp = password.kem(); + p.kemtag = tmp.tag; + password = tmp.key.slice(0,p.ks/32); + } + if (typeof plaintext === "string") { + plaintext = sjcl.codec.utf8String.toBits(plaintext); + } + if (typeof adata === "string") { + p.adata = adata = sjcl.codec.utf8String.toBits(adata); + } + prp = new sjcl.cipher[p.cipher](password); + + j._add(rp, p); + rp.key = password; + + /* do the encryption */ + if (p.mode === "ccm" && sjcl.arrayBuffer && sjcl.arrayBuffer.ccm && plaintext instanceof ArrayBuffer) { + p.ct = sjcl.arrayBuffer.ccm.encrypt(prp, plaintext, p.iv, adata, p.ts); + } else { + p.ct = sjcl.mode[p.mode].encrypt(prp, plaintext, p.iv, adata, p.ts); + } + + return p; +} + +sjcl.decryptArray = function(password, ciphertext, params) { + params = params || {}; + + var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),ciphertext), params, true), ct, tmp, prp, adata=p.adata; + if (typeof p.salt === "string") { + p.salt = sjcl.codec.base64.toBits(p.salt); + } + if (typeof p.iv === "string") { + p.iv = sjcl.codec.base64.toBits(p.iv); + } + + if (!sjcl.mode[p.mode] || + !sjcl.cipher[p.cipher] || + (typeof password === "string" && p.iter <= 100) || + (p.ts !== 64 && p.ts !== 96 && p.ts !== 128) || + (p.ks !== 128 && p.ks !== 192 && p.ks !== 256) || + (!p.iv) || + (p.iv.length < 2 || p.iv.length > 4)) { + throw new sjcl.exception.invalid("json decrypt: invalid parameters"); + } + + if (typeof password === "string") { + tmp = sjcl.misc.cachedPbkdf2(password, p); + password = tmp.key.slice(0,p.ks/32); + p.salt = tmp.salt; + } else if (sjcl.ecc && password instanceof sjcl.ecc.elGamal.secretKey) { + password = password.unkem(sjcl.codec.base64.toBits(p.kemtag)).slice(0,p.ks/32); + } + if (typeof adata === "string") { + adata = sjcl.codec.utf8String.toBits(adata); + } + prp = new sjcl.cipher[p.cipher](password); + + /* do the decryption */ + if (p.mode === "ccm" && sjcl.arrayBuffer && sjcl.arrayBuffer.ccm && p.ct instanceof ArrayBuffer) { + ct = sjcl.arrayBuffer.ccm.decrypt(prp, p.ct, p.iv, p.tag, adata, p.ts); + } else { + ct = sjcl.mode[p.mode].decrypt(prp, p.ct, p.iv, adata, p.ts); + } + + return ct; +} + +export default sjcl; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 634a8900..fc13440b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10084,6 +10084,11 @@ } } }, + "sjcl": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.8.tgz", + "integrity": "sha512-LzIjEQ0S0DpIgnxMEayM1rq9aGwGRG4OnZhCdjx7glTaJtf4zRfpg87ImfjSJjoW9vKpagd82McDOwbRT5kQKQ==" + }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", diff --git a/package.json b/package.json index 11c237ea..c7a23f7f 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "pako": "^1.0.10", "path-browserify": "^1.0.0", "safe-buffer": "^5.1.2", + "sjcl": "^1.0.8", "sql-template-strings": "^2.2.2", "sqlite": "^3.0.0", "tar-fs": "^2.0.0",