@opentdf/sdk 0.9.0-beta.92 → 0.9.0-beta.94
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/README.md +2 -2
- package/dist/cjs/src/access/access-fetch.js +1 -2
- package/dist/cjs/src/access/access-rpc.js +1 -3
- package/dist/cjs/src/access.js +1 -14
- package/dist/cjs/src/auth/auth.js +13 -10
- package/dist/cjs/src/auth/dpop.js +121 -0
- package/dist/cjs/src/auth/oidc-clientcredentials-provider.js +37 -3
- package/dist/cjs/src/auth/oidc-externaljwt-provider.js +37 -3
- package/dist/cjs/src/auth/oidc-refreshtoken-provider.js +37 -3
- package/dist/cjs/src/auth/oidc.js +10 -8
- package/dist/cjs/src/auth/providers.js +35 -12
- package/dist/cjs/src/crypto/index.js +16 -2
- package/dist/cjs/src/crypto/pemPublicToCrypto.js +17 -11
- package/dist/cjs/src/opentdf.js +50 -13
- package/dist/cjs/src/policy/discovery.js +2 -2
- package/dist/cjs/tdf3/index.js +4 -2
- package/dist/cjs/tdf3/src/assertions.js +71 -31
- package/dist/cjs/tdf3/src/ciphers/aes-gcm-cipher.js +1 -1
- package/dist/cjs/tdf3/src/ciphers/symmetric-cipher-base.js +4 -2
- package/dist/cjs/tdf3/src/client/index.js +23 -33
- package/dist/cjs/tdf3/src/crypto/crypto-utils.js +12 -5
- package/dist/cjs/tdf3/src/crypto/declarations.js +1 -1
- package/dist/cjs/tdf3/src/crypto/index.js +849 -88
- package/dist/cjs/tdf3/src/crypto/jose/jwt-claims-set.js +11 -0
- package/dist/cjs/tdf3/src/crypto/jose/validate-crit.js +8 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/buffer_utils.js +41 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/epoch.js +6 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/is_object.js +21 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/jwt_claims_set.js +112 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/secs.js +60 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/validate_crit.js +38 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/util/errors.js +135 -0
- package/dist/cjs/tdf3/src/crypto/jwt.js +183 -0
- package/dist/cjs/tdf3/src/crypto/salt.js +14 -8
- package/dist/cjs/tdf3/src/models/encryption-information.js +17 -20
- package/dist/cjs/tdf3/src/models/key-access.js +43 -63
- package/dist/cjs/tdf3/src/tdf.js +75 -75
- package/dist/cjs/tdf3/src/utils/index.js +5 -39
- package/dist/types/src/access/access-fetch.d.ts.map +1 -1
- package/dist/types/src/access/access-rpc.d.ts.map +1 -1
- package/dist/types/src/access.d.ts +0 -5
- package/dist/types/src/access.d.ts.map +1 -1
- package/dist/types/src/auth/auth.d.ts +9 -6
- package/dist/types/src/auth/auth.d.ts.map +1 -1
- package/dist/types/src/auth/dpop.d.ts +60 -0
- package/dist/types/src/auth/dpop.d.ts.map +1 -0
- package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts +3 -2
- package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts.map +1 -1
- package/dist/types/src/auth/oidc-externaljwt-provider.d.ts +3 -2
- package/dist/types/src/auth/oidc-externaljwt-provider.d.ts.map +1 -1
- package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts +3 -2
- package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts.map +1 -1
- package/dist/types/src/auth/oidc.d.ts +6 -4
- package/dist/types/src/auth/oidc.d.ts.map +1 -1
- package/dist/types/src/auth/providers.d.ts +5 -4
- package/dist/types/src/auth/providers.d.ts.map +1 -1
- package/dist/types/src/crypto/index.d.ts +2 -1
- package/dist/types/src/crypto/index.d.ts.map +1 -1
- package/dist/types/src/crypto/pemPublicToCrypto.d.ts +18 -0
- package/dist/types/src/crypto/pemPublicToCrypto.d.ts.map +1 -1
- package/dist/types/src/opentdf.d.ts +26 -7
- package/dist/types/src/opentdf.d.ts.map +1 -1
- package/dist/types/src/policy/discovery.d.ts +2 -2
- package/dist/types/tdf3/index.d.ts +3 -3
- package/dist/types/tdf3/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/assertions.d.ts +23 -8
- package/dist/types/tdf3/src/assertions.d.ts.map +1 -1
- package/dist/types/tdf3/src/ciphers/aes-gcm-cipher.d.ts +3 -3
- package/dist/types/tdf3/src/ciphers/aes-gcm-cipher.d.ts.map +1 -1
- package/dist/types/tdf3/src/ciphers/symmetric-cipher-base.d.ts +4 -4
- package/dist/types/tdf3/src/ciphers/symmetric-cipher-base.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/builders.d.ts +2 -2
- package/dist/types/tdf3/src/client/builders.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/index.d.ts +6 -5
- package/dist/types/tdf3/src/client/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/crypto/crypto-utils.d.ts +14 -4
- package/dist/types/tdf3/src/crypto/crypto-utils.d.ts.map +1 -1
- package/dist/types/tdf3/src/crypto/declarations.d.ts +283 -18
- package/dist/types/tdf3/src/crypto/declarations.d.ts.map +1 -1
- package/dist/types/tdf3/src/crypto/index.d.ts +105 -28
- package/dist/types/tdf3/src/crypto/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/crypto/jose/jwt-claims-set.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/jwt-claims-set.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/validate-crit.d.ts +5 -0
- package/dist/types/tdf3/src/crypto/jose/validate-crit.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/buffer_utils.d.ts +6 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/buffer_utils.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/epoch.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/epoch.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/is_object.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/is_object.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/jwt_claims_set.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/jwt_claims_set.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/secs.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/secs.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/validate_crit.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/validate_crit.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/util/errors.d.ts +76 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/util/errors.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jwt.d.ts +76 -0
- package/dist/types/tdf3/src/crypto/jwt.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/salt.d.ts +6 -1
- package/dist/types/tdf3/src/crypto/salt.d.ts.map +1 -1
- package/dist/types/tdf3/src/models/encryption-information.d.ts +4 -4
- package/dist/types/tdf3/src/models/encryption-information.d.ts.map +1 -1
- package/dist/types/tdf3/src/models/key-access.d.ts +8 -5
- package/dist/types/tdf3/src/models/key-access.d.ts.map +1 -1
- package/dist/types/tdf3/src/tdf.d.ts +8 -8
- package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
- package/dist/types/tdf3/src/utils/index.d.ts +4 -3
- package/dist/types/tdf3/src/utils/index.d.ts.map +1 -1
- package/dist/web/src/access/access-fetch.js +3 -4
- package/dist/web/src/access/access-rpc.js +3 -5
- package/dist/web/src/access.js +1 -13
- package/dist/web/src/auth/auth.js +13 -10
- package/dist/web/src/auth/dpop.js +118 -0
- package/dist/web/src/auth/oidc-clientcredentials-provider.js +4 -3
- package/dist/web/src/auth/oidc-externaljwt-provider.js +4 -3
- package/dist/web/src/auth/oidc-refreshtoken-provider.js +4 -3
- package/dist/web/src/auth/oidc.js +11 -9
- package/dist/web/src/auth/providers.js +13 -12
- package/dist/web/src/crypto/index.js +4 -2
- package/dist/web/src/crypto/pemPublicToCrypto.js +11 -9
- package/dist/web/src/opentdf.js +17 -13
- package/dist/web/src/policy/discovery.js +2 -2
- package/dist/web/tdf3/index.js +3 -2
- package/dist/web/tdf3/src/assertions.js +71 -31
- package/dist/web/tdf3/src/ciphers/aes-gcm-cipher.js +1 -1
- package/dist/web/tdf3/src/ciphers/symmetric-cipher-base.js +4 -2
- package/dist/web/tdf3/src/client/index.js +25 -35
- package/dist/web/tdf3/src/crypto/crypto-utils.js +12 -5
- package/dist/web/tdf3/src/crypto/declarations.js +1 -1
- package/dist/web/tdf3/src/crypto/index.js +830 -84
- package/dist/web/tdf3/src/crypto/jose/jwt-claims-set.js +5 -0
- package/dist/web/tdf3/src/crypto/jose/validate-crit.js +3 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/buffer_utils.js +35 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/epoch.js +4 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/is_object.js +19 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/jwt_claims_set.js +107 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/secs.js +58 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/validate_crit.js +36 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/util/errors.js +117 -0
- package/dist/web/tdf3/src/crypto/jwt.js +174 -0
- package/dist/web/tdf3/src/crypto/salt.js +13 -7
- package/dist/web/tdf3/src/models/encryption-information.js +11 -14
- package/dist/web/tdf3/src/models/key-access.js +44 -31
- package/dist/web/tdf3/src/tdf.js +71 -71
- package/dist/web/tdf3/src/utils/index.js +5 -6
- package/package.json +11 -4
- package/src/access/access-fetch.ts +2 -8
- package/src/access/access-rpc.ts +0 -7
- package/src/access.ts +0 -17
- package/src/auth/auth.ts +21 -12
- package/src/auth/dpop.ts +222 -0
- package/src/auth/oidc-clientcredentials-provider.ts +23 -15
- package/src/auth/oidc-externaljwt-provider.ts +23 -15
- package/src/auth/oidc-refreshtoken-provider.ts +23 -15
- package/src/auth/oidc.ts +21 -10
- package/src/auth/providers.ts +46 -29
- package/src/crypto/index.ts +21 -1
- package/src/crypto/pemPublicToCrypto.ts +11 -9
- package/src/opentdf.ts +36 -17
- package/src/policy/discovery.ts +2 -2
- package/tdf3/index.ts +32 -5
- package/tdf3/src/assertions.ts +99 -30
- package/tdf3/src/ciphers/aes-gcm-cipher.ts +7 -2
- package/tdf3/src/ciphers/symmetric-cipher-base.ts +7 -4
- package/tdf3/src/client/builders.ts +2 -2
- package/tdf3/src/client/index.ts +60 -59
- package/tdf3/src/crypto/crypto-utils.ts +15 -8
- package/tdf3/src/crypto/declarations.ts +338 -22
- package/tdf3/src/crypto/index.ts +1021 -118
- package/tdf3/src/crypto/jose/jwt-claims-set.ts +10 -0
- package/tdf3/src/crypto/jose/validate-crit.ts +9 -0
- package/tdf3/src/crypto/jose/vendor/lib/buffer_utils.ts +34 -0
- package/tdf3/src/crypto/jose/vendor/lib/epoch.ts +3 -0
- package/tdf3/src/crypto/jose/vendor/lib/is_object.ts +18 -0
- package/tdf3/src/crypto/jose/vendor/lib/jwt_claims_set.ts +106 -0
- package/tdf3/src/crypto/jose/vendor/lib/secs.ts +57 -0
- package/tdf3/src/crypto/jose/vendor/lib/validate_crit.ts +35 -0
- package/tdf3/src/crypto/jose/vendor/util/errors.ts +101 -0
- package/tdf3/src/crypto/jwt.ts +256 -0
- package/tdf3/src/crypto/salt.ts +16 -8
- package/tdf3/src/models/encryption-information.ts +14 -21
- package/tdf3/src/models/key-access.ts +57 -41
- package/tdf3/src/tdf.ts +110 -93
- package/tdf3/src/utils/index.ts +5 -6
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { JWTHeaderParameters, JWTPayload, JWTVerifyOptions } from 'jose';
|
|
2
|
+
import jwtClaimsSet from './vendor/lib/jwt_claims_set.js';
|
|
3
|
+
|
|
4
|
+
export default function joseJwtClaimsSet(
|
|
5
|
+
protectedHeader: JWTHeaderParameters,
|
|
6
|
+
encodedPayload: Uint8Array,
|
|
7
|
+
options?: JWTVerifyOptions
|
|
8
|
+
): JWTPayload {
|
|
9
|
+
return jwtClaimsSet(protectedHeader, encodedPayload, options) as JWTPayload;
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import validateCrit from './vendor/lib/validate_crit.js';
|
|
2
|
+
|
|
3
|
+
export default validateCrit as (
|
|
4
|
+
Err: new (message?: string, options?: { cause?: unknown }) => Error,
|
|
5
|
+
recognizedDefault: Map<string, boolean>,
|
|
6
|
+
recognizedOption: Record<string, boolean> | undefined,
|
|
7
|
+
protectedHeader: Record<string, unknown> | undefined,
|
|
8
|
+
joseHeader: Record<string, unknown>
|
|
9
|
+
) => Set<string>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Generated from jose@6.0.8. Do not edit directly.
|
|
3
|
+
export const encoder = new TextEncoder();
|
|
4
|
+
export const decoder = new TextDecoder();
|
|
5
|
+
const MAX_INT32 = 2 ** 32;
|
|
6
|
+
export function concat(...buffers) {
|
|
7
|
+
const size = buffers.reduce((acc, { length }) => acc + length, 0);
|
|
8
|
+
const buf = new Uint8Array(size);
|
|
9
|
+
let i = 0;
|
|
10
|
+
for (const buffer of buffers) {
|
|
11
|
+
buf.set(buffer, i);
|
|
12
|
+
i += buffer.length;
|
|
13
|
+
}
|
|
14
|
+
return buf;
|
|
15
|
+
}
|
|
16
|
+
function writeUInt32BE(buf, value, offset) {
|
|
17
|
+
if (value < 0 || value >= MAX_INT32) {
|
|
18
|
+
throw new RangeError(`value must be >= 0 and <= ${MAX_INT32 - 1}. Received ${value}`);
|
|
19
|
+
}
|
|
20
|
+
buf.set([value >>> 24, value >>> 16, value >>> 8, value & 0xff], offset);
|
|
21
|
+
}
|
|
22
|
+
export function uint64be(value) {
|
|
23
|
+
const high = Math.floor(value / MAX_INT32);
|
|
24
|
+
const low = value % MAX_INT32;
|
|
25
|
+
const buf = new Uint8Array(8);
|
|
26
|
+
writeUInt32BE(buf, high, 0);
|
|
27
|
+
writeUInt32BE(buf, low, 4);
|
|
28
|
+
return buf;
|
|
29
|
+
}
|
|
30
|
+
export function uint32be(value) {
|
|
31
|
+
const buf = new Uint8Array(4);
|
|
32
|
+
writeUInt32BE(buf, value);
|
|
33
|
+
return buf;
|
|
34
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Generated from jose@6.0.8. Do not edit directly.
|
|
3
|
+
function isObjectLike(value) {
|
|
4
|
+
return typeof value === 'object' && value !== null;
|
|
5
|
+
}
|
|
6
|
+
export default (input) => {
|
|
7
|
+
if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
if (Object.getPrototypeOf(input) === null) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
let proto = input;
|
|
14
|
+
while (Object.getPrototypeOf(proto) !== null) {
|
|
15
|
+
proto = Object.getPrototypeOf(proto);
|
|
16
|
+
}
|
|
17
|
+
return Object.getPrototypeOf(input) === proto;
|
|
18
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Generated from jose@6.0.8. Do not edit directly.
|
|
3
|
+
import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js';
|
|
4
|
+
import { decoder } from './buffer_utils.js';
|
|
5
|
+
import epoch from './epoch.js';
|
|
6
|
+
import secs from './secs.js';
|
|
7
|
+
import isObject from './is_object.js';
|
|
8
|
+
const normalizeTyp = (value) => value.toLowerCase().replace(/^application\//, '');
|
|
9
|
+
const checkAudiencePresence = (audPayload, audOption) => {
|
|
10
|
+
if (typeof audPayload === 'string') {
|
|
11
|
+
return audOption.includes(audPayload);
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(audPayload)) {
|
|
14
|
+
return audOption.some(Set.prototype.has.bind(new Set(audPayload)));
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
};
|
|
18
|
+
export default (protectedHeader, encodedPayload, options = {}) => {
|
|
19
|
+
let payload;
|
|
20
|
+
try {
|
|
21
|
+
payload = JSON.parse(decoder.decode(encodedPayload));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
}
|
|
25
|
+
if (!isObject(payload)) {
|
|
26
|
+
throw new JWTInvalid('JWT Claims Set must be a top-level JSON object');
|
|
27
|
+
}
|
|
28
|
+
const { typ } = options;
|
|
29
|
+
if (typ &&
|
|
30
|
+
(typeof protectedHeader.typ !== 'string' ||
|
|
31
|
+
normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
|
|
32
|
+
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, 'typ', 'check_failed');
|
|
33
|
+
}
|
|
34
|
+
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;
|
|
35
|
+
const presenceCheck = [...requiredClaims];
|
|
36
|
+
if (maxTokenAge !== undefined)
|
|
37
|
+
presenceCheck.push('iat');
|
|
38
|
+
if (audience !== undefined)
|
|
39
|
+
presenceCheck.push('aud');
|
|
40
|
+
if (subject !== undefined)
|
|
41
|
+
presenceCheck.push('sub');
|
|
42
|
+
if (issuer !== undefined)
|
|
43
|
+
presenceCheck.push('iss');
|
|
44
|
+
for (const claim of new Set(presenceCheck.reverse())) {
|
|
45
|
+
if (!(claim in payload)) {
|
|
46
|
+
throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, 'missing');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (issuer &&
|
|
50
|
+
!(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {
|
|
51
|
+
throw new JWTClaimValidationFailed('unexpected "iss" claim value', payload, 'iss', 'check_failed');
|
|
52
|
+
}
|
|
53
|
+
if (subject && payload.sub !== subject) {
|
|
54
|
+
throw new JWTClaimValidationFailed('unexpected "sub" claim value', payload, 'sub', 'check_failed');
|
|
55
|
+
}
|
|
56
|
+
if (audience &&
|
|
57
|
+
!checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {
|
|
58
|
+
throw new JWTClaimValidationFailed('unexpected "aud" claim value', payload, 'aud', 'check_failed');
|
|
59
|
+
}
|
|
60
|
+
let tolerance;
|
|
61
|
+
switch (typeof options.clockTolerance) {
|
|
62
|
+
case 'string':
|
|
63
|
+
tolerance = secs(options.clockTolerance);
|
|
64
|
+
break;
|
|
65
|
+
case 'number':
|
|
66
|
+
tolerance = options.clockTolerance;
|
|
67
|
+
break;
|
|
68
|
+
case 'undefined':
|
|
69
|
+
tolerance = 0;
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
throw new TypeError('Invalid clockTolerance option type');
|
|
73
|
+
}
|
|
74
|
+
const { currentDate } = options;
|
|
75
|
+
const now = epoch(currentDate || new Date());
|
|
76
|
+
if ((payload.iat !== undefined || maxTokenAge) && typeof payload.iat !== 'number') {
|
|
77
|
+
throw new JWTClaimValidationFailed('"iat" claim must be a number', payload, 'iat', 'invalid');
|
|
78
|
+
}
|
|
79
|
+
if (payload.nbf !== undefined) {
|
|
80
|
+
if (typeof payload.nbf !== 'number') {
|
|
81
|
+
throw new JWTClaimValidationFailed('"nbf" claim must be a number', payload, 'nbf', 'invalid');
|
|
82
|
+
}
|
|
83
|
+
if (payload.nbf > now + tolerance) {
|
|
84
|
+
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, 'nbf', 'check_failed');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (payload.exp !== undefined) {
|
|
88
|
+
if (typeof payload.exp !== 'number') {
|
|
89
|
+
throw new JWTClaimValidationFailed('"exp" claim must be a number', payload, 'exp', 'invalid');
|
|
90
|
+
}
|
|
91
|
+
if (payload.exp <= now - tolerance) {
|
|
92
|
+
throw new JWTExpired('"exp" claim timestamp check failed', payload, 'exp', 'check_failed');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (maxTokenAge) {
|
|
96
|
+
const age = now - payload.iat;
|
|
97
|
+
const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge);
|
|
98
|
+
if (age - tolerance > max) {
|
|
99
|
+
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, 'iat', 'check_failed');
|
|
100
|
+
}
|
|
101
|
+
if (age < 0 - tolerance) {
|
|
102
|
+
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, 'iat', 'check_failed');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return payload;
|
|
106
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Generated from jose@6.0.8. Do not edit directly.
|
|
3
|
+
const minute = 60;
|
|
4
|
+
const hour = minute * 60;
|
|
5
|
+
const day = hour * 24;
|
|
6
|
+
const week = day * 7;
|
|
7
|
+
const year = day * 365.25;
|
|
8
|
+
const REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;
|
|
9
|
+
export default (str) => {
|
|
10
|
+
const matched = REGEX.exec(str);
|
|
11
|
+
if (!matched || (matched[4] && matched[1])) {
|
|
12
|
+
throw new TypeError('Invalid time period format');
|
|
13
|
+
}
|
|
14
|
+
const value = parseFloat(matched[2]);
|
|
15
|
+
const unit = matched[3].toLowerCase();
|
|
16
|
+
let numericDate;
|
|
17
|
+
switch (unit) {
|
|
18
|
+
case 'sec':
|
|
19
|
+
case 'secs':
|
|
20
|
+
case 'second':
|
|
21
|
+
case 'seconds':
|
|
22
|
+
case 's':
|
|
23
|
+
numericDate = Math.round(value);
|
|
24
|
+
break;
|
|
25
|
+
case 'minute':
|
|
26
|
+
case 'minutes':
|
|
27
|
+
case 'min':
|
|
28
|
+
case 'mins':
|
|
29
|
+
case 'm':
|
|
30
|
+
numericDate = Math.round(value * minute);
|
|
31
|
+
break;
|
|
32
|
+
case 'hour':
|
|
33
|
+
case 'hours':
|
|
34
|
+
case 'hr':
|
|
35
|
+
case 'hrs':
|
|
36
|
+
case 'h':
|
|
37
|
+
numericDate = Math.round(value * hour);
|
|
38
|
+
break;
|
|
39
|
+
case 'day':
|
|
40
|
+
case 'days':
|
|
41
|
+
case 'd':
|
|
42
|
+
numericDate = Math.round(value * day);
|
|
43
|
+
break;
|
|
44
|
+
case 'week':
|
|
45
|
+
case 'weeks':
|
|
46
|
+
case 'w':
|
|
47
|
+
numericDate = Math.round(value * week);
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
numericDate = Math.round(value * year);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
if (matched[1] === '-' || matched[4] === 'ago') {
|
|
54
|
+
return -numericDate;
|
|
55
|
+
}
|
|
56
|
+
return numericDate;
|
|
57
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Generated from jose@6.0.8. Do not edit directly.
|
|
3
|
+
import { JOSENotSupported, JWEInvalid, JWSInvalid } from '../util/errors.js';
|
|
4
|
+
export default (Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) => {
|
|
5
|
+
if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined) {
|
|
6
|
+
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected');
|
|
7
|
+
}
|
|
8
|
+
if (!protectedHeader || protectedHeader.crit === undefined) {
|
|
9
|
+
return new Set();
|
|
10
|
+
}
|
|
11
|
+
if (!Array.isArray(protectedHeader.crit) ||
|
|
12
|
+
protectedHeader.crit.length === 0 ||
|
|
13
|
+
protectedHeader.crit.some((input) => typeof input !== 'string' || input.length === 0)) {
|
|
14
|
+
throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');
|
|
15
|
+
}
|
|
16
|
+
let recognized;
|
|
17
|
+
if (recognizedOption !== undefined) {
|
|
18
|
+
recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
recognized = recognizedDefault;
|
|
22
|
+
}
|
|
23
|
+
for (const parameter of protectedHeader.crit) {
|
|
24
|
+
if (!recognized.has(parameter)) {
|
|
25
|
+
throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`);
|
|
26
|
+
}
|
|
27
|
+
if (joseHeader[parameter] === undefined) {
|
|
28
|
+
throw new Err(`Extension Header Parameter "${parameter}" is missing`);
|
|
29
|
+
}
|
|
30
|
+
if (recognized.get(parameter) && protectedHeader[parameter] === undefined) {
|
|
31
|
+
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return new Set(protectedHeader.crit);
|
|
35
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Generated from jose@6.0.8. Do not edit directly.
|
|
3
|
+
export class JOSEError extends Error {
|
|
4
|
+
static code = 'ERR_JOSE_GENERIC';
|
|
5
|
+
code = 'ERR_JOSE_GENERIC';
|
|
6
|
+
constructor(message, options) {
|
|
7
|
+
super(message, options);
|
|
8
|
+
this.name = this.constructor.name;
|
|
9
|
+
Error.captureStackTrace?.(this, this.constructor);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class JWTClaimValidationFailed extends JOSEError {
|
|
13
|
+
static code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';
|
|
14
|
+
code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';
|
|
15
|
+
claim;
|
|
16
|
+
reason;
|
|
17
|
+
payload;
|
|
18
|
+
constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {
|
|
19
|
+
super(message, { cause: { claim, reason, payload } });
|
|
20
|
+
this.claim = claim;
|
|
21
|
+
this.reason = reason;
|
|
22
|
+
this.payload = payload;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class JWTExpired extends JOSEError {
|
|
26
|
+
static code = 'ERR_JWT_EXPIRED';
|
|
27
|
+
code = 'ERR_JWT_EXPIRED';
|
|
28
|
+
claim;
|
|
29
|
+
reason;
|
|
30
|
+
payload;
|
|
31
|
+
constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {
|
|
32
|
+
super(message, { cause: { claim, reason, payload } });
|
|
33
|
+
this.claim = claim;
|
|
34
|
+
this.reason = reason;
|
|
35
|
+
this.payload = payload;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export class JOSEAlgNotAllowed extends JOSEError {
|
|
39
|
+
static code = 'ERR_JOSE_ALG_NOT_ALLOWED';
|
|
40
|
+
code = 'ERR_JOSE_ALG_NOT_ALLOWED';
|
|
41
|
+
}
|
|
42
|
+
export class JOSENotSupported extends JOSEError {
|
|
43
|
+
static code = 'ERR_JOSE_NOT_SUPPORTED';
|
|
44
|
+
code = 'ERR_JOSE_NOT_SUPPORTED';
|
|
45
|
+
}
|
|
46
|
+
export class JWEDecryptionFailed extends JOSEError {
|
|
47
|
+
static code = 'ERR_JWE_DECRYPTION_FAILED';
|
|
48
|
+
code = 'ERR_JWE_DECRYPTION_FAILED';
|
|
49
|
+
constructor(message = 'decryption operation failed', options) {
|
|
50
|
+
super(message, options);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export class JWEInvalid extends JOSEError {
|
|
54
|
+
static code = 'ERR_JWE_INVALID';
|
|
55
|
+
code = 'ERR_JWE_INVALID';
|
|
56
|
+
}
|
|
57
|
+
export class JWSInvalid extends JOSEError {
|
|
58
|
+
static code = 'ERR_JWS_INVALID';
|
|
59
|
+
code = 'ERR_JWS_INVALID';
|
|
60
|
+
}
|
|
61
|
+
export class JWTInvalid extends JOSEError {
|
|
62
|
+
static code = 'ERR_JWT_INVALID';
|
|
63
|
+
code = 'ERR_JWT_INVALID';
|
|
64
|
+
}
|
|
65
|
+
export class JWKInvalid extends JOSEError {
|
|
66
|
+
static code = 'ERR_JWK_INVALID';
|
|
67
|
+
code = 'ERR_JWK_INVALID';
|
|
68
|
+
}
|
|
69
|
+
export class JWKSInvalid extends JOSEError {
|
|
70
|
+
static code = 'ERR_JWKS_INVALID';
|
|
71
|
+
code = 'ERR_JWKS_INVALID';
|
|
72
|
+
}
|
|
73
|
+
export class JWKSNoMatchingKey extends JOSEError {
|
|
74
|
+
static code = 'ERR_JWKS_NO_MATCHING_KEY';
|
|
75
|
+
code = 'ERR_JWKS_NO_MATCHING_KEY';
|
|
76
|
+
constructor(message = 'no applicable key found in the JSON Web Key Set', options) {
|
|
77
|
+
super(message, options);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export class JWKSMultipleMatchingKeys extends JOSEError {
|
|
81
|
+
[Symbol.asyncIterator];
|
|
82
|
+
static code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
|
|
83
|
+
code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
|
|
84
|
+
constructor(message = 'multiple matching keys found in the JSON Web Key Set', options) {
|
|
85
|
+
super(message, options);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export class JWKSTimeout extends JOSEError {
|
|
89
|
+
static code = 'ERR_JWKS_TIMEOUT';
|
|
90
|
+
code = 'ERR_JWKS_TIMEOUT';
|
|
91
|
+
constructor(message = 'request timed out', options) {
|
|
92
|
+
super(message, options);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export class JWSSignatureVerificationFailed extends JOSEError {
|
|
96
|
+
static code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';
|
|
97
|
+
code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';
|
|
98
|
+
constructor(message = 'signature verification failed', options) {
|
|
99
|
+
super(message, options);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AsymmetricSigningAlgorithm,
|
|
3
|
+
type CryptoService,
|
|
4
|
+
type PrivateKey,
|
|
5
|
+
type PublicKey,
|
|
6
|
+
type SigningAlgorithm,
|
|
7
|
+
type SymmetricKey,
|
|
8
|
+
} from './declarations.js';
|
|
9
|
+
import { base64 } from '../../../src/encodings/index.js';
|
|
10
|
+
import {
|
|
11
|
+
decodeProtectedHeader as joseDecodeProtectedHeader,
|
|
12
|
+
errors as joseErrors,
|
|
13
|
+
type JWTHeaderParameters,
|
|
14
|
+
type JWTPayload,
|
|
15
|
+
type JWTVerifyOptions,
|
|
16
|
+
type SignOptions,
|
|
17
|
+
} from 'jose';
|
|
18
|
+
import jwtClaimsSet from './jose/jwt-claims-set.js';
|
|
19
|
+
import validateCrit from './jose/validate-crit.js';
|
|
20
|
+
|
|
21
|
+
export type JwtHeader = JWTHeaderParameters & { alg: SigningAlgorithm };
|
|
22
|
+
export type JwtPayload = JWTPayload;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Options for JWT signing. Matches jose SignOptions interface.
|
|
26
|
+
*/
|
|
27
|
+
export type SignJwtOptions = SignOptions;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Options for JWT verification. Matches jose JWTVerifyOptions interface.
|
|
31
|
+
* Combines signature verification options and JWT claim verification options.
|
|
32
|
+
*/
|
|
33
|
+
export type VerifyJwtOptions = Omit<JWTVerifyOptions, 'algorithms'> & {
|
|
34
|
+
/**
|
|
35
|
+
* A list of accepted JWS "alg" (Algorithm) Header Parameter values.
|
|
36
|
+
* By default all algorithms supported by the CryptoService are allowed.
|
|
37
|
+
* Unsecured JWTs ({ "alg": "none" }) are never accepted.
|
|
38
|
+
*/
|
|
39
|
+
algorithms?: SigningAlgorithm[];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Base64url encode data per RFC 4648 Section 5.
|
|
44
|
+
* Uses URL-safe alphabet (- and _ instead of + and /) with no padding.
|
|
45
|
+
* Exported for testing purposes.
|
|
46
|
+
*/
|
|
47
|
+
export function base64urlEncode(data: string | Uint8Array): string {
|
|
48
|
+
if (typeof data === 'string') {
|
|
49
|
+
// Encode string to base64url
|
|
50
|
+
const bytes = new TextEncoder().encode(data);
|
|
51
|
+
return base64.encodeArrayBuffer(bytes.buffer, true); // urlSafe = true
|
|
52
|
+
} else {
|
|
53
|
+
// Encode Uint8Array to base64url
|
|
54
|
+
const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
55
|
+
return base64.encodeArrayBuffer(buffer, true); // urlSafe = true
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Helper to convert base64url to standard base64 with padding.
|
|
61
|
+
*/
|
|
62
|
+
function base64urlToBase64(str: string): string {
|
|
63
|
+
// Convert base64url to base64: replace - with +, _ with /
|
|
64
|
+
let b64 = str.replace(/-/g, '+').replace(/_/g, '/');
|
|
65
|
+
|
|
66
|
+
// Add padding if needed
|
|
67
|
+
const padding = (4 - (b64.length % 4)) % 4;
|
|
68
|
+
b64 += '='.repeat(padding);
|
|
69
|
+
|
|
70
|
+
return b64;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Base64url decode to Uint8Array per RFC 4648 Section 5.
|
|
75
|
+
*/
|
|
76
|
+
function base64urlDecodeBytes(str: string): Uint8Array {
|
|
77
|
+
const b64 = base64urlToBase64(str);
|
|
78
|
+
return new Uint8Array(base64.decodeArrayBuffer(b64));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Decode the protected header from a JWT without verifying the signature.
|
|
83
|
+
* Useful for inspecting the header to determine key type before verification.
|
|
84
|
+
*
|
|
85
|
+
* @param token - The JWT string
|
|
86
|
+
* @returns The decoded header
|
|
87
|
+
* @throws Error if the token is malformed or uses alg "none"
|
|
88
|
+
*/
|
|
89
|
+
export function decodeProtectedHeader(token: string): JwtHeader {
|
|
90
|
+
return joseDecodeProtectedHeader(token) as JwtHeader;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Sign a JWT using CryptoService. Replaces jose SignJWT.
|
|
95
|
+
*
|
|
96
|
+
* Implementation:
|
|
97
|
+
* 1. Base64url encode header and payload as JSON
|
|
98
|
+
* 2. Create signing input: `${headerB64}.${payloadB64}`
|
|
99
|
+
* 3. Sign via cryptoService.sign() (asymmetric) or hmac() (HS256)
|
|
100
|
+
* 4. Return compact JWT: `${headerB64}.${payloadB64}.${signatureB64}`
|
|
101
|
+
*
|
|
102
|
+
* @param cryptoService - Crypto implementation to use
|
|
103
|
+
* @param payload - JWT payload (claims)
|
|
104
|
+
* @param key - PEM-encoded private key for asymmetric algorithms, or raw key bytes for HS256
|
|
105
|
+
* @param header - JWT header (must include alg)
|
|
106
|
+
* @param options - Optional signing options (e.g., crit header handling)
|
|
107
|
+
* @returns Compact JWT string
|
|
108
|
+
*/
|
|
109
|
+
export async function signJwt(
|
|
110
|
+
cryptoService: CryptoService,
|
|
111
|
+
payload: JwtPayload,
|
|
112
|
+
key: PrivateKey | SymmetricKey,
|
|
113
|
+
header: JwtHeader,
|
|
114
|
+
options?: SignJwtOptions
|
|
115
|
+
): Promise<string> {
|
|
116
|
+
validateCrit(joseErrors.JWSInvalid, new Map([['b64', true]]), options?.crit, header, header);
|
|
117
|
+
|
|
118
|
+
// Encode header and payload per RFC 7515
|
|
119
|
+
const headerB64 = base64urlEncode(JSON.stringify(header));
|
|
120
|
+
const payloadB64 = base64urlEncode(JSON.stringify(payload));
|
|
121
|
+
|
|
122
|
+
// Create signing input
|
|
123
|
+
const signingInput = `${headerB64}.${payloadB64}`;
|
|
124
|
+
const signingInputBytes = new TextEncoder().encode(signingInput);
|
|
125
|
+
|
|
126
|
+
// Sign via CryptoService - route based on algorithm
|
|
127
|
+
let signature: Uint8Array;
|
|
128
|
+
if (header.alg === 'HS256') {
|
|
129
|
+
if (key._brand !== 'SymmetricKey') {
|
|
130
|
+
throw new Error('HS256 requires a SymmetricKey');
|
|
131
|
+
}
|
|
132
|
+
signature = await cryptoService.hmac(signingInputBytes, key);
|
|
133
|
+
} else {
|
|
134
|
+
if (key._brand !== 'PrivateKey') {
|
|
135
|
+
throw new Error(`${header.alg} requires a PrivateKey`);
|
|
136
|
+
}
|
|
137
|
+
signature = await cryptoService.sign(
|
|
138
|
+
signingInputBytes,
|
|
139
|
+
key,
|
|
140
|
+
header.alg as AsymmetricSigningAlgorithm
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Return compact JWT
|
|
145
|
+
return `${signingInput}.${base64urlEncode(signature)}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Verify a JWT and return its contents. Replaces jose jwtVerify.
|
|
150
|
+
*
|
|
151
|
+
* Implementation:
|
|
152
|
+
* 1. Split token into header.payload.signature
|
|
153
|
+
* 2. Decode header, validate algorithm against allowlist
|
|
154
|
+
* 3. Verify signature via cryptoService.verify() (asymmetric) or verifyHmac() (HS256)
|
|
155
|
+
* 4. Validate JWT claims (aud, iss, exp, nbf, etc.)
|
|
156
|
+
* 5. Return decoded header and payload
|
|
157
|
+
*
|
|
158
|
+
* @param cryptoService - Crypto implementation to use
|
|
159
|
+
* @param token - The JWT string to verify
|
|
160
|
+
* @param key - For asymmetric: PEM string or PublicKey (opaque). For HS256: Uint8Array or SymmetricKey (opaque).
|
|
161
|
+
* @param options - Verification options including algorithm allowlist and claim validations
|
|
162
|
+
* @throws Error if signature invalid, algorithm not in allowlist, claims invalid, or token malformed
|
|
163
|
+
* @returns Decoded header and payload
|
|
164
|
+
*/
|
|
165
|
+
export async function verifyJwt(
|
|
166
|
+
cryptoService: CryptoService,
|
|
167
|
+
token: string,
|
|
168
|
+
key: string | Uint8Array | PublicKey | SymmetricKey,
|
|
169
|
+
options?: VerifyJwtOptions
|
|
170
|
+
): Promise<{ header: JwtHeader; payload: JwtPayload }> {
|
|
171
|
+
const parts = token.split('.');
|
|
172
|
+
if (parts.length !== 3) {
|
|
173
|
+
throw new joseErrors.JWTInvalid('Invalid Token or Protected Header formatting');
|
|
174
|
+
}
|
|
175
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
176
|
+
|
|
177
|
+
// Decode and validate header
|
|
178
|
+
const headerRaw = decodeProtectedHeader(token);
|
|
179
|
+
if (typeof headerRaw.alg !== 'string' || !headerRaw.alg) {
|
|
180
|
+
throw new joseErrors.JWTInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
181
|
+
}
|
|
182
|
+
if ((headerRaw.alg as string) === 'none') {
|
|
183
|
+
throw new joseErrors.JWTInvalid('Invalid JWT: alg "none" not allowed');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Validate algorithm is in allowlist if provided
|
|
187
|
+
if (options?.algorithms && !options.algorithms.includes(headerRaw.alg as SigningAlgorithm)) {
|
|
188
|
+
throw new joseErrors.JWTInvalid(`Invalid JWT: algorithm "${headerRaw.alg}" not in allowlist`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const extensions = validateCrit(
|
|
192
|
+
joseErrors.JWSInvalid,
|
|
193
|
+
new Map([['b64', true]]),
|
|
194
|
+
options?.crit,
|
|
195
|
+
headerRaw,
|
|
196
|
+
headerRaw
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// Now we know it's a valid algorithm
|
|
200
|
+
const header = headerRaw as JwtHeader;
|
|
201
|
+
|
|
202
|
+
// Verify signature via CryptoService - route based on algorithm
|
|
203
|
+
const signingInput = `${headerB64}.${payloadB64}`;
|
|
204
|
+
const signingInputBytes = new TextEncoder().encode(signingInput);
|
|
205
|
+
const signature = base64urlDecodeBytes(signatureB64);
|
|
206
|
+
|
|
207
|
+
let valid: boolean;
|
|
208
|
+
if (header.alg === 'HS256') {
|
|
209
|
+
// Symmetric verification - accept Uint8Array or SymmetricKey
|
|
210
|
+
if (typeof key === 'string') {
|
|
211
|
+
throw new Error('HS256 requires a Uint8Array or SymmetricKey, not a PEM string');
|
|
212
|
+
}
|
|
213
|
+
if ('_brand' in key && key._brand === 'PublicKey') {
|
|
214
|
+
throw new Error('HS256 requires a SymmetricKey, not a PublicKey');
|
|
215
|
+
}
|
|
216
|
+
// Convert Uint8Array to SymmetricKey if needed, otherwise assume it's already SymmetricKey
|
|
217
|
+
const symmetricKey =
|
|
218
|
+
key instanceof Uint8Array
|
|
219
|
+
? await cryptoService.importSymmetricKey(key)
|
|
220
|
+
: (key as SymmetricKey);
|
|
221
|
+
valid = await cryptoService.verifyHmac(signingInputBytes, signature, symmetricKey);
|
|
222
|
+
} else {
|
|
223
|
+
// Asymmetric verification - accept string (PEM) or PublicKey
|
|
224
|
+
if (key instanceof Uint8Array) {
|
|
225
|
+
throw new Error(`${header.alg} requires a PEM string or PublicKey, not Uint8Array`);
|
|
226
|
+
}
|
|
227
|
+
if (typeof key === 'object' && '_brand' in key && key._brand === 'SymmetricKey') {
|
|
228
|
+
throw new Error(`${header.alg} requires a PublicKey, not a SymmetricKey`);
|
|
229
|
+
}
|
|
230
|
+
// Convert PEM string to PublicKey if needed, otherwise assume it's already PublicKey
|
|
231
|
+
const publicKey =
|
|
232
|
+
typeof key === 'string'
|
|
233
|
+
? await cryptoService.importPublicKey(key, { usage: 'sign' })
|
|
234
|
+
: (key as PublicKey);
|
|
235
|
+
valid = await cryptoService.verify(
|
|
236
|
+
signingInputBytes,
|
|
237
|
+
signature,
|
|
238
|
+
publicKey,
|
|
239
|
+
header.alg as AsymmetricSigningAlgorithm
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!valid) {
|
|
244
|
+
throw new joseErrors.JWTInvalid('Invalid JWT: signature verification failed');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (extensions.has('b64') && (header as { b64?: boolean }).b64 === false) {
|
|
248
|
+
throw new joseErrors.JWTInvalid('JWTs MUST NOT use unencoded payload');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Decode payload and validate JWT claims
|
|
252
|
+
const payloadBytes = base64urlDecodeBytes(payloadB64);
|
|
253
|
+
const payload = jwtClaimsSet(header, payloadBytes, options) as JwtPayload;
|
|
254
|
+
|
|
255
|
+
return { header, payload };
|
|
256
|
+
}
|
package/tdf3/src/crypto/salt.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { CryptoService } from './declarations.js';
|
|
2
|
+
|
|
3
|
+
let cachedSalt: Uint8Array | null = null;
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Get the ZTDF salt (SHA-256 of "TDF").
|
|
7
|
+
* Lazily computed on first call and cached thereafter.
|
|
8
|
+
*/
|
|
9
|
+
export async function getZtdfSalt(cryptoService: CryptoService): Promise<Uint8Array> {
|
|
10
|
+
if (cachedSalt) {
|
|
11
|
+
return cachedSalt;
|
|
12
|
+
}
|
|
7
13
|
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
const encoder = new TextEncoder();
|
|
15
|
+
const data = encoder.encode('TDF');
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
cachedSalt = await cryptoService.digest('SHA-256', data);
|
|
18
|
+
return cachedSalt;
|
|
19
|
+
}
|