@sphereon/ssi-types 0.33.1-next.2 → 0.33.1-next.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/index.cjs +1773 -0
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.cts +1589 -0
  4. package/dist/index.d.ts +1589 -6
  5. package/dist/index.js +1741 -24
  6. package/dist/index.js.map +1 -1
  7. package/package.json +23 -14
  8. package/src/index.ts +3 -2
  9. package/src/logging/index.ts +6 -4
  10. package/src/mapper/credential-constraints.ts +14 -1
  11. package/src/mapper/credential-mapper.ts +61 -23
  12. package/src/mapper/index.ts +1 -0
  13. package/src/types/cose.ts +5 -51
  14. package/src/types/did.ts +0 -90
  15. package/src/types/index.ts +13 -13
  16. package/src/types/jose.ts +18 -65
  17. package/src/types/mso_mdoc.ts +10 -138
  18. package/src/types/sd-jwt-vc.ts +4 -178
  19. package/src/types/status-list.ts +1 -8
  20. package/src/types/vc.ts +7 -29
  21. package/src/types/w3c-vc.ts +3 -26
  22. package/src/utils/cose.ts +46 -0
  23. package/src/utils/did.ts +91 -0
  24. package/src/utils/hasher.ts +5 -2
  25. package/src/utils/index.ts +6 -0
  26. package/src/utils/jose.ts +47 -0
  27. package/src/utils/mdoc.ts +144 -0
  28. package/src/utils/sd-jwt.ts +179 -0
  29. package/src/utils/vc.ts +54 -0
  30. package/dist/events/index.d.ts +0 -116
  31. package/dist/events/index.d.ts.map +0 -1
  32. package/dist/events/index.js +0 -165
  33. package/dist/events/index.js.map +0 -1
  34. package/dist/index.d.ts.map +0 -1
  35. package/dist/logging/index.d.ts +0 -73
  36. package/dist/logging/index.d.ts.map +0 -1
  37. package/dist/logging/index.js +0 -186
  38. package/dist/logging/index.js.map +0 -1
  39. package/dist/mapper/credential-constraints.d.ts +0 -16
  40. package/dist/mapper/credential-constraints.d.ts.map +0 -1
  41. package/dist/mapper/credential-constraints.js +0 -3
  42. package/dist/mapper/credential-constraints.js.map +0 -1
  43. package/dist/mapper/credential-mapper.d.ts +0 -124
  44. package/dist/mapper/credential-mapper.d.ts.map +0 -1
  45. package/dist/mapper/credential-mapper.js +0 -774
  46. package/dist/mapper/credential-mapper.js.map +0 -1
  47. package/dist/mapper/index.d.ts +0 -3
  48. package/dist/mapper/index.d.ts.map +0 -1
  49. package/dist/mapper/index.js +0 -19
  50. package/dist/mapper/index.js.map +0 -1
  51. package/dist/mapper/jsonld-language-values.d.ts +0 -18
  52. package/dist/mapper/jsonld-language-values.d.ts.map +0 -1
  53. package/dist/mapper/jsonld-language-values.js +0 -79
  54. package/dist/mapper/jsonld-language-values.js.map +0 -1
  55. package/dist/types/cose.d.ts +0 -77
  56. package/dist/types/cose.d.ts.map +0 -1
  57. package/dist/types/cose.js +0 -51
  58. package/dist/types/cose.js.map +0 -1
  59. package/dist/types/dcql.d.ts +0 -33
  60. package/dist/types/dcql.d.ts.map +0 -1
  61. package/dist/types/dcql.js +0 -4
  62. package/dist/types/dcql.js.map +0 -1
  63. package/dist/types/did.d.ts +0 -186
  64. package/dist/types/did.d.ts.map +0 -1
  65. package/dist/types/did.js +0 -78
  66. package/dist/types/did.js.map +0 -1
  67. package/dist/types/generic.d.ts +0 -44
  68. package/dist/types/generic.d.ts.map +0 -1
  69. package/dist/types/generic.js +0 -3
  70. package/dist/types/generic.js.map +0 -1
  71. package/dist/types/index.d.ts +0 -14
  72. package/dist/types/index.d.ts.map +0 -1
  73. package/dist/types/index.js +0 -30
  74. package/dist/types/index.js.map +0 -1
  75. package/dist/types/jose.d.ts +0 -89
  76. package/dist/types/jose.d.ts.map +0 -1
  77. package/dist/types/jose.js +0 -55
  78. package/dist/types/jose.js.map +0 -1
  79. package/dist/types/metadata-types.d.ts +0 -5
  80. package/dist/types/metadata-types.d.ts.map +0 -1
  81. package/dist/types/metadata-types.js +0 -3
  82. package/dist/types/metadata-types.js.map +0 -1
  83. package/dist/types/mso_mdoc.d.ts +0 -94
  84. package/dist/types/mso_mdoc.d.ts.map +0 -1
  85. package/dist/types/mso_mdoc.js +0 -121
  86. package/dist/types/mso_mdoc.js.map +0 -1
  87. package/dist/types/pex.d.ts +0 -36
  88. package/dist/types/pex.d.ts.map +0 -1
  89. package/dist/types/pex.js +0 -3
  90. package/dist/types/pex.js.map +0 -1
  91. package/dist/types/sd-jwt-type-metadata.d.ts +0 -191
  92. package/dist/types/sd-jwt-type-metadata.d.ts.map +0 -1
  93. package/dist/types/sd-jwt-type-metadata.js +0 -3
  94. package/dist/types/sd-jwt-type-metadata.js.map +0 -1
  95. package/dist/types/sd-jwt-vc.d.ts +0 -196
  96. package/dist/types/sd-jwt-vc.d.ts.map +0 -1
  97. package/dist/types/sd-jwt-vc.js +0 -142
  98. package/dist/types/sd-jwt-vc.js.map +0 -1
  99. package/dist/types/status-list.d.ts +0 -10
  100. package/dist/types/status-list.d.ts.map +0 -1
  101. package/dist/types/status-list.js +0 -9
  102. package/dist/types/status-list.js.map +0 -1
  103. package/dist/types/vc.d.ts +0 -28
  104. package/dist/types/vc.d.ts.map +0 -1
  105. package/dist/types/vc.js +0 -28
  106. package/dist/types/vc.js.map +0 -1
  107. package/dist/types/w3c-vc.d.ts +0 -243
  108. package/dist/types/w3c-vc.d.ts.map +0 -1
  109. package/dist/types/w3c-vc.js +0 -26
  110. package/dist/types/w3c-vc.js.map +0 -1
  111. package/dist/utils/hasher.d.ts +0 -4
  112. package/dist/utils/hasher.d.ts.map +0 -1
  113. package/dist/utils/hasher.js +0 -40
  114. package/dist/utils/hasher.js.map +0 -1
  115. package/dist/utils/index.d.ts +0 -3
  116. package/dist/utils/index.d.ts.map +0 -1
  117. package/dist/utils/index.js +0 -19
  118. package/dist/utils/index.js.map +0 -1
  119. package/dist/utils/object.d.ts +0 -8
  120. package/dist/utils/object.d.ts.map +0 -1
  121. package/dist/utils/object.js +0 -28
  122. package/dist/utils/object.js.map +0 -1
package/src/types/vc.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  MdocDeviceResponse,
3
3
  MdocDocument,
4
4
  MdocOid4vpIssuerSigned,
@@ -6,8 +6,8 @@ import {
6
6
  WrappedMdocCredential,
7
7
  WrappedMdocPresentation,
8
8
  } from './mso_mdoc'
9
- import { SdJwtDecodedVerifiableCredential, WrappedSdJwtVerifiableCredential, WrappedSdJwtVerifiablePresentation } from './sd-jwt-vc'
10
- import {
9
+ import type { SdJwtDecodedVerifiableCredential, WrappedSdJwtVerifiableCredential, WrappedSdJwtVerifiablePresentation } from './sd-jwt-vc'
10
+ import type {
11
11
  JwtDecodedVerifiableCredential,
12
12
  JwtDecodedVerifiablePresentation,
13
13
  W3CVerifiableCredential,
@@ -20,25 +20,13 @@ export type WrappedVerifiableCredential = WrappedW3CVerifiableCredential | Wrapp
20
20
 
21
21
  export type WrappedVerifiablePresentation = WrappedW3CVerifiablePresentation | WrappedSdJwtVerifiablePresentation | WrappedMdocPresentation
22
22
 
23
- export enum OriginalType {
24
- // W3C
25
- JSONLD = 'json-ld',
26
- JWT_ENCODED = 'jwt-encoded',
27
- JWT_DECODED = 'jwt-decoded',
28
-
29
- // SD-JWT
30
- SD_JWT_VC_ENCODED = 'sd-jwt-vc-encoded',
31
- SD_JWT_VC_DECODED = 'sd-jwt-vc-decoded',
32
-
33
- // MSO MDOCS
34
- MSO_MDOC_ENCODED = 'mso_mdoc-encoded',
35
- MSO_MDOC_DECODED = 'mso_mdoc-decoded',
36
- }
23
+ export type CredentialProofFormat = 'jwt' | 'lds' | 'vc+jwt' /*| 'EthereumEip712Signature2021'*/ | 'cbor'
37
24
 
38
25
  export type CredentialFormat =
39
26
  // W3C
40
27
  | 'jwt_vc'
41
28
  | 'ldp_vc'
29
+ | 'vc+jwt'
42
30
  // SD-JWT
43
31
  | 'vc+sd-jwt'
44
32
  // Remaining
@@ -51,8 +39,9 @@ export type PresentationFormat =
51
39
  // W3C
52
40
  | 'jwt_vp'
53
41
  | 'ldp_vp'
42
+ | 'vp+jwt'
54
43
  // SD-JWT
55
- | 'vc+sd-jwt'
44
+ | 'vp+sd-jwt'
56
45
  // Remaining
57
46
  | 'jwt'
58
47
  | 'ldp'
@@ -74,14 +63,3 @@ export type OriginalVerifiablePresentation =
74
63
  | MdocOid4vpMdocVpToken
75
64
  | MdocDeviceResponse
76
65
  export type Original = OriginalVerifiablePresentation | OriginalVerifiableCredential
77
-
78
- export const enum DocumentFormat {
79
- // W3C
80
- JWT,
81
- JSONLD,
82
- // SD-JWT
83
- SD_JWT_VC,
84
- // Remaining
85
- EIP712,
86
- MSO_MDOC,
87
- }
@@ -1,6 +1,6 @@
1
- import { IProofPurpose, IProofType } from './did'
2
- import { PresentationSubmission } from './pex'
3
- import { OriginalType, WrappedVerifiableCredential, WrappedVerifiablePresentation } from './vc'
1
+ import { type PresentationSubmission } from './pex'
2
+ import { OriginalType } from '../mapper'
3
+ import { IProofPurpose, IProofType } from '../utils'
4
4
 
5
5
  export type AdditionalClaims = Record<string, any>
6
6
 
@@ -195,8 +195,6 @@ export interface JwtDecodedVerifiablePresentation {
195
195
  [x: string]: any
196
196
  }
197
197
 
198
- export const JWT_PROOF_TYPE_2020 = 'JwtProof2020'
199
-
200
198
  export interface IVerifyStatusResult {
201
199
  verified: boolean
202
200
  /**
@@ -284,25 +282,4 @@ export enum StatusListType {
284
282
 
285
283
  export type StatusPurpose2021 = 'revocation' | 'suspension' | string
286
284
 
287
- export enum StatusListCredentialIdMode {
288
- ISSUANCE = 'ISSUANCE',
289
- // PERSISTENCE = 'PERSISTENCE',
290
- NEVER = 'NEVER',
291
- }
292
-
293
285
  export type StatusListIndexingDirection = 'rightToLeft'
294
-
295
- export enum StatusListDriverType {
296
- AGENT_TYPEORM = 'agent_typeorm',
297
- /* AGENT_KV_STORE = 'agent_kv_store',
298
- GITHUB = 'github',
299
- AGENT_FILESYSTEM = 'agent_filesystem',*/
300
- }
301
-
302
- export function isWrappedW3CVerifiableCredential(vc: WrappedVerifiableCredential): vc is WrappedW3CVerifiableCredential {
303
- return vc.format === 'jwt_vc' || vc.format === 'ldp_vc'
304
- }
305
-
306
- export function isWrappedW3CVerifiablePresentation(vp: WrappedVerifiablePresentation): vp is WrappedW3CVerifiablePresentation {
307
- return vp.format === 'jwt_vp' || vp.format === 'ldp_vp'
308
- }
@@ -0,0 +1,46 @@
1
+ export enum ICoseKeyType {
2
+ OKP = 1,
3
+ EC2 = 2,
4
+ RSA = 3,
5
+ Symmetric = 4,
6
+ Reserved = 0,
7
+ }
8
+
9
+ export enum ICoseSignatureAlgorithm {
10
+ ES256 = -7,
11
+ ES256K = -47,
12
+ ES384 = -35,
13
+ ES512 = -36,
14
+ EdDSA = -8,
15
+ HS256_64 = 4,
16
+ HS256 = 5,
17
+ HS384 = 6,
18
+ HS512 = 7,
19
+ PS256 = -37,
20
+ PS384 = -38,
21
+ PS512 = -39,
22
+ }
23
+
24
+ export enum ICoseKeyOperation {
25
+ SIGN = 1,
26
+ VERIFY = 2,
27
+ ENCRYPT = 3,
28
+ DECRYPT = 4,
29
+ WRAP_KEY = 5,
30
+ UNWRAP_KEY = 6,
31
+ DERIVE_KEY = 7,
32
+ DERIVE_BITS = 8,
33
+ MAC_CREATE = 9,
34
+ MAC_VERIFY = 10,
35
+ }
36
+
37
+ export enum ICoseCurve {
38
+ P_256 = 1,
39
+ P_384 = 2,
40
+ P_521 = 3,
41
+ X25519 = 4,
42
+ X448 = 5,
43
+ Ed25519 = 6,
44
+ Ed448 = 7,
45
+ secp256k1 = -1,
46
+ }
@@ -0,0 +1,91 @@
1
+ import { IParsedDID } from '../types'
2
+
3
+ export enum IProofPurpose {
4
+ verificationMethod = 'verificationMethod',
5
+ assertionMethod = 'assertionMethod',
6
+ authentication = 'authentication',
7
+ keyAgreement = 'keyAgreement',
8
+ contractAgreement = 'contactAgreement',
9
+ capabilityInvocation = 'capabilityInvocation',
10
+ capabilityDelegation = 'capabilityDelegation',
11
+ }
12
+
13
+ export enum IProofType {
14
+ Ed25519Signature2018 = 'Ed25519Signature2018',
15
+ Ed25519Signature2020 = 'Ed25519Signature2020',
16
+ EcdsaSecp256k1Signature2019 = 'EcdsaSecp256k1Signature2019',
17
+ EcdsaSecp256k1RecoverySignature2020 = 'EcdsaSecp256k1RecoverySignature2020',
18
+ JsonWebSignature2020 = 'JsonWebSignature2020',
19
+ RsaSignature2018 = 'RsaSignature2018',
20
+ GpgSignature2020 = 'GpgSignature2020',
21
+ JcsEd25519Signature2020 = 'JcsEd25519Signature2020',
22
+ BbsBlsSignatureProof2020 = 'BbsBlsSignatureProof2020',
23
+ BbsBlsBoundSignatureProof2020 = 'BbsBlsBoundSignatureProof2020',
24
+ JwtProof2020 = 'JwtProof2020',
25
+ SdJwtProof2024 = 'SdJwtProof2024',
26
+ MdocProof2024 = 'MsoMdocProof2024',
27
+ }
28
+
29
+ export const parseDid = (did: string): IParsedDID => {
30
+ const parsedDid = parse(did)
31
+ if (parsedDid === null) {
32
+ throw new Error('invalid did')
33
+ }
34
+
35
+ return parsedDid
36
+ }
37
+
38
+ const parse = (didUrl: string): IParsedDID | null => {
39
+ const PCT_ENCODED = '(?:%[0-9a-fA-F]{2})'
40
+ const ID_CHAR = `(?:[a-zA-Z0-9._-]|${PCT_ENCODED})`
41
+ const METHOD = '([a-z0-9]+)'
42
+ const METHOD_ID = `((?:${ID_CHAR}*:)*(${ID_CHAR}+))`
43
+ const PARAM_CHAR = '[a-zA-Z0-9_.:%-]'
44
+ const PARAM = `;${PARAM_CHAR}+=${PARAM_CHAR}*`
45
+ const PARAMS = `((${PARAM})*)`
46
+ const PATH = `(/[^#?]*)?`
47
+ const QUERY = `([?][^#]*)?`
48
+ const FRAGMENT = `(#.*)?`
49
+ const DID_MATCHER = new RegExp(`^did:${METHOD}:${METHOD_ID}${PARAMS}${PATH}${QUERY}${FRAGMENT}$`)
50
+
51
+ if (didUrl === '' || !didUrl) return null
52
+ const sections = didUrl.match(DID_MATCHER)
53
+ if (sections) {
54
+ const parts: IParsedDID = {
55
+ did: `did:${sections[1]}:${sections[2]}`,
56
+ method: sections[1],
57
+ id: sections[2],
58
+ didUrl,
59
+ }
60
+ if (sections[4]) {
61
+ const params = sections[4].slice(1).split(';')
62
+ parts.params = {}
63
+ for (const p of params) {
64
+ const kv = p.split('=')
65
+ parts.params[kv[0]] = kv[1]
66
+ }
67
+ }
68
+ if (sections[6]) parts.path = sections[6]
69
+ if (sections[7]) parts.query = sections[7].slice(1)
70
+ if (sections[8]) parts.fragment = sections[8].slice(1)
71
+ return parts
72
+ }
73
+
74
+ return null
75
+ }
76
+
77
+ // Copied from did-resolver, so we have types without external dep
78
+
79
+ // Copyright 2018 Consensys AG
80
+
81
+ // Licensed under the Apache License, Version 2.0(the "License");
82
+ // you may not use this file except in compliance with the License.
83
+ // You may obtain a copy of the License at
84
+
85
+ // http://www.apache.org/licenses/LICENSE-2.0
86
+
87
+ // Unless required by applicable law or agreed to in writing, software
88
+ // distributed under the License is distributed on an "AS IS" BASIS,
89
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
90
+ // See the License for the specific language governing permissions and
91
+ // limitations under the License.
@@ -1,6 +1,9 @@
1
1
  import { sha256, sha384, sha512 } from '@noble/hashes/sha2'
2
+
3
+ // @ts-ignore
2
4
  import * as u8a from 'uint8arrays'
3
- import { HasherSync } from '../types'
5
+ const { fromString } = u8a
6
+ import type { HasherSync } from '../types'
4
7
 
5
8
  const supportedAlgorithms = ['sha256', 'sha384', 'sha512'] as const
6
9
  type SupportedAlgorithms = (typeof supportedAlgorithms)[number]
@@ -11,7 +14,7 @@ export const shaHasher: HasherSync = (data, algorithm) => {
11
14
  throw new Error(`Unsupported hashing algorithm ${algorithm}`)
12
15
  }
13
16
  const hasher = sanitizedAlgorithm === 'sha384' ? sha384 : sanitizedAlgorithm === 'sha512' ? sha512 : sha256
14
- return hasher(typeof data === 'string' ? u8a.fromString(data) : new Uint8Array(data))
17
+ return hasher(typeof data === 'string' ? fromString(data) : new Uint8Array(data))
15
18
  }
16
19
 
17
20
  export const defaultHasher: HasherSync = shaHasher
@@ -1,2 +1,8 @@
1
1
  export * from './object'
2
2
  export * from './hasher'
3
+ export * from './vc'
4
+ export * from './did'
5
+ export * from './mdoc'
6
+ export * from './sd-jwt'
7
+ export * from './jose'
8
+ export * from './cose'
@@ -0,0 +1,47 @@
1
+ export enum JwkKeyType {
2
+ EC = 'EC',
3
+ RSA = 'RSA',
4
+ oct = 'oct',
5
+ OKP = 'OKP',
6
+ }
7
+
8
+ export enum JoseSignatureAlgorithm {
9
+ RS256 = 'RS256',
10
+ RS384 = 'RS384',
11
+ RS512 = 'RS512',
12
+ ES256 = 'ES256',
13
+ ES256K = 'ES256K',
14
+ ES384 = 'ES384',
15
+ ES512 = 'ES512',
16
+ EdDSA = 'EdDSA',
17
+ HS256 = 'HS256',
18
+ HS384 = 'HS384',
19
+ HS512 = 'HS512',
20
+ PS256 = 'PS256',
21
+ PS384 = 'PS384',
22
+ PS512 = 'PS512',
23
+ none = 'none',
24
+ }
25
+
26
+ export enum JoseKeyOperation {
27
+ SIGN = 'sign',
28
+ VERIFY = 'verify',
29
+ ENCRYPT = 'encrypt',
30
+ DECRYPT = 'decrypt',
31
+ WRAP_KEY = 'wrapKey',
32
+ UNWRAP_KEY = 'unwrapKey',
33
+ DERIVE_KEY = 'deriveKey',
34
+ DERIVE_BITS = 'deriveBits',
35
+ }
36
+
37
+ export enum JoseCurve {
38
+ P_256 = 'P-256',
39
+ P_384 = 'P-384',
40
+ P_521 = 'P-521',
41
+ X25519 = 'X25519',
42
+ X448 = 'X448',
43
+ EdDSA = 'EdDSA',
44
+ Ed25519 = 'Ed25519',
45
+ Ed448 = 'Ed448',
46
+ secp256k1 = 'secp256k1',
47
+ }
@@ -0,0 +1,144 @@
1
+ import type {
2
+ DocumentJson,
3
+ IssuerSignedItemJson,
4
+ IVerifiableCredential,
5
+ MdocDecodedPayload,
6
+ MdocDeviceResponse,
7
+ MdocDocument,
8
+ MdocIssuerSigned,
9
+ MdocOid4vpIssuerSigned,
10
+ MdocOid4vpMdocVpToken,
11
+ WrappedMdocCredential,
12
+ WrappedMdocPresentation,
13
+ WrappedVerifiableCredential,
14
+ WrappedVerifiablePresentation,
15
+ } from '../types'
16
+ import * as mdoc from '@sphereon/kmp-mdoc-core'
17
+ import { IProofPurpose, IProofType } from './did'
18
+
19
+ export function isWrappedMdocCredential(vc: WrappedVerifiableCredential): vc is WrappedMdocCredential {
20
+ return vc.format === 'mso_mdoc'
21
+ }
22
+
23
+ export function isWrappedMdocPresentation(vp: WrappedVerifiablePresentation): vp is WrappedMdocPresentation {
24
+ return vp.format === 'mso_mdoc'
25
+ }
26
+
27
+ export function getMdocDecodedPayload(mdoc: MdocDocument): MdocDecodedPayload {
28
+ const mdocJson = mdoc.toJson()
29
+ if (!mdocJson.issuerSigned.nameSpaces) {
30
+ throw Error(`Cannot access Issuer Signed items from the Mdoc`)
31
+ }
32
+
33
+ const issuerSignedJson = mdoc.issuerSigned.toJsonDTO()
34
+ const namespaces = issuerSignedJson.nameSpaces as unknown as Record<string, IssuerSignedItemJson[]>
35
+
36
+ const decodedPayload: MdocDecodedPayload = {}
37
+ for (const [namespace, items] of Object.entries(namespaces)) {
38
+ decodedPayload[namespace] = items.reduce(
39
+ (acc, item) => ({
40
+ ...acc,
41
+ [item.key]: item.value.value,
42
+ }),
43
+ {},
44
+ )
45
+ }
46
+
47
+ return decodedPayload
48
+ }
49
+
50
+ /**
51
+ * Decode an Mdoc from its issuerSigned OID4VP Base64URL (string) to an object containing the disclosures,
52
+ * signed payload, decoded payload
53
+ *
54
+ */
55
+ export function decodeMdocIssuerSigned(oid4vpIssuerSigned: MdocOid4vpIssuerSigned): MdocDocument {
56
+ // Issuer signed according to 18013-7 in base64url
57
+ const issuerSigned: MdocIssuerSigned = mdoc.com.sphereon.mdoc.data.device.IssuerSignedCbor.Static.cborDecode(
58
+ mdoc.com.sphereon.kmp.decodeFrom(oid4vpIssuerSigned, mdoc.com.sphereon.kmp.Encoding.BASE64URL),
59
+ )
60
+ // Create an mdoc from it. // Validations need to be performed by the caller after this!
61
+ const holderMdoc: MdocDocument = issuerSigned.toDocument()
62
+ return holderMdoc
63
+ }
64
+
65
+ export function encodeMdocIssuerSigned(issuerSigned: MdocIssuerSigned, encoding: 'base64url' = 'base64url') {
66
+ return mdoc.com.sphereon.kmp.encodeTo(issuerSigned.cborEncode(), mdoc.com.sphereon.kmp.Encoding.BASE64URL)
67
+ }
68
+
69
+ /**
70
+ * Decode an Mdoc from its vp_token OID4VP Base64URL (string) to an object containing the disclosures,
71
+ * signed payload, decoded payload
72
+ *
73
+ */
74
+ export function decodeMdocDeviceResponse(vpToken: MdocOid4vpMdocVpToken): MdocDeviceResponse {
75
+ const deviceResponse = mdoc.com.sphereon.mdoc.data.device.DeviceResponseCbor.Static.cborDecode(
76
+ mdoc.com.sphereon.kmp.decodeFrom(vpToken, mdoc.com.sphereon.kmp.Encoding.BASE64URL),
77
+ )
78
+ return deviceResponse
79
+ }
80
+
81
+ // TODO naive implementation of mapping a mdoc onto a IVerifiableCredential. Needs some fixes and further implementation and needs to be moved out of ssi-types
82
+ export const mdocDecodedCredentialToUniformCredential = (
83
+ decoded: MdocDocument,
84
+ // @ts-ignore
85
+ opts?: { maxTimeSkewInMS?: number },
86
+ ): IVerifiableCredential => {
87
+ const document = decoded.toJson()
88
+ const json = document.toJsonDTO<DocumentJson>()
89
+ const type = 'Personal Identification Data'
90
+ const MSO = document.MSO
91
+ if (!MSO || !json.issuerSigned?.nameSpaces) {
92
+ throw Error(`Cannot access Mobile Security Object or Issuer Signed items from the Mdoc`)
93
+ }
94
+ const nameSpaces = json.issuerSigned.nameSpaces as unknown as Record<string, IssuerSignedItemJson[]>
95
+ if (!('eu.europa.ec.eudi.pid.1' in nameSpaces)) {
96
+ throw Error(`Only PID supported at present`)
97
+ }
98
+ const items = nameSpaces['eu.europa.ec.eudi.pid.1']
99
+ if (!items || items.length === 0) {
100
+ throw Error(`No issuer signed items were found`)
101
+ }
102
+ type DisclosuresAccumulator = {
103
+ [key: string]: any
104
+ }
105
+
106
+ const credentialSubject = items.reduce((acc: DisclosuresAccumulator, item: IssuerSignedItemJson) => {
107
+ if (Array.isArray(item.value)) {
108
+ acc[item.key] = item.value.map((val) => val.value).join(', ')
109
+ } else {
110
+ acc[item.key] = item.value.value
111
+ }
112
+ return acc
113
+ }, {})
114
+ const validFrom = MSO.validityInfo.validFrom
115
+ const validUntil = MSO.validityInfo.validUntil
116
+ const docType = MSO.docType
117
+ const expirationDate = validUntil
118
+ let issuanceDateStr = validFrom
119
+
120
+ const issuanceDate = issuanceDateStr
121
+ if (!issuanceDate) {
122
+ throw Error(`JWT issuance date is required but was not present`)
123
+ }
124
+
125
+ const credential: Omit<IVerifiableCredential, 'issuer' | 'issuanceDate'> = {
126
+ type: [docType], // Mdoc not a W3C VC, so no VerifiableCredential
127
+ '@context': [], // Mdoc has no JSON-LD by default. Certainly not the VC DM1 default context for JSON-LD
128
+ credentialSubject: {
129
+ type,
130
+ ...credentialSubject,
131
+ },
132
+ issuanceDate,
133
+ expirationDate,
134
+ proof: {
135
+ type: IProofType.MdocProof2024,
136
+ created: issuanceDate,
137
+ proofPurpose: IProofPurpose.authentication,
138
+ verificationMethod: json.issuerSigned.issuerAuth.payload,
139
+ mso_mdoc: mdoc.com.sphereon.kmp.encodeTo(decoded.cborEncode(), mdoc.com.sphereon.kmp.Encoding.BASE64URL),
140
+ },
141
+ }
142
+
143
+ return credential as IVerifiableCredential
144
+ }
@@ -0,0 +1,179 @@
1
+ import { decodeSdJwt, decodeSdJwtSync, getClaims, getClaimsSync } from '@sd-jwt/decode'
2
+ import type {
3
+ CompactSdJwtVc,
4
+ Hasher,
5
+ HasherSync,
6
+ IVerifiableCredential,
7
+ SdJwtDecodedDisclosure,
8
+ SdJwtDecodedVerifiableCredential,
9
+ SdJwtDecodedVerifiableCredentialPayload,
10
+ SdJwtDisclosure,
11
+ SdJwtSignedVerifiableCredentialPayload,
12
+ SdJwtVcKbJwtHeader,
13
+ SdJwtVcKbJwtPayload,
14
+ } from '../types'
15
+ import { IProofPurpose, IProofType } from './did'
16
+
17
+ /**
18
+ * Decode an SD-JWT vc from its compact format (string) to an object containing the disclosures,
19
+ * signed payload, decoded payload and the compact SD-JWT vc.
20
+ *
21
+ * Both the input and output interfaces of this method are defined in `@sphereon/ssi-types`, so
22
+ * this method hides the actual implementation of SD-JWT (which is currently based on @sd-jwt/core)
23
+ */
24
+ export function decodeSdJwtVc(compactSdJwtVc: CompactSdJwtVc, hasher: HasherSync): SdJwtDecodedVerifiableCredential {
25
+ const { jwt, disclosures, kbJwt } = decodeSdJwtSync(compactSdJwtVc, hasher)
26
+
27
+ const signedPayload = jwt.payload as SdJwtSignedVerifiableCredentialPayload
28
+ const decodedPayload = getClaimsSync(signedPayload, disclosures, hasher)
29
+ const compactKeyBindingJwt = kbJwt ? compactSdJwtVc.split('~').pop() : undefined
30
+
31
+ return {
32
+ compactSdJwtVc,
33
+ decodedPayload: decodedPayload as SdJwtDecodedVerifiableCredentialPayload,
34
+ disclosures: disclosures.map((d) => {
35
+ const decoded = d.key ? [d.salt, d.key, d.value] : [d.salt, d.value]
36
+ if (!d._digest) throw new Error('Implementation error: digest not present in disclosure')
37
+ return {
38
+ decoded: decoded as SdJwtDecodedDisclosure,
39
+ digest: d._digest,
40
+ encoded: d.encode(),
41
+ } satisfies SdJwtDisclosure
42
+ }),
43
+ signedPayload: signedPayload as SdJwtSignedVerifiableCredentialPayload,
44
+ ...(compactKeyBindingJwt &&
45
+ kbJwt && {
46
+ kbJwt: {
47
+ header: kbJwt.header as SdJwtVcKbJwtHeader,
48
+ compact: compactKeyBindingJwt,
49
+ payload: kbJwt.payload as SdJwtVcKbJwtPayload,
50
+ },
51
+ }),
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Decode an SD-JWT vc from its compact format (string) to an object containing the disclosures,
57
+ * signed payload, decoded payload and the compact SD-JWT vc.
58
+ *
59
+ * Both the input and output interfaces of this method are defined in `@sphereon/ssi-types`, so
60
+ * this method hides the actual implementation of SD-JWT (which is currently based on @sd-jwt/core)
61
+ */
62
+ export async function decodeSdJwtVcAsync(compactSdJwtVc: CompactSdJwtVc, hasher: Hasher): Promise<SdJwtDecodedVerifiableCredential> {
63
+ const { jwt, disclosures, kbJwt } = await decodeSdJwt(compactSdJwtVc, hasher)
64
+
65
+ const signedPayload = jwt.payload as SdJwtSignedVerifiableCredentialPayload
66
+ const decodedPayload = await getClaims(signedPayload, disclosures, hasher)
67
+ const compactKeyBindingJwt = kbJwt ? compactSdJwtVc.split('~').pop() : undefined
68
+
69
+ return {
70
+ compactSdJwtVc,
71
+ decodedPayload: decodedPayload as SdJwtDecodedVerifiableCredentialPayload,
72
+ disclosures: disclosures.map((d) => {
73
+ const decoded = d.key ? [d.salt, d.key, d.value] : [d.salt, d.value]
74
+ if (!d._digest) throw new Error('Implementation error: digest not present in disclosure')
75
+ return {
76
+ decoded: decoded as SdJwtDecodedDisclosure,
77
+ digest: d._digest,
78
+ encoded: d.encode(),
79
+ } satisfies SdJwtDisclosure
80
+ }),
81
+ signedPayload: signedPayload as SdJwtSignedVerifiableCredentialPayload,
82
+ ...(compactKeyBindingJwt &&
83
+ kbJwt && {
84
+ kbJwt: {
85
+ header: kbJwt.header as SdJwtVcKbJwtHeader,
86
+ payload: kbJwt.payload as SdJwtVcKbJwtPayload,
87
+ compact: compactKeyBindingJwt,
88
+ },
89
+ }),
90
+ }
91
+ }
92
+
93
+ export const sdJwtDecodedCredentialToUniformCredential = (
94
+ decoded: SdJwtDecodedVerifiableCredential,
95
+ opts?: { maxTimeSkewInMS?: number },
96
+ ): IVerifiableCredential => {
97
+ const { decodedPayload } = decoded
98
+ const { exp, nbf, iss, iat, vct, cnf, status, sub, jti } = decodedPayload
99
+
100
+ const maxSkewInMS = opts?.maxTimeSkewInMS ?? 1500
101
+
102
+ const expirationDate = jwtDateToISOString({ jwtClaim: exp, claimName: 'exp' })
103
+ let issuanceDateStr = jwtDateToISOString({ jwtClaim: iat, claimName: 'iat' })
104
+
105
+ let nbfDateAsStr: string | undefined
106
+ if (nbf) {
107
+ nbfDateAsStr = jwtDateToISOString({ jwtClaim: nbf, claimName: 'nbf' })
108
+ if (issuanceDateStr && nbfDateAsStr && issuanceDateStr !== nbfDateAsStr) {
109
+ const diff = Math.abs(new Date(nbfDateAsStr).getTime() - new Date(iss).getTime())
110
+ if (!maxSkewInMS || diff > maxSkewInMS) {
111
+ throw Error(`Inconsistent issuance dates between JWT claim (${nbfDateAsStr}) and VC value (${iss})`)
112
+ }
113
+ }
114
+ issuanceDateStr = nbfDateAsStr
115
+ }
116
+ const issuanceDate = issuanceDateStr
117
+ if (!issuanceDate) {
118
+ throw Error(`JWT issuance date is required but was not present`)
119
+ }
120
+
121
+ // Filter out the fields we don't want in credentialSubject
122
+ const excludedFields = new Set(['vct', 'cnf', 'iss', 'iat', 'exp', 'nbf', 'jti', 'sub'])
123
+ const credentialSubject = Object.entries(decodedPayload).reduce(
124
+ (acc, [key, value]) => {
125
+ if (
126
+ !excludedFields.has(key) &&
127
+ value !== undefined &&
128
+ value !== '' &&
129
+ !(typeof value === 'object' && value !== null && Object.keys(value).length === 0)
130
+ ) {
131
+ acc[key] = value
132
+ }
133
+ return acc
134
+ },
135
+ {} as Record<string, any>,
136
+ )
137
+
138
+ const credential: Omit<IVerifiableCredential, 'issuer' | 'issuanceDate'> = {
139
+ type: [vct], // SDJwt is not a W3C VC, so no VerifiableCredential
140
+ '@context': [], // SDJwt has no JSON-LD by default. Certainly not the VC DM1 default context for JSON-LD
141
+ credentialSubject: {
142
+ ...credentialSubject,
143
+ id: credentialSubject.id ?? sub ?? jti,
144
+ },
145
+ issuanceDate,
146
+ expirationDate,
147
+ issuer: iss,
148
+ ...(cnf && { cnf }),
149
+ ...(status && { status }),
150
+ proof: {
151
+ type: IProofType.SdJwtProof2024,
152
+ created: nbfDateAsStr ?? issuanceDate,
153
+ proofPurpose: IProofPurpose.authentication,
154
+ verificationMethod: iss,
155
+ jwt: decoded.compactSdJwtVc,
156
+ },
157
+ }
158
+
159
+ return credential as IVerifiableCredential
160
+ }
161
+
162
+ const jwtDateToISOString = ({
163
+ jwtClaim,
164
+ claimName,
165
+ isRequired = false,
166
+ }: {
167
+ jwtClaim?: number
168
+ claimName: string
169
+ isRequired?: boolean
170
+ }): string | undefined => {
171
+ if (jwtClaim) {
172
+ const claim = parseInt(jwtClaim.toString())
173
+ // change JWT seconds to millisecond for the date
174
+ return new Date(claim * (claim < 9999999999 ? 1000 : 1)).toISOString().replace(/\.000Z/, 'Z')
175
+ } else if (isRequired) {
176
+ throw Error(`JWT claim ${claimName} is required but was not present`)
177
+ }
178
+ return undefined
179
+ }