nestjs-cryptography 2.2.3 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,14 @@
1
1
  [![codecov](https://codecov.io/github/mjorgegulab/nestjs-cryptography/branch/main/graph/badge.svg?token=I0ZFVZTREB)](https://codecov.io/github/mjorgegulab/nestjs-cryptography)
2
2
  [![Codacy Badge](https://app.codacy.com/project/badge/Grade/d4c33e2a77d64f89ba7f6ba54c6d8cb5)](https://app.codacy.com/gh/mjorgegulab/nestjs-cryptography/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
3
3
  [![CodeQL](https://github.com/mjorgegulab/nestjs-cryptography/actions/workflows/github-code-scanning/codeql/badge.svg?branch=main)](https://github.com/mjorgegulab/nestjs-cryptography/actions/workflows/github-code-scanning/codeql)
4
+ [![Publish Wiki](https://github.com/mjorgegulab/nestjs-cryptography/actions/workflows/deploy-wiki-prod.yaml/badge.svg?branch=main)](https://nestjs-cryptography.thewolfx41.dev)
4
5
 
5
6
  # NestJS - Cryptography
6
- ## Secure NestJS cryptography module 🔐
7
+
8
+ <p align="center">
9
+ <a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
10
+ </p>
11
+
7
12
 
8
13
  ## Quick Start
9
14
 
package/SECURITY.md ADDED
@@ -0,0 +1,14 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ currently being supported with security updates.
6
+
7
+ | Version | Supported |
8
+ | ------- | ------------------ |
9
+ | 3.x | :white_check_mark: |
10
+ | 2.x | :x: |
11
+
12
+ ## Reporting a Vulnerability
13
+
14
+ Please report security issues to security@thewolfx41.dev
@@ -1 +1,16 @@
1
+ import { Argon2Type } from './interfaces';
1
2
  export declare const CRYPTOGRAPHY_OPTIONS = "CRYPTOGRAPHY_OPTIONS";
3
+ export declare const DEFAULT_KDF_CRYPTOGRAPHY_OPTIONS: {
4
+ outputKeyLength: number;
5
+ argon2Type: Argon2Type;
6
+ memoryCost: number;
7
+ timeCost: number;
8
+ };
9
+ export declare const DEFAULT_HASHING_CRYPTOGRAPHY_OPTIONS: {
10
+ password: {
11
+ outputKeyLength: number;
12
+ argon2Type: Argon2Type;
13
+ memoryCost: number;
14
+ timeCost: number;
15
+ };
16
+ };
package/dist/constants.js CHANGED
@@ -1,4 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CRYPTOGRAPHY_OPTIONS = void 0;
3
+ exports.DEFAULT_HASHING_CRYPTOGRAPHY_OPTIONS = exports.DEFAULT_KDF_CRYPTOGRAPHY_OPTIONS = exports.CRYPTOGRAPHY_OPTIONS = void 0;
4
+ const interfaces_1 = require("./interfaces");
4
5
  exports.CRYPTOGRAPHY_OPTIONS = 'CRYPTOGRAPHY_OPTIONS';
6
+ exports.DEFAULT_KDF_CRYPTOGRAPHY_OPTIONS = {
7
+ outputKeyLength: 32,
8
+ argon2Type: interfaces_1.Argon2Type.argon2i,
9
+ memoryCost: 65536,
10
+ timeCost: 3,
11
+ };
12
+ exports.DEFAULT_HASHING_CRYPTOGRAPHY_OPTIONS = {
13
+ password: {
14
+ outputKeyLength: 64,
15
+ argon2Type: interfaces_1.Argon2Type.argon2id,
16
+ memoryCost: 131072,
17
+ timeCost: 4,
18
+ },
19
+ };
@@ -1,36 +1,35 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
1
  import * as crypto from 'node:crypto';
4
- import { CryptographyOptionsInterface } from './interfaces';
2
+ import { CryptographyOptionsInterface, GenericOptionsInterface } from './interfaces';
5
3
  export declare class CryptographyService {
6
- private options;
7
- constructor(options: CryptographyOptionsInterface);
8
- private convertDataToBuffer;
4
+ private moduleOptions;
5
+ constructor(moduleOptions: CryptographyOptionsInterface);
6
+ private convertInputData;
7
+ private checkModuleOptions;
9
8
  private extractIV;
10
9
  private extractAuthTagFromCypheredData;
11
10
  private extractCipheredData;
12
11
  private extractSalt;
12
+ private extractSaltFromHmac;
13
13
  private extractCipheredDEK;
14
14
  private extractCipheredDataWithDEK;
15
- private createSaferRandomData;
16
- private generateKeyDEK;
15
+ private createHmacSecureKey;
16
+ createSafeRandomData(length: number): Buffer;
17
17
  genUUID(secure?: boolean): string;
18
- genRandomPassword(length: number, encoding: 'base64' | 'hex'): string;
18
+ genRandomPassword(length: number): string;
19
19
  generateSymmetricKey(length?: number): crypto.KeyObject;
20
- deriveMasterKey(masterKey: string | Buffer, salt: Buffer, length: number): Promise<Buffer>;
21
- createArgonHashFromPassword(data: string | Buffer): Promise<Buffer>;
22
- verifyArgonHashFromPassword(hash: string, data: string | Buffer): Promise<boolean>;
23
- createCustomHash(algorithm: string, data: string, outputLength?: number): Buffer;
24
- verifyCustomHash(algorithm: string, data: string, oldHash: string | Buffer, outputLength?: number): boolean;
25
- createSecureHash(data: string): Buffer;
26
- verifySecureHash(data: string, oldHash: string | Buffer): boolean;
27
- createCustomHmac(algorithm: string, key: Buffer, data: string): Buffer;
28
- verifyCustomHmac(algorithm: string, key: Buffer, data: string, oldHmac: string | Buffer): boolean;
29
- createSecureHmac(data: string): Buffer;
30
- verifySecureHmac(data: string, oldHmac: Buffer | string): boolean;
31
- createInsecureFastHash(data: string): Buffer;
32
- private symmetricDataEncrypt;
33
- private symmetricDataDecrypt;
34
- symmetricSecureDataEncrypt(data: string | Buffer): Promise<Buffer>;
35
- symmetricSecureDataDecrypt(data: string | Buffer): Promise<Buffer>;
20
+ deriveMasterKey(masterKey: string | Buffer, salt: Buffer, length?: number): Promise<Buffer>;
21
+ createArgon2HashFromPassword(data: string | Buffer): Promise<Buffer>;
22
+ verifyArgon2HashFromPassword(hash: string, data: string | Buffer): Promise<boolean>;
23
+ createCustomHash(algorithm: string, data: string | Buffer, options?: GenericOptionsInterface): Buffer;
24
+ verifyCustomHash(algorithm: string, data: string | Buffer, oldHash: string | Buffer, options?: GenericOptionsInterface): boolean;
25
+ createSecureHash(data: string | Buffer, options?: GenericOptionsInterface): Buffer;
26
+ verifySecureHash(data: string | Buffer, oldHash: string | Buffer, options?: GenericOptionsInterface): boolean;
27
+ createCustomHmac(algorithm: string, key: string | Buffer, data: string | Buffer, options?: GenericOptionsInterface): Buffer;
28
+ verifyCustomHmac(algorithm: string, key: string | Buffer, data: string | Buffer, oldHmac: string | Buffer, options?: GenericOptionsInterface): boolean;
29
+ createSecureHmac(data: string | Buffer, options?: GenericOptionsInterface): Buffer;
30
+ verifySecureHmac(data: string | Buffer, oldHmac: string | Buffer, options?: GenericOptionsInterface): boolean;
31
+ symmetricDataEncrypt(data: string | Buffer, key: string | Buffer, options?: GenericOptionsInterface): Promise<Buffer>;
32
+ symmetricDataDecrypt(data: string | Buffer, key: string | Buffer, options?: GenericOptionsInterface): Promise<Buffer>;
33
+ symmetricSecureDataEncrypt(data: string | Buffer, options?: GenericOptionsInterface): Promise<Buffer>;
34
+ symmetricSecureDataDecrypt(data: string | Buffer, options?: GenericOptionsInterface): Promise<Buffer>;
36
35
  }
@@ -40,12 +40,30 @@ const crypto = __importStar(require("node:crypto"));
40
40
  const argon2 = __importStar(require("argon2"));
41
41
  const common_1 = require("@nestjs/common");
42
42
  const cryptography_module_definition_1 = require("./cryptography.module-definition");
43
+ const constants_1 = require("./constants");
43
44
  let CryptographyService = class CryptographyService {
44
- constructor(options) {
45
- this.options = options;
45
+ constructor(moduleOptions) {
46
+ this.moduleOptions = moduleOptions;
46
47
  }
47
- convertDataToBuffer(data) {
48
- return Buffer.isBuffer(data) ? data : Buffer.from(data, 'hex');
48
+ convertInputData(data, inputEncoding) {
49
+ if (Buffer.isBuffer(data)) {
50
+ return data;
51
+ }
52
+ else if (typeof data === 'string') {
53
+ return Buffer.from(data, inputEncoding ?? 'utf8');
54
+ }
55
+ else {
56
+ throw new Error('Unsupported input type');
57
+ }
58
+ }
59
+ checkModuleOptions(parent, options) {
60
+ for (const option in options) {
61
+ if (typeof option === 'object')
62
+ this.checkModuleOptions(parent, option);
63
+ if (options[option] === undefined || options[option] === null) {
64
+ throw new Error(`[CryptographyModule] [${parent}] Option ${option} is not defined on the configuration`);
65
+ }
66
+ }
49
67
  }
50
68
  extractIV(data) {
51
69
  return data.subarray(0, 12);
@@ -59,157 +77,209 @@ let CryptographyService = class CryptographyService {
59
77
  extractSalt(data) {
60
78
  return data.subarray(12, 76);
61
79
  }
80
+ extractSaltFromHmac(data) {
81
+ return data.subarray(0, 16);
82
+ }
62
83
  extractCipheredDEK(data) {
63
84
  return data.subarray(0, 124);
64
85
  }
65
86
  extractCipheredDataWithDEK(data) {
66
87
  return data.subarray(124, data.length);
67
88
  }
68
- createSaferRandomData(length) {
69
- return Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(crypto.randomBytes(64)), crypto.randomBytes(64), Buffer.alloc(0), length));
89
+ createHmacSecureKey(key, salt) {
90
+ return Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(key), salt, Buffer.alloc(0), 64));
70
91
  }
71
- generateKeyDEK() {
72
- return crypto.createSecretKey(this.createSaferRandomData(32));
92
+ createSafeRandomData(length) {
93
+ return Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(crypto.randomBytes(64)), crypto.randomBytes(64), Buffer.alloc(0), length));
73
94
  }
74
95
  genUUID(secure = false) {
75
96
  return crypto.randomUUID({
76
97
  disableEntropyCache: secure,
77
98
  });
78
99
  }
79
- genRandomPassword(length, encoding) {
80
- return crypto.randomBytes(length).toString(encoding).slice(0, length);
100
+ genRandomPassword(length) {
101
+ return crypto.randomBytes(length).toString('base64').slice(0, length);
81
102
  }
82
103
  generateSymmetricKey(length = 256) {
83
104
  return crypto.generateKeySync('hmac', { length });
84
105
  }
85
106
  async deriveMasterKey(masterKey, salt, length) {
107
+ if (!this.moduleOptions?.useDefaultValues) {
108
+ this.checkModuleOptions('KDF', {
109
+ ...(!length && {
110
+ outputKeyLength: this.moduleOptions?.kdf?.outputKeyLength,
111
+ }),
112
+ argon2Type: this.moduleOptions?.kdf?.argon2Type,
113
+ memoryCost: this.moduleOptions?.kdf?.memoryCost,
114
+ timeCost: this.moduleOptions?.kdf?.timeCost,
115
+ });
116
+ }
117
+ else {
118
+ this.moduleOptions.kdf = {
119
+ ...this.moduleOptions?.kdf,
120
+ };
121
+ this.moduleOptions.kdf.outputKeyLength =
122
+ constants_1.DEFAULT_KDF_CRYPTOGRAPHY_OPTIONS.outputKeyLength;
123
+ this.moduleOptions.kdf.argon2Type =
124
+ constants_1.DEFAULT_KDF_CRYPTOGRAPHY_OPTIONS.argon2Type;
125
+ this.moduleOptions.kdf.memoryCost =
126
+ constants_1.DEFAULT_KDF_CRYPTOGRAPHY_OPTIONS.memoryCost;
127
+ this.moduleOptions.kdf.timeCost =
128
+ constants_1.DEFAULT_KDF_CRYPTOGRAPHY_OPTIONS.timeCost;
129
+ }
130
+ console.log(this.moduleOptions.kdf);
86
131
  return await argon2.hash(masterKey, {
87
- hashLength: length ? length : this.options.kdf.defaultOutputKeyLength,
132
+ hashLength: length ?? this.moduleOptions.kdf.outputKeyLength,
88
133
  salt: salt,
89
- type: this.options.kdf.argon2Type,
90
- memoryCost: this.options.kdf.memoryCost,
91
- timeCost: this.options.kdf.timeCost,
134
+ type: this.moduleOptions.kdf.argon2Type,
135
+ memoryCost: this.moduleOptions.kdf.memoryCost,
136
+ timeCost: this.moduleOptions.kdf.timeCost,
92
137
  raw: true,
93
138
  });
94
139
  }
95
- async createArgonHashFromPassword(data) {
140
+ async createArgon2HashFromPassword(data) {
141
+ if (!this.moduleOptions?.useDefaultValues) {
142
+ this.checkModuleOptions('HASHING_PASSWORD', {
143
+ outputKeyLength: this.moduleOptions?.hashing?.password?.outputKeyLength,
144
+ argon2Type: this.moduleOptions?.hashing?.password?.argon2Type,
145
+ memoryCost: this.moduleOptions?.hashing?.password?.memoryCost,
146
+ timeCost: this.moduleOptions?.hashing?.password?.timeCost,
147
+ });
148
+ }
149
+ else {
150
+ this.moduleOptions.hashing = {
151
+ ...this.moduleOptions?.hashing,
152
+ password: {
153
+ ...this.moduleOptions.hashing?.password,
154
+ },
155
+ };
156
+ this.moduleOptions.hashing.password.outputKeyLength =
157
+ constants_1.DEFAULT_HASHING_CRYPTOGRAPHY_OPTIONS.password.outputKeyLength;
158
+ this.moduleOptions.hashing.password.argon2Type =
159
+ constants_1.DEFAULT_HASHING_CRYPTOGRAPHY_OPTIONS.password.argon2Type;
160
+ this.moduleOptions.hashing.password.memoryCost =
161
+ constants_1.DEFAULT_HASHING_CRYPTOGRAPHY_OPTIONS.password.memoryCost;
162
+ this.moduleOptions.hashing.password.timeCost =
163
+ constants_1.DEFAULT_HASHING_CRYPTOGRAPHY_OPTIONS.password.timeCost;
164
+ }
96
165
  const tmpData = await argon2.hash(data, {
97
- hashLength: this.options.hashing.password.outputKeyLength,
98
- type: this.options.hashing.password.argon2Type,
99
- memoryCost: this.options.hashing.password.memoryCost,
100
- timeCost: this.options.hashing.password.timeCost,
166
+ hashLength: this.moduleOptions.hashing.password.outputKeyLength,
167
+ type: this.moduleOptions.hashing.password.argon2Type,
168
+ memoryCost: this.moduleOptions.hashing.password.memoryCost,
169
+ timeCost: this.moduleOptions.hashing.password.timeCost,
101
170
  raw: false,
102
171
  });
103
172
  return Buffer.isBuffer(tmpData) ? tmpData : Buffer.from(tmpData);
104
173
  }
105
- async verifyArgonHashFromPassword(hash, data) {
174
+ async verifyArgon2HashFromPassword(hash, data) {
106
175
  return await argon2.verify(hash, data);
107
176
  }
108
- createCustomHash(algorithm, data, outputLength = 0) {
177
+ createCustomHash(algorithm, data, options) {
178
+ const inputData = this.convertInputData(data, options?.inputDataEncoding);
109
179
  const hash = crypto.createHash(algorithm, {
110
- ...(outputLength && { outputLength }),
180
+ ...(options?.outputLength && { outputLength: options?.outputLength }),
111
181
  });
112
- hash.update(data);
182
+ hash.update(inputData);
113
183
  return hash.digest();
114
184
  }
115
- verifyCustomHash(algorithm, data, oldHash, outputLength = 0) {
116
- const hash = this.createCustomHash(algorithm, data, outputLength);
117
- if (Buffer.isBuffer(oldHash)) {
118
- return crypto.timingSafeEqual(hash, oldHash);
119
- }
120
- else {
121
- return crypto.timingSafeEqual(Buffer.from(oldHash, 'hex'), hash);
122
- }
185
+ verifyCustomHash(algorithm, data, oldHash, options) {
186
+ const inputOldHashData = this.convertInputData(oldHash, options?.inputDataEncoding);
187
+ const hash = this.createCustomHash(algorithm, data, options);
188
+ return crypto.timingSafeEqual(hash, inputOldHashData);
123
189
  }
124
- createSecureHash(data) {
125
- return this.createCustomHash('shake256', data, 48);
190
+ createSecureHash(data, options) {
191
+ return this.createCustomHash('shake256', data, {
192
+ ...options,
193
+ outputLength: 48,
194
+ });
126
195
  }
127
- verifySecureHash(data, oldHash) {
128
- const hash = this.createCustomHash('shake256', data, 48);
129
- const buffOldHash = Buffer.isBuffer(oldHash)
130
- ? oldHash
131
- : Buffer.from(oldHash, 'hex');
132
- return crypto.timingSafeEqual(hash, buffOldHash);
196
+ verifySecureHash(data, oldHash, options) {
197
+ return this.verifyCustomHash('shake256', data, oldHash, {
198
+ ...options,
199
+ outputLength: 48,
200
+ });
133
201
  }
134
- createCustomHmac(algorithm, key, data) {
135
- const hmac = crypto.createHmac(algorithm, crypto.createSecretKey(key));
136
- hmac.update(data);
202
+ createCustomHmac(algorithm, key, data, options) {
203
+ const inputKey = this.convertInputData(key, options?.inputKeyEncoding);
204
+ const inputData = this.convertInputData(data, options?.inputDataEncoding);
205
+ const hmac = crypto.createHmac(algorithm, crypto.createSecretKey(inputKey));
206
+ hmac.update(inputData);
137
207
  key = null;
138
208
  return hmac.digest();
139
209
  }
140
- verifyCustomHmac(algorithm, key, data, oldHmac) {
141
- const hmac = this.createCustomHmac(algorithm, key, data);
142
- if (Buffer.isBuffer(oldHmac)) {
143
- return crypto.timingSafeEqual(hmac, oldHmac);
144
- }
145
- else {
146
- return crypto.timingSafeEqual(Buffer.from(oldHmac, 'hex'), hmac);
147
- }
210
+ verifyCustomHmac(algorithm, key, data, oldHmac, options) {
211
+ const inputOldHmacData = this.convertInputData(oldHmac, options?.inputDataEncoding);
212
+ const hmac = this.createCustomHmac(algorithm, key, data, options);
213
+ return crypto.timingSafeEqual(hmac, inputOldHmacData);
148
214
  }
149
- createSecureHmac(data) {
150
- const key = Buffer.from(this.options.hashing.hmac.masterKey, 'hex');
215
+ createSecureHmac(data, options) {
216
+ this.checkModuleOptions('HMAC', {
217
+ masterKey: this.moduleOptions?.hashing?.hmac?.masterKey,
218
+ });
219
+ const key = Buffer.from(this.moduleOptions.hashing.hmac.masterKey, 'hex');
151
220
  const salt = crypto.randomBytes(16);
152
- const secureKey = Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(key), salt, Buffer.alloc(0), 64));
153
- const hmac = this.createCustomHmac('sha3-256', secureKey, data);
154
- return Buffer.concat([Buffer.from(salt), Buffer.from(hmac)], salt.length + hmac.length);
155
- }
156
- verifySecureHmac(data, oldHmac) {
157
- const key = Buffer.from(this.options.hashing.hmac.masterKey, 'hex');
158
- const buffOldHmac = Buffer.isBuffer(oldHmac)
159
- ? oldHmac
160
- : Buffer.from(oldHmac, 'hex');
161
- const saltOldHmac = buffOldHmac.subarray(0, 16);
221
+ const secureKey = this.createHmacSecureKey(key, salt);
222
+ const hmac = this.createCustomHmac('sha3-256', secureKey, data, options);
223
+ return Buffer.concat([salt, hmac], salt.length + hmac.length);
224
+ }
225
+ verifySecureHmac(data, oldHmac, options) {
226
+ this.checkModuleOptions('HMAC', {
227
+ masterKey: this.moduleOptions?.hashing?.hmac?.masterKey,
228
+ });
229
+ const key = Buffer.from(this.moduleOptions.hashing.hmac.masterKey, 'hex');
230
+ const buffOldHmac = this.convertInputData(oldHmac, options?.inputDataEncoding);
231
+ const saltOldHmac = this.extractSaltFromHmac(buffOldHmac);
162
232
  const hashOldHmac = buffOldHmac.subarray(16, buffOldHmac.length);
163
- const secureKey = Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(key), saltOldHmac, Buffer.alloc(0), 64));
164
- const hmac = this.createCustomHmac('sha3-256', secureKey, data);
233
+ const secureKey = this.createHmacSecureKey(key, saltOldHmac);
234
+ const hmac = this.createCustomHmac('sha3-256', secureKey, data, options);
165
235
  return crypto.timingSafeEqual(hmac, hashOldHmac);
166
236
  }
167
- createInsecureFastHash(data) {
168
- return crypto.createHash('sha1').update(data).digest();
169
- }
170
- async symmetricDataEncrypt(data, key) {
171
- const iv = this.createSaferRandomData(12);
172
- const salt = this.createSaferRandomData(64);
173
- const secureEncryptionKey = await this.deriveMasterKey(key, salt, 32);
237
+ async symmetricDataEncrypt(data, key, options) {
238
+ const inputData = this.convertInputData(data, options?.inputDataEncoding);
239
+ const inputKey = this.convertInputData(key, options?.inputKeyEncoding);
240
+ const iv = this.createSafeRandomData(12);
241
+ const salt = this.createSafeRandomData(64);
242
+ const secureEncryptionKey = await this.deriveMasterKey(inputKey, salt, 32);
174
243
  const cipher = crypto.createCipheriv('aes-256-gcm', crypto.createSecretKey(secureEncryptionKey), iv, {
175
244
  authTagLength: 16,
176
245
  });
177
- let cipheredData = cipher.update(data);
178
- cipheredData = Buffer.concat([
179
- Buffer.from(cipheredData),
180
- Buffer.from(cipher.final()),
181
- ]);
246
+ let cipheredData = cipher.update(inputData);
247
+ cipheredData = Buffer.concat([cipheredData, cipher.final()]);
182
248
  return Buffer.concat([iv, salt, cipher.getAuthTag(), cipheredData]);
183
249
  }
184
- async symmetricDataDecrypt(data, key) {
185
- data = Buffer.isBuffer(data) ? data : Buffer.from(data, 'hex');
186
- const iv = this.extractIV(data);
187
- const salt = this.extractSalt(data);
188
- const authTag = this.extractAuthTagFromCypheredData(data);
189
- const cipheredData = this.extractCipheredData(data);
190
- const decryptionKey = await this.deriveMasterKey(key, salt, 32);
250
+ async symmetricDataDecrypt(data, key, options) {
251
+ const inputData = this.convertInputData(data, options?.inputDataEncoding);
252
+ const inputKey = this.convertInputData(key, options?.inputKeyEncoding);
253
+ const iv = this.extractIV(inputData);
254
+ const salt = this.extractSalt(inputData);
255
+ const authTag = this.extractAuthTagFromCypheredData(inputData);
256
+ const cipheredData = this.extractCipheredData(inputData);
257
+ const decryptionKey = await this.deriveMasterKey(inputKey, salt, 32);
191
258
  const decipher = crypto.createDecipheriv('aes-256-gcm', crypto.createSecretKey(decryptionKey), iv, {
192
259
  authTagLength: 16,
193
260
  });
194
261
  decipher.setAuthTag(authTag);
195
262
  let decipheredData = decipher.update(cipheredData);
196
- decipheredData = Buffer.concat([
197
- Buffer.from(decipheredData),
198
- Buffer.from(decipher.final()),
199
- ]);
263
+ decipheredData = Buffer.concat([decipheredData, decipher.final()]);
200
264
  return decipheredData;
201
265
  }
202
- async symmetricSecureDataEncrypt(data) {
203
- const dek = this.createSaferRandomData(32);
204
- const cipheredData = await this.symmetricDataEncrypt(data, dek);
205
- const cipheredDek = await this.symmetricDataEncrypt(dek, this.options.encryption.symmetric.masterKey);
266
+ async symmetricSecureDataEncrypt(data, options) {
267
+ this.checkModuleOptions('SYMMETRIC_ENCRYPTION', {
268
+ masterKey: this.moduleOptions?.encryption?.symmetric?.masterKey,
269
+ });
270
+ const dek = this.createSafeRandomData(32);
271
+ const cipheredData = await this.symmetricDataEncrypt(data, dek, options);
272
+ const cipheredDek = await this.symmetricDataEncrypt(dek, this.moduleOptions.encryption.symmetric.masterKey);
206
273
  return Buffer.concat([cipheredDek, cipheredData]);
207
274
  }
208
- async symmetricSecureDataDecrypt(data) {
209
- data = Buffer.isBuffer(data) ? data : Buffer.from(data, 'hex');
210
- const cipheredDek = this.extractCipheredDEK(data);
211
- const cipheredData = this.extractCipheredDataWithDEK(data);
212
- const decipheredDek = await this.symmetricDataDecrypt(cipheredDek, this.options.encryption.symmetric.masterKey);
275
+ async symmetricSecureDataDecrypt(data, options) {
276
+ this.checkModuleOptions('SYMMETRIC_ENCRYPTION', {
277
+ masterKey: this.moduleOptions?.encryption?.symmetric?.masterKey,
278
+ });
279
+ const inputData = this.convertInputData(data, options?.inputDataEncoding);
280
+ const cipheredDek = this.extractCipheredDEK(inputData);
281
+ const cipheredData = this.extractCipheredDataWithDEK(inputData);
282
+ const decipheredDek = await this.symmetricDataDecrypt(cipheredDek, this.moduleOptions.encryption.symmetric.masterKey);
213
283
  return await this.symmetricDataDecrypt(cipheredData, decipheredDek);
214
284
  }
215
285
  };
@@ -3,27 +3,31 @@ export declare enum Argon2Type {
3
3
  argon2i = 1,
4
4
  argon2id = 2
5
5
  }
6
- export interface CryptographyOptionsInterface {
7
- kdf: {
8
- defaultOutputKeyLength: number;
6
+ export interface CryptographyKdfOptions {
7
+ outputKeyLength: number;
8
+ argon2Type: Argon2Type;
9
+ memoryCost: number;
10
+ timeCost: number;
11
+ }
12
+ export interface CryptographyHashingOptions {
13
+ password: {
14
+ outputKeyLength: number;
9
15
  argon2Type: Argon2Type;
10
16
  memoryCost: number;
11
17
  timeCost: number;
12
18
  };
13
- hashing: {
14
- password: {
15
- outputKeyLength: number;
16
- argon2Type: Argon2Type;
17
- memoryCost: number;
18
- timeCost: number;
19
- };
20
- hmac: {
21
- masterKey: string;
22
- };
19
+ hmac: {
20
+ masterKey: string;
23
21
  };
24
- encryption: {
25
- symmetric: {
26
- masterKey: string;
27
- };
22
+ }
23
+ export interface CryptographyEncryptionOptions {
24
+ symmetric: {
25
+ masterKey: string;
28
26
  };
29
27
  }
28
+ export interface CryptographyOptionsInterface {
29
+ useDefaultValues?: boolean;
30
+ kdf?: Partial<CryptographyKdfOptions>;
31
+ hashing?: Partial<CryptographyHashingOptions>;
32
+ encryption?: Partial<CryptographyEncryptionOptions>;
33
+ }
@@ -0,0 +1,5 @@
1
+ export interface GenericOptionsInterface {
2
+ outputLength?: number;
3
+ inputDataEncoding?: BufferEncoding;
4
+ inputKeyEncoding?: BufferEncoding;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1 +1,2 @@
1
1
  export * from './cryptography-options.interface';
2
+ export * from './generic-options.interface';
@@ -15,3 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./cryptography-options.interface"), exports);
18
+ __exportStar(require("./generic-options.interface"), exports);
package/package.json CHANGED
@@ -1,15 +1,12 @@
1
1
  {
2
2
  "name": "nestjs-cryptography",
3
- "version": "2.2.3",
3
+ "version": "3.1.0",
4
4
  "author": {
5
5
  "name": "Marc Jorge Gonzalez",
6
6
  "url": "https://github.com/mjorgegulab"
7
7
  },
8
8
  "license": "MIT",
9
9
  "description": "Secure NestJS cryptography module 🔐",
10
- "publishConfig": {
11
- "registry": "https://npm.pkg.github.com/"
12
- },
13
10
  "homepage": "https://nestjs-cryptography.thewolfx41.dev",
14
11
  "repository": {
15
12
  "type": "git",
@@ -24,59 +21,70 @@
24
21
  "lint": "eslint --fix",
25
22
  "publish:npm": "npm publish --access=public",
26
23
  "prepublish": "yarn run build",
27
- "prepack": "yarn run build"
24
+ "prepack": "yarn run build",
25
+ "test": "jest",
26
+ "test:cov": "jest --coverage",
27
+ "all": "yarn format && yarn lint && yarn test && yarn prepack"
28
28
  },
29
29
  "dependencies": {
30
30
  "argon2": "^0.41.1"
31
31
  },
32
32
  "devDependencies": {
33
- "@nestjs/common": "^10.4.1",
34
- "@nestjs/core": "^10.4.1",
35
33
  "@nestjs/cli": "^10.4.5",
36
- "reflect-metadata": "^0.2.2",
37
- "rxjs": "^7.8.1",
38
- "@nestjs/platform-express": "^10.4.1",
34
+ "@nestjs/common": "^10.4.4",
35
+ "@nestjs/core": "^10.4.4",
36
+ "@nestjs/platform-express": "^10.4.4",
39
37
  "@nestjs/schematics": "^10.1.4",
40
- "@nestjs/testing": "^10.4.1",
38
+ "@nestjs/testing": "^10.4.4",
41
39
  "@types/express": "^4.17.21",
42
- "@types/jest": "29.5.12",
43
- "@types/node": "22.5.4",
40
+ "@types/jest": "29.5.13",
41
+ "@types/node": "22.7.5",
44
42
  "@types/supertest": "^6.0.2",
45
43
  "@typescript-eslint/eslint-plugin": "^7.12.0",
46
44
  "@typescript-eslint/parser": "^7.12.0",
47
45
  "eslint": "^8.57.0",
48
46
  "eslint-config-prettier": "^9.1.0",
49
- "eslint-plugin-prettier": "^5.1.3",
47
+ "eslint-plugin-prettier": "^5.2.1",
50
48
  "jest": "29.7.0",
51
49
  "prettier": "^3.3.3",
50
+ "reflect-metadata": "^0.2.2",
51
+ "rimraf": "^6.0.1",
52
+ "rxjs": "^7.8.1",
52
53
  "source-map-support": "^0.5.21",
53
54
  "supertest": "^7.0.0",
54
55
  "ts-jest": "29.2.5",
55
56
  "ts-loader": "^9.5.1",
56
57
  "ts-node": "^10.9.2",
57
58
  "tsconfig-paths": "4.2.0",
58
- "typescript": "^5.4.5",
59
- "rimraf": "^5.0.7"
59
+ "typescript": "^5.6.3"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "@nestjs/common": "^9.0.0 || ^10.0.0",
63
63
  "@nestjs/core": "^9.0.0 || ^10.0.0"
64
64
  },
65
65
  "jest": {
66
+ "testPathIgnorePatterns": [
67
+ "/node_modules/",
68
+ "/wiki/"
69
+ ],
70
+ "modulePathIgnorePatterns": [
71
+ "/wiki/"
72
+ ],
66
73
  "moduleFileExtensions": [
67
74
  "js",
68
75
  "json",
69
76
  "ts"
70
77
  ],
71
- "rootDir": "src",
72
- "testRegex": ".*\\.spec\\.ts$",
78
+ "rootDir": "./",
79
+ "testMatch": [
80
+ "**/test/**/?(*.)+(spec|test).[tj]s?(x)"
81
+ ],
73
82
  "transform": {
74
83
  "^.+\\.(t|j)s$": "ts-jest"
75
84
  },
76
85
  "collectCoverageFrom": [
77
- "**/*.(t|j)s"
86
+ "**/*.service.ts"
78
87
  ],
79
- "coverageDirectory": "../coverage",
80
88
  "testEnvironment": "node"
81
89
  }
82
90
  }