Skip to content
Snippets Groups Projects
CryptoServiceNode.ts 5.57 KiB
Newer Older
  • Learn to ignore specific revisions
  • // All functions are async in order to be compatible with the ICryptoService API
    
    import md5 from "js-md5";
    
    import * as crypto from "crypto";
    
    
    import { AESGCMOutput, RSAKeys, ICryptoService } from "./ICryptoService";
    
    Zdravko Iliev's avatar
    Zdravko Iliev committed
    import { ensureUint8Array, arrayBufferToBase64 } from "../../utils/common";
    
    
    const AES_GCM_ALGO = "aes-256-gcm";
    
    
    const getBytes = (
      value: string | ArrayBuffer,
      encoding: BufferEncoding
    ): Buffer => {
    
      let bytes;
      if (typeof value === "string") {
        const valueString = value as string;
        bytes = Buffer.from(valueString, encoding);
      } else {
        bytes = Buffer.from(value);
      }
    
    class CryptoServiceNode implements ICryptoService {
      public async encryptAESGCM(data: string): Promise<AESGCMOutput>;
      public async encryptAESGCM(data: ArrayBuffer): Promise<AESGCMOutput>;
      public async encryptAESGCM(
    
        data: string | ArrayBuffer,
        key?: Buffer,
        iv?: Buffer
    
        if (!key && !iv) {
          key = crypto.randomBytes(32);
          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 authTag = cipher.getAuthTag();
    
    Zdravko Iliev's avatar
    Zdravko Iliev committed
    
    
        const encryptedWithTag = Buffer.concat([
          Buffer.from(encrypted),
          Buffer.from(authTag),
        ]);
    
        return {
    
          key: key,
          iv: iv,
    
      public async decryptAESGCM(
        data: ArrayBuffer,
        key: ArrayBuffer,
        iv: ArrayBuffer
      ): Promise<string>;
      public async decryptAESGCM(
        data: ArrayBuffer,
        key: ArrayBuffer,
        iv: ArrayBuffer,
        returnBuffer: true
      ): Promise<ArrayBuffer>;
      public async decryptAESGCM(
        data: ArrayBuffer,
        key: ArrayBuffer,
        iv: ArrayBuffer,
        returnBuffer?: boolean
      ): Promise<string | ArrayBuffer> {
        const decipher = crypto.createDecipheriv(
          AES_GCM_ALGO,
          Buffer.from(key),
          Buffer.from(iv)
        );
    
        const authTag = data.slice(data.byteLength - 16, data.byteLength);
    
    Zdravko Iliev's avatar
    Zdravko Iliev committed
    
    
        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;
      }
    
      public async generateRSAKeys(): Promise<RSAKeys> {
        const { privateKey, publicKey } = crypto.generateKeyPairSync("rsa", {
          modulusLength: 4096,
          publicKeyEncoding: {
            type: "spki",
            format: "pem",
          },
          privateKeyEncoding: {
            type: "pkcs8",
            format: "pem",
          },
        });
    
        return {
          publicKeyPEM: publicKey,
          privateKeyPEM: privateKey,
        };
      }
    
      public async encryptRSA(
        publicKeyPEM: string,
        data: ArrayBuffer
      ): Promise<ArrayBuffer> {
        const encryptedData = crypto.publicEncrypt(
          {
            key: publicKeyPEM,
            padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
            oaepHash: "sha256",
          },
          // We convert the data string to a buffer using `Buffer.from`
          Buffer.from(data)
        );
    
        return encryptedData.buffer;
      }
    
      public async decryptRSA(
        privateKeyPEM: string,
        data: ArrayBuffer
      ): Promise<ArrayBuffer> {
        const encryptedData = crypto.privateDecrypt(
          {
            key: privateKeyPEM,
            padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
            oaepHash: "sha256",
          },
          // We convert the data string to a buffer using `Buffer.from`
          Buffer.from(data)
        );
    
        return encryptedData.buffer;
      }
    
      public async signRSA(
        privateKeyPEM: string,
        data: ArrayBuffer
      ): Promise<ArrayBuffer> {
        const sign = crypto.createSign("SHA256");
        sign.update(Buffer.from(data));
        sign.end();
    
        return sign.sign(privateKeyPEM).buffer;
      }
    
      public async SHA1(
        value: string | ArrayBuffer,
        encoding: BufferEncoding = "utf8"
      ): Promise<ArrayBuffer> {
        const bytes = getBytes(value, encoding);
        return crypto.createHash("sha1").update(bytes).digest();
      }
    
      public async SHA256(
        value: string | ArrayBuffer,
        encoding: BufferEncoding = "utf8"
      ): Promise<ArrayBuffer> {
        const bytes = getBytes(value, encoding);
        return crypto.createHash("sha256").update(bytes).digest();
      }
    
      public async SHA384(
        value: string | ArrayBuffer,
        encoding: BufferEncoding = "utf8"
      ): Promise<ArrayBuffer> {
        const bytes = getBytes(value, encoding);
        return crypto.createHash("sha384").update(bytes).digest();
      }
    
      public async SHA512(
        value: string | ArrayBuffer,
        encoding: BufferEncoding = "utf8"
      ): Promise<ArrayBuffer> {
        const bytes = getBytes(value, encoding);
        return crypto.createHash("sha512").update(bytes).digest();
      }
    
      public async MD5(
        value: string | ArrayBuffer,
        encoding: BufferEncoding = "utf8"
      ): Promise<ArrayBuffer> {
        const bytes = getBytes(value, encoding);
        return md5.arrayBuffer(bytes);
      }
    
      public async verifyRSASignature(
        publicKeyPEM: string,
        data: ArrayBuffer,
        signature: ArrayBuffer
      ): Promise<boolean> {
        const verify = crypto.createVerify("SHA256");
        verify.update(Buffer.from(data));
        verify.end();
    
        const publicKey = crypto.createPublicKey(
          Buffer.from(publicKeyPEM, "utf-8")
        );
    
        return verify.verify(publicKey, Buffer.from(signature));
      }
    }