Skip to content

Commit 0e45e52

Browse files
fix: allow verifier to pass in session transcript bytes (#103)
Signed-off-by: Berend Sliedrecht <[email protected]>
1 parent fecf6ad commit 0e45e52

File tree

6 files changed

+95
-20
lines changed

6 files changed

+95
-20
lines changed

src/mdoc/models/device-auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export class DeviceAuth extends CborStructure {
6565
document: Document
6666
verificationCallback?: VerificationCallback
6767
ephemeralMacPrivateKey?: CoseKey
68-
sessionTranscript: SessionTranscript
68+
sessionTranscript: SessionTranscript | Uint8Array
6969
},
7070
ctx: Pick<MdocContext, 'crypto' | 'cose'>
7171
) {

src/mdoc/models/device-authentication.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,23 @@ export type DeviceAuthenticationOptions = {
1717
}
1818

1919
export class DeviceAuthentication extends CborStructure {
20-
public sessionTranscript: SessionTranscript
20+
public sessionTranscript: SessionTranscript | Uint8Array
2121
public docType: DocType
2222
public deviceNamespaces: DeviceNamespaces
2323

2424
public constructor(options: DeviceAuthenticationOptions) {
2525
super()
26-
this.sessionTranscript =
27-
options.sessionTranscript instanceof SessionTranscript
28-
? options.sessionTranscript
29-
: SessionTranscript.decode(options.sessionTranscript)
26+
this.sessionTranscript = options.sessionTranscript
3027
this.docType = options.docType
3128
this.deviceNamespaces = options.deviceNamespaces
3229
}
3330

3431
public encodedStructure(): DeviceAuthenticationStructure {
3532
return [
3633
'DeviceAuthentication',
37-
this.sessionTranscript.encodedStructure(),
34+
this.sessionTranscript instanceof SessionTranscript
35+
? this.sessionTranscript.encodedStructure()
36+
: cborDecode(this.sessionTranscript),
3837
this.docType,
3938
DataItem.fromData(this.deviceNamespaces.encodedStructure()),
4039
]

src/mdoc/models/device-mac.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { type CborDecodeOptions, cborDecode } from '../../cbor'
22
import type { MdocContext } from '../../context'
33
import type { CoseKey } from '../../cose'
44
import { Mac0, type Mac0Structure } from '../../cose/mac0'
5-
import type { SessionTranscript } from './session-transcript'
5+
import { SessionTranscript } from './session-transcript'
66

77
export type DeviceMacStructure = Mac0Structure
88

@@ -12,14 +12,17 @@ export class DeviceMac extends Mac0 {
1212
publicKey: CoseKey
1313
privateKey: CoseKey
1414
info?: 'EMacKey' | 'SKReader' | 'SKDevice'
15-
sessionTranscript: SessionTranscript
15+
sessionTranscript: SessionTranscript | Uint8Array
1616
},
1717
ctx: Pick<MdocContext, 'crypto' | 'cose'>
1818
) {
1919
const key = await ctx.crypto.calculateEphemeralMacKey({
2020
privateKey: options.privateKey.privateKey,
2121
publicKey: options.publicKey.publicKey,
22-
sessionTranscriptBytes: options.sessionTranscript.encode({ asDataItem: true }),
22+
sessionTranscriptBytes:
23+
options.sessionTranscript instanceof SessionTranscript
24+
? options.sessionTranscript.encode({ asDataItem: true })
25+
: options.sessionTranscript,
2326
info: options.info ?? 'EMacKey',
2427
})
2528

src/mdoc/models/device-response.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { DeviceSigned } from './device-signed'
1717
import { Document, type DocumentStructure } from './document'
1818
import { DocumentError, type DocumentErrorStructure } from './document-error'
1919
import { IssuerSigned } from './issuer-signed'
20-
import { SessionTranscript } from './session-transcript'
20+
import type { SessionTranscript } from './session-transcript'
2121

2222
export type DeviceResponseStructure = {
2323
version: string
@@ -128,10 +128,7 @@ export class DeviceResponse extends CborStructure {
128128
{
129129
document,
130130
ephemeralMacPrivateKey: options.ephemeralReaderKey,
131-
sessionTranscript:
132-
options.sessionTranscript instanceof SessionTranscript
133-
? options.sessionTranscript
134-
: SessionTranscript.decode(options.sessionTranscript),
131+
sessionTranscript: options.sessionTranscript,
135132
verificationCallback: onCheck,
136133
},
137134
ctx

src/verifier.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@ import type { VerificationCallback } from './mdoc/check-callback.js'
44
import { type DeviceRequest, DeviceResponse, type SessionTranscript } from './mdoc/index.js'
55

66
export class Verifier {
7-
/**
8-
*
9-
* @todo check whether the device response is compatible with the device request / dcql request
10-
*
11-
*/
127
public static async verifyDeviceResponse(
138
options: {
149
deviceRequest?: DeviceRequest

tests/verification/verify.test.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { X509Certificate } from '@peculiar/x509'
22
import { expect, suite, test } from 'vitest'
33
import {
44
CoseKey,
5+
cborEncode,
56
DeviceRequest,
67
DeviceResponse,
78
DocRequest,
@@ -201,6 +202,86 @@ suite('Verification', () => {
201202
).resolves.toBeUndefined()
202203
})
203204

205+
test('Verify with custom session transcript', async () => {
206+
const issuer = new Issuer('org.iso.18013.5.1', mdocContext)
207+
208+
issuer.addIssuerNamespace('org.iso.18013.5.1.mDL', {
209+
first_name: 'First',
210+
last_name: 'Last',
211+
})
212+
213+
const issuerSigned = await issuer.sign({
214+
signingKey: CoseKey.fromJwk(ISSUER_PRIVATE_KEY_JWK),
215+
certificate: new Uint8Array(new X509Certificate(ISSUER_CERTIFICATE).rawData),
216+
algorithm: SignatureAlgorithm.ES256,
217+
digestAlgorithm: 'SHA-256',
218+
deviceKeyInfo: { deviceKey: CoseKey.fromJwk(DEVICE_JWK) },
219+
validityInfo: { signed, validFrom, validUntil },
220+
})
221+
222+
const encodedIssuerSigned = issuerSigned.encodedForOid4Vci
223+
224+
// openid4vci protocol
225+
226+
const credential = IssuerSigned.fromEncodedForOid4Vci(encodedIssuerSigned)
227+
228+
await expect(
229+
Holder.verifyIssuerSigned(
230+
{
231+
issuerSigned: credential,
232+
trustedCertificates: [new Uint8Array(new X509Certificate(ISSUER_CERTIFICATE).rawData)],
233+
},
234+
mdocContext
235+
)
236+
).resolves.toBeUndefined()
237+
238+
const deviceRequest = new DeviceRequest({
239+
docRequests: [
240+
new DocRequest({
241+
itemsRequest: new ItemsRequest({
242+
docType: 'org.iso.18013.5.1',
243+
namespaces: {
244+
'org.iso.18013.5.1.mDL': {
245+
first_name: true,
246+
last_name: true,
247+
},
248+
},
249+
}),
250+
}),
251+
],
252+
})
253+
254+
const fakeSessionTranscript = cborEncode([1, 2, 3])
255+
256+
const deviceResponse = await Holder.createDeviceResponseForDeviceRequest(
257+
{
258+
deviceRequest,
259+
issuerSigned: [credential],
260+
sessionTranscript: fakeSessionTranscript,
261+
signature: { signingKey: CoseKey.fromJwk(DEVICE_JWK) },
262+
},
263+
mdocContext
264+
)
265+
266+
const encodedDeviceResponse = deviceResponse.encodedForOid4Vp
267+
268+
// openid4vp protocol
269+
270+
const decodedDeviceResponse = DeviceResponse.fromEncodedForOid4Vp(encodedDeviceResponse)
271+
272+
await expect(
273+
Verifier.verifyDeviceResponse(
274+
{
275+
deviceRequest,
276+
deviceResponse: decodedDeviceResponse,
277+
sessionTranscript: fakeSessionTranscript,
278+
trustedCertificates: [new Uint8Array(new X509Certificate(ISSUER_CERTIFICATE).rawData)],
279+
},
280+
mdocContext
281+
)
282+
).resolves.toBeUndefined()
283+
})
284+
204285
test('Fail to create mdoc with not enough attributes', async () => {
205286
const issuer = new Issuer('org.iso.18013.5.1', mdocContext)
206287

0 commit comments

Comments
 (0)