diff --git a/dist/services/CryptoService/CryptoServiceMobile.d.ts b/dist/services/CryptoService/CryptoServiceMobile.d.ts index a50019ab4e2b81d5f3eedc89fc668a61a069e0b3..869650b28e77f94b4250a64e04088e03f588f0da 100644 --- a/dist/services/CryptoService/CryptoServiceMobile.d.ts +++ b/dist/services/CryptoService/CryptoServiceMobile.d.ts @@ -1,5 +1,9 @@ /// <reference types="node" /> import { AESGCMOutput, RSAKeys, ICryptoService } from "./ICryptoService"; +export declare const arrayBufferToBase64: (buffer: ArrayBuffer) => string; +export declare const arrayBufferToHex: (buffer: ArrayBuffer) => string; +export declare const base64ToArrayBuffer: (base64: string) => ArrayBuffer; +export declare const ensureUint8Array: (data: string | ArrayBuffer | Uint8Array) => Uint8Array; declare class CryptoServiceMobile implements ICryptoService { encryptAESGCM(data: string): Promise<AESGCMOutput>; encryptAESGCM(data: ArrayBuffer): Promise<AESGCMOutput>; diff --git a/dist/services/CryptoService/CryptoServiceMobile.js b/dist/services/CryptoService/CryptoServiceMobile.js index 9054c260ce8a6bd64949cad772a91d0907d85908..8bd98f213181ac337b3622fec12186c0c830f574 100644 --- a/dist/services/CryptoService/CryptoServiceMobile.js +++ b/dist/services/CryptoService/CryptoServiceMobile.js @@ -12,10 +12,35 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.ensureUint8Array = exports.base64ToArrayBuffer = exports.arrayBufferToHex = exports.arrayBufferToBase64 = void 0; const crypto_js_1 = __importDefault(require("crypto-js")); -const common_1 = require("../../utils/common"); +const jscrypto_1 = __importDefault(require("jscrypto")); const AES_GCM_ALGO = "aes-256-gcm"; const crypto = {}; +const arrayBufferToBase64 = (buffer) => { + return Buffer.from(buffer).toString("base64"); +}; +exports.arrayBufferToBase64 = arrayBufferToBase64; +const arrayBufferToHex = (buffer) => { + return [...new Uint8Array(buffer)] + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); +}; +exports.arrayBufferToHex = arrayBufferToHex; +const base64ToArrayBuffer = (base64) => { + return Buffer.from(base64, "base64"); +}; +exports.base64ToArrayBuffer = base64ToArrayBuffer; +const ensureUint8Array = (data) => { + return data instanceof Uint8Array + ? data + : typeof data === "string" + ? new Uint8Array((0, exports.base64ToArrayBuffer)(data)) + : data instanceof ArrayBuffer + ? new Uint8Array(data) + : data; +}; +exports.ensureUint8Array = ensureUint8Array; crypto_js_1.default.enc.u8array = { /** * Converts a word array to a Uint8Array. @@ -80,43 +105,47 @@ const getBytes = (value, encoding) => { class CryptoServiceMobile { encryptAESGCM(data) { return __awaiter(this, void 0, void 0, function* () { - const key = crypto.randomBytes(32); - const iv = crypto.randomBytes(12); - const cipher = crypto.createCipheriv(AES_GCM_ALGO, key, iv); - let encrypted; - if (typeof data === "string") { - encrypted = cipher.update(data, "utf8"); - } - else { - encrypted = cipher.update((0, common_1.ensureUint8Array)(data)); - } - cipher.final(); - const authTag = cipher.getAuthTag(); + const key = jscrypto_1.default.Word32Array.random(32); + const iv = jscrypto_1.default.Word32Array.random(12); + const u8a = new jscrypto_1.default.Word32Array(new TextEncoder().encode(data)); + const encryptedData = jscrypto_1.default.AES.encrypt(u8a, key, { iv, mode: jscrypto_1.default.mode.GCM }); + const authData = jscrypto_1.default.Utf8.parse("some plain text data for authentication. This will not be encrypted."); + const tagLength = 16; + const authTag = jscrypto_1.default.mode.GCM.mac(jscrypto_1.default.AES, key, iv, authData, encryptedData.cipherText, tagLength); const encryptedWithTag = Buffer.concat([ - Buffer.from(encrypted), - Buffer.from(authTag), + Buffer.from(encryptedData.cipherText.toUint8Array()), + Buffer.from(authTag.toUint8Array()), ]); return { - key: key.buffer, - iv: iv.buffer, - data: encryptedWithTag, + key: key.toUint8Array(), + iv: iv.toUint8Array(), + data: encryptedWithTag }; }); } decryptAESGCM(data, key, iv, returnBuffer) { return __awaiter(this, void 0, void 0, function* () { - const decipher = crypto.createDecipheriv(AES_GCM_ALGO, Buffer.from(key), Buffer.from(iv)); - const authTag = data.slice(data.byteLength - 16, data.byteLength); - const encrypted = data.slice(0, data.byteLength - 16); - if (returnBuffer) { - const decrypted = decipher.update((0, common_1.ensureUint8Array)(encrypted)); - decipher.setAuthTag(Buffer.from(authTag)); - return Buffer.concat([decrypted, decipher.final()]); - } - let str = decipher.update((0, common_1.ensureUint8Array)(encrypted), null, "utf8"); - decipher.setAuthTag(Buffer.from(authTag)); - str += decipher.final("utf8"); - return str; + const keyString = new jscrypto_1.default.Word32Array(key); + const ivString = new jscrypto_1.default.Word32Array(iv); + const encryptedStringified11 = (0, exports.arrayBufferToBase64)(data); + const decryptedData = jscrypto_1.default.AES.decrypt(encryptedStringified11, keyString, { iv: ivString, mode: jscrypto_1.default.mode.GCM }); + return new TextDecoder().decode(decryptedData.toUint8Array()); + // Encrypt/Decrypt as usual + // const decipher = crypto.createDecipheriv( + // AES_GCM_ALGO, + // Buffer.from(key), + // Buffer.from(iv) + // ); + // if (returnBuffer) { + // const decrypted: Buffer = decipher.update(ensureUint8Array(encrypted)); + // decipher.setAuthTag(Buffer.from(authTag)); + // return Buffer.concat([decrypted, decipher.final()]); + // } + // + // let str = decipher.update(ensureUint8Array(encrypted), null, "utf8"); + // decipher.setAuthTag(Buffer.from(authTag)); + // str += decipher.final("utf8"); + // return str; }); } generateRSAKeys() { @@ -176,7 +205,8 @@ class CryptoServiceMobile { return hash.toString(crypto_js_1.default.enc.u8array); }); } - SHA256(value, encoding = "utf8") { + SHA256(value, encoding = "utf8" // todo add support for base64 and other encodings + ) { return __awaiter(this, void 0, void 0, function* () { const hash = crypto_js_1.default.SHA256(value); return hash.toString(crypto_js_1.default.enc.u8array); diff --git a/dist/services/CryptoService/CryptoServiceNode.d.ts b/dist/services/CryptoService/CryptoServiceNode.d.ts index 9b91b3caabc22a7c3cd53dabfb017c040546a537..d56ceb4ef0079ef3a29160d5117b0d46a8c5b789 100644 --- a/dist/services/CryptoService/CryptoServiceNode.d.ts +++ b/dist/services/CryptoService/CryptoServiceNode.d.ts @@ -1,5 +1,7 @@ /// <reference types="node" /> import { AESGCMOutput, RSAKeys, ICryptoService } from "./ICryptoService"; +export declare const base64ToArrayBuffer: (base64: string) => ArrayBuffer; +export declare const ensureUint8Array: (data: string | ArrayBuffer | Uint8Array) => Uint8Array; declare class CryptoServiceNode implements ICryptoService { encryptAESGCM(data: string): Promise<AESGCMOutput>; encryptAESGCM(data: ArrayBuffer): Promise<AESGCMOutput>; diff --git a/dist/services/CryptoService/CryptoServiceNode.js b/dist/services/CryptoService/CryptoServiceNode.js index f789e304e709d2a7f8862a7f6d358e83b8b79f9c..a3272a77884057d06daf869716c104dbb371652b 100644 --- a/dist/services/CryptoService/CryptoServiceNode.js +++ b/dist/services/CryptoService/CryptoServiceNode.js @@ -1,7 +1,11 @@ "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; @@ -31,10 +35,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.ensureUint8Array = exports.base64ToArrayBuffer = void 0; // All functions are async in order to be compatible with the ICryptoService API const js_md5_1 = __importDefault(require("js-md5")); const crypto = __importStar(require("crypto")); -const common_1 = require("../../utils/common"); +const base64ToArrayBuffer = (base64) => { + return Buffer.from(base64, "base64"); +}; +exports.base64ToArrayBuffer = base64ToArrayBuffer; +const ensureUint8Array = (data) => { + return data instanceof Uint8Array + ? data + : typeof data === "string" + ? new Uint8Array((0, exports.base64ToArrayBuffer)(data)) + : data instanceof ArrayBuffer + ? new Uint8Array(data) + : data; +}; +exports.ensureUint8Array = ensureUint8Array; const AES_GCM_ALGO = "aes-256-gcm"; const getBytes = (value, encoding) => { let bytes; @@ -58,7 +76,7 @@ class CryptoServiceNode { encrypted = cipher.update(data, "utf8"); } else { - encrypted = cipher.update((0, common_1.ensureUint8Array)(data)); + encrypted = cipher.update((0, exports.ensureUint8Array)(data)); } cipher.final(); const authTag = cipher.getAuthTag(); @@ -79,11 +97,11 @@ class CryptoServiceNode { const authTag = data.slice(data.byteLength - 16, data.byteLength); const encrypted = data.slice(0, data.byteLength - 16); if (returnBuffer) { - const decrypted = decipher.update((0, common_1.ensureUint8Array)(encrypted)); + const decrypted = decipher.update((0, exports.ensureUint8Array)(encrypted)); decipher.setAuthTag(Buffer.from(authTag)); return Buffer.concat([decrypted, decipher.final()]); } - let str = decipher.update((0, common_1.ensureUint8Array)(encrypted), null, "utf8"); + let str = decipher.update((0, exports.ensureUint8Array)(encrypted), null, "utf8"); decipher.setAuthTag(Buffer.from(authTag)); str += decipher.final("utf8"); return str; diff --git a/dist/test.d.ts b/dist/test.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb0ff5c3b541f646105198ee23ac0fc3d805023e --- /dev/null +++ b/dist/test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/test.js b/dist/test.js new file mode 100644 index 0000000000000000000000000000000000000000..816a3dc0e69ebe37d13933c0693114bece43c627 --- /dev/null +++ b/dist/test.js @@ -0,0 +1,57 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const CryptoServiceMobile_1 = __importDefault(require("./services/CryptoService/CryptoServiceMobile")); +const start = () => __awaiter(void 0, void 0, void 0, function* () { + console.log("hey"); + // const cryptoServiceNode = new CryptoServiceNode(); + const cryptoServiceMobile = new CryptoServiceMobile_1.default(); + const sealUrl = 'https://azure-dev.vrgnservices.com/#CiApUU6ISnCiB5Kyz6J5ushcJoz_3arnBuP8MlPFykEIDBIgQFycG-RzpoI_xBORVhI4fGqlt7gSIErY9SJX2F_s6hg='; + const sealHash = yield cryptoServiceMobile.SHA256(sealUrl); + // const b64Result = CommonUtils.arrayBufferToBase64(sealHash); + console.log(sealHash); +}); +start(); +// 0 = 17 +// 1 = 241 +// 2 = 53 +// 3 = 121 +// 4 = 15 +// 5 = 136 +// 6 = 207 +// 7 = 182 +// 8 = 172 +// 9 = 68 +// 10 = 68 +// 11 = 109 +// 12 = 86 +// 13 = 15 +// 14 = 245 +// 15 = 18 +// 16 = 131 +// 17 = 13 +// 18 = 118 +// 19 = 113 +// 20 = 146 +// 21 = 242 +// 22 = 208 +// 23 = 255 +// 24 = 213 +// 25 = 27 +// 26 = 11 +// 27 = 118 +// 28 = 154 +// 29 = 87 +// 30 = 71 +// 31 = 217 diff --git a/package.json b/package.json index 9552e4d3149b7fea853d360c0f1f61204b2515da..a77554900cb7b7c0da700e46ccd201664abb3482 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "fflate": "^0.7.3", "google-protobuf": "^3.13.0", "js-md5": "^0.7.3", + "jscrypto": "^1.0.3", "protobufjs": "^6.10.1", "ts-node": "^10.9.1" }, diff --git a/src/services/CryptoService/CryptoServiceMobile.ts b/src/services/CryptoService/CryptoServiceMobile.ts index ae96a9392aa0973e4ad9a1062dce22a245ceb07a..58787f7adb267c4887445110e6451c690174f4d4 100644 --- a/src/services/CryptoService/CryptoServiceMobile.ts +++ b/src/services/CryptoService/CryptoServiceMobile.ts @@ -2,13 +2,38 @@ import md5 from "js-md5"; import CryptoJS from "crypto-js"; +import JsCrypto from "jscrypto"; import { AESGCMOutput, RSAKeys, ICryptoService } from "./ICryptoService"; -import { base64ToArrayBuffer, ensureUint8Array } from "../../utils/common"; const AES_GCM_ALGO = "aes-256-gcm"; const crypto: any = {}; +export const arrayBufferToBase64 = (buffer: ArrayBuffer): string => { + return Buffer.from(buffer).toString("base64"); +}; + +export const arrayBufferToHex = (buffer: ArrayBuffer): string => { + return [...new Uint8Array(buffer)] + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); +}; + +export const base64ToArrayBuffer = (base64: string): ArrayBuffer => { + return Buffer.from(base64, "base64"); +}; +export const ensureUint8Array = ( + data: string | ArrayBuffer | Uint8Array +): Uint8Array => { + return data instanceof Uint8Array + ? data + : typeof data === "string" + ? new Uint8Array(base64ToArrayBuffer(data)) + : data instanceof ArrayBuffer + ? new Uint8Array(data) + : data; +}; + CryptoJS.enc.u8array = { /** * Converts a word array to a Uint8Array. @@ -85,27 +110,39 @@ class CryptoServiceMobile implements ICryptoService { public async encryptAESGCM( data: string | ArrayBuffer ): Promise<AESGCMOutput> { - const key = crypto.randomBytes(32); - const iv = crypto.randomBytes(12); - const cipher = crypto.createCipheriv(AES_GCM_ALGO, key, iv); - - let encrypted; - if (typeof data === "string") { - encrypted = cipher.update(data, "utf8"); - } else { - encrypted = cipher.update(ensureUint8Array(data)); - } - cipher.final(); + const key = JsCrypto.Word32Array.random(32); + const iv = JsCrypto.Word32Array.random(12); + + const u8a = new JsCrypto.Word32Array( + new TextEncoder().encode(data as string) + ); + const encryptedData = JsCrypto.AES.encrypt(u8a, key, { + iv, + mode: JsCrypto.mode.GCM, + }); + + const authData = JsCrypto.Utf8.parse( + "some plain text data for authentication. This will not be encrypted." + ); + + const tagLength = 16; + const authTag = JsCrypto.mode.GCM.mac( + JsCrypto.AES, + key, + iv, + authData, + encryptedData.cipherText, + tagLength + ); - const authTag = cipher.getAuthTag(); const encryptedWithTag = Buffer.concat([ - Buffer.from(encrypted), - Buffer.from(authTag), + Buffer.from(encryptedData.cipherText.toUint8Array()), + Buffer.from(authTag.toUint8Array()), ]); return { - key: key.buffer, - iv: iv.buffer, + key: key.toUint8Array(), + iv: iv.toUint8Array(), data: encryptedWithTag, }; } @@ -127,24 +164,34 @@ class CryptoServiceMobile implements ICryptoService { iv: ArrayBuffer, returnBuffer?: boolean ): Promise<string | ArrayBuffer> { - const decipher = crypto.createDecipheriv( - AES_GCM_ALGO, - Buffer.from(key), - Buffer.from(iv) + const keyString = new JsCrypto.Word32Array(key); + const ivString = new JsCrypto.Word32Array(iv); + + const encryptedStringified11 = arrayBufferToBase64(data); + const decryptedData = JsCrypto.AES.decrypt( + encryptedStringified11, + keyString, + { iv: ivString, mode: JsCrypto.mode.GCM } ); - - const authTag = data.slice(data.byteLength - 16, data.byteLength); - const encrypted = data.slice(0, data.byteLength - 16); - if (returnBuffer) { - const decrypted: Buffer = decipher.update(ensureUint8Array(encrypted)); - decipher.setAuthTag(Buffer.from(authTag)); - return Buffer.concat([decrypted, decipher.final()]); - } - - let str = decipher.update(ensureUint8Array(encrypted), null, "utf8"); - decipher.setAuthTag(Buffer.from(authTag)); - str += decipher.final("utf8"); - return str; + return new TextDecoder().decode(decryptedData.toUint8Array()); + + // Encrypt/Decrypt as usual + // const decipher = crypto.createDecipheriv( + // AES_GCM_ALGO, + // Buffer.from(key), + // Buffer.from(iv) + // ); + + // if (returnBuffer) { + // const decrypted: Buffer = decipher.update(ensureUint8Array(encrypted)); + // decipher.setAuthTag(Buffer.from(authTag)); + // return Buffer.concat([decrypted, decipher.final()]); + // } + // + // let str = decipher.update(ensureUint8Array(encrypted), null, "utf8"); + // decipher.setAuthTag(Buffer.from(authTag)); + // str += decipher.final("utf8"); + // return str; } public async generateRSAKeys(): Promise<RSAKeys> { @@ -221,7 +268,7 @@ class CryptoServiceMobile implements ICryptoService { public async SHA256( value: string | ArrayBuffer, - encoding: BufferEncoding = "utf8" + encoding: BufferEncoding = "utf8" // todo add support for base64 and other encodings ): Promise<ArrayBuffer> { const hash = CryptoJS.SHA256(value); return hash.toString(CryptoJS.enc.u8array); diff --git a/src/services/CryptoService/CryptoServiceNode.ts b/src/services/CryptoService/CryptoServiceNode.ts index c98ecf2187558d3e30df438d1601e3ed6de46d41..8b99f7b0e4a6103dc80d8637ba502cbd95ab694c 100644 --- a/src/services/CryptoService/CryptoServiceNode.ts +++ b/src/services/CryptoService/CryptoServiceNode.ts @@ -4,7 +4,21 @@ import md5 from "js-md5"; import * as crypto from "crypto"; import { AESGCMOutput, RSAKeys, ICryptoService } from "./ICryptoService"; -import { ensureUint8Array } from "../../utils/common"; + +export const base64ToArrayBuffer = (base64: string): ArrayBuffer => { + return Buffer.from(base64, "base64"); +}; +export const ensureUint8Array = ( + data: string | ArrayBuffer | Uint8Array +): Uint8Array => { + return data instanceof Uint8Array + ? data + : typeof data === "string" + ? new Uint8Array(base64ToArrayBuffer(data)) + : data instanceof ArrayBuffer + ? new Uint8Array(data) + : data; +}; const AES_GCM_ALGO = "aes-256-gcm"; diff --git a/test.ts b/test.ts new file mode 100644 index 0000000000000000000000000000000000000000..f6f0a962e6fd802d0a7ab8de017e7a724072d41f --- /dev/null +++ b/test.ts @@ -0,0 +1,59 @@ +import CryptoServiceNode from "./src/services/CryptoService/CryptoServiceNode"; +import CryptoServiceMobile from "./src/services/CryptoService/CryptoServiceMobile"; + +const start = async () => { + console.log("hey"); + const cryptoServiceNode = new CryptoServiceNode(); + const cryptoServiceMobile = new CryptoServiceMobile(); + + const sealUrl = "Abc汉字Кириллица"; + + const shaMobile = await cryptoServiceMobile.SHA256(sealUrl); + const shaNode = await cryptoServiceNode.SHA256(sealUrl); + + // const { key, iv, data } = await cryptoServiceNode.encryptAESGCM(sealUrl); + // const initialString = await cryptoServiceMobile.decryptAESGCM(data, key, iv); + + const { key, iv, data } = await cryptoServiceMobile.encryptAESGCM(sealUrl); + const initialString = await cryptoServiceNode.decryptAESGCM(data, key, iv); + + // const sealHashMobile = await cryptoServiceMobile.MD5(sealUrl); + // const b64Result = CommonUtils.arrayBufferToBase64(sealHash); + console.log(initialString); + // console.log(sealHashMobile); +}; + +start(); + +// 0 = 17 +// 1 = 241 +// 2 = 53 +// 3 = 121 +// 4 = 15 +// 5 = 136 +// 6 = 207 +// 7 = 182 +// 8 = 172 +// 9 = 68 +// 10 = 68 +// 11 = 109 +// 12 = 86 +// 13 = 15 +// 14 = 245 +// 15 = 18 +// 16 = 131 +// 17 = 13 +// 18 = 118 +// 19 = 113 +// 20 = 146 +// 21 = 242 +// 22 = 208 +// 23 = 255 +// 24 = 213 +// 25 = 27 +// 26 = 11 +// 27 = 118 +// 28 = 154 +// 29 = 87 +// 30 = 71 +// 31 = 217 diff --git a/yarn.lock b/yarn.lock index 40ff264a327fe05c25f2bd4bc2b82862f7e282c3..879fd24d9369d2f621d6288901a541217d003206 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3807,6 +3807,11 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +jscrypto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/jscrypto/-/jscrypto-1.0.3.tgz#598febca2a939d6f679c54f56e1fe364cef30cc9" + integrity sha512-lryZl0flhodv4SZHOqyb1bx5sKcJxj0VBo0Kzb4QMAg3L021IC9uGpl0RCZa+9KJwlRGSK2C80ITcwbe19OKLQ== + jsdom@^16.4.0, jsdom@^16.5.3: version "16.7.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710"