nestjs-cryptography 2.2.2 → 3.0.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.
Files changed (64) hide show
  1. package/README.md +147 -0
  2. package/dist/cryptography.service.d.ts +21 -23
  3. package/dist/cryptography.service.js +82 -82
  4. package/dist/interfaces/cryptography-options.interface.d.ts +1 -1
  5. package/dist/interfaces/generic-options.interface.d.ts +5 -0
  6. package/dist/interfaces/generic-options.interface.js +2 -0
  7. package/dist/interfaces/index.d.ts +1 -0
  8. package/dist/interfaces/index.js +1 -0
  9. package/package.json +16 -15
  10. package/wiki/README.md +41 -0
  11. package/wiki/babel.config.js +3 -0
  12. package/wiki/docs/Internals/_category_.json +7 -0
  13. package/wiki/docs/Internals/create-safe-random-data.mdx +41 -0
  14. package/wiki/docs/Internals/create-secure-hmac.mdx +31 -0
  15. package/wiki/docs/Internals/symmetric-data-encrypt.mdx +103 -0
  16. package/wiki/docs/Internals/symmetric-secure-data-encrypt.mdx +161 -0
  17. package/wiki/docs/api-reference/_category_.json +7 -0
  18. package/wiki/docs/api-reference/settings.mdx +199 -0
  19. package/wiki/docs/guides/_category_.json +7 -0
  20. package/wiki/docs/guides/generics.mdx +170 -0
  21. package/wiki/docs/guides/hashing.mdx +258 -0
  22. package/wiki/docs/guides/hmac.mdx +271 -0
  23. package/wiki/docs/guides/key-derivation.mdx +101 -0
  24. package/wiki/docs/guides/password-hashing.mdx +136 -0
  25. package/wiki/docs/guides/symmetric-encryption.mdx +272 -0
  26. package/wiki/docs/intro.mdx +148 -0
  27. package/wiki/docusaurus.config.ts +138 -0
  28. package/wiki/package.json +48 -0
  29. package/wiki/sidebars.ts +20 -0
  30. package/wiki/src/common/timing-attack.mdx +3 -0
  31. package/wiki/src/common/tips.mdx +18 -0
  32. package/wiki/src/components/GenerateHexButton/index.tsx +35 -0
  33. package/wiki/src/components/GenerateHexButton/styles.module.css +10 -0
  34. package/wiki/src/components/GenericLabel/index.tsx +19 -0
  35. package/wiki/src/components/HomepageFeatures/index.tsx +70 -0
  36. package/wiki/src/components/HomepageFeatures/styles.module.css +11 -0
  37. package/wiki/src/components/RecommendedLabel/index.tsx +19 -0
  38. package/wiki/src/components/RequiredLabel/index.tsx +12 -0
  39. package/wiki/src/css/custom.css +30 -0
  40. package/wiki/src/pages/index.module.css +23 -0
  41. package/wiki/src/pages/index.tsx +43 -0
  42. package/wiki/src/pages/markdown-page.md +7 -0
  43. package/wiki/static/.nojekyll +0 -0
  44. package/wiki/static/img/gear_api.png +0 -0
  45. package/wiki/static/img/logo.svg +1 -0
  46. package/wiki/static/img/nestjs_favicon.ico +0 -0
  47. package/wiki/static/img/node_crypto.png +0 -0
  48. package/wiki/static/img/phc_logo.png +0 -0
  49. package/wiki/static/img/profile.png +0 -0
  50. package/wiki/versioned_docs/version-2.x/Internals/_category_.json +8 -0
  51. package/wiki/versioned_docs/version-2.x/Internals/create-secure-hmac.mdx +30 -0
  52. package/wiki/versioned_docs/version-2.x/Internals/symmetric-secure-data-encrypt.mdx +160 -0
  53. package/wiki/versioned_docs/version-2.x/api-reference/_category_.json +8 -0
  54. package/wiki/versioned_docs/version-2.x/api-reference/settings.mdx +197 -0
  55. package/wiki/versioned_docs/version-2.x/guides/_category_.json +7 -0
  56. package/wiki/versioned_docs/version-2.x/guides/generics.mdx +133 -0
  57. package/wiki/versioned_docs/version-2.x/guides/hashing.mdx +229 -0
  58. package/wiki/versioned_docs/version-2.x/guides/hmac.mdx +198 -0
  59. package/wiki/versioned_docs/version-2.x/guides/key-derivation.mdx +98 -0
  60. package/wiki/versioned_docs/version-2.x/guides/password-hashing.mdx +132 -0
  61. package/wiki/versioned_docs/version-2.x/guides/symmetric-encryption.mdx +107 -0
  62. package/wiki/versioned_docs/version-2.x/intro.mdx +148 -0
  63. package/wiki/versioned_sidebars/version-2.x-sidebars.json +8 -0
  64. package/wiki/versions.json +3 -0
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ [![codecov](https://codecov.io/github/mjorgegulab/nestjs-cryptography/branch/main/graph/badge.svg?token=I0ZFVZTREB)](https://codecov.io/github/mjorgegulab/nestjs-cryptography)
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
+ [![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)
5
+
6
+ # NestJS - Cryptography
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
+
12
+
13
+ ## Quick Start
14
+
15
+ [Overview & Tutorial][6]
16
+
17
+ ## Introduction
18
+
19
+ This library was created to address a common problem encountered when performing cryptographic operations in our projects.
20
+ It simplifies and streamlines the process, making it easier to implement secure and efficient cryptographic solutions.
21
+ Additionally, it helps avoid common mistakes,
22
+ such as the _**[reuse of initialization vectors][1]**_,
23
+ _**[reuse of encryption keys][2]**_,
24
+ or simple the use of _**[keys that are not cryptographically secure][3]**_.
25
+
26
+
27
+ Our library employs modern cryptographic standards to provide robust security and protect your data against advanced threats. We utilize a suite of trusted algorithms and practices recognized in the cryptographic community:
28
+
29
+ - **Argon2**: A cutting-edge key derivation function designed to resist GPU and ASIC attacks, making it highly effective against brute-force attempts. It offers configurable memory and time costs to balance performance and security.
30
+ - **SHA3**: The latest member of the Secure Hash Algorithm family, SHA3 provides enhanced security over its predecessors (SHA-1 and SHA-2) and is resilient against known cryptographic attacks.
31
+ - **AES-256-GCM**: Advanced Encryption Standard with a 256-bit key in Galois/Counter Mode ensures both data confidentiality and integrity. AES-256-GCM is widely used and trusted for its high level of security and performance.
32
+ - **SHAKE256**: A versatile extendable-output function (XOF) from the SHA-3 family, SHAKE256 allows for variable-length output, making it suitable for a variety of cryptographic applications like key generation and hashing.
33
+ - **HKDF-SHA3-256**: A HMAC-based Key Derivation Function using SHA3-256 as the underlying hash function. HKDF-SHA3-256 ensures secure and reliable derivation of cryptographic keys from a master secret.
34
+ - **HMAC-SHA3-256**: A mechanism for message authentication using SHA3-256. HMAC-SHA3-256 provides data integrity and authenticity by allowing verification that a message has not been altered.
35
+ - **Constant-Time Secret Comparisons**: To protect against timing attacks, our library implements constant-time algorithms for comparing secrets. This means the time taken to perform the comparison does not depend on the data being compared, preventing attackers from inferring information based on execution time.
36
+
37
+ ## Installation
38
+
39
+ This package are available on the [npm][4] registry.
40
+
41
+
42
+ ```bash
43
+ yarn add nestjs-cryptography
44
+ ```
45
+ or
46
+ ```bash
47
+ npm install nestjs-cryptography
48
+ ```
49
+
50
+
51
+ ## Usage on Services
52
+ To access cryptography methods from our `CryptographyService`, you could inject it using standard constructor injection
53
+
54
+ ```typescript
55
+ import { Injectable } from '@nestjs/common';
56
+ import { CryptographyService } from 'nestjs-cryptography';
57
+
58
+ @Injectable()
59
+ export class SomeService {
60
+ constructor(
61
+ // Inject using constructor injection
62
+ private readonly cryptographyService: CryptographyService
63
+ ) {}
64
+
65
+ async someMethod(): Promise<string> {
66
+ // Access service methods
67
+ return this.cryptographyService.genUUID();
68
+ }
69
+ }
70
+ ```
71
+
72
+
73
+ ## Configuration
74
+
75
+ Once the installation is complete, the `CryptographyModule` can be configured as any other
76
+ Nest package with _`forRoot`_ or _`forRootAsync`_ methods.
77
+
78
+ You could see the complete available settings [here][5]
79
+
80
+ ```typescript title="app.module.ts"
81
+ import {
82
+ CryptographyModule,
83
+ CryptographyOptionsInterface,
84
+ } from 'nestjs-cryptography';
85
+
86
+ @Module({
87
+ imports: [
88
+ CryptographyModule.forRoot<CryptographyOptionsInterface>({
89
+ // The rest of the configuration
90
+ encryption: {
91
+ symmetric: {
92
+ masterKey: '5f7f...46bf'
93
+ }
94
+ }
95
+ }),
96
+ ],
97
+ })
98
+ export class AppModule {}
99
+ ```
100
+
101
+ > [!TIP]
102
+ > Like other factory providers, our factory function can be async and can inject dependencies through inject.
103
+ > For example, you mat want to get the configuration using the `ConfigurationModule`,
104
+ so to do this you should use the _`forRootAsync`_ method ⬇️⬇️⬇️.
105
+
106
+
107
+ ```typescript title="app.module.ts"
108
+ import {
109
+ CryptographyModule,
110
+ CryptographyOptionsInterface,
111
+ } from 'nestjs-cryptography';
112
+
113
+ @Module({
114
+ imports: [
115
+ CryptographyModule.forRootAsync<CryptographyOptionsInterface>({
116
+ imports: [ConfigModule],
117
+ useFactory: async (configService: ConfigService) => ({
118
+ // The rest of the configuration
119
+ encryption: {
120
+ symmetric: {
121
+ masterKey: configService.get<string>('CRYPTOGRAPHY.MASTER_KEY')
122
+ }
123
+ }
124
+ }),
125
+ inject: [ConfigService],
126
+ }),
127
+ ],
128
+ })
129
+ export class AppModule {}
130
+ ```
131
+
132
+
133
+ The _`forRoot()`_ and _`forRootAsync`_ method takes an options object as an argument.
134
+ These options are passed through to the underlying cryptographic operations of the instance module.
135
+
136
+ > [!NOTE]
137
+ > Please take a look at the [documentation site][6] to see the available [methods][7] and the complete [configuration][8]
138
+
139
+
140
+ [1]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=26
141
+ [2]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=27
142
+ [3]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r5.pdf#page=112
143
+ [4]: https://www.npmjs.com/package/nestjs-cryptography
144
+ [5]: https://nestjs-cryptography.thewolfx41.dev/docs/api-reference/settings
145
+ [6]: https://nestjs-cryptography.thewolfx41.dev
146
+ [7]: https://nestjs-cryptography.thewolfx41.dev/docs/category/guides
147
+ [8]: https://nestjs-cryptography.thewolfx41.dev/docs/api-reference/settings
@@ -1,36 +1,34 @@
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
4
  private options;
7
5
  constructor(options: CryptographyOptionsInterface);
8
- private convertDataToBuffer;
6
+ private convertInputData;
9
7
  private extractIV;
10
8
  private extractAuthTagFromCypheredData;
11
9
  private extractCipheredData;
12
10
  private extractSalt;
11
+ private extractSaltFromHmac;
13
12
  private extractCipheredDEK;
14
13
  private extractCipheredDataWithDEK;
15
- private createSaferRandomData;
16
- private generateKeyDEK;
14
+ private createHmacSecureKey;
15
+ createSafeRandomData(length: number): Buffer;
17
16
  genUUID(secure?: boolean): string;
18
- genRandomPassword(length: number, encoding: 'base64' | 'hex'): string;
17
+ genRandomPassword(length: number): string;
19
18
  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>;
19
+ deriveMasterKey(masterKey: string | Buffer, salt: Buffer, length?: number): Promise<Buffer>;
20
+ createArgon2HashFromPassword(data: string | Buffer): Promise<Buffer>;
21
+ verifyArgon2HashFromPassword(hash: string, data: string | Buffer): Promise<boolean>;
22
+ createCustomHash(algorithm: string, data: string | Buffer, options?: GenericOptionsInterface): Buffer;
23
+ verifyCustomHash(algorithm: string, data: string | Buffer, oldHash: string | Buffer, options?: GenericOptionsInterface): boolean;
24
+ createSecureHash(data: string | Buffer, options?: GenericOptionsInterface): Buffer;
25
+ verifySecureHash(data: string | Buffer, oldHash: string | Buffer, options?: GenericOptionsInterface): boolean;
26
+ createCustomHmac(algorithm: string, key: string | Buffer, data: string | Buffer, options?: GenericOptionsInterface): Buffer;
27
+ verifyCustomHmac(algorithm: string, key: string | Buffer, data: string | Buffer, oldHmac: string | Buffer, options?: GenericOptionsInterface): boolean;
28
+ createSecureHmac(data: string | Buffer, options?: GenericOptionsInterface): Buffer;
29
+ verifySecureHmac(data: string | Buffer, oldHmac: string | Buffer, options?: GenericOptionsInterface): boolean;
30
+ symmetricDataEncrypt(data: string | Buffer, key: string | Buffer, options?: GenericOptionsInterface): Promise<Buffer>;
31
+ symmetricDataDecrypt(data: string | Buffer, key: string | Buffer, options?: GenericOptionsInterface): Promise<Buffer>;
32
+ symmetricSecureDataEncrypt(data: string | Buffer, options?: GenericOptionsInterface): Promise<Buffer>;
33
+ symmetricSecureDataDecrypt(data: string | Buffer, options?: GenericOptionsInterface): Promise<Buffer>;
36
34
  }
@@ -44,8 +44,16 @@ let CryptographyService = class CryptographyService {
44
44
  constructor(options) {
45
45
  this.options = options;
46
46
  }
47
- convertDataToBuffer(data) {
48
- return Buffer.isBuffer(data) ? data : Buffer.from(data, 'hex');
47
+ convertInputData(data, inputEncoding) {
48
+ if (Buffer.isBuffer(data)) {
49
+ return data;
50
+ }
51
+ else if (typeof data === 'string') {
52
+ return Buffer.from(data, inputEncoding ?? 'utf8');
53
+ }
54
+ else {
55
+ throw new Error('Unsupported input type');
56
+ }
49
57
  }
50
58
  extractIV(data) {
51
59
  return data.subarray(0, 12);
@@ -59,32 +67,35 @@ let CryptographyService = class CryptographyService {
59
67
  extractSalt(data) {
60
68
  return data.subarray(12, 76);
61
69
  }
70
+ extractSaltFromHmac(data) {
71
+ return data.subarray(0, 16);
72
+ }
62
73
  extractCipheredDEK(data) {
63
74
  return data.subarray(0, 124);
64
75
  }
65
76
  extractCipheredDataWithDEK(data) {
66
77
  return data.subarray(124, data.length);
67
78
  }
68
- createSaferRandomData(length) {
69
- return Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(crypto.randomBytes(64)), crypto.randomBytes(64), Buffer.alloc(0), length));
79
+ createHmacSecureKey(key, salt) {
80
+ return Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(key), salt, Buffer.alloc(0), 64));
70
81
  }
71
- generateKeyDEK() {
72
- return crypto.createSecretKey(this.createSaferRandomData(32));
82
+ createSafeRandomData(length) {
83
+ return Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(crypto.randomBytes(64)), crypto.randomBytes(64), Buffer.alloc(0), length));
73
84
  }
74
85
  genUUID(secure = false) {
75
86
  return crypto.randomUUID({
76
87
  disableEntropyCache: secure,
77
88
  });
78
89
  }
79
- genRandomPassword(length, encoding) {
80
- return crypto.randomBytes(length).toString(encoding).slice(0, length);
90
+ genRandomPassword(length) {
91
+ return crypto.randomBytes(length).toString('base64').slice(0, length);
81
92
  }
82
93
  generateSymmetricKey(length = 256) {
83
94
  return crypto.generateKeySync('hmac', { length });
84
95
  }
85
96
  async deriveMasterKey(masterKey, salt, length) {
86
97
  return await argon2.hash(masterKey, {
87
- hashLength: length ? length : this.options.kdf.defaultOutputKeyLength,
98
+ hashLength: length ?? this.options.kdf.outputKeyLength,
88
99
  salt: salt,
89
100
  type: this.options.kdf.argon2Type,
90
101
  memoryCost: this.options.kdf.memoryCost,
@@ -92,7 +103,7 @@ let CryptographyService = class CryptographyService {
92
103
  raw: true,
93
104
  });
94
105
  }
95
- async createArgonHashFromPassword(data) {
106
+ async createArgon2HashFromPassword(data) {
96
107
  const tmpData = await argon2.hash(data, {
97
108
  hashLength: this.options.hashing.password.outputKeyLength,
98
109
  type: this.options.hashing.password.argon2Type,
@@ -102,113 +113,102 @@ let CryptographyService = class CryptographyService {
102
113
  });
103
114
  return Buffer.isBuffer(tmpData) ? tmpData : Buffer.from(tmpData);
104
115
  }
105
- async verifyArgonHashFromPassword(hash, data) {
116
+ async verifyArgon2HashFromPassword(hash, data) {
106
117
  return await argon2.verify(hash, data);
107
118
  }
108
- createCustomHash(algorithm, data, outputLength = 0) {
119
+ createCustomHash(algorithm, data, options) {
120
+ const inputData = this.convertInputData(data, options?.inputDataEncoding);
109
121
  const hash = crypto.createHash(algorithm, {
110
- ...(outputLength && { outputLength }),
122
+ ...(options?.outputLength && { outputLength: options?.outputLength }),
111
123
  });
112
- hash.update(data);
124
+ hash.update(inputData);
113
125
  return hash.digest();
114
126
  }
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
- }
127
+ verifyCustomHash(algorithm, data, oldHash, options) {
128
+ const inputOldHashData = this.convertInputData(oldHash, options?.inputDataEncoding);
129
+ const hash = this.createCustomHash(algorithm, data, options);
130
+ return crypto.timingSafeEqual(hash, inputOldHashData);
123
131
  }
124
- createSecureHash(data) {
125
- return this.createCustomHash('shake256', data, 48);
132
+ createSecureHash(data, options) {
133
+ return this.createCustomHash('shake256', data, {
134
+ ...options,
135
+ outputLength: 48,
136
+ });
126
137
  }
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);
138
+ verifySecureHash(data, oldHash, options) {
139
+ return this.verifyCustomHash('shake256', data, oldHash, {
140
+ ...options,
141
+ outputLength: 48,
142
+ });
133
143
  }
134
- createCustomHmac(algorithm, key, data) {
135
- const hmac = crypto.createHmac(algorithm, crypto.createSecretKey(key));
136
- hmac.update(data);
144
+ createCustomHmac(algorithm, key, data, options) {
145
+ const inputKey = this.convertInputData(key, options?.inputKeyEncoding);
146
+ const inputData = this.convertInputData(data, options?.inputDataEncoding);
147
+ const hmac = crypto.createHmac(algorithm, crypto.createSecretKey(inputKey));
148
+ hmac.update(inputData);
137
149
  key = null;
138
150
  return hmac.digest();
139
151
  }
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
- }
152
+ verifyCustomHmac(algorithm, key, data, oldHmac, options) {
153
+ const inputOldHmacData = this.convertInputData(oldHmac, options?.inputDataEncoding);
154
+ const hmac = this.createCustomHmac(algorithm, key, data, options);
155
+ return crypto.timingSafeEqual(hmac, inputOldHmacData);
148
156
  }
149
- createSecureHmac(data) {
157
+ createSecureHmac(data, options) {
150
158
  const key = Buffer.from(this.options.hashing.hmac.masterKey, 'hex');
151
159
  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);
160
+ const secureKey = this.createHmacSecureKey(key, salt);
161
+ const hmac = this.createCustomHmac('sha3-256', secureKey, data, options);
162
+ return Buffer.concat([salt, hmac], salt.length + hmac.length);
155
163
  }
156
- verifySecureHmac(data, oldHmac) {
164
+ verifySecureHmac(data, oldHmac, options) {
157
165
  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);
166
+ const buffOldHmac = this.convertInputData(oldHmac, options?.inputDataEncoding);
167
+ const saltOldHmac = this.extractSaltFromHmac(buffOldHmac);
162
168
  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);
169
+ const secureKey = this.createHmacSecureKey(key, saltOldHmac);
170
+ const hmac = this.createCustomHmac('sha3-256', secureKey, data, options);
165
171
  return crypto.timingSafeEqual(hmac, hashOldHmac);
166
172
  }
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);
173
+ async symmetricDataEncrypt(data, key, options) {
174
+ const inputData = this.convertInputData(data, options?.inputDataEncoding);
175
+ const inputKey = this.convertInputData(key, options?.inputKeyEncoding);
176
+ const iv = this.createSafeRandomData(12);
177
+ const salt = this.createSafeRandomData(64);
178
+ const secureEncryptionKey = await this.deriveMasterKey(inputKey, salt, 32);
174
179
  const cipher = crypto.createCipheriv('aes-256-gcm', crypto.createSecretKey(secureEncryptionKey), iv, {
175
180
  authTagLength: 16,
176
181
  });
177
- let cipheredData = cipher.update(data);
178
- cipheredData = Buffer.concat([
179
- Buffer.from(cipheredData),
180
- Buffer.from(cipher.final()),
181
- ]);
182
+ let cipheredData = cipher.update(inputData);
183
+ cipheredData = Buffer.concat([cipheredData, cipher.final()]);
182
184
  return Buffer.concat([iv, salt, cipher.getAuthTag(), cipheredData]);
183
185
  }
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);
186
+ async symmetricDataDecrypt(data, key, options) {
187
+ const inputData = this.convertInputData(data, options?.inputDataEncoding);
188
+ const inputKey = this.convertInputData(key, options?.inputKeyEncoding);
189
+ const iv = this.extractIV(inputData);
190
+ const salt = this.extractSalt(inputData);
191
+ const authTag = this.extractAuthTagFromCypheredData(inputData);
192
+ const cipheredData = this.extractCipheredData(inputData);
193
+ const decryptionKey = await this.deriveMasterKey(inputKey, salt, 32);
191
194
  const decipher = crypto.createDecipheriv('aes-256-gcm', crypto.createSecretKey(decryptionKey), iv, {
192
195
  authTagLength: 16,
193
196
  });
194
197
  decipher.setAuthTag(authTag);
195
198
  let decipheredData = decipher.update(cipheredData);
196
- decipheredData = Buffer.concat([
197
- Buffer.from(decipheredData),
198
- Buffer.from(decipher.final()),
199
- ]);
199
+ decipheredData = Buffer.concat([decipheredData, decipher.final()]);
200
200
  return decipheredData;
201
201
  }
202
- async symmetricSecureDataEncrypt(data) {
203
- const dek = this.createSaferRandomData(32);
204
- const cipheredData = await this.symmetricDataEncrypt(data, dek);
202
+ async symmetricSecureDataEncrypt(data, options) {
203
+ const dek = this.createSafeRandomData(32);
204
+ const cipheredData = await this.symmetricDataEncrypt(data, dek, options);
205
205
  const cipheredDek = await this.symmetricDataEncrypt(dek, this.options.encryption.symmetric.masterKey);
206
206
  return Buffer.concat([cipheredDek, cipheredData]);
207
207
  }
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);
208
+ async symmetricSecureDataDecrypt(data, options) {
209
+ const inputData = this.convertInputData(data, options?.inputDataEncoding);
210
+ const cipheredDek = this.extractCipheredDEK(inputData);
211
+ const cipheredData = this.extractCipheredDataWithDEK(inputData);
212
212
  const decipheredDek = await this.symmetricDataDecrypt(cipheredDek, this.options.encryption.symmetric.masterKey);
213
213
  return await this.symmetricDataDecrypt(cipheredData, decipheredDek);
214
214
  }
@@ -5,7 +5,7 @@ export declare enum Argon2Type {
5
5
  }
6
6
  export interface CryptographyOptionsInterface {
7
7
  kdf: {
8
- defaultOutputKeyLength: number;
8
+ outputKeyLength: number;
9
9
  argon2Type: Argon2Type;
10
10
  memoryCost: number;
11
11
  timeCost: number;
@@ -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.2",
3
+ "version": "3.0.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,17 +21,18 @@
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/cli": "^10.4.5",
33
34
  "@nestjs/common": "^10.4.1",
34
35
  "@nestjs/core": "^10.4.1",
35
- "@nestjs/cli": "^10.4.5",
36
- "reflect-metadata": "^0.2.2",
37
- "rxjs": "^7.8.1",
38
36
  "@nestjs/platform-express": "^10.4.1",
39
37
  "@nestjs/schematics": "^10.1.4",
40
38
  "@nestjs/testing": "^10.4.1",
@@ -46,17 +44,19 @@
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.2"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "@nestjs/common": "^9.0.0 || ^10.0.0",
@@ -68,15 +68,16 @@
68
68
  "json",
69
69
  "ts"
70
70
  ],
71
- "rootDir": "src",
72
- "testRegex": ".*\\.spec\\.ts$",
71
+ "rootDir": "./",
72
+ "testMatch": [
73
+ "**/test/**/?(*.)+(spec|test).[tj]s?(x)"
74
+ ],
73
75
  "transform": {
74
76
  "^.+\\.(t|j)s$": "ts-jest"
75
77
  },
76
78
  "collectCoverageFrom": [
77
- "**/*.(t|j)s"
79
+ "**/*.service.ts"
78
80
  ],
79
- "coverageDirectory": "../coverage",
80
81
  "testEnvironment": "node"
81
82
  }
82
83
  }
package/wiki/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # Website
2
+
3
+ This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
4
+
5
+ ### Installation
6
+
7
+ ```
8
+ $ yarn
9
+ ```
10
+
11
+ ### Local Development
12
+
13
+ ```
14
+ $ yarn start
15
+ ```
16
+
17
+ This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18
+
19
+ ### Build
20
+
21
+ ```
22
+ $ yarn build
23
+ ```
24
+
25
+ This command generates static content into the `build` directory and can be served using any static contents hosting service.
26
+
27
+ ### Deployment
28
+
29
+ Using SSH:
30
+
31
+ ```
32
+ $ USE_SSH=true yarn deploy
33
+ ```
34
+
35
+ Not using SSH:
36
+
37
+ ```
38
+ $ GIT_USER=<Your GitHub username> yarn deploy
39
+ ```
40
+
41
+ If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "label": "Internals",
3
+ "position": 4,
4
+ "link": {
5
+ "type": "generated-index",
6
+ }
7
+ }