@nest-boot/crypt 7.0.2 → 7.1.1
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/dist/crypt.module.d.ts +9 -3
- package/dist/crypt.module.js +42 -4
- package/dist/crypt.module.js.map +1 -1
- package/dist/crypt.module.spec.d.ts +1 -0
- package/dist/crypt.module.spec.js +73 -0
- package/dist/crypt.module.spec.js.map +1 -0
- package/dist/crypt.service.d.ts +59 -25
- package/dist/crypt.service.js +84 -104
- package/dist/crypt.service.js.map +1 -1
- package/dist/crypt.service.spec.js +77 -38
- package/dist/crypt.service.spec.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/derive-key.d.ts +7 -0
- package/dist/utils/derive-key.js +22 -0
- package/dist/utils/derive-key.js.map +1 -0
- package/dist/utils/estimate-entropy.d.ts +8 -0
- package/dist/utils/estimate-entropy.js +17 -0
- package/dist/utils/estimate-entropy.js.map +1 -0
- package/dist/utils/estimate-entropy.spec.d.ts +1 -0
- package/dist/utils/estimate-entropy.spec.js +17 -0
- package/dist/utils/estimate-entropy.spec.js.map +1 -0
- package/dist/utils/is-jwe.d.ts +14 -0
- package/dist/utils/is-jwe.js +38 -0
- package/dist/utils/is-jwe.js.map +1 -0
- package/dist/utils/is-jwe.spec.d.ts +1 -0
- package/dist/utils/is-jwe.spec.js +50 -0
- package/dist/utils/is-jwe.spec.js.map +1 -0
- package/package.json +13 -7
package/dist/crypt.module.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type DynamicModule } from "@nestjs/common";
|
|
2
2
|
import { ASYNC_OPTIONS_TYPE, ConfigurableModuleClass, OPTIONS_TYPE } from "./crypt.module-definition";
|
|
3
3
|
/**
|
|
4
|
-
* Module that provides encryption and decryption services using
|
|
4
|
+
* Module that provides encryption and decryption services using JWE (A256GCMKW + A256GCM).
|
|
5
|
+
*
|
|
6
|
+
* Uses HKDF to derive a 32-byte key from the secret, so secrets of any length are accepted.
|
|
5
7
|
*
|
|
6
8
|
* @example
|
|
7
9
|
* ```typescript
|
|
@@ -10,13 +12,17 @@ import { ASYNC_OPTIONS_TYPE, ConfigurableModuleClass, OPTIONS_TYPE } from "./cry
|
|
|
10
12
|
* @Module({
|
|
11
13
|
* imports: [
|
|
12
14
|
* CryptModule.register({
|
|
13
|
-
* secret:
|
|
14
|
-
* isGlobal: true,
|
|
15
|
+
* secret: process.env.CRYPT_SECRET
|
|
15
16
|
* }),
|
|
16
17
|
* ],
|
|
17
18
|
* })
|
|
18
19
|
* export class AppModule {}
|
|
19
20
|
* ```
|
|
21
|
+
*
|
|
22
|
+
* Generate a secure secret:
|
|
23
|
+
* ```bash
|
|
24
|
+
* node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"
|
|
25
|
+
* ```
|
|
20
26
|
*/
|
|
21
27
|
export declare class CryptModule extends ConfigurableModuleClass {
|
|
22
28
|
/**
|
package/dist/crypt.module.js
CHANGED
|
@@ -10,8 +10,11 @@ exports.CryptModule = void 0;
|
|
|
10
10
|
const common_1 = require("@nestjs/common");
|
|
11
11
|
const crypt_module_definition_1 = require("./crypt.module-definition");
|
|
12
12
|
const crypt_service_1 = require("./crypt.service");
|
|
13
|
+
const estimate_entropy_1 = require("./utils/estimate-entropy");
|
|
13
14
|
/**
|
|
14
|
-
* Module that provides encryption and decryption services using
|
|
15
|
+
* Module that provides encryption and decryption services using JWE (A256GCMKW + A256GCM).
|
|
16
|
+
*
|
|
17
|
+
* Uses HKDF to derive a 32-byte key from the secret, so secrets of any length are accepted.
|
|
15
18
|
*
|
|
16
19
|
* @example
|
|
17
20
|
* ```typescript
|
|
@@ -20,13 +23,17 @@ const crypt_service_1 = require("./crypt.service");
|
|
|
20
23
|
* @Module({
|
|
21
24
|
* imports: [
|
|
22
25
|
* CryptModule.register({
|
|
23
|
-
* secret:
|
|
24
|
-
* isGlobal: true,
|
|
26
|
+
* secret: process.env.CRYPT_SECRET
|
|
25
27
|
* }),
|
|
26
28
|
* ],
|
|
27
29
|
* })
|
|
28
30
|
* export class AppModule {}
|
|
29
31
|
* ```
|
|
32
|
+
*
|
|
33
|
+
* Generate a secure secret:
|
|
34
|
+
* ```bash
|
|
35
|
+
* node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"
|
|
36
|
+
* ```
|
|
30
37
|
*/
|
|
31
38
|
let CryptModule = class CryptModule extends crypt_module_definition_1.ConfigurableModuleClass {
|
|
32
39
|
/**
|
|
@@ -49,6 +56,37 @@ let CryptModule = class CryptModule extends crypt_module_definition_1.Configurab
|
|
|
49
56
|
exports.CryptModule = CryptModule;
|
|
50
57
|
exports.CryptModule = CryptModule = __decorate([
|
|
51
58
|
(0, common_1.Global)(),
|
|
52
|
-
(0, common_1.Module)({
|
|
59
|
+
(0, common_1.Module)({
|
|
60
|
+
providers: [
|
|
61
|
+
{
|
|
62
|
+
provide: crypt_service_1.CryptService,
|
|
63
|
+
inject: [{ token: crypt_module_definition_1.MODULE_OPTIONS_TOKEN, optional: true }],
|
|
64
|
+
useFactory: (options) => {
|
|
65
|
+
const secret = options.secret ?? process.env.CRYPT_SECRET ?? process.env.APP_SECRET;
|
|
66
|
+
if (!secret) {
|
|
67
|
+
throw new Error("Crypt secret is required.\n" +
|
|
68
|
+
"Set CRYPT_SECRET or APP_SECRET environment variable, or pass a secret option.\n" +
|
|
69
|
+
"Generate a secure secret with:\n" +
|
|
70
|
+
" node -e \"console.log(require('crypto').randomBytes(32).toString('base64url'))\"");
|
|
71
|
+
}
|
|
72
|
+
if (secret.length < 32) {
|
|
73
|
+
throw new Error("Crypt secret must be at least 32 characters long.\n" +
|
|
74
|
+
"Set CRYPT_SECRET or APP_SECRET environment variable, or pass a secret option.\n" +
|
|
75
|
+
"Generate a secure secret with:\n" +
|
|
76
|
+
" node -e \"console.log(require('crypto').randomBytes(32).toString('base64url'))\"");
|
|
77
|
+
}
|
|
78
|
+
if ((0, estimate_entropy_1.estimateEntropy)(secret) < 120) {
|
|
79
|
+
throw new Error("Crypt secret appears low-entropy.\n" +
|
|
80
|
+
"Use a randomly generated secret for production.\n" +
|
|
81
|
+
"Generate a secure secret with:\n" +
|
|
82
|
+
" node -e \"console.log(require('crypto').randomBytes(32).toString('base64url'))\"");
|
|
83
|
+
}
|
|
84
|
+
crypt_service_1.CryptService.init(secret);
|
|
85
|
+
return crypt_service_1.CryptService.instance;
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
exports: [crypt_service_1.CryptService],
|
|
90
|
+
})
|
|
53
91
|
], CryptModule);
|
|
54
92
|
//# sourceMappingURL=crypt.module.js.map
|
package/dist/crypt.module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypt.module.js","sourceRoot":"","sources":["../src/crypt.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAoE;AAEpE,
|
|
1
|
+
{"version":3,"file":"crypt.module.js","sourceRoot":"","sources":["../src/crypt.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAoE;AAEpE,uEAKmC;AACnC,mDAA+C;AAE/C,+DAA2D;AAE3D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA8CI,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,iDAAuB;IACtD;;;;OAIG;IACH,MAAM,CAAU,QAAQ,CAAC,OAA4B;QACnD,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAU,aAAa,CAC3B,OAAkC;QAElC,OAAO,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;CACF,CAAA;AApBY,kCAAW;sBAAX,WAAW;IA7CvB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,4BAAY;gBACrB,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,8CAAoB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACzD,UAAU,EAAE,CAAC,OAA2B,EAAE,EAAE;oBAC1C,MAAM,MAAM,GACV,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;oBAEvE,IAAI,CAAC,MAAM,EAAE,CAAC;wBACZ,MAAM,IAAI,KAAK,CACb,6BAA6B;4BAC3B,iFAAiF;4BACjF,kCAAkC;4BAClC,oFAAoF,CACvF,CAAC;oBACJ,CAAC;oBAED,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;wBACvB,MAAM,IAAI,KAAK,CACb,qDAAqD;4BACnD,iFAAiF;4BACjF,kCAAkC;4BAClC,oFAAoF,CACvF,CAAC;oBACJ,CAAC;oBAED,IAAI,IAAA,kCAAe,EAAC,MAAM,CAAC,GAAG,GAAG,EAAE,CAAC;wBAClC,MAAM,IAAI,KAAK,CACb,qCAAqC;4BACnC,mDAAmD;4BACnD,kCAAkC;4BAClC,oFAAoF,CACvF,CAAC;oBACJ,CAAC;oBAED,4BAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAE1B,OAAO,4BAAY,CAAC,QAAQ,CAAC;gBAC/B,CAAC;aACF;SACF;QACD,OAAO,EAAE,CAAC,4BAAY,CAAC;KACxB,CAAC;GACW,WAAW,CAoBvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const testing_1 = require("@nestjs/testing");
|
|
4
|
+
const _1 = require(".");
|
|
5
|
+
const TEST_SECRET = "myTestSecretThatIsAtLeast32Chars!";
|
|
6
|
+
describe("CryptModule", () => {
|
|
7
|
+
const originalEnv = process.env;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
// Reset static instance before each test
|
|
10
|
+
_1.CryptService._instance = undefined;
|
|
11
|
+
// Reset environment variables
|
|
12
|
+
process.env = { ...originalEnv };
|
|
13
|
+
delete process.env.CRYPT_SECRET;
|
|
14
|
+
delete process.env.APP_SECRET;
|
|
15
|
+
});
|
|
16
|
+
afterAll(() => {
|
|
17
|
+
process.env = originalEnv;
|
|
18
|
+
});
|
|
19
|
+
describe("register", () => {
|
|
20
|
+
it("should throw error when secret is missing", async () => {
|
|
21
|
+
await expect(testing_1.Test.createTestingModule({
|
|
22
|
+
imports: [_1.CryptModule.register({})],
|
|
23
|
+
}).compile()).rejects.toThrow("Crypt secret is required.");
|
|
24
|
+
});
|
|
25
|
+
it("should use CRYPT_SECRET env when no secret option provided", async () => {
|
|
26
|
+
process.env.CRYPT_SECRET = TEST_SECRET;
|
|
27
|
+
const moduleRef = await testing_1.Test.createTestingModule({
|
|
28
|
+
imports: [_1.CryptModule.register({})],
|
|
29
|
+
}).compile();
|
|
30
|
+
const cryptService = moduleRef.get(_1.CryptService);
|
|
31
|
+
expect(cryptService).toBeDefined();
|
|
32
|
+
});
|
|
33
|
+
it("should use APP_SECRET env when no secret or CRYPT_SECRET provided", async () => {
|
|
34
|
+
process.env.APP_SECRET = TEST_SECRET;
|
|
35
|
+
const moduleRef = await testing_1.Test.createTestingModule({
|
|
36
|
+
imports: [_1.CryptModule.register({})],
|
|
37
|
+
}).compile();
|
|
38
|
+
const cryptService = moduleRef.get(_1.CryptService);
|
|
39
|
+
expect(cryptService).toBeDefined();
|
|
40
|
+
});
|
|
41
|
+
it("should throw error when secret is too short", async () => {
|
|
42
|
+
await expect(testing_1.Test.createTestingModule({
|
|
43
|
+
imports: [_1.CryptModule.register({ secret: "short" })],
|
|
44
|
+
}).compile()).rejects.toThrow("Crypt secret must be at least 32 characters long.");
|
|
45
|
+
});
|
|
46
|
+
it("should throw error when secret has low entropy", async () => {
|
|
47
|
+
// A secret that is 32 chars but low entropy (all same character)
|
|
48
|
+
const lowEntropySecret = "a".repeat(32);
|
|
49
|
+
await expect(testing_1.Test.createTestingModule({
|
|
50
|
+
imports: [_1.CryptModule.register({ secret: lowEntropySecret })],
|
|
51
|
+
}).compile()).rejects.toThrow("Crypt secret appears low-entropy.");
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("registerAsync", () => {
|
|
55
|
+
it("should register module with async factory", async () => {
|
|
56
|
+
const moduleRef = await testing_1.Test.createTestingModule({
|
|
57
|
+
imports: [
|
|
58
|
+
_1.CryptModule.registerAsync({
|
|
59
|
+
useFactory: () => ({
|
|
60
|
+
secret: TEST_SECRET,
|
|
61
|
+
}),
|
|
62
|
+
}),
|
|
63
|
+
],
|
|
64
|
+
}).compile();
|
|
65
|
+
const cryptService = moduleRef.get(_1.CryptService);
|
|
66
|
+
expect(cryptService).toBeDefined();
|
|
67
|
+
const encrypted = await cryptService.encrypt("password");
|
|
68
|
+
const decrypted = await cryptService.decrypt(encrypted);
|
|
69
|
+
expect(decrypted).toBe("password");
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=crypt.module.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypt.module.spec.js","sourceRoot":"","sources":["../src/crypt.module.spec.ts"],"names":[],"mappings":";;AAAA,6CAAuC;AAEvC,wBAA8C;AAE9C,MAAM,WAAW,GAAG,mCAAmC,CAAC;AAExD,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC;IAEhC,UAAU,CAAC,GAAG,EAAE;QACd,yCAAyC;QACxC,eAAoD,CAAC,SAAS,GAAG,SAAS,CAAC;QAC5E,8BAA8B;QAC9B,OAAO,CAAC,GAAG,GAAG,EAAE,GAAG,WAAW,EAAE,CAAC;QACjC,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAChC,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,GAAG,EAAE;QACZ,OAAO,CAAC,GAAG,GAAG,WAAW,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;YACzD,MAAM,MAAM,CACV,cAAI,CAAC,mBAAmB,CAAC;gBACvB,OAAO,EAAE,CAAC,cAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;aACpC,CAAC,CAAC,OAAO,EAAE,CACb,CAAC,OAAO,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;YAC1E,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,WAAW,CAAC;YAEvC,MAAM,SAAS,GAAG,MAAM,cAAI,CAAC,mBAAmB,CAAC;gBAC/C,OAAO,EAAE,CAAC,cAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;aACpC,CAAC,CAAC,OAAO,EAAE,CAAC;YAEb,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,CAAe,eAAY,CAAC,CAAC;YAE/D,MAAM,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;YACjF,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,WAAW,CAAC;YAErC,MAAM,SAAS,GAAG,MAAM,cAAI,CAAC,mBAAmB,CAAC;gBAC/C,OAAO,EAAE,CAAC,cAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;aACpC,CAAC,CAAC,OAAO,EAAE,CAAC;YAEb,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,CAAe,eAAY,CAAC,CAAC;YAE/D,MAAM,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,MAAM,MAAM,CACV,cAAI,CAAC,mBAAmB,CAAC;gBACvB,OAAO,EAAE,CAAC,cAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;aACrD,CAAC,CAAC,OAAO,EAAE,CACb,CAAC,OAAO,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC9D,iEAAiE;YACjE,MAAM,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAExC,MAAM,MAAM,CACV,cAAI,CAAC,mBAAmB,CAAC;gBACvB,OAAO,EAAE,CAAC,cAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;aAC9D,CAAC,CAAC,OAAO,EAAE,CACb,CAAC,OAAO,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;YACzD,MAAM,SAAS,GAAG,MAAM,cAAI,CAAC,mBAAmB,CAAC;gBAC/C,OAAO,EAAE;oBACP,cAAW,CAAC,aAAa,CAAC;wBACxB,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;4BACjB,MAAM,EAAE,WAAW;yBACpB,CAAC;qBACH,CAAC;iBACH;aACF,CAAC,CAAC,OAAO,EAAE,CAAC;YAEb,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,CAAe,eAAY,CAAC,CAAC;YAE/D,MAAM,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;YAEnC,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YACzD,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACxD,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/crypt.service.d.ts
CHANGED
|
@@ -1,45 +1,79 @@
|
|
|
1
|
-
import { CryptModuleOptions } from "./crypt-module-options.interface";
|
|
2
1
|
/**
|
|
3
|
-
* Service that provides encryption and decryption functionality using
|
|
2
|
+
* Service that provides encryption and decryption functionality using JWE (JSON Web Encryption).
|
|
3
|
+
*
|
|
4
|
+
* Uses HKDF to derive a 32-byte key from the secret, then A256GCMKW for key management
|
|
5
|
+
* and A256GCM for content encryption. Accepts secrets of any length.
|
|
4
6
|
*
|
|
5
7
|
* @example
|
|
6
8
|
* ```typescript
|
|
7
9
|
* import { CryptService } from '@nest-boot/crypt';
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* constructor(private readonly cryptService: CryptService) {}
|
|
12
|
-
*
|
|
13
|
-
* async encryptData(data: string): Promise<string> {
|
|
14
|
-
* return this.cryptService.encrypt(data);
|
|
15
|
-
* }
|
|
11
|
+
* // Initialize at application startup for static usage
|
|
12
|
+
* CryptService.init(process.env.CRYPT_SECRET);
|
|
16
13
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* }
|
|
14
|
+
* // Use static methods
|
|
15
|
+
* const encrypted = await CryptService.encrypt(data);
|
|
16
|
+
* const decrypted = await CryptService.decrypt(encrypted);
|
|
21
17
|
* ```
|
|
22
18
|
*/
|
|
23
19
|
export declare class CryptService {
|
|
24
|
-
|
|
20
|
+
private static _instance?;
|
|
21
|
+
/**
|
|
22
|
+
* Gets the static CryptService instance.
|
|
23
|
+
* @throws Error if CryptService has not been initialized via `init()`
|
|
24
|
+
* @returns The CryptService instance
|
|
25
|
+
*/
|
|
26
|
+
static get instance(): CryptService;
|
|
27
|
+
/**
|
|
28
|
+
* Initializes the static CryptService instance with the given secret.
|
|
29
|
+
* Call this method at application startup to configure the default secret.
|
|
30
|
+
*
|
|
31
|
+
* @param secret - The secret key to use for encryption/decryption
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* // In your application bootstrap
|
|
36
|
+
* CryptService.init(process.env.CRYPT_SECRET);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
static init(secret: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* Encrypts a string value using the static instance.
|
|
42
|
+
* @param value - The plaintext string to encrypt
|
|
43
|
+
* @returns A JWE compact serialization string
|
|
44
|
+
* @throws Error if CryptService has not been initialized via `init()`
|
|
45
|
+
*/
|
|
46
|
+
static encrypt(value: string): Promise<string>;
|
|
47
|
+
/**
|
|
48
|
+
* Decrypts a JWE string using the static instance.
|
|
49
|
+
* @param value - The JWE compact serialization string to decrypt
|
|
50
|
+
* @returns The decrypted plaintext string
|
|
51
|
+
* @throws Error if CryptService has not been initialized via `init()`
|
|
52
|
+
*/
|
|
53
|
+
static decrypt(value: string): Promise<string>;
|
|
54
|
+
private readonly secret;
|
|
55
|
+
private derivedKeyPromise?;
|
|
25
56
|
/**
|
|
26
57
|
* Creates an instance of CryptService.
|
|
27
|
-
* @param
|
|
28
|
-
|
|
58
|
+
* @param secret - The secret key to use for encryption/decryption
|
|
59
|
+
*/
|
|
60
|
+
constructor(secret: string);
|
|
61
|
+
/**
|
|
62
|
+
* Gets or creates the derived key asynchronously.
|
|
29
63
|
*/
|
|
30
|
-
|
|
64
|
+
private getDerivedKey;
|
|
31
65
|
/**
|
|
32
|
-
* Encrypts a string value using
|
|
66
|
+
* Encrypts a string value using JWE with A256GCMKW and A256GCM.
|
|
67
|
+
* The secret is first derived using HKDF-SHA256.
|
|
33
68
|
* @param value - The plaintext string to encrypt
|
|
34
|
-
* @
|
|
35
|
-
* @returns A base64-encoded encrypted string containing IV, auth tag, data, and salt
|
|
69
|
+
* @returns A JWE compact serialization string
|
|
36
70
|
*/
|
|
37
|
-
encrypt(value: string
|
|
71
|
+
encrypt(value: string): Promise<string>;
|
|
38
72
|
/**
|
|
39
|
-
* Decrypts
|
|
40
|
-
*
|
|
41
|
-
* @param
|
|
73
|
+
* Decrypts a JWE compact serialization string.
|
|
74
|
+
* The secret is first derived using HKDF-SHA256.
|
|
75
|
+
* @param value - The JWE string to decrypt
|
|
42
76
|
* @returns The decrypted plaintext string
|
|
43
77
|
*/
|
|
44
|
-
decrypt(value: string
|
|
78
|
+
decrypt(value: string): Promise<string>;
|
|
45
79
|
}
|
package/dist/crypt.service.js
CHANGED
|
@@ -1,130 +1,110 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
-
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
-
};
|
|
14
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
15
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
16
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
17
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
18
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
19
|
-
};
|
|
20
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
21
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
22
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
23
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
24
|
-
};
|
|
25
|
-
var _CryptService_instances, _CryptService_algorithm, _CryptService_encoding, _CryptService_keyByteLength, _CryptService_saltByteLength, _CryptService_viByteLength, _CryptService_secret, _CryptService_getKeyAndSalt;
|
|
26
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
3
|
exports.CryptService = void 0;
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const util_1 = require("util");
|
|
31
|
-
const crypt_module_definition_1 = require("./crypt.module-definition");
|
|
4
|
+
const jose_1 = require("jose");
|
|
5
|
+
const derive_key_1 = require("./utils/derive-key");
|
|
32
6
|
/**
|
|
33
|
-
* Service that provides encryption and decryption functionality using
|
|
7
|
+
* Service that provides encryption and decryption functionality using JWE (JSON Web Encryption).
|
|
8
|
+
*
|
|
9
|
+
* Uses HKDF to derive a 32-byte key from the secret, then A256GCMKW for key management
|
|
10
|
+
* and A256GCM for content encryption. Accepts secrets of any length.
|
|
34
11
|
*
|
|
35
12
|
* @example
|
|
36
13
|
* ```typescript
|
|
37
14
|
* import { CryptService } from '@nest-boot/crypt';
|
|
38
15
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* constructor(private readonly cryptService: CryptService) {}
|
|
42
|
-
*
|
|
43
|
-
* async encryptData(data: string): Promise<string> {
|
|
44
|
-
* return this.cryptService.encrypt(data);
|
|
45
|
-
* }
|
|
16
|
+
* // Initialize at application startup for static usage
|
|
17
|
+
* CryptService.init(process.env.CRYPT_SECRET);
|
|
46
18
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* }
|
|
19
|
+
* // Use static methods
|
|
20
|
+
* const encrypted = await CryptService.encrypt(data);
|
|
21
|
+
* const decrypted = await CryptService.decrypt(encrypted);
|
|
51
22
|
* ```
|
|
52
23
|
*/
|
|
53
|
-
|
|
24
|
+
class CryptService {
|
|
54
25
|
/**
|
|
55
|
-
*
|
|
56
|
-
* @
|
|
57
|
-
* @
|
|
26
|
+
* Gets the static CryptService instance.
|
|
27
|
+
* @throws Error if CryptService has not been initialized via `init()`
|
|
28
|
+
* @returns The CryptService instance
|
|
58
29
|
*/
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
_CryptService_encoding.set(this, "base64");
|
|
63
|
-
_CryptService_keyByteLength.set(this, 32);
|
|
64
|
-
_CryptService_saltByteLength.set(this, 16);
|
|
65
|
-
_CryptService_viByteLength.set(this, 16);
|
|
66
|
-
_CryptService_secret.set(this, void 0);
|
|
67
|
-
const secret = options.secret ?? process.env.CRYPT_SECRET ?? process.env.APP_SECRET;
|
|
68
|
-
if (!secret) {
|
|
69
|
-
throw new Error("Crypt secret is missing. Set CRYPT_SECRET or APP_SECRET or pass a secret option.");
|
|
30
|
+
static get instance() {
|
|
31
|
+
if (!this._instance) {
|
|
32
|
+
throw new Error("CryptService not initialized");
|
|
70
33
|
}
|
|
71
|
-
|
|
34
|
+
return this._instance;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Initializes the static CryptService instance with the given secret.
|
|
38
|
+
* Call this method at application startup to configure the default secret.
|
|
39
|
+
*
|
|
40
|
+
* @param secret - The secret key to use for encryption/decryption
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // In your application bootstrap
|
|
45
|
+
* CryptService.init(process.env.CRYPT_SECRET);
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
static init(secret) {
|
|
49
|
+
this._instance = new CryptService(secret);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Encrypts a string value using the static instance.
|
|
53
|
+
* @param value - The plaintext string to encrypt
|
|
54
|
+
* @returns A JWE compact serialization string
|
|
55
|
+
* @throws Error if CryptService has not been initialized via `init()`
|
|
56
|
+
*/
|
|
57
|
+
static encrypt(value) {
|
|
58
|
+
return this.instance.encrypt(value);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Decrypts a JWE string using the static instance.
|
|
62
|
+
* @param value - The JWE compact serialization string to decrypt
|
|
63
|
+
* @returns The decrypted plaintext string
|
|
64
|
+
* @throws Error if CryptService has not been initialized via `init()`
|
|
65
|
+
*/
|
|
66
|
+
static decrypt(value) {
|
|
67
|
+
return this.instance.decrypt(value);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Creates an instance of CryptService.
|
|
71
|
+
* @param secret - The secret key to use for encryption/decryption
|
|
72
|
+
*/
|
|
73
|
+
constructor(secret) {
|
|
74
|
+
this.secret = secret;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Gets or creates the derived key asynchronously.
|
|
78
|
+
*/
|
|
79
|
+
getDerivedKey() {
|
|
80
|
+
return (this.derivedKeyPromise ??= (0, derive_key_1.deriveKey)(this.secret));
|
|
72
81
|
}
|
|
73
82
|
/**
|
|
74
|
-
* Encrypts a string value using
|
|
83
|
+
* Encrypts a string value using JWE with A256GCMKW and A256GCM.
|
|
84
|
+
* The secret is first derived using HKDF-SHA256.
|
|
75
85
|
* @param value - The plaintext string to encrypt
|
|
76
|
-
* @
|
|
77
|
-
* @returns A base64-encoded encrypted string containing IV, auth tag, data, and salt
|
|
86
|
+
* @returns A JWE compact serialization string
|
|
78
87
|
*/
|
|
79
|
-
async encrypt(value
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const tag = cipher.getAuthTag();
|
|
85
|
-
return Buffer.from(JSON.stringify({
|
|
86
|
-
iv: iv.toString(__classPrivateFieldGet(this, _CryptService_encoding, "f")),
|
|
87
|
-
tag: tag.toString(__classPrivateFieldGet(this, _CryptService_encoding, "f")),
|
|
88
|
-
data: data.toString(__classPrivateFieldGet(this, _CryptService_encoding, "f")),
|
|
89
|
-
salt: salt.toString(__classPrivateFieldGet(this, _CryptService_encoding, "f")),
|
|
90
|
-
})).toString(__classPrivateFieldGet(this, _CryptService_encoding, "f"));
|
|
88
|
+
async encrypt(value) {
|
|
89
|
+
const key = await this.getDerivedKey();
|
|
90
|
+
return await new jose_1.CompactEncrypt(new TextEncoder().encode(value))
|
|
91
|
+
.setProtectedHeader({ alg: "A256GCMKW", enc: "A256GCM" })
|
|
92
|
+
.encrypt(key);
|
|
91
93
|
}
|
|
92
94
|
/**
|
|
93
|
-
* Decrypts
|
|
94
|
-
*
|
|
95
|
-
* @param
|
|
95
|
+
* Decrypts a JWE compact serialization string.
|
|
96
|
+
* The secret is first derived using HKDF-SHA256.
|
|
97
|
+
* @param value - The JWE string to decrypt
|
|
96
98
|
* @returns The decrypted plaintext string
|
|
97
99
|
*/
|
|
98
|
-
async decrypt(value
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
decipher.setAuthTag(tag);
|
|
106
|
-
return Buffer.concat([decipher.update(data), decipher.final()]).toString("utf8");
|
|
100
|
+
async decrypt(value) {
|
|
101
|
+
const key = await this.getDerivedKey();
|
|
102
|
+
const { plaintext } = await (0, jose_1.compactDecrypt)(value, key, {
|
|
103
|
+
keyManagementAlgorithms: ["A256GCMKW"],
|
|
104
|
+
contentEncryptionAlgorithms: ["A256GCM"],
|
|
105
|
+
});
|
|
106
|
+
return new TextDecoder().decode(plaintext);
|
|
107
107
|
}
|
|
108
|
-
}
|
|
108
|
+
}
|
|
109
109
|
exports.CryptService = CryptService;
|
|
110
|
-
_CryptService_algorithm = new WeakMap();
|
|
111
|
-
_CryptService_encoding = new WeakMap();
|
|
112
|
-
_CryptService_keyByteLength = new WeakMap();
|
|
113
|
-
_CryptService_saltByteLength = new WeakMap();
|
|
114
|
-
_CryptService_viByteLength = new WeakMap();
|
|
115
|
-
_CryptService_secret = new WeakMap();
|
|
116
|
-
_CryptService_instances = new WeakSet();
|
|
117
|
-
_CryptService_getKeyAndSalt = async function _CryptService_getKeyAndSalt(secret) {
|
|
118
|
-
const salt = (0, crypto_1.randomBytes)(__classPrivateFieldGet(this, _CryptService_saltByteLength, "f"));
|
|
119
|
-
return {
|
|
120
|
-
key: (await (0, util_1.promisify)(crypto_1.scrypt)(secret, salt, __classPrivateFieldGet(this, _CryptService_keyByteLength, "f"))),
|
|
121
|
-
salt,
|
|
122
|
-
};
|
|
123
|
-
};
|
|
124
|
-
exports.CryptService = CryptService = __decorate([
|
|
125
|
-
(0, common_1.Injectable)(),
|
|
126
|
-
__param(0, (0, common_1.Optional)()),
|
|
127
|
-
__param(0, (0, common_1.Inject)(crypt_module_definition_1.MODULE_OPTIONS_TOKEN)),
|
|
128
|
-
__metadata("design:paramtypes", [Object])
|
|
129
|
-
], CryptService);
|
|
130
110
|
//# sourceMappingURL=crypt.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypt.service.js","sourceRoot":"","sources":["../src/crypt.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"crypt.service.js","sourceRoot":"","sources":["../src/crypt.service.ts"],"names":[],"mappings":";;;AAAA,+BAAsD;AAEtD,mDAA+C;AAE/C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAa,YAAY;IAGvB;;;;OAIG;IACH,MAAM,KAAK,QAAQ;QACjB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,IAAI,CAAC,MAAc;QACxB,IAAI,CAAC,SAAS,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CAAC,KAAa;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CAAC,KAAa;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAKD;;;OAGG;IACH,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;OAEG;IACK,aAAa;QACnB,OAAO,CAAC,IAAI,CAAC,iBAAiB,KAAK,IAAA,sBAAS,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CAAC,KAAa;QACzB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAEvC,OAAO,MAAM,IAAI,qBAAc,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;aAC7D,kBAAkB,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;aACxD,OAAO,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CAAC,KAAa;QACzB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAEvC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,IAAA,qBAAc,EAAC,KAAK,EAAE,GAAG,EAAE;YACrD,uBAAuB,EAAE,CAAC,WAAW,CAAC;YACtC,2BAA2B,EAAE,CAAC,SAAS,CAAC;SACzC,CAAC,CAAC;QAEH,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC;CACF;AApGD,oCAoGC"}
|