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.
- package/README.md +147 -0
- package/dist/cryptography.service.d.ts +21 -23
- package/dist/cryptography.service.js +82 -82
- package/dist/interfaces/cryptography-options.interface.d.ts +1 -1
- 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 +16 -15
- package/wiki/README.md +41 -0
- package/wiki/babel.config.js +3 -0
- package/wiki/docs/Internals/_category_.json +7 -0
- package/wiki/docs/Internals/create-safe-random-data.mdx +41 -0
- package/wiki/docs/Internals/create-secure-hmac.mdx +31 -0
- package/wiki/docs/Internals/symmetric-data-encrypt.mdx +103 -0
- package/wiki/docs/Internals/symmetric-secure-data-encrypt.mdx +161 -0
- package/wiki/docs/api-reference/_category_.json +7 -0
- package/wiki/docs/api-reference/settings.mdx +199 -0
- package/wiki/docs/guides/_category_.json +7 -0
- package/wiki/docs/guides/generics.mdx +170 -0
- package/wiki/docs/guides/hashing.mdx +258 -0
- package/wiki/docs/guides/hmac.mdx +271 -0
- package/wiki/docs/guides/key-derivation.mdx +101 -0
- package/wiki/docs/guides/password-hashing.mdx +136 -0
- package/wiki/docs/guides/symmetric-encryption.mdx +272 -0
- package/wiki/docs/intro.mdx +148 -0
- package/wiki/docusaurus.config.ts +138 -0
- package/wiki/package.json +48 -0
- package/wiki/sidebars.ts +20 -0
- package/wiki/src/common/timing-attack.mdx +3 -0
- package/wiki/src/common/tips.mdx +18 -0
- package/wiki/src/components/GenerateHexButton/index.tsx +35 -0
- package/wiki/src/components/GenerateHexButton/styles.module.css +10 -0
- package/wiki/src/components/GenericLabel/index.tsx +19 -0
- package/wiki/src/components/HomepageFeatures/index.tsx +70 -0
- package/wiki/src/components/HomepageFeatures/styles.module.css +11 -0
- package/wiki/src/components/RecommendedLabel/index.tsx +19 -0
- package/wiki/src/components/RequiredLabel/index.tsx +12 -0
- package/wiki/src/css/custom.css +30 -0
- package/wiki/src/pages/index.module.css +23 -0
- package/wiki/src/pages/index.tsx +43 -0
- package/wiki/src/pages/markdown-page.md +7 -0
- package/wiki/static/.nojekyll +0 -0
- package/wiki/static/img/gear_api.png +0 -0
- package/wiki/static/img/logo.svg +1 -0
- package/wiki/static/img/nestjs_favicon.ico +0 -0
- package/wiki/static/img/node_crypto.png +0 -0
- package/wiki/static/img/phc_logo.png +0 -0
- package/wiki/static/img/profile.png +0 -0
- package/wiki/versioned_docs/version-2.x/Internals/_category_.json +8 -0
- package/wiki/versioned_docs/version-2.x/Internals/create-secure-hmac.mdx +30 -0
- package/wiki/versioned_docs/version-2.x/Internals/symmetric-secure-data-encrypt.mdx +160 -0
- package/wiki/versioned_docs/version-2.x/api-reference/_category_.json +8 -0
- package/wiki/versioned_docs/version-2.x/api-reference/settings.mdx +197 -0
- package/wiki/versioned_docs/version-2.x/guides/_category_.json +7 -0
- package/wiki/versioned_docs/version-2.x/guides/generics.mdx +133 -0
- package/wiki/versioned_docs/version-2.x/guides/hashing.mdx +229 -0
- package/wiki/versioned_docs/version-2.x/guides/hmac.mdx +198 -0
- package/wiki/versioned_docs/version-2.x/guides/key-derivation.mdx +98 -0
- package/wiki/versioned_docs/version-2.x/guides/password-hashing.mdx +132 -0
- package/wiki/versioned_docs/version-2.x/guides/symmetric-encryption.mdx +107 -0
- package/wiki/versioned_docs/version-2.x/intro.mdx +148 -0
- package/wiki/versioned_sidebars/version-2.x-sidebars.json +8 -0
- package/wiki/versions.json +3 -0
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
[](https://codecov.io/github/mjorgegulab/nestjs-cryptography)
|
|
2
|
+
[](https://app.codacy.com/gh/mjorgegulab/nestjs-cryptography/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
|
3
|
+
[](https://github.com/mjorgegulab/nestjs-cryptography/actions/workflows/github-code-scanning/codeql)
|
|
4
|
+
[](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
|
|
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
|
|
16
|
-
|
|
14
|
+
private createHmacSecureKey;
|
|
15
|
+
createSafeRandomData(length: number): Buffer;
|
|
17
16
|
genUUID(secure?: boolean): string;
|
|
18
|
-
genRandomPassword(length: number
|
|
17
|
+
genRandomPassword(length: number): string;
|
|
19
18
|
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>;
|
|
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
|
-
|
|
48
|
-
|
|
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
|
-
|
|
69
|
-
return Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(
|
|
79
|
+
createHmacSecureKey(key, salt) {
|
|
80
|
+
return Buffer.from(crypto.hkdfSync('sha3-256', crypto.createSecretKey(key), salt, Buffer.alloc(0), 64));
|
|
70
81
|
}
|
|
71
|
-
|
|
72
|
-
return crypto.createSecretKey(
|
|
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
|
|
80
|
-
return crypto.randomBytes(length).toString(
|
|
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
|
|
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
|
|
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
|
|
116
|
+
async verifyArgon2HashFromPassword(hash, data) {
|
|
106
117
|
return await argon2.verify(hash, data);
|
|
107
118
|
}
|
|
108
|
-
createCustomHash(algorithm, data,
|
|
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(
|
|
124
|
+
hash.update(inputData);
|
|
113
125
|
return hash.digest();
|
|
114
126
|
}
|
|
115
|
-
verifyCustomHash(algorithm, data, oldHash,
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
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,
|
|
132
|
+
createSecureHash(data, options) {
|
|
133
|
+
return this.createCustomHash('shake256', data, {
|
|
134
|
+
...options,
|
|
135
|
+
outputLength: 48,
|
|
136
|
+
});
|
|
126
137
|
}
|
|
127
|
-
verifySecureHash(data, oldHash) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
136
|
-
|
|
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
|
|
142
|
-
|
|
143
|
-
|
|
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 =
|
|
153
|
-
const hmac = this.createCustomHmac('sha3-256', secureKey, data);
|
|
154
|
-
return Buffer.concat([
|
|
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 =
|
|
159
|
-
|
|
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 =
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
const
|
|
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(
|
|
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
|
-
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
const
|
|
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.
|
|
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
|
-
|
|
210
|
-
const cipheredDek = this.extractCipheredDEK(
|
|
211
|
-
const cipheredData = this.extractCipheredDataWithDEK(
|
|
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
|
}
|
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.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
|
|
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.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": "
|
|
72
|
-
"
|
|
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
|
-
"**/*.
|
|
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.
|