@sphereon/ssi-types 0.37.2-next.34 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sphereon/ssi-types",
3
3
  "description": "SSI Common Types",
4
- "version": "0.37.2-next.34+99164365",
4
+ "version": "0.38.0",
5
5
  "source": "./src/index.ts",
6
6
  "type": "module",
7
7
  "main": "./dist/index.cjs",
@@ -59,5 +59,5 @@
59
59
  "Verifiable Credentials",
60
60
  "DIDs"
61
61
  ],
62
- "gitHead": "99164365a50f29bc9b528a94d23eedd285399f23"
62
+ "gitHead": "a93cb5bf52d46acaf3b2b2d8eba83cc88aa5cda4"
63
63
  }
@@ -73,7 +73,7 @@ export class CredentialMapper {
73
73
  */
74
74
  static decodeVerifiablePresentation(
75
75
  presentation: OriginalVerifiablePresentation,
76
- hasher?: HasherSync
76
+ hasher?: HasherSync,
77
77
  ): JwtDecodedVerifiablePresentation | IVerifiablePresentation | SdJwtDecodedVerifiableCredential | MdocOid4vpMdocVpToken | MdocDeviceResponse {
78
78
  if (CredentialMapper.isJwtEncoded(presentation)) {
79
79
  const payload = jwtDecode(presentation as string) as JwtDecodedVerifiablePresentation
@@ -116,8 +116,8 @@ export class CredentialMapper {
116
116
  */
117
117
  static decodeVerifiableCredential(
118
118
  credential: OriginalVerifiableCredential,
119
- hasher?: HasherSync
120
- ): JwtDecodedVerifiableCredential | IVerifiableCredential | SdJwtDecodedVerifiableCredential {
119
+ hasher?: HasherSync,
120
+ ): JwtDecodedVerifiableCredential | IVerifiableCredential | SdJwtDecodedVerifiableCredential | MdocDocument {
121
121
  if (CredentialMapper.isJwtEncoded(credential)) {
122
122
  const payload = jwtDecode(credential as string) as JwtDecodedVerifiableCredential
123
123
  const header = jwtDecode(credential as string, { header: true }) as Record<string, any>
@@ -138,6 +138,11 @@ export class CredentialMapper {
138
138
  return decodeSdJwtVc(credential, hasher ?? sha256)
139
139
  } else if (CredentialMapper.isSdJwtDecodedCredential(credential)) {
140
140
  return credential
141
+ } else if (CredentialMapper.isMsoMdocOid4VPEncoded(credential)) {
142
+ // ISO 18013-5/-7 mdoc IssuerSigned in base64url. Mirrors decodeVerifiablePresentation, which already handles mdoc.
143
+ return decodeMdocIssuerSigned(credential)
144
+ } else if (CredentialMapper.isMsoMdocDecodedCredential(credential)) {
145
+ return credential
141
146
  } else {
142
147
  return credential as IVerifiableCredential
143
148
  }
@@ -155,7 +160,7 @@ export class CredentialMapper {
155
160
  */
156
161
  static toWrappedVerifiablePresentation(
157
162
  originalPresentation: OriginalVerifiablePresentation,
158
- opts?: { maxTimeSkewInMS?: number; hasher?: HasherSync }
163
+ opts?: { maxTimeSkewInMS?: number; hasher?: HasherSync },
159
164
  ): WrappedVerifiablePresentation {
160
165
  // MSO_MDOC
161
166
  if (CredentialMapper.isMsoMdocDecodedPresentation(originalPresentation) || CredentialMapper.isMsoMdocOid4VPEncoded(originalPresentation)) {
@@ -170,7 +175,7 @@ export class CredentialMapper {
170
175
  }
171
176
 
172
177
  const mdocCredentials = deviceResponse.documents?.map(
173
- (doc) => CredentialMapper.toWrappedVerifiableCredential(doc, opts) as WrappedMdocCredential
178
+ (doc) => CredentialMapper.toWrappedVerifiableCredential(doc, opts) as WrappedMdocCredential,
174
179
  )
175
180
  if (!mdocCredentials || mdocCredentials.length === 0) {
176
181
  throw new Error('could not extract any mdoc credentials from mdoc device response')
@@ -210,7 +215,7 @@ export class CredentialMapper {
210
215
  typeof originalPresentation !== 'string' && CredentialMapper.hasJWTProofType(originalPresentation) ? proof?.jwt : originalPresentation
211
216
  if (!original) {
212
217
  throw Error(
213
- 'Could not determine original presentation, probably it was a converted JWT presentation, that is now missing the JWT value in the proof'
218
+ 'Could not determine original presentation, probably it was a converted JWT presentation, that is now missing the JWT value in the proof',
214
219
  )
215
220
  }
216
221
  const decoded = CredentialMapper.decodeVerifiablePresentation(original) as IVerifiablePresentation | JwtDecodedVerifiablePresentation
@@ -238,7 +243,7 @@ export class CredentialMapper {
238
243
  ? []
239
244
  : (CredentialMapper.toWrappedVerifiableCredentials(
240
245
  vp.verifiableCredential ?? [] /*.map(value => value.original)*/,
241
- opts
246
+ opts,
242
247
  ) as WrappedW3CVerifiableCredential[])
243
248
 
244
249
  const presentation = {
@@ -266,7 +271,7 @@ export class CredentialMapper {
266
271
  */
267
272
  static toWrappedVerifiableCredentials(
268
273
  verifiableCredentials: OriginalVerifiableCredential[],
269
- opts?: { maxTimeSkewInMS?: number; hasher?: HasherSync }
274
+ opts?: { maxTimeSkewInMS?: number; hasher?: HasherSync },
270
275
  ): WrappedVerifiableCredential[] {
271
276
  return verifiableCredentials.map((vc) => CredentialMapper.toWrappedVerifiableCredential(vc, opts))
272
277
  }
@@ -282,7 +287,7 @@ export class CredentialMapper {
282
287
  */
283
288
  static toWrappedVerifiableCredential(
284
289
  verifiableCredential: OriginalVerifiableCredential,
285
- opts?: { maxTimeSkewInMS?: number; hasher?: HasherSync }
290
+ opts?: { maxTimeSkewInMS?: number; hasher?: HasherSync },
286
291
  ): WrappedVerifiableCredential {
287
292
  // MSO_MDOC
288
293
  if (CredentialMapper.isMsoMdocDecodedCredential(verifiableCredential) || CredentialMapper.isMsoMdocOid4VPEncoded(verifiableCredential)) {
@@ -322,10 +327,10 @@ export class CredentialMapper {
322
327
 
323
328
  // If the VC is not an encoded/decoded SD-JWT, we assume it will be a W3C VC
324
329
  const proof = CredentialMapper.getFirstProof(verifiableCredential)
325
- const original = CredentialMapper.hasJWTProofType(verifiableCredential) && proof ? proof.jwt ?? verifiableCredential : verifiableCredential
330
+ const original = CredentialMapper.hasJWTProofType(verifiableCredential) && proof ? (proof.jwt ?? verifiableCredential) : verifiableCredential
326
331
  if (!original) {
327
332
  throw Error(
328
- 'Could not determine original credential, probably it was a converted JWT credential, that is now missing the JWT value in the proof'
333
+ 'Could not determine original credential, probably it was a converted JWT credential, that is now missing the JWT value in the proof',
329
334
  )
330
335
  }
331
336
  const decoded = CredentialMapper.decodeVerifiableCredential(original) as JwtDecodedVerifiableCredential | IVerifiableCredential
@@ -441,7 +446,7 @@ export class CredentialMapper {
441
446
  }
442
447
 
443
448
  public static isW3cPresentation(
444
- presentation: UniformVerifiablePresentation | IPresentation | SdJwtDecodedVerifiableCredential | DeviceResponseCbor
449
+ presentation: UniformVerifiablePresentation | IPresentation | SdJwtDecodedVerifiableCredential | DeviceResponseCbor,
445
450
  ): presentation is IPresentation {
446
451
  return (
447
452
  typeof presentation === 'object' &&
@@ -451,7 +456,7 @@ export class CredentialMapper {
451
456
  }
452
457
 
453
458
  public static isSdJwtDecodedCredentialPayload(
454
- credential: ICredential | SdJwtDecodedVerifiableCredentialPayload
459
+ credential: ICredential | SdJwtDecodedVerifiableCredentialPayload,
455
460
  ): credential is SdJwtDecodedVerifiableCredentialPayload {
456
461
  return typeof credential === 'object' && 'vct' in credential
457
462
  }
@@ -483,7 +488,7 @@ export class CredentialMapper {
483
488
  }
484
489
 
485
490
  public static isSdJwtDecodedCredential(
486
- original: OriginalVerifiableCredential | OriginalVerifiablePresentation | ICredential | IPresentation
491
+ original: OriginalVerifiableCredential | OriginalVerifiablePresentation | ICredential | IPresentation,
487
492
  ): original is SdJwtDecodedVerifiableCredential {
488
493
  return (
489
494
  typeof original === 'object' &&
@@ -492,7 +497,7 @@ export class CredentialMapper {
492
497
  }
493
498
 
494
499
  public static isSdJwtVcdm2DecodedCredential(
495
- original: OriginalVerifiableCredential | OriginalVerifiablePresentation | ICredential | IPresentation
500
+ original: OriginalVerifiableCredential | OriginalVerifiablePresentation | ICredential | IPresentation,
496
501
  ): original is SdJwtDecodedVerifiableCredential {
497
502
  if (typeof original !== 'object') {
498
503
  return false
@@ -502,7 +507,7 @@ export class CredentialMapper {
502
507
  }
503
508
 
504
509
  public static isMsoMdocDecodedCredential(
505
- original: OriginalVerifiableCredential | OriginalVerifiablePresentation | ICredential | IPresentation
510
+ original: OriginalVerifiableCredential | OriginalVerifiablePresentation | ICredential | IPresentation,
506
511
  ): original is MdocDocument {
507
512
  return typeof original === 'object' && 'issuerSigned' in original && (<MdocDocument>original).issuerSigned !== undefined
508
513
  }
@@ -537,7 +542,7 @@ export class CredentialMapper {
537
542
  static jwtEncodedPresentationToUniformPresentation(
538
543
  jwt: string,
539
544
  makeCredentialsUniform: boolean = true,
540
- opts?: { maxTimeSkewInMS?: number }
545
+ opts?: { maxTimeSkewInMS?: number },
541
546
  ): IPresentation {
542
547
  return CredentialMapper.jwtDecodedPresentationToUniformPresentation(jwtDecode(jwt), makeCredentialsUniform, opts)
543
548
  }
@@ -545,7 +550,7 @@ export class CredentialMapper {
545
550
  static jwtDecodedPresentationToUniformPresentation(
546
551
  decoded: JwtDecodedVerifiablePresentation,
547
552
  makeCredentialsUniform: boolean = true,
548
- opts?: { maxTimeSkewInMS?: number }
553
+ opts?: { maxTimeSkewInMS?: number },
549
554
  ): IVerifiablePresentation {
550
555
  const { iss, aud, jti, vp, ...rest } = decoded
551
556
 
@@ -592,7 +597,7 @@ export class CredentialMapper {
592
597
  opts?: {
593
598
  maxTimeSkewInMS?: number
594
599
  hasher?: HasherSync
595
- }
600
+ },
596
601
  ): IVerifiableCredential {
597
602
  if (CredentialMapper.isMsoMdocDecodedCredential(verifiableCredential)) {
598
603
  return mdocDecodedCredentialToUniformCredential(verifiableCredential)
@@ -606,7 +611,7 @@ export class CredentialMapper {
606
611
  : verifiableCredential
607
612
  if (!original) {
608
613
  throw Error(
609
- 'Could not determine original credential from passed in credential. Probably because a JWT proof type was present, but now is not available anymore'
614
+ 'Could not determine original credential from passed in credential. Probably because a JWT proof type was present, but now is not available anymore',
610
615
  )
611
616
  }
612
617
  const decoded = CredentialMapper.decodeVerifiableCredential(original, opts?.hasher ?? sha256)
@@ -629,7 +634,7 @@ export class CredentialMapper {
629
634
 
630
635
  static toUniformPresentation(
631
636
  presentation: OriginalVerifiablePresentation,
632
- opts?: { maxTimeSkewInMS?: number; addContextIfMissing?: boolean; hasher?: HasherSync }
637
+ opts?: { maxTimeSkewInMS?: number; addContextIfMissing?: boolean; hasher?: HasherSync },
633
638
  ): IVerifiablePresentation {
634
639
  if (CredentialMapper.isSdJwtDecodedCredential(presentation)) {
635
640
  throw new Error('Converting SD-JWT VC to uniform VP is not supported.')
@@ -641,7 +646,7 @@ export class CredentialMapper {
641
646
  const original = typeof presentation !== 'string' && CredentialMapper.hasJWTProofType(presentation) ? proof?.jwt : presentation
642
647
  if (!original) {
643
648
  throw Error(
644
- 'Could not determine original presentation, probably it was a converted JWT presentation, that is now missing the JWT value in the proof'
649
+ 'Could not determine original presentation, probably it was a converted JWT presentation, that is now missing the JWT value in the proof',
645
650
  )
646
651
  }
647
652
  const decoded = CredentialMapper.decodeVerifiablePresentation(original, opts?.hasher ?? sha256)
@@ -658,7 +663,7 @@ export class CredentialMapper {
658
663
  }
659
664
 
660
665
  uniformPresentation.verifiableCredential = uniformPresentation.verifiableCredential?.map((vc) =>
661
- CredentialMapper.toUniformCredential(vc, opts)
666
+ CredentialMapper.toUniformCredential(vc, opts),
662
667
  ) as IVerifiableCredential[] // We cast it because we IPresentation needs a VC. The internal Credential doesn't have the required Proof anymore (that is intended)
663
668
  return uniformPresentation
664
669
  }
@@ -667,14 +672,14 @@ export class CredentialMapper {
667
672
  jwt: string,
668
673
  opts?: {
669
674
  maxTimeSkewInMS?: number
670
- }
675
+ },
671
676
  ): IVerifiableCredential {
672
677
  return CredentialMapper.jwtDecodedCredentialToUniformCredential(jwtDecode(jwt), opts)
673
678
  }
674
679
 
675
680
  static jwtDecodedCredentialToUniformCredential(
676
681
  decoded: JwtDecodedVerifiableCredential,
677
- opts?: { maxTimeSkewInMS?: number }
682
+ opts?: { maxTimeSkewInMS?: number },
678
683
  ): IVerifiableCredential {
679
684
  const { exp, nbf, iss, vc, sub, jti, ...rest } = decoded
680
685
  const credential: IVerifiableCredential = {
@@ -842,7 +847,7 @@ export class CredentialMapper {
842
847
  }
843
848
 
844
849
  static toCompactJWT(
845
- jwtDocument: W3CVerifiableCredential | JwtDecodedVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiablePresentation | string
850
+ jwtDocument: W3CVerifiableCredential | JwtDecodedVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiablePresentation | string,
846
851
  ): string {
847
852
  if (!jwtDocument || CredentialMapper.detectDocumentType(jwtDocument) !== DocumentFormat.JWT) {
848
853
  throw Error('Cannot convert non JWT credential to JWT')
@@ -872,7 +877,7 @@ export class CredentialMapper {
872
877
  | JwtDecodedVerifiablePresentation
873
878
  | SdJwtDecodedVerifiableCredential
874
879
  | MdocDeviceResponse
875
- | MdocDocument
880
+ | MdocDocument,
876
881
  ): DocumentFormat {
877
882
  if (this.isMsoMdocOid4VPEncoded(document as any) || this.isMsoMdocDecodedCredential(document as any)) {
878
883
  return DocumentFormat.MSO_MDOC
@@ -900,7 +905,7 @@ export class CredentialMapper {
900
905
  }
901
906
 
902
907
  private static hasJWTProofType(
903
- document: W3CVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiableCredential | JwtDecodedVerifiablePresentation
908
+ document: W3CVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiableCredential | JwtDecodedVerifiablePresentation,
904
909
  ): boolean {
905
910
  if (typeof document === 'string') {
906
911
  return false
@@ -909,7 +914,7 @@ export class CredentialMapper {
909
914
  }
910
915
 
911
916
  private static getFirstProof(
912
- document: W3CVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiableCredential | JwtDecodedVerifiablePresentation
917
+ document: W3CVerifiableCredential | W3CVerifiablePresentation | JwtDecodedVerifiableCredential | JwtDecodedVerifiablePresentation,
913
918
  ): IProof | undefined {
914
919
  if (!document || typeof document === 'string') {
915
920
  return undefined
@@ -152,6 +152,11 @@ export interface SdJwtSimpleRenderingMetadata {
152
152
  */
153
153
  logo?: SdJwtLogoMetadata
154
154
 
155
+ /**
156
+ * OPTIONAL. Metadata for the background image.
157
+ */
158
+ background_image?: SdJwtImageMetadata
159
+
155
160
  /**
156
161
  * OPTIONAL. Background color for the credential.
157
162
  */
@@ -163,6 +168,26 @@ export interface SdJwtSimpleRenderingMetadata {
163
168
  text_color?: string
164
169
  }
165
170
 
171
+ /**
172
+ * Represents metadata for an image (logo, background image, etc.).
173
+ */
174
+ export interface SdJwtImageMetadata {
175
+ /**
176
+ * REQUIRED. URI pointing to the image.
177
+ */
178
+ uri: string
179
+
180
+ /**
181
+ * OPTIONAL. Integrity metadata string for the 'uri' field.
182
+ */
183
+ ['uri#integrity']?: string
184
+
185
+ /**
186
+ * OPTIONAL. Alternative text for the image.
187
+ */
188
+ alt_text?: string
189
+ }
190
+
166
191
  /**
167
192
  * Represents metadata for a logo.
168
193
  */
package/src/utils/mdoc.ts CHANGED
@@ -79,36 +79,76 @@ export function decodeMdocDeviceResponse(vpToken: MdocOid4vpMdocVpToken): MdocDe
79
79
  return deviceResponse
80
80
  }
81
81
 
82
+ function bytesToImageDataUri(value: unknown, mimeType = 'image/jpeg'): string {
83
+ if (typeof value === 'string') {
84
+ if (value.startsWith('data:image/')) return value
85
+ // Convert base64url to standard base64 if needed
86
+ let b64 = value.replace(/-/g, '+').replace(/_/g, '/')
87
+ const pad = b64.length % 4
88
+ if (pad) b64 += '='.repeat(4 - pad)
89
+ return `data:${mimeType};base64,${b64}`
90
+ }
91
+ // Int8Array or Uint8Array from CBOR byte string decoder
92
+ if (value && typeof value === 'object' && ('length' in value || Symbol.iterator in Object(value))) {
93
+ try {
94
+ const int8 = value instanceof Int8Array ? value : new Int8Array(value as ArrayLike<number>)
95
+ const base64 = com.sphereon.kmp.encodeTo(int8, com.sphereon.kmp.Encoding.BASE64)
96
+ return `data:${mimeType};base64,${base64}`
97
+ } catch {
98
+ // Fallback: try manual base64 encoding for Uint8Array
99
+ const bytes = value instanceof Uint8Array ? value : new Uint8Array(value as ArrayLike<number>)
100
+ let binary = ''
101
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i])
102
+ const base64 = typeof btoa === 'function' ? btoa(binary) : com.sphereon.kmp.encodeTo(new Int8Array(bytes), com.sphereon.kmp.Encoding.BASE64)
103
+ return `data:${mimeType};base64,${base64}`
104
+ }
105
+ }
106
+ return String(value)
107
+ }
108
+
82
109
  // 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
83
110
  export const mdocDecodedCredentialToUniformCredential = (
84
111
  decoded: MdocDocument,
85
112
  // @ts-ignore
86
- opts?: { maxTimeSkewInMS?: number },
113
+ opts?: { maxTimeSkewInMS?: number; issuer?: string },
87
114
  ): IVerifiableCredential => {
88
115
  const document = decoded.toJson()
89
116
  const json = document.toJsonDTO<DocumentJson>()
90
- const type = 'Personal Identification Data'
91
117
  const MSO = document.MSO
92
118
  if (!MSO || !json.issuerSigned?.nameSpaces) {
93
119
  throw Error(`Cannot access Mobile Security Object or Issuer Signed items from the Mdoc`)
94
120
  }
95
121
  const nameSpaces = json.issuerSigned.nameSpaces as unknown as Record<string, IssuerSignedItemJson[]>
96
- if (!('eu.europa.ec.eudi.pid.1' in nameSpaces)) {
97
- throw Error(`Only PID supported at present`)
122
+ const nameSpaceKeys = Object.keys(nameSpaces)
123
+ if (nameSpaceKeys.length === 0) {
124
+ throw Error(`No namespaces found in issuer signed items`)
125
+ }
126
+ // Collect items from all namespaces
127
+ const items: IssuerSignedItemJson[] = []
128
+ for (const ns of nameSpaceKeys) {
129
+ items.push(...(nameSpaces[ns] || []))
98
130
  }
99
- const items = nameSpaces['eu.europa.ec.eudi.pid.1']
100
- if (!items || items.length === 0) {
131
+ if (items.length === 0) {
101
132
  throw Error(`No issuer signed items were found`)
102
133
  }
103
134
  type DisclosuresAccumulator = {
104
135
  [key: string]: any
105
136
  }
106
137
 
138
+ // Known image claims in ISO 18013-5
139
+ const IMAGE_CLAIMS = new Set(['portrait', 'signature_usual_mark'])
140
+
107
141
  const credentialSubject = items.reduce((acc: DisclosuresAccumulator, item: IssuerSignedItemJson) => {
108
142
  if (Array.isArray(item.value)) {
109
143
  acc[item.key] = item.value.map((val) => val.value).join(', ')
110
144
  } else {
111
- acc[item.key] = item.value.value
145
+ const value = item.value.value
146
+ if (IMAGE_CLAIMS.has(item.key) && value != null) {
147
+ // Convert byte string to data URI for image rendering
148
+ acc[item.key] = bytesToImageDataUri(value)
149
+ } else {
150
+ acc[item.key] = value
151
+ }
112
152
  }
113
153
  return acc
114
154
  }, {})
@@ -123,11 +163,55 @@ export const mdocDecodedCredentialToUniformCredential = (
123
163
  throw Error(`JWT issuance date is required but was not present`)
124
164
  }
125
165
 
126
- const credential: Omit<IVerifiableCredential, 'issuer' | 'issuanceDate'> = {
166
+ // Determine issuer: x5c CN > issuing_authority claim > opts.issuer fallback > docType
167
+ let issuer: string = opts?.issuer ?? docType
168
+ // Try issuing_authority from credential claims (present in mDL and some other doctypes)
169
+ if (credentialSubject.issuing_authority) {
170
+ issuer = credentialSubject.issuing_authority
171
+ }
172
+ // Try to extract CN from x5chain certificate in issuerAuth (most authoritative for mdoc)
173
+ try {
174
+ const x5chain = json.issuerSigned?.issuerAuth?.unprotectedHeader?.x5chain ?? json.issuerSigned?.issuerAuth?.protectedHeader?.x5chain
175
+ if (x5chain && x5chain.length > 0) {
176
+ // x5chain[0] is base64-encoded DER certificate. Extract CN by searching for OID 2.5.4.3 (55 04 03)
177
+ const b64 = x5chain[0]
178
+ let bytes: Uint8Array
179
+ if (typeof atob === 'function') {
180
+ const binaryStr = atob(b64)
181
+ bytes = new Uint8Array(binaryStr.length)
182
+ for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i)
183
+ } else {
184
+ const int8 = com.sphereon.kmp.decodeFrom(b64, com.sphereon.kmp.Encoding.BASE64)
185
+ bytes = new Uint8Array(int8)
186
+ }
187
+ // Find last CN OID (55 04 03) — last occurrence is the subject CN
188
+ let lastCn: string | undefined
189
+ for (let i = 0; i < bytes.length - 5; i++) {
190
+ if (bytes[i] === 0x55 && bytes[i + 1] === 0x04 && bytes[i + 2] === 0x03) {
191
+ const tag = bytes[i + 3] // 0x0c=UTF8String, 0x13=PrintableString
192
+ if (tag === 0x0c || tag === 0x13) {
193
+ const len = bytes[i + 4]
194
+ if (i + 5 + len <= bytes.length) {
195
+ const cn = String.fromCharCode(...bytes.slice(i + 5, i + 5 + len))
196
+ lastCn = cn
197
+ }
198
+ }
199
+ }
200
+ }
201
+ if (lastCn) {
202
+ issuer = lastCn
203
+ }
204
+ }
205
+ } catch {
206
+ // Certificate parsing failed, keep previous issuer value
207
+ }
208
+
209
+ const credential: Omit<IVerifiableCredential, 'issuanceDate'> = {
127
210
  type: [docType], // Mdoc not a W3C VC, so no VerifiableCredential
128
211
  '@context': [], // Mdoc has no JSON-LD by default. Certainly not the VC DM1 default context for JSON-LD
212
+ issuer,
129
213
  credentialSubject: {
130
- type,
214
+ type: docType,
131
215
  ...credentialSubject,
132
216
  },
133
217
  issuanceDate,