Skip to content

Commit

Permalink
feat: support multi-document mdoc device response (#269)
Browse files Browse the repository at this point in the history
  • Loading branch information
TimoGlastra authored Jan 28, 2025
1 parent 723c9d4 commit 7fcf887
Show file tree
Hide file tree
Showing 4 changed files with 328 additions and 373 deletions.
66 changes: 14 additions & 52 deletions apps/easypid/src/features/proximity/mdocProximity.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
import { mdocDataTransfer } from '@animo-id/expo-mdoc-data-transfer'
import {
COSEKey,
DataItem,
DeviceRequest,
DeviceResponse,
MDoc,
type MdocContext,
cborDecode,
cborEncode,
parseIssuerSigned,
} from '@animo-id/mdoc'
import { TypedArrayEncoder } from '@credo-ts/core'
import { getMdocContext } from '@credo-ts/core/build/modules/mdoc/MdocContext'
import { DataItem, DeviceRequest, cborDecode, cborEncode } from '@animo-id/mdoc'
import { Mdoc, MdocService, TypedArrayEncoder } from '@credo-ts/core'
import type { EasyPIDAppAgent, FormattedSubmission, MdocRecord } from '@package/agent'
import { handleBatchCredential } from '@package/agent/src/batch'
import { type Permission, PermissionsAndroid, Platform } from 'react-native'
Expand Down Expand Up @@ -61,7 +50,6 @@ export const getMdocQrCode = async () => {
* Wait for the device request
*
* Returns the device request and session transcript
*
*/
export const waitForDeviceRequest = async () => {
const mdt = mdocDataTransfer.instance()
Expand All @@ -75,25 +63,16 @@ export const waitForDeviceRequest = async () => {
}

/**
*
* Naive way to share the device response based on the device request
*
* Send a device response based on the device request
* Optimalisations:
*
* 1. pre-filter the `agent.mdoc.getAll()` based on the `deviceRequest`
* 2. Allow the user to pick which specific mdoc is being used
*
* 1. Allow the user to pick which specific mdoc is being used
*/
export const shareDeviceResponse = async (options: ShareDeviceResponseOptions) => {
if (!options.submission.areAllSatisfied) {
throw new Error('Not all requirements are satisfied')
}

if (options.submission.entries.length > 1) {
throw new Error('Only one mdoc supported at the moment due to only being able to sign with one device key')
}

const issuerSignedDocuments = await Promise.all(
const mdocs = await Promise.all(
options.submission.entries.map(async (e) => {
if (!e.isSatisfied) throw new Error(`Requirement for doctype ${e.inputDescriptorId} not satisfied`)

Expand All @@ -102,38 +81,21 @@ export const shareDeviceResponse = async (options: ShareDeviceResponseOptions) =
// Optionally handle batch issuance
const credentialRecord = await handleBatchCredential(options.agent, credential)

return parseIssuerSigned(TypedArrayEncoder.fromBase64(credentialRecord.base64Url), credential.getTags().docType)
return Mdoc.fromBase64Url(credentialRecord.base64Url, credential.getTags().docType)
})
)

const mdoc = new MDoc(issuerSignedDocuments)
const mdocService = options.agent.dependencyManager.resolve(MdocService)

const mdocContext = getMdocContext(options.agent.context) as unknown as {
cose: MdocContext['cose']
crypto: MdocContext['crypto']
}
// TODO: return type in credo should be Uint8Array
const { deviceResponseBase64Url } = await mdocService.createDeviceResponse(options.agent.context, {
deviceRequest: DeviceRequest.parse(options.deviceRequest),
mdocs: mdocs as [Mdoc, ...Mdoc[]],
sessionTranscriptBytes: options.sessionTranscript,
})

const mdt = mdocDataTransfer.instance()

if (mdoc.documents.length > 1) {
throw new Error('Only one mdoc supported at the moment due to only being able to sign with one device key')
}
const mso = mdoc.documents[0].issuerSigned.issuerAuth.decodedPayload
const deviceKeyInfo = mso.deviceKeyInfo
if (!deviceKeyInfo?.deviceKey) {
throw new Error('Device key info is missing')
}

const publicDeviceJwk = COSEKey.import(deviceKeyInfo.deviceKey).toJWK()
const deviceRequest = DeviceRequest.parse(options.deviceRequest)

const deviceResponse = await DeviceResponse.from(mdoc)
.usingSessionTranscriptBytes(options.sessionTranscript)
.usingDeviceRequest(deviceRequest)
.authenticateWithSignature(publicDeviceJwk, 'ES256')
.sign(mdocContext)

await mdt.sendDeviceResponse(deviceResponse.encode())
await mdt.sendDeviceResponse(TypedArrayEncoder.fromBase64(deviceResponseBase64Url))
}

export const shutdownDataTransfer = () => {
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"dcql": "catalog:",
"@credo-ts/anoncreds": "catalog:",
"@credo-ts/askar": "catalog:",
"@credo-ts/node": "catalog:",
"@credo-ts/cheqd": "catalog:",
"@credo-ts/core": "catalog:",
"@credo-ts/openid4vc": "catalog:",
Expand All @@ -44,7 +43,7 @@
"@openid-federation/core": "catalog:"
},
"patchedDependencies": {
"@credo-ts/askar@0.5.13": "patches/@credo-ts__askar@0.5.13.patch",
"@animo-id/credo-ts-askar@0.5.14-alpha-20250128050818": "patches/@credo-ts__askar@0.5.13.patch",
"@sphereon/kmp-mdl-mdoc": "patches/@sphereon__kmp-mdl-mdoc.patch",
"@hyperledger/aries-askar-react-native@0.2.3": "patches/@hyperledger__aries-askar-react-native@0.2.3.patch",
"@hyperledger/anoncreds-react-native@0.2.4": "patches/@hyperledger__anoncreds-react-native@0.2.4.patch"
Expand Down
Loading

0 comments on commit 7fcf887

Please sign in to comment.