@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.
- package/bin/suveren-gateway.js +86 -5
- package/content/integrations/calendar.json +116 -22
- package/content/integrations/deploy-github.json +164 -0
- package/content/integrations/gmail.json +13 -4
- package/content/integrations/index.json +2 -1
- package/dist/control-plane/index.mjs +84 -7
- package/dist/mcp-server/http.mjs +147 -21
- package/dist/ui/assets/index-DecSrutK.js +105 -0
- package/dist/ui/index.html +1 -1
- package/node_modules/@hap/core/dist/index.d.mts +21 -0
- package/node_modules/@hap/core/dist/index.d.ts +21 -0
- package/node_modules/@hap/core/dist/index.js +17 -0
- package/node_modules/@hap/core/dist/index.mjs +17 -0
- package/node_modules/@hap/core/package.json +1 -1
- package/node_modules/@hap/core/src/gatekeeper.ts +28 -0
- package/node_modules/@hap/core/src/types.ts +21 -0
- package/node_modules/fast-uri/index.js +37 -1
- package/node_modules/fast-uri/package.json +1 -1
- package/node_modules/fast-uri/test/security.test.js +136 -0
- package/node_modules/hono/dist/cjs/helper/proxy/index.js +4 -0
- package/node_modules/hono/dist/cjs/jsx/base.js +1 -9
- package/node_modules/hono/dist/cjs/jsx/hooks/index.js +14 -11
- package/node_modules/hono/dist/cjs/middleware/cors/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/language/language.js +10 -6
- package/node_modules/hono/dist/cjs/utils/cookie.js +2 -1
- package/node_modules/hono/dist/helper/proxy/index.js +4 -0
- package/node_modules/hono/dist/jsx/base.js +1 -9
- package/node_modules/hono/dist/jsx/hooks/index.js +14 -11
- package/node_modules/hono/dist/middleware/cors/index.js +1 -1
- package/node_modules/hono/dist/middleware/language/language.js +10 -6
- package/node_modules/hono/dist/utils/cookie.js +2 -1
- package/node_modules/hono/package.json +2 -3
- package/node_modules/ip-address/README.md +142 -128
- package/node_modules/ip-address/dist/common.d.ts +6 -0
- package/node_modules/ip-address/dist/common.js +17 -1
- package/node_modules/ip-address/dist/common.js.map +1 -1
- package/node_modules/ip-address/dist/ipv4.js +3 -12
- package/node_modules/ip-address/dist/ipv4.js.map +1 -1
- package/node_modules/ip-address/dist/ipv6.d.ts +9 -2
- package/node_modules/ip-address/dist/ipv6.js +35 -16
- package/node_modules/ip-address/dist/ipv6.js.map +1 -1
- package/node_modules/ip-address/package.json +5 -3
- package/node_modules/jose/README.md +13 -13
- package/node_modules/jose/dist/types/index.d.ts +6 -5
- package/node_modules/jose/dist/types/jwe/compact/decrypt.d.ts +17 -11
- package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +7 -19
- package/node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts +17 -11
- package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +7 -19
- package/node_modules/jose/dist/types/jwe/general/decrypt.d.ts +18 -13
- package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +20 -21
- package/node_modules/jose/dist/types/jwk/embedded.d.ts +8 -12
- package/node_modules/jose/dist/types/jwk/thumbprint.d.ts +0 -15
- package/node_modules/jose/dist/types/jwks/local.d.ts +12 -21
- package/node_modules/jose/dist/types/jwks/remote.d.ts +34 -165
- package/node_modules/jose/dist/types/jws/compact/sign.d.ts +2 -13
- package/node_modules/jose/dist/types/jws/compact/verify.d.ts +17 -13
- package/node_modules/jose/dist/types/jws/flattened/sign.d.ts +2 -13
- package/node_modules/jose/dist/types/jws/flattened/verify.d.ts +17 -13
- package/node_modules/jose/dist/types/jws/general/sign.d.ts +16 -17
- package/node_modules/jose/dist/types/jws/general/verify.d.ts +18 -15
- package/node_modules/jose/dist/types/jwt/decrypt.d.ts +17 -11
- package/node_modules/jose/dist/types/jwt/encrypt.d.ts +10 -34
- package/node_modules/jose/dist/types/jwt/sign.d.ts +2 -13
- package/node_modules/jose/dist/types/jwt/unsecured.d.ts +4 -13
- package/node_modules/jose/dist/types/jwt/verify.d.ts +14 -13
- package/node_modules/jose/dist/types/key/export.d.ts +0 -14
- package/node_modules/jose/dist/types/key/generate_key_pair.d.ts +7 -14
- package/node_modules/jose/dist/types/key/generate_secret.d.ts +16 -14
- package/node_modules/jose/dist/types/key/import.d.ts +18 -28
- package/node_modules/jose/dist/types/types.d.ts +239 -222
- package/node_modules/jose/dist/types/util/base64url.d.ts +11 -4
- package/node_modules/jose/dist/types/util/decode_jwt.d.ts +1 -9
- package/node_modules/jose/dist/types/util/decode_protected_header.d.ts +0 -8
- package/node_modules/jose/dist/types/util/errors.d.ts +128 -121
- package/node_modules/jose/dist/webapi/jwe/compact/decrypt.js +4 -22
- package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +5 -153
- package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +16 -111
- package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +15 -3
- package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +65 -81
- package/node_modules/jose/dist/webapi/jwk/embedded.js +5 -3
- package/node_modules/jose/dist/webapi/jwks/local.js +31 -72
- package/node_modules/jose/dist/webapi/jwks/remote.js +19 -28
- package/node_modules/jose/dist/webapi/jws/compact/sign.js +5 -2
- package/node_modules/jose/dist/webapi/jws/compact/verify.js +4 -16
- package/node_modules/jose/dist/webapi/jws/flattened/sign.js +7 -65
- package/node_modules/jose/dist/webapi/jws/flattened/verify.js +3 -89
- package/node_modules/jose/dist/webapi/jws/general/sign.js +19 -15
- package/node_modules/jose/dist/webapi/jws/general/verify.js +29 -9
- package/node_modules/jose/dist/webapi/jwt/decrypt.js +5 -5
- package/node_modules/jose/dist/webapi/jwt/sign.js +2 -3
- package/node_modules/jose/dist/webapi/jwt/unsecured.js +4 -3
- package/node_modules/jose/dist/webapi/jwt/verify.js +6 -6
- package/node_modules/jose/dist/webapi/key/export.js +37 -4
- package/node_modules/jose/dist/webapi/key/generate_key_pair.js +33 -82
- package/node_modules/jose/dist/webapi/key/import.js +8 -8
- package/node_modules/jose/dist/webapi/lib/asn1.js +53 -122
- package/node_modules/jose/dist/webapi/lib/buffer_utils.js +1 -0
- package/node_modules/jose/dist/webapi/lib/content_encryption.js +34 -109
- package/node_modules/jose/dist/webapi/lib/crypto_key.js +18 -126
- package/node_modules/jose/dist/webapi/lib/helpers.js +23 -0
- package/node_modules/jose/dist/webapi/lib/invalid_key_input.js +0 -1
- package/node_modules/jose/dist/webapi/lib/jwe_algorithms.js +116 -0
- package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +186 -0
- package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +110 -0
- package/node_modules/jose/dist/webapi/lib/jwk_to_key.js +8 -98
- package/node_modules/jose/dist/webapi/lib/jws_algorithms.js +76 -0
- package/node_modules/jose/dist/webapi/lib/jws_sign.js +74 -0
- package/node_modules/jose/dist/webapi/lib/jws_verify.js +107 -0
- package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +68 -109
- package/node_modules/jose/dist/webapi/lib/key.js +132 -0
- package/node_modules/jose/dist/webapi/lib/key_algorithm.js +10 -0
- package/node_modules/jose/dist/webapi/lib/key_descriptor.js +7 -0
- package/node_modules/jose/dist/webapi/lib/key_management.js +162 -92
- package/node_modules/jose/dist/webapi/lib/options.js +48 -0
- package/node_modules/jose/dist/webapi/lib/signing.js +14 -56
- package/node_modules/jose/dist/webapi/lib/type_checks.js +13 -18
- package/node_modules/jose/dist/webapi/util/base64url.js +13 -4
- package/node_modules/jose/dist/webapi/util/decode_jwt.js +2 -2
- package/node_modules/jose/dist/webapi/util/decode_protected_header.js +5 -15
- package/node_modules/jose/dist/webapi/util/errors.js +1 -1
- package/node_modules/jose/package.json +2 -4
- package/package.json +2 -2
- package/profiles/deploy/0.6.profile.json +168 -0
- package/profiles/deploy/0.7.profile.json +168 -0
- package/profiles/email/0.4.profile.json +9 -2
- package/profiles/index.json +3 -1
- package/dist/ui/assets/index-Cog54HOo.js +0 -105
- package/node_modules/jose/dist/webapi/lib/aesgcmkw.js +0 -15
- package/node_modules/jose/dist/webapi/lib/aeskw.js +0 -25
- package/node_modules/jose/dist/webapi/lib/check_key_type.js +0 -122
- package/node_modules/jose/dist/webapi/lib/ecdhes.js +0 -52
- package/node_modules/jose/dist/webapi/lib/key_to_jwk.js +0 -34
- package/node_modules/jose/dist/webapi/lib/normalize_key.js +0 -166
- package/node_modules/jose/dist/webapi/lib/pbes2kw.js +0 -42
- package/node_modules/jose/dist/webapi/lib/rsaes.js +0 -24
- package/node_modules/jose/dist/webapi/lib/validate_algorithms.js +0 -10
- 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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
name: '
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
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
|
|
77
|
-
expectTag(state, 0x30,
|
|
84
|
+
function parseKeyHeader(state, keyFormat) {
|
|
85
|
+
expectTag(state, 0x30, `Invalid ${keyFormat === 'spki' ? 'SPKI' : 'PKCS#8'} structure`);
|
|
78
86
|
parseLength(state);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
])
|
|
111
|
-
|
|
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
|
-
|
|
119
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
155
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 {
|
|
2
|
+
import { checkCryptoKey } from './crypto_key.js';
|
|
3
3
|
import { invalidKeyInput } from './invalid_key_input.js';
|
|
4
|
-
import {
|
|
4
|
+
import { JWEDecryptionFailed, JWEInvalid } from '../util/errors.js';
|
|
5
5
|
import { isCryptoKey } from './is_key_like.js';
|
|
6
|
-
export
|
|
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
|
-
|
|
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 !==
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
88
|
-
|
|
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
|
|
98
|
-
const macData = concat(aad, iv, ciphertext, uint64be(aad.length
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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
|
-
|
|
202
|
-
|
|
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
|
}
|