File size: 4,503 Bytes
b39afbe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/**
 * Copyright (c) 2023 MERCENARIES.AI PTE. LTD.
 * All rights reserved.
 */

import crypto from 'crypto';
import { customAlphabet } from 'nanoid';
import { promises as fs } from 'fs';
import path from 'path';

function convertMapsToObjects(obj: any): any {
  if (obj instanceof Map) {
    obj = Object.fromEntries(obj);
  }

  for (const key of Object.keys(obj)) {
    if (obj[key] instanceof Map) {
      obj[key] = Object.fromEntries(obj[key]);
    } else if (typeof obj[key] === 'object' && obj[key] !== null) {
      obj[key] = convertMapsToObjects(obj[key]);
    }
  }

  return obj;
}

function encrypt(text: string, secretKey: Buffer, algorithm: string, signature?: { hmacSecret: Buffer, data: string }): string {
  // Generate an initialization vector
  const iv = crypto.randomBytes(16);

  // Create a new cipher using the algorithm, key, and iv
  const cipher = crypto.createCipheriv(algorithm, secretKey, iv);

  // Create the encrypted buffer
  const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);

  let result = `${iv.toString('hex')}:${encrypted.toString('hex')}`;

  // Create an HMAC of the baseURL
  if (signature) {
    const hmac = crypto.createHmac('sha256', signature.hmacSecret);
    hmac.update(signature.data);
    const hmacDigest = hmac.digest('hex');
    omnilog.debug(`Encrypt: HMAC: ${hmacDigest}`)
    result += `:${hmacDigest}`;
  }

  // Return the concatenated iv, encrypted content
  return result;
}

function decrypt(encryptedData: string, secretKey: Buffer, algorithm: string, signature?: { hmacSecret: Buffer, data: string }): string | null {
  // Split the data into its components
  const textParts = encryptedData.split(':');

  if (signature && textParts.length !== 3) {
    throw new Error('Invalid encrypted data format');
  } else if (!signature && textParts.length !== 2) {
    throw new Error('Invalid encrypted data format');
  }

  const iv = Buffer.from(textParts[0], 'hex');
  const encryptedText = Buffer.from(textParts[1], 'hex');

  // If signature is provided, validate the HMAC
  if (signature) {
    const hmacDigest = textParts[2];
    const hmac = crypto.createHmac('sha256', signature.hmacSecret);
    hmac.update(signature.data);
    const generatedHmac = hmac.digest('hex');
    omnilog.debug(`Decrypt: HMAC: ${generatedHmac} vs ${hmacDigest}`)
    // const hmacBuffer = hmac.digest();
    // const hmacDigestBuffer = Buffer.from(hmacDigest, 'hex');
    // omnilog.debug('Lengths:', hmacDigestBuffer.length, hmacBuffer.length)
    // omnilog.debug('Original:', hmacDigestBuffer.toString('binary'));
    // omnilog.debug('Generated:', hmacBuffer.toString('binary'));
    omnilog.debug('Siganature', signature.data)
    // if (!hmacBuffer.equals(hmacDigestBuffer)) {
    //   throw new Error('Data signature is invalid');
    // }
    if (hmacDigest.trim().toLowerCase() !== generatedHmac.trim().toLowerCase()) {
      throw new Error('Data signature is invalid');
    }
  }

  // Proceed with decryption
  const decipher = crypto.createDecipheriv(algorithm, secretKey, iv);
  let decrypted;
  try {
    decrypted = Buffer.concat([decipher.update(encryptedText), decipher.final()]);
  } catch (error) {
    throw new Error('Decryption failed');
  }

  return decrypted.toString();
}

function hashPassword(password: string, saltBuff: Buffer) {
  return crypto.pbkdf2Sync(password, saltBuff, 210000, 64, 'sha512');
}

function generateId() {
  const characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  const length = 16;
  const nanoid = customAlphabet(characters, length);
  return nanoid();
}

function capitalize(word: string): string {
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
}

function randomBytes(length: number): string {
  return crypto.randomBytes(length).toString('hex');
}

export { convertMapsToObjects, decrypt, encrypt, hashPassword, generateId, capitalize, randomBytes };

async function* getFiles(directory: string): AsyncGenerator<string> {
  const entries = await fs.readdir(directory, { withFileTypes: true });

  for (const entry of entries) {
    const fullPath = path.join(directory, entry.name);
    if (entry.isDirectory()) {
      yield* getFiles(fullPath);
    } else if (entry.isFile()) {
      yield fullPath;
    }
  }
}

export async function scanDirectory(directoryPath: string): Promise<string[]> {
  const files: string[] = [];

  for await (const file of getFiles(directoryPath)) {
    files.push(file);
  }

  return files;
}