@suveren/gateway 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/bin/suveren-gateway.js +86 -5
  2. package/content/integrations/calendar.json +116 -22
  3. package/content/integrations/deploy-github.json +164 -0
  4. package/content/integrations/gmail.json +13 -4
  5. package/content/integrations/index.json +2 -1
  6. package/dist/control-plane/index.mjs +84 -7
  7. package/dist/mcp-server/http.mjs +147 -21
  8. package/dist/ui/assets/index-DecSrutK.js +105 -0
  9. package/dist/ui/index.html +1 -1
  10. package/node_modules/@hap/core/dist/index.d.mts +21 -0
  11. package/node_modules/@hap/core/dist/index.d.ts +21 -0
  12. package/node_modules/@hap/core/dist/index.js +17 -0
  13. package/node_modules/@hap/core/dist/index.mjs +17 -0
  14. package/node_modules/@hap/core/package.json +1 -1
  15. package/node_modules/@hap/core/src/gatekeeper.ts +28 -0
  16. package/node_modules/@hap/core/src/types.ts +21 -0
  17. package/node_modules/fast-uri/index.js +37 -1
  18. package/node_modules/fast-uri/package.json +1 -1
  19. package/node_modules/fast-uri/test/security.test.js +136 -0
  20. package/node_modules/hono/dist/cjs/helper/proxy/index.js +4 -0
  21. package/node_modules/hono/dist/cjs/jsx/base.js +1 -9
  22. package/node_modules/hono/dist/cjs/jsx/hooks/index.js +14 -11
  23. package/node_modules/hono/dist/cjs/middleware/cors/index.js +1 -1
  24. package/node_modules/hono/dist/cjs/middleware/language/language.js +10 -6
  25. package/node_modules/hono/dist/cjs/utils/cookie.js +2 -1
  26. package/node_modules/hono/dist/helper/proxy/index.js +4 -0
  27. package/node_modules/hono/dist/jsx/base.js +1 -9
  28. package/node_modules/hono/dist/jsx/hooks/index.js +14 -11
  29. package/node_modules/hono/dist/middleware/cors/index.js +1 -1
  30. package/node_modules/hono/dist/middleware/language/language.js +10 -6
  31. package/node_modules/hono/dist/utils/cookie.js +2 -1
  32. package/node_modules/hono/package.json +2 -3
  33. package/node_modules/ip-address/README.md +142 -128
  34. package/node_modules/ip-address/dist/common.d.ts +6 -0
  35. package/node_modules/ip-address/dist/common.js +17 -1
  36. package/node_modules/ip-address/dist/common.js.map +1 -1
  37. package/node_modules/ip-address/dist/ipv4.js +3 -12
  38. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  39. package/node_modules/ip-address/dist/ipv6.d.ts +9 -2
  40. package/node_modules/ip-address/dist/ipv6.js +35 -16
  41. package/node_modules/ip-address/dist/ipv6.js.map +1 -1
  42. package/node_modules/ip-address/package.json +5 -3
  43. package/node_modules/jose/README.md +13 -13
  44. package/node_modules/jose/dist/types/index.d.ts +6 -5
  45. package/node_modules/jose/dist/types/jwe/compact/decrypt.d.ts +17 -11
  46. package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +7 -19
  47. package/node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts +17 -11
  48. package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +7 -19
  49. package/node_modules/jose/dist/types/jwe/general/decrypt.d.ts +18 -13
  50. package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +20 -21
  51. package/node_modules/jose/dist/types/jwk/embedded.d.ts +8 -12
  52. package/node_modules/jose/dist/types/jwk/thumbprint.d.ts +0 -15
  53. package/node_modules/jose/dist/types/jwks/local.d.ts +12 -21
  54. package/node_modules/jose/dist/types/jwks/remote.d.ts +34 -165
  55. package/node_modules/jose/dist/types/jws/compact/sign.d.ts +2 -13
  56. package/node_modules/jose/dist/types/jws/compact/verify.d.ts +17 -13
  57. package/node_modules/jose/dist/types/jws/flattened/sign.d.ts +2 -13
  58. package/node_modules/jose/dist/types/jws/flattened/verify.d.ts +17 -13
  59. package/node_modules/jose/dist/types/jws/general/sign.d.ts +16 -17
  60. package/node_modules/jose/dist/types/jws/general/verify.d.ts +18 -15
  61. package/node_modules/jose/dist/types/jwt/decrypt.d.ts +17 -11
  62. package/node_modules/jose/dist/types/jwt/encrypt.d.ts +10 -34
  63. package/node_modules/jose/dist/types/jwt/sign.d.ts +2 -13
  64. package/node_modules/jose/dist/types/jwt/unsecured.d.ts +4 -13
  65. package/node_modules/jose/dist/types/jwt/verify.d.ts +14 -13
  66. package/node_modules/jose/dist/types/key/export.d.ts +0 -14
  67. package/node_modules/jose/dist/types/key/generate_key_pair.d.ts +7 -14
  68. package/node_modules/jose/dist/types/key/generate_secret.d.ts +16 -14
  69. package/node_modules/jose/dist/types/key/import.d.ts +18 -28
  70. package/node_modules/jose/dist/types/types.d.ts +239 -222
  71. package/node_modules/jose/dist/types/util/base64url.d.ts +11 -4
  72. package/node_modules/jose/dist/types/util/decode_jwt.d.ts +1 -9
  73. package/node_modules/jose/dist/types/util/decode_protected_header.d.ts +0 -8
  74. package/node_modules/jose/dist/types/util/errors.d.ts +128 -121
  75. package/node_modules/jose/dist/webapi/jwe/compact/decrypt.js +4 -22
  76. package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +5 -153
  77. package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +16 -111
  78. package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +15 -3
  79. package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +65 -81
  80. package/node_modules/jose/dist/webapi/jwk/embedded.js +5 -3
  81. package/node_modules/jose/dist/webapi/jwks/local.js +31 -72
  82. package/node_modules/jose/dist/webapi/jwks/remote.js +19 -28
  83. package/node_modules/jose/dist/webapi/jws/compact/sign.js +5 -2
  84. package/node_modules/jose/dist/webapi/jws/compact/verify.js +4 -16
  85. package/node_modules/jose/dist/webapi/jws/flattened/sign.js +7 -65
  86. package/node_modules/jose/dist/webapi/jws/flattened/verify.js +3 -89
  87. package/node_modules/jose/dist/webapi/jws/general/sign.js +19 -15
  88. package/node_modules/jose/dist/webapi/jws/general/verify.js +29 -9
  89. package/node_modules/jose/dist/webapi/jwt/decrypt.js +5 -5
  90. package/node_modules/jose/dist/webapi/jwt/sign.js +2 -3
  91. package/node_modules/jose/dist/webapi/jwt/unsecured.js +4 -3
  92. package/node_modules/jose/dist/webapi/jwt/verify.js +6 -6
  93. package/node_modules/jose/dist/webapi/key/export.js +37 -4
  94. package/node_modules/jose/dist/webapi/key/generate_key_pair.js +33 -82
  95. package/node_modules/jose/dist/webapi/key/import.js +8 -8
  96. package/node_modules/jose/dist/webapi/lib/asn1.js +53 -122
  97. package/node_modules/jose/dist/webapi/lib/buffer_utils.js +1 -0
  98. package/node_modules/jose/dist/webapi/lib/content_encryption.js +34 -109
  99. package/node_modules/jose/dist/webapi/lib/crypto_key.js +18 -126
  100. package/node_modules/jose/dist/webapi/lib/helpers.js +23 -0
  101. package/node_modules/jose/dist/webapi/lib/invalid_key_input.js +0 -1
  102. package/node_modules/jose/dist/webapi/lib/jwe_algorithms.js +116 -0
  103. package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +186 -0
  104. package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +110 -0
  105. package/node_modules/jose/dist/webapi/lib/jwk_to_key.js +8 -98
  106. package/node_modules/jose/dist/webapi/lib/jws_algorithms.js +76 -0
  107. package/node_modules/jose/dist/webapi/lib/jws_sign.js +74 -0
  108. package/node_modules/jose/dist/webapi/lib/jws_verify.js +107 -0
  109. package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +68 -109
  110. package/node_modules/jose/dist/webapi/lib/key.js +132 -0
  111. package/node_modules/jose/dist/webapi/lib/key_algorithm.js +10 -0
  112. package/node_modules/jose/dist/webapi/lib/key_descriptor.js +7 -0
  113. package/node_modules/jose/dist/webapi/lib/key_management.js +162 -92
  114. package/node_modules/jose/dist/webapi/lib/options.js +48 -0
  115. package/node_modules/jose/dist/webapi/lib/signing.js +14 -56
  116. package/node_modules/jose/dist/webapi/lib/type_checks.js +13 -18
  117. package/node_modules/jose/dist/webapi/util/base64url.js +13 -4
  118. package/node_modules/jose/dist/webapi/util/decode_jwt.js +2 -2
  119. package/node_modules/jose/dist/webapi/util/decode_protected_header.js +5 -15
  120. package/node_modules/jose/dist/webapi/util/errors.js +1 -1
  121. package/node_modules/jose/package.json +2 -4
  122. package/package.json +2 -2
  123. package/profiles/deploy/0.6.profile.json +168 -0
  124. package/profiles/deploy/0.7.profile.json +168 -0
  125. package/profiles/email/0.4.profile.json +9 -2
  126. package/profiles/index.json +3 -1
  127. package/dist/ui/assets/index-Cog54HOo.js +0 -105
  128. package/node_modules/jose/dist/webapi/lib/aesgcmkw.js +0 -15
  129. package/node_modules/jose/dist/webapi/lib/aeskw.js +0 -25
  130. package/node_modules/jose/dist/webapi/lib/check_key_type.js +0 -122
  131. package/node_modules/jose/dist/webapi/lib/ecdhes.js +0 -52
  132. package/node_modules/jose/dist/webapi/lib/key_to_jwk.js +0 -34
  133. package/node_modules/jose/dist/webapi/lib/normalize_key.js +0 -166
  134. package/node_modules/jose/dist/webapi/lib/pbes2kw.js +0 -42
  135. package/node_modules/jose/dist/webapi/lib/rsaes.js +0 -24
  136. package/node_modules/jose/dist/webapi/lib/validate_algorithms.js +0 -10
  137. package/node_modules/jose/dist/webapi/lib/validate_crit.js +0 -33
@@ -1,4 +1,5 @@
1
1
  import { JOSENotSupported } from '../util/errors.js';
2
+ import { keyAlgorithm } from '../lib/key_algorithm.js';
2
3
  function getModulusLengthOption(options) {
3
4
  const modulusLength = options?.modulusLength ?? 2048;
4
5
  if (typeof modulusLength !== 'number' || modulusLength < 2048) {
@@ -7,91 +8,41 @@ function getModulusLengthOption(options) {
7
8
  return modulusLength;
8
9
  }
9
10
  export async function generateKeyPair(alg, options) {
11
+ const entry = keyAlgorithm(alg);
12
+ if (entry.secret) {
13
+ throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
14
+ }
10
15
  let algorithm;
11
- let keyUsages;
12
- switch (alg) {
13
- case 'PS256':
14
- case 'PS384':
15
- case 'PS512':
16
- algorithm = {
17
- name: 'RSA-PSS',
18
- hash: `SHA-${alg.slice(-3)}`,
19
- publicExponent: Uint8Array.of(0x01, 0x00, 0x01),
20
- modulusLength: getModulusLengthOption(options),
21
- };
22
- keyUsages = ['sign', 'verify'];
23
- break;
24
- case 'RS256':
25
- case 'RS384':
26
- case 'RS512':
27
- algorithm = {
28
- name: 'RSASSA-PKCS1-v1_5',
29
- hash: `SHA-${alg.slice(-3)}`,
30
- publicExponent: Uint8Array.of(0x01, 0x00, 0x01),
31
- modulusLength: getModulusLengthOption(options),
32
- };
33
- keyUsages = ['sign', 'verify'];
34
- break;
35
- case 'RSA-OAEP':
36
- case 'RSA-OAEP-256':
37
- case 'RSA-OAEP-384':
38
- case 'RSA-OAEP-512':
39
- algorithm = {
40
- name: 'RSA-OAEP',
41
- hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`,
42
- publicExponent: Uint8Array.of(0x01, 0x00, 0x01),
43
- modulusLength: getModulusLengthOption(options),
44
- };
45
- keyUsages = ['decrypt', 'unwrapKey', 'encrypt', 'wrapKey'];
46
- break;
47
- case 'ES256':
48
- algorithm = { name: 'ECDSA', namedCurve: 'P-256' };
49
- keyUsages = ['sign', 'verify'];
50
- break;
51
- case 'ES384':
52
- algorithm = { name: 'ECDSA', namedCurve: 'P-384' };
53
- keyUsages = ['sign', 'verify'];
54
- break;
55
- case 'ES512':
56
- algorithm = { name: 'ECDSA', namedCurve: 'P-521' };
57
- keyUsages = ['sign', 'verify'];
58
- break;
59
- case 'Ed25519':
60
- case 'EdDSA': {
61
- keyUsages = ['sign', 'verify'];
62
- algorithm = { name: 'Ed25519' };
63
- break;
16
+ if (entry.resolve) {
17
+ const crv = options?.crv ?? 'P-256';
18
+ switch (crv) {
19
+ case 'P-256':
20
+ case 'P-384':
21
+ case 'P-521':
22
+ algorithm = { name: 'ECDH', namedCurve: crv };
23
+ break;
24
+ case 'X25519':
25
+ algorithm = { name: 'X25519' };
26
+ break;
27
+ default:
28
+ throw new JOSENotSupported('Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, and X25519');
64
29
  }
65
- case 'ML-DSA-44':
66
- case 'ML-DSA-65':
67
- case 'ML-DSA-87': {
68
- keyUsages = ['sign', 'verify'];
69
- algorithm = { name: alg };
70
- break;
30
+ }
31
+ else {
32
+ if (entry.crv !== undefined && options?.crv !== undefined && options.crv !== entry.crv) {
33
+ throw new JOSENotSupported(`Invalid or unsupported crv option provided, the only supported value for ${alg} is ${entry.crv}`);
71
34
  }
72
- case 'ECDH-ES':
73
- case 'ECDH-ES+A128KW':
74
- case 'ECDH-ES+A192KW':
75
- case 'ECDH-ES+A256KW': {
76
- keyUsages = ['deriveBits'];
77
- const crv = options?.crv ?? 'P-256';
78
- switch (crv) {
79
- case 'P-256':
80
- case 'P-384':
81
- case 'P-521': {
82
- algorithm = { name: 'ECDH', namedCurve: crv };
83
- break;
35
+ algorithm =
36
+ entry.kty[0] === 'RSA'
37
+ ? {
38
+ ...entry.subtle,
39
+ publicExponent: Uint8Array.of(0x01, 0x00, 0x01),
40
+ modulusLength: getModulusLengthOption(options),
84
41
  }
85
- case 'X25519':
86
- algorithm = { name: 'X25519' };
87
- break;
88
- default:
89
- throw new JOSENotSupported('Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, and X25519');
90
- }
91
- break;
92
- }
93
- default:
94
- throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
42
+ : entry.subtle;
95
43
  }
96
- return crypto.subtle.generateKey(algorithm, options?.extractable ?? false, keyUsages);
44
+ return crypto.subtle.generateKey(algorithm, options?.extractable ?? false, [
45
+ ...entry.usages[1],
46
+ ...entry.usages[0],
47
+ ]);
97
48
  }
@@ -1,6 +1,7 @@
1
1
  import { decode as decodeBase64URL } from '../util/base64url.js';
2
2
  import { fromSPKI, fromPKCS8, fromX509 } from '../lib/asn1.js';
3
3
  import { jwkToKey } from '../lib/jwk_to_key.js';
4
+ import { keyAlgorithm } from '../lib/key_algorithm.js';
4
5
  import { JOSENotSupported } from '../util/errors.js';
5
6
  import { isObject } from '../lib/type_checks.js';
6
7
  export async function importSPKI(spki, alg, options) {
@@ -25,9 +26,11 @@ export async function importJWK(jwk, alg, options) {
25
26
  if (!isObject(jwk)) {
26
27
  throw new TypeError('JWK must be an object');
27
28
  }
28
- let ext;
29
29
  alg ??= jwk.alg;
30
- ext ??= options?.extractable ?? jwk.ext;
30
+ const ext = options?.extractable ?? jwk.ext;
31
+ if (jwk.kty !== 'oct' && !alg) {
32
+ throw new TypeError('"alg" argument is required when "jwk.alg" is not present');
33
+ }
31
34
  switch (jwk.kty) {
32
35
  case 'oct':
33
36
  if (typeof jwk.k !== 'string' || !jwk.k) {
@@ -35,10 +38,7 @@ export async function importJWK(jwk, alg, options) {
35
38
  }
36
39
  return decodeBase64URL(jwk.k);
37
40
  case 'RSA':
38
- if ('oth' in jwk && jwk.oth !== undefined) {
39
- throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');
40
- }
41
- return jwkToKey({ ...jwk, alg, ext });
41
+ return jwkToKey(keyAlgorithm(alg), { ...jwk, alg, ext });
42
42
  case 'AKP': {
43
43
  if (typeof jwk.alg !== 'string' || !jwk.alg) {
44
44
  throw new TypeError('missing "alg" (Algorithm) Parameter value');
@@ -46,11 +46,11 @@ export async function importJWK(jwk, alg, options) {
46
46
  if (alg !== undefined && alg !== jwk.alg) {
47
47
  throw new TypeError('JWK alg and alg option value mismatch');
48
48
  }
49
- return jwkToKey({ ...jwk, ext });
49
+ return jwkToKey(keyAlgorithm(jwk.alg), { ...jwk, ext });
50
50
  }
51
51
  case 'EC':
52
52
  case 'OKP':
53
- return jwkToKey({ ...jwk, alg, ext });
53
+ return jwkToKey(keyAlgorithm(alg), { ...jwk, alg, ext });
54
54
  default:
55
55
  throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value');
56
56
  }
@@ -1,6 +1,7 @@
1
1
  import { invalidKeyInput } from './invalid_key_input.js';
2
2
  import { encodeBase64, decodeBase64 } from '../lib/base64.js';
3
3
  import { JOSENotSupported } from '../util/errors.js';
4
+ import { keyAlgorithm } from './key_algorithm.js';
4
5
  import { isCryptoKey, isKeyObject } from './is_key_like.js';
5
6
  const formatPEM = (b64, descriptor) => {
6
7
  const newlined = (b64.match(/.{1,64}/g) || []).join('\n');
@@ -36,34 +37,41 @@ const bytesEqual = (a, b) => {
36
37
  return true;
37
38
  };
38
39
  const createASN1State = (data) => ({ data, pos: 0 });
40
+ const readByte = (state) => {
41
+ const byte = state.data[state.pos++];
42
+ if (byte === undefined) {
43
+ throw new Error('Unexpected end of ASN.1 input');
44
+ }
45
+ return byte;
46
+ };
39
47
  const parseLength = (state) => {
40
- const first = state.data[state.pos++];
48
+ const first = readByte(state);
41
49
  if (first & 0x80) {
42
50
  const lengthOfLen = first & 0x7f;
43
51
  let length = 0;
44
52
  for (let i = 0; i < lengthOfLen; i++) {
45
- length = (length << 8) | state.data[state.pos++];
53
+ length = (length << 8) | readByte(state);
46
54
  }
47
55
  return length;
48
56
  }
49
57
  return first;
50
58
  };
51
59
  const skipElement = (state, count = 1) => {
52
- if (count <= 0)
53
- return;
54
- state.pos++;
55
- const length = parseLength(state);
56
- state.pos += length;
57
- if (count > 1) {
58
- skipElement(state, count - 1);
60
+ while (count-- > 0) {
61
+ state.pos++;
62
+ const length = parseLength(state);
63
+ state.pos += length;
59
64
  }
60
65
  };
61
66
  const expectTag = (state, expectedTag, errorMessage) => {
62
- if (state.data[state.pos++] !== expectedTag) {
67
+ if (readByte(state) !== expectedTag) {
63
68
  throw new Error(errorMessage);
64
69
  }
65
70
  };
66
71
  const getSubarray = (state, length) => {
72
+ if (length < 0 || state.pos + length > state.data.length) {
73
+ throw new Error('Unexpected end of ASN.1 input');
74
+ }
67
75
  const result = state.data.subarray(state.pos, state.pos + length);
68
76
  state.pos += length;
69
77
  return result;
@@ -73,24 +81,16 @@ const parseAlgorithmOID = (state) => {
73
81
  const oidLen = parseLength(state);
74
82
  return getSubarray(state, oidLen);
75
83
  };
76
- function parsePKCS8Header(state) {
77
- expectTag(state, 0x30, 'Invalid PKCS#8 structure');
84
+ function parseKeyHeader(state, keyFormat) {
85
+ expectTag(state, 0x30, `Invalid ${keyFormat === 'spki' ? 'SPKI' : 'PKCS#8'} structure`);
78
86
  parseLength(state);
79
- expectTag(state, 0x02, 'Expected version field');
80
- const verLen = parseLength(state);
81
- state.pos += verLen;
87
+ if (keyFormat === 'pkcs8') {
88
+ expectTag(state, 0x02, 'Expected version field');
89
+ const length = parseLength(state);
90
+ state.pos += length;
91
+ }
82
92
  expectTag(state, 0x30, 'Expected algorithm identifier');
83
- const algIdLen = parseLength(state);
84
- const algIdStart = state.pos;
85
- return { algIdStart, algIdLength: algIdLen };
86
- }
87
- function parseSPKIHeader(state) {
88
- expectTag(state, 0x30, 'Invalid SPKI structure');
89
93
  parseLength(state);
90
- expectTag(state, 0x30, 'Expected algorithm identifier');
91
- const algIdLen = parseLength(state);
92
- const algIdStart = state.pos;
93
- return { algIdStart, algIdLength: algIdLen };
94
94
  }
95
95
  const parseECAlgorithmIdentifier = (state) => {
96
96
  const algOid = parseAlgorithmOID(state);
@@ -103,112 +103,46 @@ const parseECAlgorithmIdentifier = (state) => {
103
103
  expectTag(state, 0x06, 'Expected curve OID');
104
104
  const curveOidLen = parseLength(state);
105
105
  const curveOid = getSubarray(state, curveOidLen);
106
- for (const { name, oid } of [
107
- { name: 'P-256', oid: [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07] },
108
- { name: 'P-384', oid: [0x2b, 0x81, 0x04, 0x00, 0x22] },
109
- { name: 'P-521', oid: [0x2b, 0x81, 0x04, 0x00, 0x23] },
110
- ]) {
111
- if (bytesEqual(curveOid, oid)) {
112
- return name;
113
- }
114
- }
106
+ if (bytesEqual(curveOid, [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07]))
107
+ return 'P-256';
108
+ if (bytesEqual(curveOid, [0x2b, 0x81, 0x04, 0x00, 0x22]))
109
+ return 'P-384';
110
+ if (bytesEqual(curveOid, [0x2b, 0x81, 0x04, 0x00, 0x23]))
111
+ return 'P-521';
115
112
  throw new Error('Unsupported named curve');
116
113
  };
117
114
  const genericImport = async (keyFormat, keyData, alg, options) => {
118
- let algorithm;
119
- let keyUsages;
115
+ const entry = keyAlgorithm(alg);
116
+ if (entry.secret) {
117
+ throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value');
118
+ }
120
119
  const isPublic = keyFormat === 'spki';
121
- const getSigUsages = () => (isPublic ? ['verify'] : ['sign']);
122
- const getEncUsages = () => isPublic ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey'];
123
- switch (alg) {
124
- case 'PS256':
125
- case 'PS384':
126
- case 'PS512':
127
- algorithm = { name: 'RSA-PSS', hash: `SHA-${alg.slice(-3)}` };
128
- keyUsages = getSigUsages();
129
- break;
130
- case 'RS256':
131
- case 'RS384':
132
- case 'RS512':
133
- algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${alg.slice(-3)}` };
134
- keyUsages = getSigUsages();
135
- break;
136
- case 'RSA-OAEP':
137
- case 'RSA-OAEP-256':
138
- case 'RSA-OAEP-384':
139
- case 'RSA-OAEP-512':
140
- algorithm = {
141
- name: 'RSA-OAEP',
142
- hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`,
143
- };
144
- keyUsages = getEncUsages();
145
- break;
146
- case 'ES256':
147
- case 'ES384':
148
- case 'ES512': {
149
- const curveMap = { ES256: 'P-256', ES384: 'P-384', ES512: 'P-521' };
150
- algorithm = { name: 'ECDSA', namedCurve: curveMap[alg] };
151
- keyUsages = getSigUsages();
152
- break;
120
+ let algorithm;
121
+ if (entry.resolve) {
122
+ try {
123
+ const state = createASN1State(keyData);
124
+ parseKeyHeader(state, keyFormat);
125
+ algorithm = entry.resolve({ crv: parseECAlgorithmIdentifier(state) });
153
126
  }
154
- case 'ECDH-ES':
155
- case 'ECDH-ES+A128KW':
156
- case 'ECDH-ES+A192KW':
157
- case 'ECDH-ES+A256KW': {
158
- try {
159
- const namedCurve = options.getNamedCurve(keyData);
160
- algorithm = namedCurve === 'X25519' ? { name: 'X25519' } : { name: 'ECDH', namedCurve };
161
- }
162
- catch (cause) {
163
- throw new JOSENotSupported('Invalid or unsupported key format');
164
- }
165
- keyUsages = isPublic ? [] : ['deriveBits'];
166
- break;
127
+ catch {
128
+ throw new JOSENotSupported('Invalid or unsupported key format');
167
129
  }
168
- case 'Ed25519':
169
- case 'EdDSA':
170
- algorithm = { name: 'Ed25519' };
171
- keyUsages = getSigUsages();
172
- break;
173
- case 'ML-DSA-44':
174
- case 'ML-DSA-65':
175
- case 'ML-DSA-87':
176
- algorithm = { name: alg };
177
- keyUsages = getSigUsages();
178
- break;
179
- default:
180
- throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value');
181
- }
182
- return crypto.subtle.importKey(keyFormat, keyData, algorithm, options?.extractable ?? (isPublic ? true : false), keyUsages);
130
+ }
131
+ else {
132
+ algorithm = entry.subtle;
133
+ }
134
+ return crypto.subtle.importKey(keyFormat, keyData, algorithm, options?.extractable ?? isPublic, entry.usages[isPublic ? 0 : 1]);
183
135
  };
184
136
  const processPEMData = (pem, pattern) => {
185
137
  return decodeBase64(pem.replace(pattern, ''));
186
138
  };
187
139
  export const fromPKCS8 = (pem, alg, options) => {
188
140
  const keyData = processPEMData(pem, /(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g);
189
- let opts = options;
190
- if (alg?.startsWith?.('ECDH-ES')) {
191
- opts ||= {};
192
- opts.getNamedCurve = (keyData) => {
193
- const state = createASN1State(keyData);
194
- parsePKCS8Header(state);
195
- return parseECAlgorithmIdentifier(state);
196
- };
197
- }
198
- return genericImport('pkcs8', keyData, alg, opts);
141
+ return genericImport('pkcs8', keyData, alg, options);
199
142
  };
200
143
  export const fromSPKI = (pem, alg, options) => {
201
144
  const keyData = processPEMData(pem, /(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g);
202
- let opts = options;
203
- if (alg?.startsWith?.('ECDH-ES')) {
204
- opts ||= {};
205
- opts.getNamedCurve = (keyData) => {
206
- const state = createASN1State(keyData);
207
- parseSPKIHeader(state);
208
- return parseECAlgorithmIdentifier(state);
209
- };
210
- }
211
- return genericImport('spki', keyData, alg, opts);
145
+ return genericImport('spki', keyData, alg, options);
212
146
  };
213
147
  function spkiFromX509(buf) {
214
148
  const state = createASN1State(buf);
@@ -227,17 +161,14 @@ function spkiFromX509(buf) {
227
161
  const spkiContentLen = parseLength(state);
228
162
  return buf.subarray(spkiStart, spkiStart + spkiContentLen + (state.pos - spkiStart));
229
163
  }
230
- function extractX509SPKI(x509) {
231
- const derBytes = processPEMData(x509, /(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g);
232
- return spkiFromX509(derBytes);
233
- }
234
164
  export const fromX509 = (pem, alg, options) => {
235
165
  let spki;
236
166
  try {
237
- spki = extractX509SPKI(pem);
167
+ const certificate = processPEMData(pem, /(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g);
168
+ spki = spkiFromX509(certificate);
238
169
  }
239
170
  catch (cause) {
240
171
  throw new TypeError('Failed to parse the X.509 certificate', { cause });
241
172
  }
242
- return fromSPKI(formatPEM(encodeBase64(spki), 'PUBLIC KEY'), alg, options);
173
+ return genericImport('spki', spki, alg, options);
243
174
  };
@@ -1,5 +1,6 @@
1
1
  export const encoder = new TextEncoder();
2
2
  export const decoder = new TextDecoder();
3
+ export const strictDecoder = new TextDecoder('utf-8', { fatal: true });
3
4
  const MAX_INT32 = 2 ** 32;
4
5
  export function concat(...buffers) {
5
6
  const size = buffers.reduce((acc, { length }) => acc + length, 0);
@@ -1,52 +1,18 @@
1
1
  import { concat, uint64be } from './buffer_utils.js';
2
- import { checkEncCryptoKey } from './crypto_key.js';
2
+ import { checkCryptoKey } from './crypto_key.js';
3
3
  import { invalidKeyInput } from './invalid_key_input.js';
4
- import { JOSENotSupported, JWEDecryptionFailed, JWEInvalid } from '../util/errors.js';
4
+ import { JWEDecryptionFailed, JWEInvalid } from '../util/errors.js';
5
5
  import { isCryptoKey } from './is_key_like.js';
6
- export function cekLength(alg) {
7
- switch (alg) {
8
- case 'A128GCM':
9
- return 128;
10
- case 'A192GCM':
11
- return 192;
12
- case 'A256GCM':
13
- case 'A128CBC-HS256':
14
- return 256;
15
- case 'A192CBC-HS384':
16
- return 384;
17
- case 'A256CBC-HS512':
18
- return 512;
19
- default:
20
- throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`);
21
- }
22
- }
23
- export const generateCek = (alg) => crypto.getRandomValues(new Uint8Array(cekLength(alg) >> 3));
6
+ export const generateCek = (enc) => crypto.getRandomValues(new Uint8Array(enc.cekBits >> 3));
24
7
  function checkCekLength(cek, expected) {
25
8
  const actual = cek.byteLength << 3;
26
9
  if (actual !== expected) {
27
10
  throw new JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`);
28
11
  }
29
12
  }
30
- function ivBitLength(alg) {
31
- switch (alg) {
32
- case 'A128GCM':
33
- case 'A128GCMKW':
34
- case 'A192GCM':
35
- case 'A192GCMKW':
36
- case 'A256GCM':
37
- case 'A256GCMKW':
38
- return 96;
39
- case 'A128CBC-HS256':
40
- case 'A192CBC-HS384':
41
- case 'A256CBC-HS512':
42
- return 128;
43
- default:
44
- throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`);
45
- }
46
- }
47
- export const generateIv = (alg) => crypto.getRandomValues(new Uint8Array(ivBitLength(alg) >> 3));
13
+ export const generateIv = (enc) => crypto.getRandomValues(new Uint8Array(enc.ivBits >> 3));
48
14
  export function checkIvLength(enc, iv) {
49
- if (iv.length << 3 !== ivBitLength(enc)) {
15
+ if (iv.length << 3 !== enc.ivBits) {
50
16
  throw new JWEInvalid('Invalid Initialization Vector length');
51
17
  }
52
18
  }
@@ -54,48 +20,36 @@ async function cbcKeySetup(enc, cek, usage) {
54
20
  if (!(cek instanceof Uint8Array)) {
55
21
  throw new TypeError(invalidKeyInput(cek, 'Uint8Array'));
56
22
  }
57
- const keySize = parseInt(enc.slice(1, 4), 10);
23
+ const keySize = enc.cekBits >> 1;
58
24
  const encKey = await crypto.subtle.importKey('raw', cek.subarray(keySize >> 3), 'AES-CBC', false, [usage]);
59
25
  const macKey = await crypto.subtle.importKey('raw', cek.subarray(0, keySize >> 3), {
60
26
  hash: `SHA-${keySize << 1}`,
61
27
  name: 'HMAC',
62
28
  }, false, ['sign']);
63
- return { encKey, macKey, keySize };
29
+ return [encKey, macKey, keySize];
64
30
  }
65
31
  async function cbcHmacTag(macKey, macData, keySize) {
66
32
  return new Uint8Array((await crypto.subtle.sign('HMAC', macKey, macData)).slice(0, keySize >> 3));
67
33
  }
68
34
  async function cbcEncrypt(enc, plaintext, cek, iv, aad) {
69
- const { encKey, macKey, keySize } = await cbcKeySetup(enc, cek, 'encrypt');
35
+ const [encKey, macKey, keySize] = await cbcKeySetup(enc, cek, 'encrypt');
70
36
  const ciphertext = new Uint8Array(await crypto.subtle.encrypt({
71
37
  iv: iv,
72
38
  name: 'AES-CBC',
73
39
  }, encKey, plaintext));
74
- const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3));
40
+ const macData = concat(aad, iv, ciphertext, uint64be(aad.length * 8));
75
41
  const tag = await cbcHmacTag(macKey, macData, keySize);
76
42
  return { ciphertext, tag, iv };
77
43
  }
78
44
  async function timingSafeEqual(a, b) {
79
- if (!(a instanceof Uint8Array)) {
80
- throw new TypeError('First argument must be a buffer');
81
- }
82
- if (!(b instanceof Uint8Array)) {
83
- throw new TypeError('Second argument must be a buffer');
84
- }
85
45
  const algorithm = { name: 'HMAC', hash: 'SHA-256' };
86
- const key = (await crypto.subtle.generateKey(algorithm, false, ['sign']));
87
- const aHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, a));
88
- const bHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, b));
89
- let out = 0;
90
- let i = -1;
91
- while (++i < 32) {
92
- out |= aHmac[i] ^ bHmac[i];
93
- }
94
- return out === 0;
46
+ const key = (await crypto.subtle.generateKey(algorithm, false, ['sign', 'verify']));
47
+ const aHmac = await crypto.subtle.sign(algorithm, key, a);
48
+ return crypto.subtle.verify(algorithm, key, aHmac, b);
95
49
  }
96
50
  async function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) {
97
- const { encKey, macKey, keySize } = await cbcKeySetup(enc, cek, 'decrypt');
98
- const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3));
51
+ const [encKey, macKey, keySize] = await cbcKeySetup(enc, cek, 'decrypt');
52
+ const macData = concat(aad, iv, ciphertext, uint64be(aad.length * 8));
99
53
  const expectedTag = await cbcHmacTag(macKey, macData, keySize);
100
54
  let macCheckPassed;
101
55
  try {
@@ -118,14 +72,11 @@ async function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) {
118
72
  return plaintext;
119
73
  }
120
74
  async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
121
- let encKey;
122
- if (cek instanceof Uint8Array) {
123
- encKey = await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['encrypt']);
124
- }
125
- else {
126
- checkEncCryptoKey(cek, enc, 'encrypt');
127
- encKey = cek;
128
- }
75
+ const encKey = cek instanceof Uint8Array
76
+ ? await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, [
77
+ 'encrypt',
78
+ ])
79
+ : (checkCryptoKey(cek, enc.subtle, 'encrypt'), cek);
129
80
  const encrypted = new Uint8Array(await crypto.subtle.encrypt({
130
81
  additionalData: aad,
131
82
  iv: iv,
@@ -137,14 +88,11 @@ async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
137
88
  return { ciphertext, tag, iv };
138
89
  }
139
90
  async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
140
- let encKey;
141
- if (cek instanceof Uint8Array) {
142
- encKey = await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['decrypt']);
143
- }
144
- else {
145
- checkEncCryptoKey(cek, enc, 'decrypt');
146
- encKey = cek;
147
- }
91
+ const encKey = cek instanceof Uint8Array
92
+ ? await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, [
93
+ 'decrypt',
94
+ ])
95
+ : (checkCryptoKey(cek, enc.subtle, 'decrypt'), cek);
148
96
  try {
149
97
  return new Uint8Array(await crypto.subtle.decrypt({
150
98
  additionalData: aad,
@@ -157,7 +105,6 @@ async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
157
105
  throw new JWEDecryptionFailed();
158
106
  }
159
107
  }
160
- const unsupportedEnc = 'Unsupported JWE Content Encryption Algorithm';
161
108
  export async function encrypt(enc, plaintext, cek, iv, aad) {
162
109
  if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) {
163
110
  throw new TypeError(invalidKeyInput(cek, 'CryptoKey', 'KeyObject', 'Uint8Array', 'JSON Web Key'));
@@ -168,24 +115,12 @@ export async function encrypt(enc, plaintext, cek, iv, aad) {
168
115
  else {
169
116
  iv = generateIv(enc);
170
117
  }
171
- switch (enc) {
172
- case 'A128CBC-HS256':
173
- case 'A192CBC-HS384':
174
- case 'A256CBC-HS512':
175
- if (cek instanceof Uint8Array) {
176
- checkCekLength(cek, parseInt(enc.slice(-3), 10));
177
- }
178
- return cbcEncrypt(enc, plaintext, cek, iv, aad);
179
- case 'A128GCM':
180
- case 'A192GCM':
181
- case 'A256GCM':
182
- if (cek instanceof Uint8Array) {
183
- checkCekLength(cek, parseInt(enc.slice(1, 4), 10));
184
- }
185
- return gcmEncrypt(enc, plaintext, cek, iv, aad);
186
- default:
187
- throw new JOSENotSupported(unsupportedEnc);
118
+ if (cek instanceof Uint8Array) {
119
+ checkCekLength(cek, enc.cekBits);
188
120
  }
121
+ return enc.cbc
122
+ ? cbcEncrypt(enc, plaintext, cek, iv, aad)
123
+ : gcmEncrypt(enc, plaintext, cek, iv, aad);
189
124
  }
190
125
  export async function decrypt(enc, cek, ciphertext, iv, tag, aad) {
191
126
  if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) {
@@ -198,20 +133,10 @@ export async function decrypt(enc, cek, ciphertext, iv, tag, aad) {
198
133
  throw new JWEInvalid('JWE Authentication Tag missing');
199
134
  }
200
135
  checkIvLength(enc, iv);
201
- switch (enc) {
202
- case 'A128CBC-HS256':
203
- case 'A192CBC-HS384':
204
- case 'A256CBC-HS512':
205
- if (cek instanceof Uint8Array)
206
- checkCekLength(cek, parseInt(enc.slice(-3), 10));
207
- return cbcDecrypt(enc, cek, ciphertext, iv, tag, aad);
208
- case 'A128GCM':
209
- case 'A192GCM':
210
- case 'A256GCM':
211
- if (cek instanceof Uint8Array)
212
- checkCekLength(cek, parseInt(enc.slice(1, 4), 10));
213
- return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad);
214
- default:
215
- throw new JOSENotSupported(unsupportedEnc);
136
+ if (cek instanceof Uint8Array) {
137
+ checkCekLength(cek, enc.cekBits);
216
138
  }
139
+ return enc.cbc
140
+ ? cbcDecrypt(enc, cek, ciphertext, iv, tag, aad)
141
+ : gcmDecrypt(enc, cek, ciphertext, iv, tag, aad);
217
142
  }