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 +6 -1
- package/SECURITY.md +14 -0
- package/dist/constants.d.ts +15 -0
- package/dist/constants.js +16 -1
- package/dist/cryptography.service.d.ts +24 -25
- package/dist/cryptography.service.js +166 -96
- package/dist/interfaces/cryptography-options.interface.d.ts +21 -17
- package/dist/interfaces/generic-options.interface.d.ts +5 -0
- package/dist/interfaces/generic-options.interface.js +2 -0
- package/dist/interfaces/index.d.ts +1 -0
- package/dist/interfaces/index.js +1 -0
- package/package.json +28 -20
package/README.md
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
[](https://codecov.io/github/mjorgegulab/nestjs-cryptography)
|
|
2
2
|
[](https://app.codacy.com/gh/mjorgegulab/nestjs-cryptography/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
|
3
3
|
[](https://github.com/mjorgegulab/nestjs-cryptography/actions/workflows/github-code-scanning/codeql)
|
|
4
|
+
[](https://nestjs-cryptography.thewolfx41.dev)
|
|
4
5
|
|
|
5
6
|
# NestJS - Cryptography
|
|
6
|
-
|
|
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
|
package/dist/constants.d.ts
CHANGED
|
@@ -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
|
|
7
|
-
constructor(
|
|
8
|
-
private
|
|
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
|
|
16
|
-
|
|
15
|
+
private createHmacSecureKey;
|
|
16
|
+
createSafeRandomData(length: number): Buffer;
|
|
17
17
|
genUUID(secure?: boolean): string;
|
|
18
|
-
genRandomPassword(length: number
|
|
18
|
+
genRandomPassword(length: number): string;
|
|
19
19
|
generateSymmetricKey(length?: number): crypto.KeyObject;
|
|
20
|
-
deriveMasterKey(masterKey: string | Buffer, salt: Buffer, length
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
createCustomHash(algorithm: string, data: string,
|
|
24
|
-
verifyCustomHash(algorithm: string, data: string, oldHash: string | Buffer,
|
|
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:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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(
|
|
45
|
-
this.
|
|
45
|
+
constructor(moduleOptions) {
|
|
46
|
+
this.moduleOptions = moduleOptions;
|
|
46
47
|
}
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
69
|
-
return Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(
|
|
89
|
+
createHmacSecureKey(key, salt) {
|
|
90
|
+
return Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(key), salt, Buffer.alloc(0), 64));
|
|
70
91
|
}
|
|
71
|
-
|
|
72
|
-
return crypto.createSecretKey(
|
|
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
|
|
80
|
-
return crypto.randomBytes(length).toString(
|
|
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
|
|
132
|
+
hashLength: length ?? this.moduleOptions.kdf.outputKeyLength,
|
|
88
133
|
salt: salt,
|
|
89
|
-
type: this.
|
|
90
|
-
memoryCost: this.
|
|
91
|
-
timeCost: this.
|
|
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
|
|
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.
|
|
98
|
-
type: this.
|
|
99
|
-
memoryCost: this.
|
|
100
|
-
timeCost: this.
|
|
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
|
|
174
|
+
async verifyArgon2HashFromPassword(hash, data) {
|
|
106
175
|
return await argon2.verify(hash, data);
|
|
107
176
|
}
|
|
108
|
-
createCustomHash(algorithm, data,
|
|
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(
|
|
182
|
+
hash.update(inputData);
|
|
113
183
|
return hash.digest();
|
|
114
184
|
}
|
|
115
|
-
verifyCustomHash(algorithm, data, oldHash,
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
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,
|
|
190
|
+
createSecureHash(data, options) {
|
|
191
|
+
return this.createCustomHash('shake256', data, {
|
|
192
|
+
...options,
|
|
193
|
+
outputLength: 48,
|
|
194
|
+
});
|
|
126
195
|
}
|
|
127
|
-
verifySecureHash(data, oldHash) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
136
|
-
|
|
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
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
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 =
|
|
153
|
-
const hmac = this.createCustomHmac('sha3-256', secureKey, data);
|
|
154
|
-
return Buffer.concat([
|
|
155
|
-
}
|
|
156
|
-
verifySecureHmac(data, oldHmac) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const
|
|
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 =
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
const
|
|
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(
|
|
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
|
-
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
const
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const
|
|
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
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
+
}
|
package/dist/interfaces/index.js
CHANGED
|
@@ -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": "
|
|
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
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"@nestjs/platform-express": "^10.4.
|
|
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.
|
|
38
|
+
"@nestjs/testing": "^10.4.4",
|
|
41
39
|
"@types/express": "^4.17.21",
|
|
42
|
-
"@types/jest": "29.5.
|
|
43
|
-
"@types/node": "22.5
|
|
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
|
|
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.
|
|
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": "
|
|
72
|
-
"
|
|
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
|
-
"**/*.
|
|
86
|
+
"**/*.service.ts"
|
|
78
87
|
],
|
|
79
|
-
"coverageDirectory": "../coverage",
|
|
80
88
|
"testEnvironment": "node"
|
|
81
89
|
}
|
|
82
90
|
}
|