All files / src/modules/crypto CoreCryptoClient.ts

97.72% Statements 43/44
87.5% Branches 7/8
100% Functions 14/14
97.72% Lines 43/44

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127        1x 1x 1x                                     3x 2x   1x 1x 1x       2x 2x       1x             1x 1x 1x     1x                     2x 2x   2x 2x 2x               2x 2x 2x 2x 2x       1x       1x             1x       1x 1x 1x       2x 2x 2x 1x   1x             3x 1x 1x 1x   2x 2x 2x      
import * as crypto from 'crypto';
import { timingSafeEqual } from 'crypto';
import type { ICoreCClient, ICoreKernel } from '../../lib';
 
const encryptionType = 'aes-256-gcm';
const encryptionEncoding = 'base64';
const bufferEncryption = 'utf8';
 
/**
 * Provides cryptographic utilities used by the Core module.
 *
 * This client handles key derivation, encryption/decryption,
 * secure token generation, hashing, UUID generation, and
 * interaction with the kernel's key storage. The implementation
 * relies on the Node.js `crypto` module and expects a 32‑byte
 * AES key.
 */
export default class CoreCryptoClient implements ICoreCClient {
  protected AesKey: string;
 
  protected AesIV: string;
 
  protected kernel: ICoreKernel<any>;
 
  constructor(kernel: ICoreKernel<any>, key: string) {
    if (key.length !== 32) {
      throw new Error('INVALID KEY LENGTH');
    }
    this.AesKey = key;
    this.AesIV = key.substring(0, 16);
    this.kernel = kernel;
  }
 
  static fromPW(pw: string): string {
    const shasum = crypto.createHash('sha512').update(pw).digest('hex');
    return shasum.substring(0, 32);
  }
 
  static clientFromPW(kernel: ICoreKernel<any>, pw: string): CoreCryptoClient {
    return new CoreCryptoClient(
      kernel,
      crypto.createHash('sha512').update(pw).digest('hex'),
    );
  }
 
  generateSecureToken(length: number): Promise<string> {
    return new Promise<string>((resolve) => {
      crypto.randomBytes(length, (err, buf) => {
        Iif (err) {
          resolve('');
        } else {
          resolve(buf.toString('hex'));
        }
      });
    });
  }
 
  encrypt(message: string): {
    auth: Buffer;
    iv: Buffer;
    enc: string;
  } {
    const iv = crypto.randomBytes(12);
    const cipher = crypto.createCipheriv(encryptionType, this.AesKey, iv);
 
    let enc = cipher.update(message, bufferEncryption, encryptionEncoding);
    enc += cipher.final(encryptionEncoding);
    return {
      auth: cipher.getAuthTag(),
      iv,
      enc,
    };
  }
 
  decrypt(enc: string, iv: Buffer, authTag: Buffer): string {
    const decipher = crypto.createDecipheriv(encryptionType, this.AesKey, iv);
    decipher.setAuthTag(Buffer.from(authTag));
    let str = decipher.update(enc, encryptionEncoding, bufferEncryption);
    str += decipher.final(bufferEncryption);
    return str;
  }
 
  isValid(): boolean {
    return this.AesKey.length === 32;
  }
 
  getHash(seed: string, val: string): string {
    return crypto
      .createHash('sha512')
      .update(seed + val)
      .digest('hex');
  }
 
  getUUID(): string {
    return crypto.randomUUID();
  }
 
  async keyStoreSave(data: string): Promise<string> {
    const db = this.kernel.getDb();
    const keyCrypt = this.encrypt(data);
    return db.setKey(keyCrypt.enc, keyCrypt.iv, keyCrypt.auth);
  }
 
  async keyStoreLoad(e_id: string): Promise<string | null> {
    const db = this.kernel.getDb();
    const key = await db.getKey(e_id);
    if (!key) {
      return null;
    }
    return this.decrypt(key.secret, key.iv, key.auth);
  }
 
  timeSavePWValidation(content: string, validator: string): boolean {
    let a;
    let b;
 
    if (content.length !== validator.length) {
      a = Buffer.from('a');
      b = Buffer.from('b');
      return timingSafeEqual(a, b);
    }
    a = Buffer.from(content);
    b = Buffer.from(validator);
    return timingSafeEqual(a, b);
  }
}