@sphereon/ssi-types 0.30.2-unstable.9 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/events/index.d.ts +5 -2
  2. package/dist/events/index.d.ts.map +1 -1
  3. package/dist/events/index.js +4 -0
  4. package/dist/events/index.js.map +1 -1
  5. package/dist/logging/index.d.ts +1 -1
  6. package/dist/logging/index.d.ts.map +1 -1
  7. package/dist/logging/index.js +3 -2
  8. package/dist/logging/index.js.map +1 -1
  9. package/dist/mapper/credential-constraints.d.ts +16 -0
  10. package/dist/mapper/credential-constraints.d.ts.map +1 -0
  11. package/dist/mapper/credential-constraints.js +3 -0
  12. package/dist/mapper/credential-constraints.js.map +1 -0
  13. package/dist/mapper/credential-mapper.d.ts +3 -2
  14. package/dist/mapper/credential-mapper.d.ts.map +1 -1
  15. package/dist/mapper/credential-mapper.js +14 -10
  16. package/dist/mapper/credential-mapper.js.map +1 -1
  17. package/dist/mapper/jsonld-language-values.d.ts.map +1 -1
  18. package/dist/types/cose.d.ts +12 -0
  19. package/dist/types/cose.d.ts.map +1 -1
  20. package/dist/types/cose.js.map +1 -1
  21. package/dist/types/index.d.ts +2 -0
  22. package/dist/types/index.d.ts.map +1 -1
  23. package/dist/types/index.js +2 -0
  24. package/dist/types/index.js.map +1 -1
  25. package/dist/types/metadata-types.d.ts +5 -0
  26. package/dist/types/metadata-types.d.ts.map +1 -0
  27. package/dist/types/metadata-types.js +3 -0
  28. package/dist/types/metadata-types.js.map +1 -0
  29. package/dist/types/mso_mdoc.d.ts +16 -8
  30. package/dist/types/mso_mdoc.d.ts.map +1 -1
  31. package/dist/types/mso_mdoc.js +29 -11
  32. package/dist/types/mso_mdoc.js.map +1 -1
  33. package/dist/types/sd-jwt-type-metadata.d.ts +147 -0
  34. package/dist/types/sd-jwt-type-metadata.d.ts.map +1 -0
  35. package/dist/types/sd-jwt-type-metadata.js +84 -0
  36. package/dist/types/sd-jwt-type-metadata.js.map +1 -0
  37. package/dist/types/sd-jwt-vc.d.ts.map +1 -1
  38. package/dist/types/sd-jwt-vc.js +17 -12
  39. package/dist/types/sd-jwt-vc.js.map +1 -1
  40. package/dist/types/w3c-vc.js +5 -5
  41. package/dist/types/w3c-vc.js.map +1 -1
  42. package/package.json +7 -6
  43. package/src/events/index.ts +8 -4
  44. package/src/logging/index.ts +1 -0
  45. package/src/mapper/credential-constraints.ts +19 -0
  46. package/src/mapper/credential-mapper.ts +45 -40
  47. package/src/types/cose.ts +13 -0
  48. package/src/types/index.ts +2 -0
  49. package/src/types/metadata-types.ts +5 -0
  50. package/src/types/mso_mdoc.ts +41 -7
  51. package/src/types/sd-jwt-type-metadata.ts +220 -0
  52. package/src/types/sd-jwt-vc.ts +19 -17
  53. package/src/types/w3c-vc.ts +2 -2
@@ -0,0 +1,19 @@
1
+ import { CredentialFormat, PresentationFormat } from '../types'
2
+
3
+ export type CredentialEncoding = 'json' /*includes json-ld*/ | 'jwt' | 'cbor'
4
+
5
+ export type IssuerAttributeName = 'iss' | 'issuer' | 'issuerAuth'
6
+ export type SubjectAttributeName = 'subject' | 'id' | 'deviceMac' | 'TODO'
7
+ export type TypeAttributeName = 'type' | 'vct'
8
+
9
+ export type DataModel = 'W3C_VCDM' | 'IETF_SD_JWT' | 'ISO_MSO_MDOC'
10
+
11
+ export interface CredentialConstraint {
12
+ credentialFormat: CredentialFormat
13
+ presentationFormat: PresentationFormat
14
+ maxSignatures: number
15
+ encoding: CredentialEncoding
16
+ dataModel: DataModel
17
+ typeAttribute?: TypeAttributeName // optional since mdocs use namespace maps without an explicit type attribute
18
+ issuerAttributes: [IssuerAttributeName]
19
+ }
@@ -41,9 +41,9 @@ import {
41
41
  WrappedVerifiablePresentation,
42
42
  WrappedW3CVerifiableCredential,
43
43
  } from '../types'
44
- import { MdocDocument } from '../types/mso_mdoc'
44
+ import { getMdocDecodedPayload, MdocDocument } from '../types/mso_mdoc'
45
45
  import { ObjectUtils } from '../utils'
46
- import { com } from '@sphereon/kmp-mdl-mdoc'
46
+ import { com } from '@sphereon/kmp-mdoc-core'
47
47
  import DeviceResponseCbor = com.sphereon.mdoc.data.device.DeviceResponseCbor
48
48
 
49
49
  export class CredentialMapper {
@@ -66,7 +66,7 @@ export class CredentialMapper {
66
66
  */
67
67
  static decodeVerifiablePresentation(
68
68
  presentation: OriginalVerifiablePresentation,
69
- hasher?: Hasher
69
+ hasher?: Hasher,
70
70
  ): JwtDecodedVerifiablePresentation | IVerifiablePresentation | SdJwtDecodedVerifiableCredential | MdocOid4vpMdocVpToken | MdocDeviceResponse {
71
71
  if (CredentialMapper.isJwtEncoded(presentation)) {
72
72
  const payload = jwt_decode(presentation as string) as JwtDecodedVerifiablePresentation
@@ -111,7 +111,7 @@ export class CredentialMapper {
111
111
  */
112
112
  static decodeVerifiableCredential(
113
113
  credential: OriginalVerifiableCredential,
114
- hasher?: Hasher
114
+ hasher?: Hasher,
115
115
  ): JwtDecodedVerifiableCredential | IVerifiableCredential | SdJwtDecodedVerifiableCredential {
116
116
  if (CredentialMapper.isJwtEncoded(credential)) {
117
117
  const payload = jwt_decode(credential as string) as JwtDecodedVerifiableCredential
@@ -151,7 +151,7 @@ export class CredentialMapper {
151
151
  */
152
152
  static toWrappedVerifiablePresentation(
153
153
  originalPresentation: OriginalVerifiablePresentation,
154
- opts?: { maxTimeSkewInMS?: number; hasher?: Hasher }
154
+ opts?: { maxTimeSkewInMS?: number; hasher?: Hasher },
155
155
  ): WrappedVerifiablePresentation {
156
156
  // MSO_MDOC
157
157
  if (CredentialMapper.isMsoMdocDecodedPresentation(originalPresentation) || CredentialMapper.isMsoMdocOid4VPEncoded(originalPresentation)) {
@@ -162,19 +162,20 @@ export class CredentialMapper {
162
162
  deviceResponse = originalPresentation
163
163
  }
164
164
 
165
- const mdocCredential: WrappedMdocCredential | undefined = deviceResponse.documents
166
- ?.map((doc) => CredentialMapper.toWrappedVerifiableCredential(doc, opts) as WrappedMdocCredential)
167
- .find((value) => value !== undefined)
168
- if (mdocCredential === undefined) {
169
- throw Error('mdoc credential data corruption')
165
+ const mdocCredentials = deviceResponse.documents?.map(
166
+ (doc) => CredentialMapper.toWrappedVerifiableCredential(doc, opts) as WrappedMdocCredential,
167
+ )
168
+ if (!mdocCredentials || mdocCredentials.length === 0) {
169
+ throw new Error('could not extract any mdoc credentials from mdoc device response')
170
170
  }
171
+
171
172
  return {
172
173
  type: CredentialMapper.isMsoMdocDecodedPresentation(originalPresentation) ? OriginalType.MSO_MDOC_DECODED : OriginalType.MSO_MDOC_ENCODED,
173
174
  format: 'mso_mdoc',
174
175
  original: originalPresentation,
175
176
  presentation: deviceResponse,
176
177
  decoded: deviceResponse,
177
- vcs: [mdocCredential],
178
+ vcs: mdocCredentials,
178
179
  }
179
180
  }
180
181
  // SD-JWT
@@ -205,7 +206,7 @@ export class CredentialMapper {
205
206
  typeof originalPresentation !== 'string' && CredentialMapper.hasJWTProofType(originalPresentation) ? proof?.jwt : originalPresentation
206
207
  if (!original) {
207
208
  throw Error(
208
- 'Could not determine original presentation, probably it was a converted JWT presentation, that is now missing the JWT value in the proof'
209
+ 'Could not determine original presentation, probably it was a converted JWT presentation, that is now missing the JWT value in the proof',
209
210
  )
210
211
  }
211
212
  const decoded = CredentialMapper.decodeVerifiablePresentation(original) as IVerifiablePresentation | JwtDecodedVerifiablePresentation
@@ -233,7 +234,7 @@ export class CredentialMapper {
233
234
  ? []
234
235
  : (CredentialMapper.toWrappedVerifiableCredentials(
235
236
  vp.verifiableCredential ?? [] /*.map(value => value.original)*/,
236
- opts
237
+ opts,
237
238
  ) as WrappedW3CVerifiableCredential[])
238
239
 
239
240
  const presentation = {
@@ -261,7 +262,7 @@ export class CredentialMapper {
261
262
  */
262
263
  static toWrappedVerifiableCredentials(
263
264
  verifiableCredentials: OriginalVerifiableCredential[],
264
- opts?: { maxTimeSkewInMS?: number; hasher?: Hasher }
265
+ opts?: { maxTimeSkewInMS?: number; hasher?: Hasher },
265
266
  ): WrappedVerifiableCredential[] {
266
267
  return verifiableCredentials.map((vc) => CredentialMapper.toWrappedVerifiableCredential(vc, opts))
267
268
  }
@@ -277,10 +278,10 @@ export class CredentialMapper {
277
278
  */
278
279
  static toWrappedVerifiableCredential(
279
280
  verifiableCredential: OriginalVerifiableCredential,
280
- opts?: { maxTimeSkewInMS?: number; hasher?: Hasher }
281
+ opts?: { maxTimeSkewInMS?: number; hasher?: Hasher },
281
282
  ): WrappedVerifiableCredential {
282
283
  // MSO_MDOC
283
- if (CredentialMapper.isMsoMdocDecodedCredential(verifiableCredential) || CredentialMapper.isMsoMdocDecodedCredential(verifiableCredential)) {
284
+ if (CredentialMapper.isMsoMdocDecodedCredential(verifiableCredential) || CredentialMapper.isMsoMdocOid4VPEncoded(verifiableCredential)) {
284
285
  let mdoc: MdocDocument
285
286
  if (CredentialMapper.isMsoMdocOid4VPEncoded(verifiableCredential)) {
286
287
  mdoc = decodeMdocIssuerSigned(verifiableCredential)
@@ -291,8 +292,8 @@ export class CredentialMapper {
291
292
  type: CredentialMapper.isMsoMdocDecodedCredential(verifiableCredential) ? OriginalType.MSO_MDOC_DECODED : OriginalType.MSO_MDOC_ENCODED,
292
293
  format: 'mso_mdoc',
293
294
  original: verifiableCredential,
294
- credential: mdocDecodedCredentialToUniformCredential(mdoc),
295
- decoded: mdoc,
295
+ credential: mdoc,
296
+ decoded: getMdocDecodedPayload(mdoc),
296
297
  }
297
298
  }
298
299
 
@@ -319,10 +320,10 @@ export class CredentialMapper {
319
320
 
320
321
  // If the VC is not an encoded/decoded SD-JWT, we assume it will be a W3C VC
321
322
  const proof = CredentialMapper.getFirstProof(verifiableCredential)
322
- const original = CredentialMapper.hasJWTProofType(verifiableCredential) && proof ? proof.jwt ?? verifiableCredential : verifiableCredential
323
+ const original = CredentialMapper.hasJWTProofType(verifiableCredential) && proof ? (proof.jwt ?? verifiableCredential) : verifiableCredential
323
324
  if (!original) {
324
325
  throw Error(
325
- 'Could not determine original credential, probably it was a converted JWT credential, that is now missing the JWT value in the proof'
326
+ 'Could not determine original credential, probably it was a converted JWT credential, that is now missing the JWT value in the proof',
326
327
  )
327
328
  }
328
329
  const decoded = CredentialMapper.decodeVerifiableCredential(original) as JwtDecodedVerifiableCredential | IVerifiableCredential
@@ -358,7 +359,7 @@ export class CredentialMapper {
358
359
  return ObjectUtils.isString(original) && !original.startsWith('ey') && ObjectUtils.isBase64(original)
359
360
  }
360
361
 
361
- public static isW3cCredential(credential: ICredential | SdJwtDecodedVerifiableCredential): credential is ICredential {
362
+ public static isW3cCredential(credential: ICredential | SdJwtDecodedVerifiableCredential | MdocDocument): credential is ICredential {
362
363
  return typeof credential === 'object' && '@context' in credential && ((credential as ICredential).type?.includes('VerifiableCredential') || false)
363
364
  }
364
365
 
@@ -438,7 +439,7 @@ export class CredentialMapper {
438
439
  }
439
440
 
440
441
  public static isW3cPresentation(
441
- presentation: UniformVerifiablePresentation | IPresentation | SdJwtDecodedVerifiableCredential | DeviceResponseCbor
442
+ presentation: UniformVerifiablePresentation | IPresentation | SdJwtDecodedVerifiableCredential | DeviceResponseCbor,
442
443
  ): presentation is IPresentation {
443
444
  return (
444
445
  typeof presentation === 'object' &&
@@ -448,7 +449,7 @@ export class CredentialMapper {
448
449
  }
449
450
 
450
451
  public static isSdJwtDecodedCredentialPayload(
451
- credential: ICredential | SdJwtDecodedVerifiableCredentialPayload
452
+ credential: ICredential | SdJwtDecodedVerifiableCredentialPayload,
452
453
  ): credential is SdJwtDecodedVerifiableCredentialPayload {
453
454
  return typeof credential === 'object' && 'vct' in credential
454
455
  }
@@ -462,7 +463,11 @@ export class CredentialMapper {
462
463
  // We are doing this over here, as the rest of the logic around it would otherwise need to be adjusted substantially
463
464
  return false
464
465
  }
465
- return firstOriginal.toJson().toJsonString() === secondOriginal.toJson().toJsonString()
466
+
467
+ // FIXME: mdoc library fails on parsing the device signed, so for now we just check whether the issuerSigned
468
+ // is equal, then we have a good chance it is the same credential. Once device signed parsing is fixed in mdl
469
+ // library we can move the .equals() to the top-level object.
470
+ return firstOriginal.issuerSigned.equals(secondOriginal.issuerSigned)
466
471
  } else if (CredentialMapper.isSdJwtDecodedCredential(firstOriginal) || CredentialMapper.isSdJwtDecodedCredential(secondOriginal)) {
467
472
  return firstOriginal.compactSdJwtVc === secondOriginal.compactSdJwtVc
468
473
  } else {
@@ -476,7 +481,7 @@ export class CredentialMapper {
476
481
  }
477
482
 
478
483
  public static isSdJwtDecodedCredential(
479
- original: OriginalVerifiableCredential | OriginalVerifiablePresentation | ICredential | IPresentation
484
+ original: OriginalVerifiableCredential | OriginalVerifiablePresentation | ICredential | IPresentation,
480
485
  ): original is SdJwtDecodedVerifiableCredential {
481
486
  return (
482
487
  typeof original === 'object' &&
@@ -485,7 +490,7 @@ export class CredentialMapper {
485
490
  }
486
491
 
487
492
  public static isMsoMdocDecodedCredential(
488
- original: OriginalVerifiableCredential | OriginalVerifiablePresentation | ICredential | IPresentation
493
+ original: OriginalVerifiableCredential | OriginalVerifiablePresentation | ICredential | IPresentation,
489
494
  ): original is MdocDocument {
490
495
  return typeof original === 'object' && 'issuerSigned' in original && (<MdocDocument>original).issuerSigned !== undefined
491
496
  }
@@ -520,7 +525,7 @@ export class CredentialMapper {
520
525
  static jwtEncodedPresentationToUniformPresentation(
521
526
  jwt: string,
522
527
  makeCredentialsUniform: boolean = true,
523
- opts?: { maxTimeSkewInMS?: number }
528
+ opts?: { maxTimeSkewInMS?: number },
524
529
  ): IPresentation {
525
530
  return CredentialMapper.jwtDecodedPresentationToUniformPresentation(jwt_decode(jwt), makeCredentialsUniform, opts)
526
531
  }
@@ -528,7 +533,7 @@ export class CredentialMapper {
528
533
  static jwtDecodedPresentationToUniformPresentation(
529
534
  decoded: JwtDecodedVerifiablePresentation,
530
535
  makeCredentialsUniform: boolean = true,
531
- opts?: { maxTimeSkewInMS?: number }
536
+ opts?: { maxTimeSkewInMS?: number },
532
537
  ): IVerifiablePresentation {
533
538
  const { iss, aud, jti, vp, ...rest } = decoded
534
539
 
@@ -575,7 +580,7 @@ export class CredentialMapper {
575
580
  opts?: {
576
581
  maxTimeSkewInMS?: number
577
582
  hasher?: Hasher
578
- }
583
+ },
579
584
  ): IVerifiableCredential {
580
585
  if (CredentialMapper.isMsoMdocDecodedCredential(verifiableCredential)) {
581
586
  return mdocDecodedCredentialToUniformCredential(verifiableCredential)
@@ -589,7 +594,7 @@ export class CredentialMapper {
589
594
  : verifiableCredential
590
595
  if (!original) {
591
596
  throw Error(
592
- 'Could not determine original credential from passed in credential. Probably because a JWT proof type was present, but now is not available anymore'
597
+ 'Could not determine original credential from passed in credential. Probably because a JWT proof type was present, but now is not available anymore',
593
598
  )
594
599
  }
595
600
  const decoded = CredentialMapper.decodeVerifiableCredential(original, opts?.hasher)
@@ -612,7 +617,7 @@ export class CredentialMapper {
612
617
 
613
618
  static toUniformPresentation(
614
619
  presentation: OriginalVerifiablePresentation,
615
- opts?: { maxTimeSkewInMS?: number; addContextIfMissing?: boolean }
620
+ opts?: { maxTimeSkewInMS?: number; addContextIfMissing?: boolean; hasher?: Hasher },
616
621
  ): IVerifiablePresentation {
617
622
  if (CredentialMapper.isSdJwtDecodedCredential(presentation)) {
618
623
  throw new Error('Converting SD-JWT VC to uniform VP is not supported.')
@@ -624,10 +629,10 @@ export class CredentialMapper {
624
629
  const original = typeof presentation !== 'string' && CredentialMapper.hasJWTProofType(presentation) ? proof?.jwt : presentation
625
630
  if (!original) {
626
631
  throw Error(
627
- 'Could not determine original presentation, probably it was a converted JWT presentation, that is now missing the JWT value in the proof'
632
+ 'Could not determine original presentation, probably it was a converted JWT presentation, that is now missing the JWT value in the proof',
628
633
  )
629
634
  }
630
- const decoded = CredentialMapper.decodeVerifiablePresentation(original)
635
+ const decoded = CredentialMapper.decodeVerifiablePresentation(original, opts?.hasher)
631
636
  const isJwtEncoded: boolean = CredentialMapper.isJwtEncoded(original)
632
637
  const isJwtDecoded: boolean = CredentialMapper.isJwtDecodedPresentation(original)
633
638
  const uniformPresentation =
@@ -641,7 +646,7 @@ export class CredentialMapper {
641
646
  }
642
647
 
643
648
  uniformPresentation.verifiableCredential = uniformPresentation.verifiableCredential?.map((vc) =>
644
- CredentialMapper.toUniformCredential(vc, opts)
649
+ CredentialMapper.toUniformCredential(vc, opts),
645
650
  ) as IVerifiableCredential[] // We cast it because we IPresentation needs a VC. The internal Credential doesn't have the required Proof anymore (that is intended)
646
651
  return uniformPresentation
647
652
  }
@@ -650,14 +655,14 @@ export class CredentialMapper {
650
655
  jwt: string,
651
656
  opts?: {
652
657
  maxTimeSkewInMS?: number
653
- }
658
+ },
654
659
  ): IVerifiableCredential {
655
660
  return CredentialMapper.jwtDecodedCredentialToUniformCredential(jwt_decode(jwt), opts)
656
661
  }
657
662
 
658
663
  static jwtDecodedCredentialToUniformCredential(
659
664
  decoded: JwtDecodedVerifiableCredential,
660
- opts?: { maxTimeSkewInMS?: number }
665
+ opts?: { maxTimeSkewInMS?: number },
661
666
  ): IVerifiableCredential {
662
667
  const { exp, nbf, iss, vc, sub, jti, ...rest } = decoded
663
668
  const credential: IVerifiableCredential = {
@@ -825,7 +830,7 @@ export class CredentialMapper {
825
830
  }
826
831
 
827
832
  static toCompactJWT(
828
- jwtDocument: W3CVerifiableCredential | JwtDecodedVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiablePresentation | string
833
+ jwtDocument: W3CVerifiableCredential | JwtDecodedVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiablePresentation | string,
829
834
  ): string {
830
835
  if (!jwtDocument || CredentialMapper.detectDocumentType(jwtDocument) !== DocumentFormat.JWT) {
831
836
  throw Error('Cannot convert non JWT credential to JWT')
@@ -855,7 +860,7 @@ export class CredentialMapper {
855
860
  | JwtDecodedVerifiablePresentation
856
861
  | SdJwtDecodedVerifiableCredential
857
862
  | MdocDeviceResponse
858
- | MdocDocument
863
+ | MdocDocument,
859
864
  ): DocumentFormat {
860
865
  if (this.isMsoMdocOid4VPEncoded(document as any) || this.isMsoMdocDecodedCredential(document as any)) {
861
866
  return DocumentFormat.MSO_MDOC
@@ -883,7 +888,7 @@ export class CredentialMapper {
883
888
  }
884
889
 
885
890
  private static hasJWTProofType(
886
- document: W3CVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiableCredential | JwtDecodedVerifiablePresentation
891
+ document: W3CVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiableCredential | JwtDecodedVerifiablePresentation,
887
892
  ): boolean {
888
893
  if (typeof document === 'string') {
889
894
  return false
@@ -892,7 +897,7 @@ export class CredentialMapper {
892
897
  }
893
898
 
894
899
  private static getFirstProof(
895
- document: W3CVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiableCredential | JwtDecodedVerifiablePresentation
900
+ document: W3CVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiableCredential | JwtDecodedVerifiablePresentation,
896
901
  ): IProof | undefined {
897
902
  if (!document || typeof document === 'string') {
898
903
  return undefined
package/src/types/cose.ts CHANGED
@@ -1,3 +1,8 @@
1
+ import { com } from '@sphereon/kmp-mdoc-core'
2
+ import SignatureAlgorithm = com.sphereon.crypto.generic.SignatureAlgorithm
3
+ import KeyType = com.sphereon.crypto.generic.KeyType
4
+ import KeyOperations = com.sphereon.crypto.generic.KeyOperations
5
+
1
6
  /**
2
7
  * See our mdl-mdoc and crypto library for more information
3
8
  * https://github.com/Sphereon-Opensource/mdoc-cbor-crypto-multiplatform
@@ -17,6 +22,14 @@ export interface ICoseKeyJson {
17
22
  y?: string
18
23
  d?: string
19
24
  x5chain?: Array<string>
25
+ getSignatureAlgorithm(): SignatureAlgorithm
26
+ getKty(): KeyType
27
+ getKeyOperations(): Array<KeyOperations>
28
+ getX509CertificateChain(): Array<string> | undefined
29
+ toPublicKey(): ICoseKeyJson
30
+ getKidAsString(): string | undefined
31
+ getXAsString(): string
32
+ getYAsString(): string
20
33
 
21
34
  [k: string]: unknown
22
35
  }
@@ -3,7 +3,9 @@ export * from './pex'
3
3
  export * from './vc'
4
4
  export * from './generic'
5
5
  export * from './sd-jwt-vc'
6
+ export * from './sd-jwt-type-metadata'
6
7
  export * from './w3c-vc'
7
8
  export * from './jose'
8
9
  export * from './cose'
9
10
  export * from './mso_mdoc'
11
+ export * from './metadata-types'
@@ -0,0 +1,5 @@
1
+ export type MetadataType = 'issuer' | 'authorizationServer' | 'openidFederation'
2
+
3
+ export interface IMetadataImportArgs {
4
+ metadataType: MetadataType
5
+ }
@@ -5,7 +5,7 @@
5
5
  * for sd-jwts
6
6
  */
7
7
 
8
- import { com } from '@sphereon/kmp-mdl-mdoc'
8
+ import { com } from '@sphereon/kmp-mdoc-core'
9
9
  import { IProofPurpose, IProofType } from './did'
10
10
  import { OriginalType, WrappedVerifiableCredential, WrappedVerifiablePresentation } from './vc'
11
11
  import { IVerifiableCredential } from './w3c-vc'
@@ -25,6 +25,8 @@ export type MdocOid4vpMdocVpToken = string
25
25
  export type MdocIssuerSigned = com.sphereon.mdoc.data.device.IssuerSignedCbor
26
26
  export type MdocDocument = com.sphereon.mdoc.data.device.DocumentCbor
27
27
  export type MdocDocumentJson = com.sphereon.mdoc.data.device.DocumentJson
28
+ export type IssuerSignedJson = com.sphereon.mdoc.data.device.IssuerSignedJson
29
+ export type DeviceSignedJson = com.sphereon.mdoc.data.device.DeviceSignedJson
28
30
  export type MdocDeviceResponse = com.sphereon.mdoc.data.device.DeviceResponseCbor
29
31
 
30
32
  export interface WrappedMdocCredential {
@@ -33,9 +35,10 @@ export interface WrappedMdocCredential {
33
35
  */
34
36
  original: MdocDocument | MdocOid4vpIssuerSigned
35
37
  /**
36
- * Decoded version of the Mdoc payload. We add the record to make sure existing implementations remain happy
38
+ * Record where keys are the namespaces and the values are objects again with the namespace values
39
+ * @todo which types can be there? (it doesn't matter for matching as mdoc only matches on path)
37
40
  */
38
- decoded: MdocDocument & { [key: string]: any }
41
+ decoded: MdocDecodedPayload
39
42
  /**
40
43
  * Type of this credential.
41
44
  */
@@ -47,7 +50,7 @@ export interface WrappedMdocCredential {
47
50
  /**
48
51
  * Internal stable representation of a Credential
49
52
  */
50
- credential: IVerifiableCredential
53
+ credential: MdocDocument
51
54
  }
52
55
 
53
56
  export interface WrappedMdocPresentation {
@@ -72,9 +75,10 @@ export interface WrappedMdocPresentation {
72
75
  */
73
76
  presentation: MdocDeviceResponse
74
77
  /**
75
- * Wrapped Mdocs belonging to the Presentation. .
78
+ * Wrapped Mdocs belonging to the Presentation. There can be multiple
79
+ * documents in a single device response
76
80
  */
77
- vcs: [WrappedMdocCredential]
81
+ vcs: WrappedMdocCredential[]
78
82
  }
79
83
 
80
84
  export function isWrappedMdocCredential(vc: WrappedVerifiableCredential): vc is WrappedMdocCredential {
@@ -84,6 +88,32 @@ export function isWrappedMdocCredential(vc: WrappedVerifiableCredential): vc is
84
88
  export function isWrappedMdocPresentation(vp: WrappedVerifiablePresentation): vp is WrappedMdocPresentation {
85
89
  return vp.format === 'mso_mdoc'
86
90
  }
91
+ /**
92
+ * Record where keys are the namespaces and the values are objects again with the namespace values
93
+ */
94
+ export type MdocDecodedPayload = Record<string, Record<string, string | number | boolean>>
95
+ export function getMdocDecodedPayload(mdoc: MdocDocument): MdocDecodedPayload {
96
+ const mdocJson = mdoc.toJson()
97
+ if (!mdocJson.issuerSigned.nameSpaces) {
98
+ throw Error(`Cannot access Issuer Signed items from the Mdoc`)
99
+ }
100
+
101
+ const issuerSignedJson = mdocJson.issuerSigned.toJsonDTO<IssuerSignedJson>()
102
+ const namespaces = issuerSignedJson.nameSpaces as unknown as Record<string, IssuerSignedItemJson[]>
103
+
104
+ const decodedPayload: MdocDecodedPayload = {}
105
+ for (const [namespace, items] of Object.entries(namespaces)) {
106
+ decodedPayload[namespace] = items.reduce(
107
+ (acc, item) => ({
108
+ ...acc,
109
+ [item.key]: item.value.value,
110
+ }),
111
+ {},
112
+ )
113
+ }
114
+
115
+ return decodedPayload
116
+ }
87
117
 
88
118
  /**
89
119
  * Decode an Mdoc from its issuerSigned OID4VP Base64URL (string) to an object containing the disclosures,
@@ -98,6 +128,10 @@ export function decodeMdocIssuerSigned(oid4vpIssuerSigned: MdocOid4vpIssuerSigne
98
128
  return holderMdoc
99
129
  }
100
130
 
131
+ export function encodeMdocIssuerSigned(issuerSigned: MdocIssuerSigned, encoding: 'base64url' = 'base64url') {
132
+ return encodeTo(issuerSigned.cborEncode(), Encoding.BASE64URL)
133
+ }
134
+
101
135
  /**
102
136
  * Decode an Mdoc from its vp_token OID4VP Base64URL (string) to an object containing the disclosures,
103
137
  * signed payload, decoded payload
@@ -112,7 +146,7 @@ export function decodeMdocDeviceResponse(vpToken: MdocOid4vpMdocVpToken): MdocDe
112
146
  export const mdocDecodedCredentialToUniformCredential = (
113
147
  decoded: MdocDocument,
114
148
  // @ts-ignore
115
- opts?: { maxTimeSkewInMS?: number }
149
+ opts?: { maxTimeSkewInMS?: number },
116
150
  ): IVerifiableCredential => {
117
151
  const mdoc = decoded.toJson()
118
152
  const json = mdoc.toJsonDTO<DocumentJson>()
@@ -0,0 +1,220 @@
1
+ import * as u8a from 'uint8arrays'
2
+ import { AsyncHasher } from './sd-jwt-vc'
3
+
4
+ /**
5
+ * Represents the metadata associated with a specific SD-JWT VC type.
6
+ */
7
+ interface SdJwtTypeMetadata {
8
+ /**
9
+ * REQUIRED. The VC type URI.
10
+ */
11
+ vct: string;
12
+
13
+ /**
14
+ * OPTIONAL. A human-readable name for the type.
15
+ */
16
+ name?: string;
17
+
18
+ /**
19
+ * OPTIONAL. A human-readable description for the type.
20
+ */
21
+ description?: string;
22
+
23
+ /**
24
+ * OPTIONAL. A URI of another type that this type extends.
25
+ */
26
+ extends?: string;
27
+
28
+ /**
29
+ * OPTIONAL. Integrity metadata string for the 'extends' field.
30
+ */
31
+ ['extends#integrity']?: string;
32
+
33
+ /**
34
+ * OPTIONAL. URL pointing towards a JSON Schema document describing the VC's structure.
35
+ */
36
+ schema_uri?: string;
37
+
38
+ /**
39
+ * OPTIONAL. Integrity metadata string for the 'schema_uri' field.
40
+ */
41
+ ['schema_uri#integrity']?: string;
42
+
43
+ /**
44
+ * OPTIONAL. Display metadata for various languages.
45
+ */
46
+ display?: Array<SdJwtTypeDisplayMetadata>;
47
+
48
+ /**
49
+ * OPTIONAL. Metadata for the claims within the VC.
50
+ */
51
+ // TODO:
52
+ claims?: Array<any>;
53
+ }
54
+
55
+ /**
56
+ * Represents display metadata for a specific language.
57
+ */
58
+ interface SdJwtTypeDisplayMetadata {
59
+ /**
60
+ * REQUIRED. Language tag for the display information.
61
+ */
62
+ lang: string;
63
+
64
+ /**
65
+ * REQUIRED. Human-readable name for the type.
66
+ */
67
+ name: string;
68
+
69
+ /**
70
+ * OPTIONAL. Human-readable description for the type.
71
+ */
72
+ description?: string;
73
+
74
+ /**
75
+ * OPTIONAL. Rendering metadata for the type.
76
+ */
77
+ rendering?: SdJwtTypeRenderingMetadata;
78
+ }
79
+
80
+ /**
81
+ * Contains rendering metadata for different methods.
82
+ */
83
+ interface SdJwtTypeRenderingMetadata {
84
+ /**
85
+ * OPTIONAL. Simple rendering method metadata.
86
+ */
87
+ simple?: SdJwtSimpleRenderingMetadata;
88
+
89
+ /**
90
+ * OPTIONAL. Metadata for SVG templates.
91
+ */
92
+ svg_template?: Array<SdJwtSVGTemplateMetadata>;
93
+ }
94
+
95
+ /**
96
+ * Represents metadata for simple rendering.
97
+ */
98
+ interface SdJwtSimpleRenderingMetadata {
99
+ /**
100
+ * OPTIONAL. Metadata for the logo image.
101
+ */
102
+ logo?: SdJwtLogoMetadata;
103
+
104
+ /**
105
+ * OPTIONAL. Background color for the credential.
106
+ */
107
+ background_color?: string;
108
+
109
+ /**
110
+ * OPTIONAL. Text color for the credential.
111
+ */
112
+ text_color?: string;
113
+ }
114
+
115
+ /**
116
+ * Represents metadata for a logo.
117
+ */
118
+ interface SdJwtLogoMetadata {
119
+ /**
120
+ * REQUIRED. URI pointing to the logo image.
121
+ */
122
+ uri: string;
123
+
124
+ /**
125
+ * OPTIONAL. Integrity metadata string for the 'uri' field.
126
+ */
127
+ ['uri#integrity']?: string;
128
+
129
+ /**
130
+ * OPTIONAL. Alternative text for the logo image.
131
+ */
132
+ alt_text?: string;
133
+ }
134
+
135
+ /**
136
+ * Represents metadata for SVG templates.
137
+ */
138
+ interface SdJwtSVGTemplateMetadata {
139
+ /**
140
+ * REQUIRED. URI pointing to the SVG template.
141
+ */
142
+ uri: string;
143
+
144
+ /**
145
+ * OPTIONAL. Integrity metadata string for the 'uri' field.
146
+ */
147
+ ['uri#integrity']?: string;
148
+
149
+ /**
150
+ * OPTIONAL. Properties for the SVG template.
151
+ */
152
+ properties?: SdJwtSVGTemplateProperties;
153
+ }
154
+
155
+ /**
156
+ * Contains properties for SVG templates.
157
+ */
158
+ interface SdJwtSVGTemplateProperties {
159
+ /**
160
+ * OPTIONAL. The orientation for which the SVG template is optimized.
161
+ */
162
+ orientation?: string;
163
+
164
+ /**
165
+ * OPTIONAL. The color scheme for which the SVG template is optimized.
166
+ */
167
+ color_scheme?: string;
168
+ }
169
+
170
+
171
+ // Helper function to fetch API with error handling
172
+ async function fetchUrlWithErrorHandling(url: string): Promise<Response> {
173
+ const response = await fetch(url)
174
+ if (!response.ok) {
175
+ throw new Error(`${response.status}: ${response.statusText}`)
176
+ }
177
+ return response
178
+ }
179
+
180
+ export type SdJwtTypeHasher = (input: any, alg?: string) => string
181
+
182
+ async function validateIntegrity(input: any, integrityValue: string, hasher: AsyncHasher, alg?: string): Promise<boolean> {
183
+ const hash = await hasher(input, alg ?? 'sha256')
184
+ return u8a.toString(hash, 'utf-8') === integrityValue
185
+ }
186
+
187
+ // Fetch and validate Type Metadata
188
+ export async function fetchSdJwtTypeMetadataFromVctUrl(vct: string, opts?: { hasher?: AsyncHasher, integrity?: string }): Promise<SdJwtTypeMetadata> {
189
+ const url = new URL(vct)
190
+ const wellKnownUrl = `${url.origin}/.well-known/vct${url.pathname}`
191
+
192
+ const response = await fetchUrlWithErrorHandling(wellKnownUrl)
193
+ const metadata: SdJwtTypeMetadata = await response.json()
194
+ assertValidTypeMetadata(metadata, vct)
195
+ if (opts?.integrity && opts.hasher) {
196
+ if (!await validateIntegrity(metadata, opts.integrity, opts.hasher)) {
197
+ throw new Error('Integrity check failed')
198
+ }
199
+ }
200
+ return metadata
201
+ }
202
+
203
+ function assertValidTypeMetadata(metadata: SdJwtTypeMetadata, vct: string): void {
204
+ if (metadata.vct !== vct) {
205
+ throw new Error('VCT mismatch in metadata and credential')
206
+ }
207
+ }
208
+
209
+
210
+ /*
211
+ // Example usage
212
+ try {
213
+ const vct = 'https://betelgeuse.example.com/education_credential'
214
+ const typeMetadata = await fetchSdJwtTypeMetadataFromVctUrl(vct)
215
+ console.log('Type Metadata retrieved successfully:', typeMetadata)
216
+ } catch (error) {
217
+ console.error('Error fetching type metadata:', error.message)
218
+ }
219
+ */
220
+