@sd-jwt/core 0.4.0 → 0.4.1-next.12

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/dist/index.d.mts CHANGED
@@ -6,11 +6,13 @@ type JwtData<Header extends Record<string, unknown>, Payload extends Record<stri
6
6
  header?: Header;
7
7
  payload?: Payload;
8
8
  signature?: Base64urlString;
9
+ encoded?: string;
9
10
  };
10
11
  declare class Jwt<Header extends Record<string, unknown> = Record<string, unknown>, Payload extends Record<string, unknown> = Record<string, unknown>> {
11
12
  header?: Header;
12
13
  payload?: Payload;
13
14
  signature?: Base64urlString;
15
+ private encoded?;
14
16
  constructor(data?: JwtData<Header, Payload>);
15
17
  static decodeJWT<Header extends Record<string, unknown> = Record<string, unknown>, Payload extends Record<string, unknown> = Record<string, unknown>>(jwt: string): {
16
18
  header: Header;
@@ -20,11 +22,12 @@ declare class Jwt<Header extends Record<string, unknown> = Record<string, unknow
20
22
  static fromEncode<Header extends Record<string, unknown> = Record<string, unknown>, Payload extends Record<string, unknown> = Record<string, unknown>>(encodedJwt: string): Jwt<Header, Payload>;
21
23
  setHeader(header: Header): Jwt<Header, Payload>;
22
24
  setPayload(payload: Payload): Jwt<Header, Payload>;
25
+ protected getUnsignedToken(): string;
23
26
  sign(signer: Signer): Promise<string>;
24
27
  encodeJwt(): string;
25
28
  verify(verifier: Verifier): Promise<{
26
- payload: Payload;
27
- header: Header;
29
+ payload: Payload | undefined;
30
+ header: Header | undefined;
28
31
  }>;
29
32
  }
30
33
 
@@ -92,11 +95,11 @@ declare class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
92
95
  }): Promise<SDJWTCompact>;
93
96
  verify(encodedSDJwt: string, requiredClaimKeys?: string[], requireKeyBindings?: boolean): Promise<{
94
97
  payload: unknown;
95
- header: Record<string, unknown>;
98
+ header: Record<string, unknown> | undefined;
96
99
  kb?: undefined;
97
100
  } | {
98
101
  payload: unknown;
99
- header: Record<string, unknown>;
102
+ header: Record<string, unknown> | undefined;
100
103
  kb: {
101
104
  payload: _sd_jwt_types.kbPayload;
102
105
  header: _sd_jwt_types.kbHeader;
@@ -105,7 +108,7 @@ declare class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
105
108
  private calculateSDHash;
106
109
  validate(encodedSDJwt: string): Promise<{
107
110
  payload: unknown;
108
- header: Record<string, unknown>;
111
+ header: Record<string, unknown> | undefined;
109
112
  }>;
110
113
  config(newConfig: SDJWTConfig): void;
111
114
  encode(sdJwt: SDJwt): SDJWTCompact;
package/dist/index.d.ts CHANGED
@@ -6,11 +6,13 @@ type JwtData<Header extends Record<string, unknown>, Payload extends Record<stri
6
6
  header?: Header;
7
7
  payload?: Payload;
8
8
  signature?: Base64urlString;
9
+ encoded?: string;
9
10
  };
10
11
  declare class Jwt<Header extends Record<string, unknown> = Record<string, unknown>, Payload extends Record<string, unknown> = Record<string, unknown>> {
11
12
  header?: Header;
12
13
  payload?: Payload;
13
14
  signature?: Base64urlString;
15
+ private encoded?;
14
16
  constructor(data?: JwtData<Header, Payload>);
15
17
  static decodeJWT<Header extends Record<string, unknown> = Record<string, unknown>, Payload extends Record<string, unknown> = Record<string, unknown>>(jwt: string): {
16
18
  header: Header;
@@ -20,11 +22,12 @@ declare class Jwt<Header extends Record<string, unknown> = Record<string, unknow
20
22
  static fromEncode<Header extends Record<string, unknown> = Record<string, unknown>, Payload extends Record<string, unknown> = Record<string, unknown>>(encodedJwt: string): Jwt<Header, Payload>;
21
23
  setHeader(header: Header): Jwt<Header, Payload>;
22
24
  setPayload(payload: Payload): Jwt<Header, Payload>;
25
+ protected getUnsignedToken(): string;
23
26
  sign(signer: Signer): Promise<string>;
24
27
  encodeJwt(): string;
25
28
  verify(verifier: Verifier): Promise<{
26
- payload: Payload;
27
- header: Header;
29
+ payload: Payload | undefined;
30
+ header: Header | undefined;
28
31
  }>;
29
32
  }
30
33
 
@@ -92,11 +95,11 @@ declare class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
92
95
  }): Promise<SDJWTCompact>;
93
96
  verify(encodedSDJwt: string, requiredClaimKeys?: string[], requireKeyBindings?: boolean): Promise<{
94
97
  payload: unknown;
95
- header: Record<string, unknown>;
98
+ header: Record<string, unknown> | undefined;
96
99
  kb?: undefined;
97
100
  } | {
98
101
  payload: unknown;
99
- header: Record<string, unknown>;
102
+ header: Record<string, unknown> | undefined;
100
103
  kb: {
101
104
  payload: _sd_jwt_types.kbPayload;
102
105
  header: _sd_jwt_types.kbHeader;
@@ -105,7 +108,7 @@ declare class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
105
108
  private calculateSDHash;
106
109
  validate(encodedSDJwt: string): Promise<{
107
110
  payload: unknown;
108
- header: Record<string, unknown>;
111
+ header: Record<string, unknown> | undefined;
109
112
  }>;
110
113
  config(newConfig: SDJWTConfig): void;
111
114
  encode(sdJwt: SDJwt): SDJWTCompact;
package/dist/index.js CHANGED
@@ -76,6 +76,7 @@ var Jwt = class _Jwt {
76
76
  this.header = data == null ? void 0 : data.header;
77
77
  this.payload = data == null ? void 0 : data.payload;
78
78
  this.signature = data == null ? void 0 : data.signature;
79
+ this.encoded = data == null ? void 0 : data.encoded;
79
80
  }
80
81
  static decodeJWT(jwt) {
81
82
  return (0, import_decode.decodeJwt)(jwt);
@@ -87,31 +88,48 @@ var Jwt = class _Jwt {
87
88
  const jwt = new _Jwt({
88
89
  header,
89
90
  payload,
90
- signature
91
+ signature,
92
+ encoded: encodedJwt
91
93
  });
92
94
  return jwt;
93
95
  }
94
96
  setHeader(header) {
95
97
  this.header = header;
98
+ this.encoded = void 0;
96
99
  return this;
97
100
  }
98
101
  setPayload(payload) {
99
102
  this.payload = payload;
103
+ this.encoded = void 0;
100
104
  return this;
101
105
  }
106
+ getUnsignedToken() {
107
+ if (!this.header || !this.payload) {
108
+ throw new import_utils.SDJWTException("Serialize Error: Invalid JWT");
109
+ }
110
+ if (this.encoded) {
111
+ const parts = this.encoded.split(".");
112
+ if (parts.length !== 3) {
113
+ throw new import_utils.SDJWTException(`Invalid JWT format: ${this.encoded}`);
114
+ }
115
+ const unsignedToken = parts.slice(0, 2).join(".");
116
+ return unsignedToken;
117
+ }
118
+ const header = (0, import_utils.Base64urlEncode)(JSON.stringify(this.header));
119
+ const payload = (0, import_utils.Base64urlEncode)(JSON.stringify(this.payload));
120
+ return `${header}.${payload}`;
121
+ }
102
122
  sign(signer) {
103
123
  return __async(this, null, function* () {
104
- if (!this.header || !this.payload) {
105
- throw new import_utils.SDJWTException("Sign Error: Invalid JWT");
106
- }
107
- const header = (0, import_utils.Base64urlEncode)(JSON.stringify(this.header));
108
- const payload = (0, import_utils.Base64urlEncode)(JSON.stringify(this.payload));
109
- const data = `${header}.${payload}`;
124
+ const data = this.getUnsignedToken();
110
125
  this.signature = yield signer(data);
111
126
  return this.encodeJwt();
112
127
  });
113
128
  }
114
129
  encodeJwt() {
130
+ if (this.encoded) {
131
+ return this.encoded;
132
+ }
115
133
  if (!this.header || !this.payload || !this.signature) {
116
134
  throw new import_utils.SDJWTException("Serialize Error: Invalid JWT");
117
135
  }
@@ -119,16 +137,15 @@ var Jwt = class _Jwt {
119
137
  const payload = (0, import_utils.Base64urlEncode)(JSON.stringify(this.payload));
120
138
  const signature = this.signature;
121
139
  const compact = `${header}.${payload}.${signature}`;
140
+ this.encoded = compact;
122
141
  return compact;
123
142
  }
124
143
  verify(verifier) {
125
144
  return __async(this, null, function* () {
126
- if (!this.header || !this.payload || !this.signature) {
127
- throw new import_utils.SDJWTException("Verify Error: Invalid JWT");
145
+ if (!this.signature) {
146
+ throw new import_utils.SDJWTException("Verify Error: no signature in JWT");
128
147
  }
129
- const header = (0, import_utils.Base64urlEncode)(JSON.stringify(this.header));
130
- const payload = (0, import_utils.Base64urlEncode)(JSON.stringify(this.payload));
131
- const data = `${header}.${payload}`;
148
+ const data = this.getUnsignedToken();
132
149
  const verified = yield verifier(data, this.signature);
133
150
  if (!verified) {
134
151
  throw new import_utils.SDJWTException("Verify Error: Invalid JWT Signature");
@@ -154,9 +171,7 @@ var KBJwt = class _KBJwt extends Jwt {
154
171
  !(this.payload.sd_hash || ((_a = this.payload) == null ? void 0 : _a._sd_hash))) {
155
172
  throw new import_utils2.SDJWTException("Invalid Key Binding Jwt");
156
173
  }
157
- const header = (0, import_utils2.Base64urlEncode)(JSON.stringify(this.header));
158
- const payload = (0, import_utils2.Base64urlEncode)(JSON.stringify(this.payload));
159
- const data = `${header}.${payload}`;
174
+ const data = this.getUnsignedToken();
160
175
  const verified = yield values.verifier(
161
176
  data,
162
177
  this.signature,
@@ -176,7 +191,8 @@ var KBJwt = class _KBJwt extends Jwt {
176
191
  const jwt = new _KBJwt({
177
192
  header,
178
193
  payload,
179
- signature
194
+ signature,
195
+ encoded: encodedJwt
180
196
  });
181
197
  return jwt;
182
198
  }
@@ -341,7 +357,7 @@ var pack = (claims, disclosureFrame, hash, saltGenerator) => __async(void 0, nul
341
357
  const recursivePackedClaims2 = {};
342
358
  for (const key in disclosureFrame) {
343
359
  if (key !== import_types2.SD_DIGEST) {
344
- const idx = parseInt(key);
360
+ const idx = Number.parseInt(key);
345
361
  const packed = yield pack(
346
362
  claims[idx],
347
363
  disclosureFrame[idx],
package/dist/index.mjs CHANGED
@@ -49,6 +49,7 @@ var Jwt = class _Jwt {
49
49
  this.header = data == null ? void 0 : data.header;
50
50
  this.payload = data == null ? void 0 : data.payload;
51
51
  this.signature = data == null ? void 0 : data.signature;
52
+ this.encoded = data == null ? void 0 : data.encoded;
52
53
  }
53
54
  static decodeJWT(jwt) {
54
55
  return decodeJwt(jwt);
@@ -60,31 +61,48 @@ var Jwt = class _Jwt {
60
61
  const jwt = new _Jwt({
61
62
  header,
62
63
  payload,
63
- signature
64
+ signature,
65
+ encoded: encodedJwt
64
66
  });
65
67
  return jwt;
66
68
  }
67
69
  setHeader(header) {
68
70
  this.header = header;
71
+ this.encoded = void 0;
69
72
  return this;
70
73
  }
71
74
  setPayload(payload) {
72
75
  this.payload = payload;
76
+ this.encoded = void 0;
73
77
  return this;
74
78
  }
79
+ getUnsignedToken() {
80
+ if (!this.header || !this.payload) {
81
+ throw new SDJWTException("Serialize Error: Invalid JWT");
82
+ }
83
+ if (this.encoded) {
84
+ const parts = this.encoded.split(".");
85
+ if (parts.length !== 3) {
86
+ throw new SDJWTException(`Invalid JWT format: ${this.encoded}`);
87
+ }
88
+ const unsignedToken = parts.slice(0, 2).join(".");
89
+ return unsignedToken;
90
+ }
91
+ const header = Base64urlEncode(JSON.stringify(this.header));
92
+ const payload = Base64urlEncode(JSON.stringify(this.payload));
93
+ return `${header}.${payload}`;
94
+ }
75
95
  sign(signer) {
76
96
  return __async(this, null, function* () {
77
- if (!this.header || !this.payload) {
78
- throw new SDJWTException("Sign Error: Invalid JWT");
79
- }
80
- const header = Base64urlEncode(JSON.stringify(this.header));
81
- const payload = Base64urlEncode(JSON.stringify(this.payload));
82
- const data = `${header}.${payload}`;
97
+ const data = this.getUnsignedToken();
83
98
  this.signature = yield signer(data);
84
99
  return this.encodeJwt();
85
100
  });
86
101
  }
87
102
  encodeJwt() {
103
+ if (this.encoded) {
104
+ return this.encoded;
105
+ }
88
106
  if (!this.header || !this.payload || !this.signature) {
89
107
  throw new SDJWTException("Serialize Error: Invalid JWT");
90
108
  }
@@ -92,16 +110,15 @@ var Jwt = class _Jwt {
92
110
  const payload = Base64urlEncode(JSON.stringify(this.payload));
93
111
  const signature = this.signature;
94
112
  const compact = `${header}.${payload}.${signature}`;
113
+ this.encoded = compact;
95
114
  return compact;
96
115
  }
97
116
  verify(verifier) {
98
117
  return __async(this, null, function* () {
99
- if (!this.header || !this.payload || !this.signature) {
100
- throw new SDJWTException("Verify Error: Invalid JWT");
118
+ if (!this.signature) {
119
+ throw new SDJWTException("Verify Error: no signature in JWT");
101
120
  }
102
- const header = Base64urlEncode(JSON.stringify(this.header));
103
- const payload = Base64urlEncode(JSON.stringify(this.payload));
104
- const data = `${header}.${payload}`;
121
+ const data = this.getUnsignedToken();
105
122
  const verified = yield verifier(data, this.signature);
106
123
  if (!verified) {
107
124
  throw new SDJWTException("Verify Error: Invalid JWT Signature");
@@ -112,7 +129,7 @@ var Jwt = class _Jwt {
112
129
  };
113
130
 
114
131
  // src/kbjwt.ts
115
- import { Base64urlEncode as Base64urlEncode2, SDJWTException as SDJWTException2 } from "@sd-jwt/utils";
132
+ import { SDJWTException as SDJWTException2 } from "@sd-jwt/utils";
116
133
  import {
117
134
  KB_JWT_TYP
118
135
  } from "@sd-jwt/types";
@@ -129,9 +146,7 @@ var KBJwt = class _KBJwt extends Jwt {
129
146
  !(this.payload.sd_hash || ((_a = this.payload) == null ? void 0 : _a._sd_hash))) {
130
147
  throw new SDJWTException2("Invalid Key Binding Jwt");
131
148
  }
132
- const header = Base64urlEncode2(JSON.stringify(this.header));
133
- const payload = Base64urlEncode2(JSON.stringify(this.payload));
134
- const data = `${header}.${payload}`;
149
+ const data = this.getUnsignedToken();
135
150
  const verified = yield values.verifier(
136
151
  data,
137
152
  this.signature,
@@ -151,7 +166,8 @@ var KBJwt = class _KBJwt extends Jwt {
151
166
  const jwt = new _KBJwt({
152
167
  header,
153
168
  payload,
154
- signature
169
+ signature,
170
+ encoded: encodedJwt
155
171
  });
156
172
  return jwt;
157
173
  }
@@ -321,7 +337,7 @@ var pack = (claims, disclosureFrame, hash, saltGenerator) => __async(void 0, nul
321
337
  const recursivePackedClaims2 = {};
322
338
  for (const key in disclosureFrame) {
323
339
  if (key !== SD_DIGEST) {
324
- const idx = parseInt(key);
340
+ const idx = Number.parseInt(key);
325
341
  const packed = yield pack(
326
342
  claims[idx],
327
343
  disclosureFrame[idx],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sd-jwt/core",
3
- "version": "0.4.0",
3
+ "version": "0.4.1-next.12+eb259c7",
4
4
  "description": "sd-jwt draft 7 implementation in typescript",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -38,13 +38,13 @@
38
38
  },
39
39
  "license": "Apache-2.0",
40
40
  "devDependencies": {
41
- "@sd-jwt/crypto-nodejs": "0.4.0"
41
+ "@sd-jwt/crypto-nodejs": "0.4.1-next.12+eb259c7"
42
42
  },
43
43
  "dependencies": {
44
- "@sd-jwt/decode": "0.4.0",
45
- "@sd-jwt/present": "0.4.0",
46
- "@sd-jwt/types": "0.4.0",
47
- "@sd-jwt/utils": "0.4.0"
44
+ "@sd-jwt/decode": "0.4.1-next.12+eb259c7",
45
+ "@sd-jwt/present": "0.4.1-next.12+eb259c7",
46
+ "@sd-jwt/types": "0.4.1-next.12+eb259c7",
47
+ "@sd-jwt/utils": "0.4.1-next.12+eb259c7"
48
48
  },
49
49
  "publishConfig": {
50
50
  "access": "public"
@@ -62,5 +62,5 @@
62
62
  "esm"
63
63
  ]
64
64
  },
65
- "gitHead": "391a36550a66833b3d393ff0deb699b1b72cca59"
65
+ "gitHead": "eb259c74a386b0ca867b373f6ffa08b6b6b78d7b"
66
66
  }
package/src/decoy.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { HasherAndAlg, SaltGenerator } from '@sd-jwt/types';
1
+ import type { HasherAndAlg, SaltGenerator } from '@sd-jwt/types';
2
2
  import { Uint8ArrayToBase64Url } from '@sd-jwt/utils';
3
3
 
4
4
  // This function creates a decoy value that can be used to obscure SD JWT payload.
package/src/index.ts CHANGED
@@ -3,16 +3,16 @@ import { Jwt } from './jwt';
3
3
  import { KBJwt } from './kbjwt';
4
4
  import { SDJwt, pack } from './sdjwt';
5
5
  import {
6
- DisclosureFrame,
7
- Hasher,
8
- KBOptions,
6
+ type DisclosureFrame,
7
+ type Hasher,
8
+ type KBOptions,
9
9
  KB_JWT_TYP,
10
- PresentationFrame,
11
- SDJWTCompact,
12
- SDJWTConfig,
10
+ type PresentationFrame,
11
+ type SDJWTCompact,
12
+ type SDJWTConfig,
13
13
  } from '@sd-jwt/types';
14
14
  import { getSDAlgAndPayload } from '@sd-jwt/decode';
15
- import { JwtPayload } from '@sd-jwt/types';
15
+ import type { JwtPayload } from '@sd-jwt/types';
16
16
 
17
17
  export * from './sdjwt';
18
18
  export * from './kbjwt';
package/src/jwt.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Base64urlEncode, SDJWTException } from '@sd-jwt/utils';
2
- import { Base64urlString, Signer, Verifier } from '@sd-jwt/types';
2
+ import type { Base64urlString, Signer, Verifier } from '@sd-jwt/types';
3
3
  import { decodeJwt } from '@sd-jwt/decode';
4
4
 
5
5
  export type JwtData<
@@ -9,6 +9,7 @@ export type JwtData<
9
9
  header?: Header;
10
10
  payload?: Payload;
11
11
  signature?: Base64urlString;
12
+ encoded?: string;
12
13
  };
13
14
 
14
15
  // This class is used to create and verify JWT
@@ -20,11 +21,13 @@ export class Jwt<
20
21
  public header?: Header;
21
22
  public payload?: Payload;
22
23
  public signature?: Base64urlString;
24
+ private encoded?: string;
23
25
 
24
26
  constructor(data?: JwtData<Header, Payload>) {
25
27
  this.header = data?.header;
26
28
  this.payload = data?.payload;
27
29
  this.signature = data?.signature;
30
+ this.encoded = data?.encoded;
28
31
  }
29
32
 
30
33
  public static decodeJWT<
@@ -48,6 +51,7 @@ export class Jwt<
48
51
  header,
49
52
  payload,
50
53
  signature,
54
+ encoded: encodedJwt,
51
55
  });
52
56
 
53
57
  return jwt;
@@ -55,28 +59,47 @@ export class Jwt<
55
59
 
56
60
  public setHeader(header: Header): Jwt<Header, Payload> {
57
61
  this.header = header;
62
+ this.encoded = undefined;
58
63
  return this;
59
64
  }
60
65
 
61
66
  public setPayload(payload: Payload): Jwt<Header, Payload> {
62
67
  this.payload = payload;
68
+ this.encoded = undefined;
63
69
  return this;
64
70
  }
65
71
 
66
- public async sign(signer: Signer) {
72
+ protected getUnsignedToken() {
67
73
  if (!this.header || !this.payload) {
68
- throw new SDJWTException('Sign Error: Invalid JWT');
74
+ throw new SDJWTException('Serialize Error: Invalid JWT');
75
+ }
76
+
77
+ if (this.encoded) {
78
+ const parts = this.encoded.split('.');
79
+ if (parts.length !== 3) {
80
+ throw new SDJWTException(`Invalid JWT format: ${this.encoded}`);
81
+ }
82
+ const unsignedToken = parts.slice(0, 2).join('.');
83
+ return unsignedToken;
69
84
  }
70
85
 
71
86
  const header = Base64urlEncode(JSON.stringify(this.header));
72
87
  const payload = Base64urlEncode(JSON.stringify(this.payload));
73
- const data = `${header}.${payload}`;
88
+ return `${header}.${payload}`;
89
+ }
90
+
91
+ public async sign(signer: Signer) {
92
+ const data = this.getUnsignedToken();
74
93
  this.signature = await signer(data);
75
94
 
76
95
  return this.encodeJwt();
77
96
  }
78
97
 
79
98
  public encodeJwt(): string {
99
+ if (this.encoded) {
100
+ return this.encoded;
101
+ }
102
+
80
103
  if (!this.header || !this.payload || !this.signature) {
81
104
  throw new SDJWTException('Serialize Error: Invalid JWT');
82
105
  }
@@ -85,18 +108,16 @@ export class Jwt<
85
108
  const payload = Base64urlEncode(JSON.stringify(this.payload));
86
109
  const signature = this.signature;
87
110
  const compact = `${header}.${payload}.${signature}`;
111
+ this.encoded = compact;
88
112
 
89
113
  return compact;
90
114
  }
91
115
 
92
116
  public async verify(verifier: Verifier) {
93
- if (!this.header || !this.payload || !this.signature) {
94
- throw new SDJWTException('Verify Error: Invalid JWT');
117
+ if (!this.signature) {
118
+ throw new SDJWTException('Verify Error: no signature in JWT');
95
119
  }
96
-
97
- const header = Base64urlEncode(JSON.stringify(this.header));
98
- const payload = Base64urlEncode(JSON.stringify(this.payload));
99
- const data = `${header}.${payload}`;
120
+ const data = this.getUnsignedToken();
100
121
 
101
122
  const verified = await verifier(data, this.signature);
102
123
  if (!verified) {
package/src/kbjwt.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { Base64urlEncode, SDJWTException } from '@sd-jwt/utils';
2
2
  import { Jwt } from './jwt';
3
3
  import {
4
- JwtPayload,
4
+ type JwtPayload,
5
5
  KB_JWT_TYP,
6
- kbHeader,
7
- kbPayload,
8
- KbVerifier,
6
+ type kbHeader,
7
+ type kbPayload,
8
+ type KbVerifier,
9
9
  } from '@sd-jwt/types';
10
10
 
11
11
  export class KBJwt<
@@ -36,9 +36,7 @@ export class KBJwt<
36
36
  throw new SDJWTException('Invalid Key Binding Jwt');
37
37
  }
38
38
 
39
- const header = Base64urlEncode(JSON.stringify(this.header));
40
- const payload = Base64urlEncode(JSON.stringify(this.payload));
41
- const data = `${header}.${payload}`;
39
+ const data = this.getUnsignedToken();
42
40
  const verified = await values.verifier(
43
41
  data,
44
42
  this.signature,
@@ -63,6 +61,7 @@ export class KBJwt<
63
61
  header,
64
62
  payload,
65
63
  signature,
64
+ encoded: encodedJwt,
66
65
  });
67
66
 
68
67
  return jwt;
package/src/sdjwt.ts CHANGED
@@ -3,18 +3,18 @@ import { SDJWTException, Disclosure } from '@sd-jwt/utils';
3
3
  import { Jwt } from './jwt';
4
4
  import { KBJwt } from './kbjwt';
5
5
  import {
6
- DisclosureFrame,
7
- Hasher,
8
- HasherAndAlg,
9
- PresentationFrame,
10
- SDJWTCompact,
6
+ type DisclosureFrame,
7
+ type Hasher,
8
+ type HasherAndAlg,
9
+ type PresentationFrame,
10
+ type SDJWTCompact,
11
11
  SD_DECOY,
12
12
  SD_DIGEST,
13
13
  SD_LIST_KEY,
14
14
  SD_SEPARATOR,
15
- SaltGenerator,
16
- kbHeader,
17
- kbPayload,
15
+ type SaltGenerator,
16
+ type kbHeader,
17
+ type kbPayload,
18
18
  } from '@sd-jwt/types';
19
19
  import { createHashMapping, getSDAlgAndPayload, unpack } from '@sd-jwt/decode';
20
20
  import { transformPresentationFrame } from '@sd-jwt/present';
@@ -236,7 +236,7 @@ export const pack = async <T extends Record<string, unknown>>(
236
236
 
237
237
  for (const key in disclosureFrame) {
238
238
  if (key !== SD_DIGEST) {
239
- const idx = parseInt(key);
239
+ const idx = Number.parseInt(key);
240
240
  const packed = await pack(
241
241
  claims[idx],
242
242
  disclosureFrame[idx],
@@ -1,9 +1,15 @@
1
- import { SDJwtInstance, SdJwtPayload } from '../index';
2
- import { Signer, Verifier, KbVerifier, JwtPayload } from '@sd-jwt/types';
3
- import Crypto, { KeyLike } from 'node:crypto';
1
+ import { SDJwtInstance, type SdJwtPayload } from '../index';
2
+ import type { Signer, Verifier, KbVerifier, JwtPayload } from '@sd-jwt/types';
3
+ import Crypto, { type KeyLike } from 'node:crypto';
4
4
  import { describe, expect, test } from 'vitest';
5
- import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
6
- import { importJWK, exportJWK, JWK } from 'jose';
5
+ import { digest, generateSalt, ES256 } from '@sd-jwt/crypto-nodejs';
6
+ import { importJWK, exportJWK, type JWK } from 'jose';
7
+
8
+ // Extract the major version as a number
9
+ const nodeVersionMajor = Number.parseInt(
10
+ process.version.split('.')[0].substring(1),
11
+ 10,
12
+ );
7
13
 
8
14
  export const createSignerVerifier = () => {
9
15
  const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
@@ -590,4 +596,33 @@ describe('index', () => {
590
596
  expect(decoded.disclosures).toBeDefined();
591
597
  expect(decoded.kbJwt).toBeDefined();
592
598
  });
599
+
600
+ (nodeVersionMajor < 20 ? test.skip : test)(
601
+ 'validate sd-jwt that created in other implemenation',
602
+ async () => {
603
+ const publicKeyExampleJwt: JsonWebKey = {
604
+ kty: 'EC',
605
+ crv: 'P-256',
606
+ x: 'b28d4MwZMjw8-00CG4xfnn9SLMVMM19SlqZpVb_uNtQ',
607
+ y: 'Xv5zWwuoaTgdS6hV43yI6gBwTnjukmFQQnJ_kCxzqk8',
608
+ };
609
+ const kbPubkey: JsonWebKey = {
610
+ kty: 'EC',
611
+ crv: 'P-256',
612
+ x: 'TCAER19Zvu3OHF4j4W4vfSVoHIP1ILilDls7vCeGemc',
613
+ y: 'ZxjiWWbZMQGHVWKVQ4hbSIirsVfuecCE6t4jT9F2HZQ',
614
+ };
615
+ const encodedJwt =
616
+ 'eyJhbGciOiAiRVMyNTYiLCAidHlwIjogInZjK3NkLWp3dCIsICJraWQiOiAiZG9jLXNpZ25lci0wNS0yNS0yMDIyIn0.eyJfc2QiOiBbIjA5dktySk1PbHlUV00wc2pwdV9wZE9CVkJRMk0xeTNLaHBINTE1blhrcFkiLCAiMnJzakdiYUMwa3k4bVQwcEpyUGlvV1RxMF9kYXcxc1g3NnBvVWxnQ3diSSIsICJFa084ZGhXMGRIRUpidlVIbEVfVkNldUM5dVJFTE9pZUxaaGg3WGJVVHRBIiwgIklsRHpJS2VpWmREd3BxcEs2WmZieXBoRnZ6NUZnbldhLXNONndxUVhDaXciLCAiSnpZakg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsICJQb3JGYnBLdVZ1Nnh5bUphZ3ZrRnNGWEFiUm9jMkpHbEFVQTJCQTRvN2NJIiwgIlRHZjRvTGJnd2Q1SlFhSHlLVlFaVTlVZEdFMHc1cnREc3JaemZVYW9tTG8iLCAiamRyVEU4WWNiWTRFaWZ1Z2loaUFlX0JQZWt4SlFaSUNlaVVRd1k5UXF4SSIsICJqc3U5eVZ1bHdRUWxoRmxNXzNKbHpNYVNGemdsaFFHMERwZmF5UXdMVUs0Il0sICJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAiaWF0IjogMTY4MzAwMDAwMCwgImV4cCI6IDE4ODMwMDAwMDAsICJ2Y3QiOiAiaHR0cHM6Ly9jcmVkZW50aWFscy5leGFtcGxlLmNvbS9pZGVudGl0eV9jcmVkZW50aWFsIiwgIl9zZF9hbGciOiAic2hhLTI1NiIsICJjbmYiOiB7Imp3ayI6IHsia3R5IjogIkVDIiwgImNydiI6ICJQLTI1NiIsICJ4IjogIlRDQUVSMTladnUzT0hGNGo0VzR2ZlNWb0hJUDFJTGlsRGxzN3ZDZUdlbWMiLCAieSI6ICJaeGppV1diWk1RR0hWV0tWUTRoYlNJaXJzVmZ1ZWNDRTZ0NGpUOUYySFpRIn19fQ.QXgzrePAdq_WZVGCwDxP-l8h0iyckrHBNidxVqGtKJ0LMzObqgaXUD1cgGEf7d9TexPkBcgQYqjuzlfbeCxxuA~WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd~eyJhbGciOiAiRVMyNTYiLCAidHlwIjogImtiK2p3dCJ9.eyJub25jZSI6ICIxMjM0NTY3ODkwIiwgImF1ZCI6ICJodHRwczovL2V4YW1wbGUuY29tL3ZlcmlmaWVyIiwgImlhdCI6IDE3MDk5OTYxODUsICJzZF9oYXNoIjogIjc4cFFEazJOblNEM1dKQm5SN015aWpmeUVqcGJ5a01yRnlpb2ZYSjlsN0kifQ.7k4goAlxM4a3tHnvCBCe70j_I-BCwtzhBRXQNk9cWJnQWxxt2kIqCyzcwzzUc0gTwtbGWVQoeWCiL5K6y3a4VQ';
617
+
618
+ const sdjwt = new SDJwtInstance({
619
+ hasher: digest,
620
+ verifier: await ES256.getVerifier(publicKeyExampleJwt),
621
+ kbVerifier: await ES256.getVerifier(kbPubkey),
622
+ });
623
+
624
+ const decode = await sdjwt.verify(encodedJwt, undefined, true);
625
+ expect(decode).toBeDefined();
626
+ },
627
+ );
593
628
  });
@@ -1,7 +1,7 @@
1
1
  import { SDJWTException } from '@sd-jwt/utils';
2
2
  import { Jwt } from '../jwt';
3
3
  import Crypto from 'node:crypto';
4
- import { Signer, Verifier } from '@sd-jwt/types';
4
+ import type { Signer, Verifier } from '@sd-jwt/types';
5
5
  import { describe, expect, test } from 'vitest';
6
6
 
7
7
  describe('JWT', () => {
@@ -138,4 +138,65 @@ describe('JWT', () => {
138
138
  expect(e).toBeInstanceOf(SDJWTException);
139
139
  }
140
140
  });
141
+
142
+ test('getUnsignedToken failed', async () => {
143
+ const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
144
+ const testSigner: Signer = async (data: string) => {
145
+ const sig = Crypto.sign(null, Buffer.from(data), privateKey);
146
+ return Buffer.from(sig).toString('base64url');
147
+ };
148
+
149
+ const jwt = new Jwt({
150
+ header: { alg: 'EdDSA' },
151
+ });
152
+
153
+ try {
154
+ await jwt.sign(testSigner);
155
+ } catch (e: unknown) {
156
+ expect(e).toBeInstanceOf(SDJWTException);
157
+ }
158
+ });
159
+
160
+ test('wrong encoded field', async () => {
161
+ const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
162
+ const testSigner: Signer = async (data: string) => {
163
+ const sig = Crypto.sign(null, Buffer.from(data), privateKey);
164
+ return Buffer.from(sig).toString('base64url');
165
+ };
166
+
167
+ const jwt = new Jwt({
168
+ header: { alg: 'EdDSA' },
169
+ payload: { foo: 'bar' },
170
+ encoded: 'asfasfafaf.dfasfafafasf', // it has to be 3 parts
171
+ });
172
+
173
+ try {
174
+ await jwt.sign(testSigner);
175
+ } catch (e: unknown) {
176
+ expect(e).toBeInstanceOf(SDJWTException);
177
+ }
178
+ });
179
+
180
+ test('verify failed no signature', async () => {
181
+ const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
182
+ const testVerifier: Verifier = async (data: string, sig: string) => {
183
+ return Crypto.verify(
184
+ null,
185
+ Buffer.from(data),
186
+ publicKey,
187
+ Buffer.from(sig, 'base64url'),
188
+ );
189
+ };
190
+
191
+ const jwt = new Jwt({
192
+ header: { alg: 'EdDSA' },
193
+ payload: { foo: 'bar' },
194
+ });
195
+
196
+ try {
197
+ await jwt.verify(testVerifier);
198
+ } catch (e: unknown) {
199
+ expect(e).toBeInstanceOf(SDJWTException);
200
+ }
201
+ });
141
202
  });
@@ -1,15 +1,15 @@
1
- import { SDJWTException } from '@sd-jwt/utils';
1
+ import type { SDJWTException } from '@sd-jwt/utils';
2
2
  import { KBJwt } from '../kbjwt';
3
3
  import {
4
- JwtPayload,
4
+ type JwtPayload,
5
5
  KB_JWT_TYP,
6
- KbVerifier,
7
- Signer,
6
+ type KbVerifier,
7
+ type Signer,
8
8
  Verifier,
9
9
  } from '@sd-jwt/types';
10
- import Crypto, { KeyLike } from 'node:crypto';
10
+ import Crypto, { type KeyLike } from 'node:crypto';
11
11
  import { describe, expect, test } from 'vitest';
12
- import { JWK, exportJWK, importJWK } from 'jose';
12
+ import { type JWK, exportJWK, importJWK } from 'jose';
13
13
 
14
14
  describe('KB JWT', () => {
15
15
  test('create', async () => {
@@ -3,7 +3,7 @@ import { Jwt } from '../jwt';
3
3
  import { SDJwt, listKeys, pack } from '../sdjwt';
4
4
  import Crypto from 'node:crypto';
5
5
  import { describe, test, expect } from 'vitest';
6
- import { DisclosureFrame, Signer } from '@sd-jwt/types';
6
+ import type { DisclosureFrame, Signer } from '@sd-jwt/types';
7
7
  import { generateSalt, digest as hasher } from '@sd-jwt/crypto-nodejs';
8
8
  import { unpack, createHashMapping } from '@sd-jwt/decode';
9
9
 
@@ -376,7 +376,9 @@ describe('SD JWT', () => {
376
376
 
377
377
  const credential = sdJwt.encodeSDJwt();
378
378
  const decoded = await SDJwt.decodeSDJwt(credential, hasher);
379
- expect(jwt).toEqual(decoded.jwt);
379
+ expect(jwt.header).toEqual(decoded.jwt.header);
380
+ expect(jwt.payload).toEqual(decoded.jwt.payload);
381
+ expect(jwt.signature).toEqual(decoded.jwt.signature);
380
382
  expect(decoded.disclosures).toEqual([]);
381
383
  });
382
384
  });
@@ -1,13 +1,13 @@
1
1
  import Crypto from 'node:crypto';
2
- import { SDJwtInstance, SdJwtPayload } from '../src';
3
- import {
2
+ import { SDJwtInstance, type SdJwtPayload } from '../src';
3
+ import type {
4
4
  DisclosureFrame,
5
5
  PresentationFrame,
6
6
  Signer,
7
7
  Verifier,
8
8
  } from '@sd-jwt/types';
9
- import fs from 'fs';
10
- import path from 'path';
9
+ import fs from 'node:fs';
10
+ import path from 'node:path';
11
11
  import { describe, expect, test } from 'vitest';
12
12
  import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
13
13