@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.
@@ -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 AES-256-GCM.
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: 'your-secret-key',
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
  /**
@@ -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 AES-256-GCM.
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: 'your-secret-key',
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)({ providers: [crypt_service_1.CryptService], exports: [crypt_service_1.CryptService] })
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
@@ -1 +1 @@
1
- {"version":3,"file":"crypt.module.js","sourceRoot":"","sources":["../src/crypt.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAoE;AAEpE,uEAImC;AACnC,mDAA+C;AAE/C;;;;;;;;;;;;;;;;;GAiBG;AAGI,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;IAFvB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC,EAAE,SAAS,EAAE,CAAC,4BAAY,CAAC,EAAE,OAAO,EAAE,CAAC,4BAAY,CAAC,EAAE,CAAC;GAClD,WAAW,CAoBvB"}
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":"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"}
@@ -1,45 +1,79 @@
1
- import { CryptModuleOptions } from "./crypt-module-options.interface";
2
1
  /**
3
- * Service that provides encryption and decryption functionality using AES-256-GCM algorithm.
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
- * @Injectable()
10
- * export class MyService {
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
- * async decryptData(encrypted: string): Promise<string> {
18
- * return this.cryptService.decrypt(encrypted);
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
- #private;
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 options - Configuration options for the crypt service
28
- * @throws Error if no secret is provided via options or environment variables
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
- constructor(options?: CryptModuleOptions);
64
+ private getDerivedKey;
31
65
  /**
32
- * Encrypts a string value using AES-256-GCM algorithm.
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
- * @param secret - Optional secret key to use instead of the default
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, secret?: string): Promise<string>;
71
+ encrypt(value: string): Promise<string>;
38
72
  /**
39
- * Decrypts an encrypted string value.
40
- * @param value - The base64-encoded encrypted string to decrypt
41
- * @param secret - Optional secret key to use instead of the default
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, secret?: string): Promise<string>;
78
+ decrypt(value: string): Promise<string>;
45
79
  }
@@ -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 common_1 = require("@nestjs/common");
29
- const crypto_1 = require("crypto");
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 AES-256-GCM algorithm.
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
- * @Injectable()
40
- * export class MyService {
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
- * async decryptData(encrypted: string): Promise<string> {
48
- * return this.cryptService.decrypt(encrypted);
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
- let CryptService = class CryptService {
24
+ class CryptService {
54
25
  /**
55
- * Creates an instance of CryptService.
56
- * @param options - Configuration options for the crypt service
57
- * @throws Error if no secret is provided via options or environment variables
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
- constructor(options = {}) {
60
- _CryptService_instances.add(this);
61
- _CryptService_algorithm.set(this, "aes-256-gcm");
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
- __classPrivateFieldSet(this, _CryptService_secret, secret, "f");
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 AES-256-GCM algorithm.
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
- * @param secret - Optional secret key to use instead of the default
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, secret) {
80
- const { key, salt } = await __classPrivateFieldGet(this, _CryptService_instances, "m", _CryptService_getKeyAndSalt).call(this, secret ?? __classPrivateFieldGet(this, _CryptService_secret, "f"));
81
- const iv = (0, crypto_1.randomBytes)(__classPrivateFieldGet(this, _CryptService_viByteLength, "f"));
82
- const cipher = (0, crypto_1.createCipheriv)(__classPrivateFieldGet(this, _CryptService_algorithm, "f"), key, iv);
83
- const data = Buffer.concat([cipher.update(value), cipher.final()]);
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 an encrypted string value.
94
- * @param value - The base64-encoded encrypted string to decrypt
95
- * @param secret - Optional secret key to use instead of the default
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, secret) {
99
- const payload = JSON.parse(Buffer.from(value, __classPrivateFieldGet(this, _CryptService_encoding, "f")).toString("utf8"));
100
- const key = (await (0, util_1.promisify)(crypto_1.scrypt)(secret ?? __classPrivateFieldGet(this, _CryptService_secret, "f"), Buffer.from(payload.salt, __classPrivateFieldGet(this, _CryptService_encoding, "f")), __classPrivateFieldGet(this, _CryptService_keyByteLength, "f")));
101
- const iv = Buffer.from(payload.iv, __classPrivateFieldGet(this, _CryptService_encoding, "f"));
102
- const tag = Buffer.from(payload.tag, __classPrivateFieldGet(this, _CryptService_encoding, "f"));
103
- const data = Buffer.from(payload.data, __classPrivateFieldGet(this, _CryptService_encoding, "f"));
104
- const decipher = (0, crypto_1.createDecipheriv)(__classPrivateFieldGet(this, _CryptService_algorithm, "f"), key, iv);
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":"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"}
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"}