@nivinjoseph/n-sec 6.0.3 → 7.0.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.
Files changed (39) hide show
  1. package/.yarn/releases/yarn-4.14.1.cjs +940 -0
  2. package/.yarnrc.yml +6 -1
  3. package/dist/api-security/claim.js +2 -0
  4. package/dist/api-security/claim.js.map +1 -1
  5. package/dist/api-security/claims-identity.js +1 -0
  6. package/dist/api-security/claims-identity.js.map +1 -1
  7. package/dist/api-security/expired-token-exception.js +1 -0
  8. package/dist/api-security/expired-token-exception.js.map +1 -1
  9. package/dist/api-security/invalid-token-exception.js +2 -0
  10. package/dist/api-security/invalid-token-exception.js.map +1 -1
  11. package/dist/api-security/json-web-token.d.ts +1 -1
  12. package/dist/api-security/json-web-token.d.ts.map +1 -1
  13. package/dist/api-security/json-web-token.js +35 -10
  14. package/dist/api-security/json-web-token.js.map +1 -1
  15. package/dist/api-security/security-token.js +2 -0
  16. package/dist/api-security/security-token.js.map +1 -1
  17. package/dist/bin.js +1 -1
  18. package/dist/bin.js.map +1 -1
  19. package/dist/crypto/hash.d.ts +40 -0
  20. package/dist/crypto/hash.d.ts.map +1 -1
  21. package/dist/crypto/hash.js +60 -1
  22. package/dist/crypto/hash.js.map +1 -1
  23. package/dist/crypto/symmetric-encryption.d.ts +51 -3
  24. package/dist/crypto/symmetric-encryption.d.ts.map +1 -1
  25. package/dist/crypto/symmetric-encryption.js +90 -44
  26. package/dist/crypto/symmetric-encryption.js.map +1 -1
  27. package/dist/tsconfig.json +1 -0
  28. package/eslint.config.js +5 -5
  29. package/package.json +15 -15
  30. package/src/api-security/json-web-token.ts +37 -12
  31. package/src/bin.ts +1 -1
  32. package/src/crypto/hash.ts +66 -1
  33. package/src/crypto/symmetric-encryption.ts +98 -55
  34. package/test/hash.test.ts +89 -0
  35. package/test/hmac.test.ts +12 -12
  36. package/test/json-web-token.test.ts +76 -10
  37. package/test/symmetric-encryption.test.ts +51 -12
  38. package/tsconfig.json +5 -2
  39. package/.yarn/releases/yarn-4.0.2.cjs +0 -893
@@ -2,20 +2,29 @@ import { describe, test } from "node:test";
2
2
  import assert from "node:assert";
3
3
  import { JsonWebToken } from "./../src/api-security/json-web-token.js";
4
4
  import { Claim } from "../src/api-security/claim.js";
5
- import { SymmetricEncryption } from "../src/index.js";
5
+ import { Hmac, SymmetricEncryption } from "../src/index.js";
6
6
  import { InvalidTokenException } from "../src/api-security/invalid-token-exception.js";
7
7
  import { ExpiredTokenException } from "../src/api-security/expired-token-exception.js";
8
8
 
9
9
 
10
+ function buildToken(key: string, headerJson: string, bodyJson: string): string
11
+ {
12
+ const headerHex = Buffer.from(headerJson, "utf8").toString("hex").toUpperCase();
13
+ const bodyHex = Buffer.from(bodyJson, "utf8").toString("hex").toUpperCase();
14
+ const signature = Hmac.create(key, `${headerHex}.${bodyHex}`);
15
+ return `${headerHex}.${bodyHex}.${signature}`;
16
+ }
17
+
18
+
10
19
  await describe("Json Web Token ", async () =>
11
20
  {
12
21
  await describe("Hmac", async () =>
13
22
  {
14
23
 
15
- await test("should successfully create a token using hmac with one claim", async () =>
24
+ await test("should successfully create a token using hmac with one claim", () =>
16
25
  {
17
26
  const claim = new Claim("this_claim", "ThisValue");
18
- const key = await SymmetricEncryption.generateKey();
27
+ const key = SymmetricEncryption.generateKey();
19
28
  const time = Date.now();
20
29
  const token = JsonWebToken.fromClaims("issuer1", 1, key, time + 10000000, [claim]).generateToken();
21
30
  const jwt = JsonWebToken.fromToken("issuer1", 1, key, token);
@@ -31,7 +40,7 @@ await describe("Json Web Token ", async () =>
31
40
  {
32
41
  const claim1 = new Claim("this_claim", "ThisValue");
33
42
  const claim2 = new Claim("that_claim", "ThatValue");
34
- const key = await SymmetricEncryption.generateKey();
43
+ const key = SymmetricEncryption.generateKey();
35
44
  const time = Date.now();
36
45
  const token = JsonWebToken.fromClaims("issuer1", 1, key, time + 10000000, [claim1, claim2]).generateToken();
37
46
  const jwt = JsonWebToken.fromToken("issuer1", 1, key, token);
@@ -47,7 +56,7 @@ await describe("Json Web Token ", async () =>
47
56
  {
48
57
  const claim1 = new Claim("this_claim", "ThisValue");
49
58
  const claim2 = new Claim("that_claim", "ThatValue");
50
- const key = await SymmetricEncryption.generateKey();
59
+ const key = SymmetricEncryption.generateKey();
51
60
  const time = Date.now();
52
61
  const token = JsonWebToken.fromClaims("issuer1", 1, key, time + 10000000, [claim1, claim2]).generateToken();
53
62
  const jwt = JsonWebToken.fromToken("issuer1", 1, key, token);
@@ -63,7 +72,7 @@ await describe("Json Web Token ", async () =>
63
72
  {
64
73
  const claim1 = new Claim("this_claim", "ThisValue");
65
74
  const claim2 = new Claim("that_claim", "ThatValue");
66
- const key = await SymmetricEncryption.generateKey();
75
+ const key = SymmetricEncryption.generateKey();
67
76
  const time = Date.now();
68
77
  const token = JsonWebToken.fromClaims("issuer1", 1, key, time + 10000000, [claim1, claim2]).generateToken();
69
78
  try
@@ -83,7 +92,7 @@ await describe("Json Web Token ", async () =>
83
92
  {
84
93
  const claim1 = new Claim("this_claim", "ThisValue");
85
94
  const claim2 = new Claim("that_claim", "ThatValue");
86
- const key = await SymmetricEncryption.generateKey();
95
+ const key = SymmetricEncryption.generateKey();
87
96
  const time = Date.now();
88
97
  const token = JsonWebToken.fromClaims("issuer1", 1, key, time, [claim1, claim2]).generateToken();
89
98
  try
@@ -125,8 +134,8 @@ await describe("Json Web Token ", async () =>
125
134
  {
126
135
  const claim1 = new Claim("this_claim", "ThisValue");
127
136
  const claim2 = new Claim("that_claim", "ThatValue");
128
- const key = await SymmetricEncryption.generateKey();
129
- const key2 = await SymmetricEncryption.generateKey();
137
+ const key = SymmetricEncryption.generateKey();
138
+ const key2 = SymmetricEncryption.generateKey();
130
139
  const time = Date.now();
131
140
  const token = JsonWebToken.fromClaims("issuer1", 1, key, time + 1000000, [claim1, claim2]).generateToken();
132
141
  try
@@ -146,7 +155,7 @@ await describe("Json Web Token ", async () =>
146
155
  {
147
156
  const claim1 = new Claim("this_claim", "ThisValue");
148
157
  const claim2 = new Claim("that_claim", "ThatValue");
149
- const key = await SymmetricEncryption.generateKey();
158
+ const key = SymmetricEncryption.generateKey();
150
159
  const time = Date.now();
151
160
  let token = JsonWebToken.fromClaims("issuer1", 1, key, time + 1000000, [claim1, claim2]).generateToken();
152
161
  token = token + "someStuff";
@@ -162,6 +171,63 @@ await describe("Json Web Token ", async () =>
162
171
  }
163
172
  assert.ok(false);
164
173
  });
174
+
175
+ await test("should throw an exception when body contains '__proto__' as a claim type", () =>
176
+ {
177
+ const key = SymmetricEncryption.generateKey();
178
+ const headerJson = JSON.stringify({ iss: "issuer1", alg: 1, exp: Date.now() + 10000000 });
179
+ const bodyJson = `{"__proto__":"evil"}`;
180
+ const token = buildToken(key, headerJson, bodyJson);
181
+ try
182
+ {
183
+ JsonWebToken.fromToken("issuer1", 1, key, token);
184
+ }
185
+ catch (exp)
186
+ {
187
+ assert.ok(exp instanceof InvalidTokenException);
188
+ assert.equal(exp.message, `Token '${token}' is invalid because body contains invalid key '__proto__'.`);
189
+ return;
190
+ }
191
+ assert.ok(false);
192
+ });
193
+
194
+ await test("should throw an exception when body contains 'constructor' as a claim type", () =>
195
+ {
196
+ const key = SymmetricEncryption.generateKey();
197
+ const headerJson = JSON.stringify({ iss: "issuer1", alg: 1, exp: Date.now() + 10000000 });
198
+ const bodyJson = `{"constructor":"evil"}`;
199
+ const token = buildToken(key, headerJson, bodyJson);
200
+ try
201
+ {
202
+ JsonWebToken.fromToken("issuer1", 1, key, token);
203
+ }
204
+ catch (exp)
205
+ {
206
+ assert.ok(exp instanceof InvalidTokenException);
207
+ assert.equal(exp.message, `Token '${token}' is invalid because body contains invalid key 'constructor'.`);
208
+ return;
209
+ }
210
+ assert.ok(false);
211
+ });
212
+
213
+ await test("should throw an exception when body contains 'prototype' as a claim type", () =>
214
+ {
215
+ const key = SymmetricEncryption.generateKey();
216
+ const headerJson = JSON.stringify({ iss: "issuer1", alg: 1, exp: Date.now() + 10000000 });
217
+ const bodyJson = `{"prototype":"evil"}`;
218
+ const token = buildToken(key, headerJson, bodyJson);
219
+ try
220
+ {
221
+ JsonWebToken.fromToken("issuer1", 1, key, token);
222
+ }
223
+ catch (exp)
224
+ {
225
+ assert.ok(exp instanceof InvalidTokenException);
226
+ assert.equal(exp.message, `Token '${token}' is invalid because body contains invalid key 'prototype'.`);
227
+ return;
228
+ }
229
+ assert.ok(false);
230
+ });
165
231
  });
166
232
 
167
233
  // await describe("digital Signature", () =>
@@ -1,7 +1,6 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
- import { SymmetricEncryption } from "./../src/index.js";
4
- // import { CryptoException } from "./../src/crypto-exception";
3
+ import { CryptoException, SymmetricEncryption } from "./../src/index.js";
5
4
  import "@nivinjoseph/n-ext";
6
5
 
7
6
 
@@ -9,30 +8,30 @@ await describe("SymmetricEncryption", async () =>
9
8
  {
10
9
  await describe("generateKey", async () =>
11
10
  {
12
- await test("must return string value that is not null, empty or whitespace", async () =>
11
+ await test("must return string value that is not null, empty or whitespace", () =>
13
12
  {
14
- const key = await SymmetricEncryption.generateKey();
13
+ const key = SymmetricEncryption.generateKey();
15
14
  console.log("key", key);
16
15
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
17
16
  assert.ok(key !== null && !key.isEmptyOrWhiteSpace());
18
17
  });
19
18
 
20
19
 
21
- await test("consecutive calls must yield unique values", async () =>
20
+ await test("consecutive calls must yield unique values", () =>
22
21
  {
23
- const key1 = await SymmetricEncryption.generateKey();
24
- const key2 = await SymmetricEncryption.generateKey();
22
+ const key1 = SymmetricEncryption.generateKey();
23
+ const key2 = SymmetricEncryption.generateKey();
25
24
  assert.notStrictEqual(key1, key2);
26
25
  });
27
26
  });
28
27
 
29
28
  await describe("encrypt", async () =>
30
29
  {
31
- await test("must return cipher text value when called with key and plain text value", async () =>
30
+ await test("must return cipher text value when called with key and plain text value", () =>
32
31
  {
33
32
  const key = "E25B269440F88601C453CD171D76EDDC11D8CF33230742DF8CAD5873D28F78B2";
34
33
  const value = "password";
35
- const encrypted = await SymmetricEncryption.encrypt(key, value);
34
+ const encrypted = SymmetricEncryption.encrypt(key, value);
36
35
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
37
36
  assert.ok(encrypted !== null);
38
37
  assert.notStrictEqual(encrypted, value);
@@ -172,15 +171,55 @@ await describe("SymmetricEncryption", async () =>
172
171
 
173
172
  await describe("decrypt", async () =>
174
173
  {
175
- await test("must return plain text value when called with key and cipher text value", async () =>
174
+ await test("must return plain text value when called with key and cipher text value", () =>
176
175
  {
177
- const key = await SymmetricEncryption.generateKey();
176
+ const key = SymmetricEncryption.generateKey();
178
177
  const value = "password";
179
- const encrypted = await SymmetricEncryption.encrypt(key, value);
178
+ const encrypted = SymmetricEncryption.encrypt(key, value);
180
179
  const decrypted = SymmetricEncryption.decrypt(key, encrypted);
181
180
  assert.strictEqual(decrypted, value);
182
181
  });
183
182
 
183
+ await test("round-trips successfully when the same aad is supplied to encrypt and decrypt", () =>
184
+ {
185
+ const key = SymmetricEncryption.generateKey();
186
+ const value = "password";
187
+ const aad = "user:42|purpose:session";
188
+ const encrypted = SymmetricEncryption.encrypt(key, value, aad);
189
+ const decrypted = SymmetricEncryption.decrypt(key, encrypted, aad);
190
+ assert.strictEqual(decrypted, value);
191
+ });
192
+
193
+ await test("throws CryptoException when decrypting with a different aad than was used to encrypt", () =>
194
+ {
195
+ const key = SymmetricEncryption.generateKey();
196
+ const encrypted = SymmetricEncryption.encrypt(key, "password", "user:42");
197
+ assert.throws(
198
+ () => SymmetricEncryption.decrypt(key, encrypted, "user:99"),
199
+ CryptoException
200
+ );
201
+ });
202
+
203
+ await test("throws CryptoException when decrypting without aad a ciphertext that was encrypted with aad", () =>
204
+ {
205
+ const key = SymmetricEncryption.generateKey();
206
+ const encrypted = SymmetricEncryption.encrypt(key, "password", "user:42");
207
+ assert.throws(
208
+ () => SymmetricEncryption.decrypt(key, encrypted),
209
+ CryptoException
210
+ );
211
+ });
212
+
213
+ await test("throws CryptoException when decrypting with aad a ciphertext that was encrypted without aad", () =>
214
+ {
215
+ const key = SymmetricEncryption.generateKey();
216
+ const encrypted = SymmetricEncryption.encrypt(key, "password");
217
+ assert.throws(
218
+ () => SymmetricEncryption.decrypt(key, encrypted, "user:42"),
219
+ CryptoException
220
+ );
221
+ });
222
+
184
223
  // await test("decrypt with a valid encryption key", async () =>
185
224
  // {
186
225
  // let key = await SymmetricEncryption.generateKey();
package/tsconfig.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "module": "NodeNext",
4
- "target": "ES2017",
4
+ "target": "ES2023",
5
5
  "lib": [
6
6
  "ES2023"
7
7
  ],
8
+ "types": [
9
+ "node"
10
+ ],
8
11
  "strict": true,
9
12
  "strictNullChecks": true,
10
13
  "strictFunctionTypes": true,
@@ -23,7 +26,7 @@
23
26
  "noEmitHelpers": true,
24
27
  "noImplicitOverride": true,
25
28
  "pretty": true,
26
- "esModuleInterop": false,
29
+ "esModuleInterop": true,
27
30
  "allowSyntheticDefaultImports": true
28
31
  }
29
32
  }