Skip to content

Commit

Permalink
fix: some nits
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Auer <[email protected]>
  • Loading branch information
auer-martin committed Oct 8, 2024
1 parent 8728d37 commit 95eb0db
Show file tree
Hide file tree
Showing 16 changed files with 165 additions and 129 deletions.
6 changes: 3 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
"@peculiar/asn1-schema": "^2.3.8",
"@peculiar/asn1-x509": "^2.3.8",
"@peculiar/x509": "^1.11.0",
"@protokoll/core": "0.2.26",
"@protokoll/crypto": "0.2.26",
"@protokoll/mdoc-client": "0.2.26",
"@protokoll/core": "0.2.27",
"@protokoll/crypto": "0.2.27",
"@protokoll/mdoc-client": "0.2.27",
"@sd-jwt/core": "^0.7.0",
"@sd-jwt/decode": "^0.7.0",
"@sd-jwt/jwt-status-list": "^0.7.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import type {
W3CVerifiablePresentation,
} from '@sphereon/ssi-types'

import { PEVersion, PEX, Status } from '@sphereon/pex'
import { PEVersion, PEX, PresentationSubmissionLocation, Status } from '@sphereon/pex'
import { PartialSdJwtDecodedVerifiableCredential } from '@sphereon/pex/dist/main/lib'
import { injectable } from 'tsyringe'

Expand Down Expand Up @@ -197,7 +197,7 @@ export class DifPresentationExchangeService {

const { deviceResponseBase64Url, presentationSubmission } = await MdocDeviceResponse.openId4Vp(agentContext, {
mdocs: [Mdoc.fromBase64Url(mdocRecord.base64Url)],
presentationDefinition: presentationDefinition as DifPresentationExchangeDefinitionV2,
presentationDefinition: presentationDefinition,
sessionTranscriptOptions: {
...openid4vp,
},
Expand All @@ -211,81 +211,79 @@ export class DifPresentationExchangeService {
},
claimFormat: presentationToCreate.claimFormat,
})
} else {
// Get all the credentials for the presentation
const credentialsForPresentation = presentationToCreate.verifiableCredentials.map((c) =>
getSphereonOriginalVerifiableCredential(c.credential)
)

continue
}

// Get all the credentials for the presentation
const credentialsForPresentation = presentationToCreate.verifiableCredentials.map((c) =>
getSphereonOriginalVerifiableCredential(c.credential)
)

const verifiablePresentationResult = await this.pex.verifiablePresentationFrom(
presentationDefinitionForSubject,
credentialsForPresentation,
this.getPresentationSignCallback(agentContext, presentationToCreate),
{
proofOptions: {
challenge,
domain,
},
signatureOptions: {},
presentationSubmissionLocation:
presentationSubmissionLocation ?? DifPresentationExchangeSubmissionLocation.PRESENTATION,
}
)
const verifiablePresentationResult = await this.pex.verifiablePresentationFrom(
presentationDefinitionForSubject,
credentialsForPresentation,
this.getPresentationSignCallback(agentContext, presentationToCreate),
{
proofOptions: {
challenge,
domain,
},
signatureOptions: {},
presentationSubmissionLocation:
presentationSubmissionLocation ?? DifPresentationExchangeSubmissionLocation.PRESENTATION,
}
)

verifiablePresentationResultsWithFormat.push({
verifiablePresentationResult,
claimFormat: presentationToCreate.claimFormat,
})
}
verifiablePresentationResultsWithFormat.push({
verifiablePresentationResult,
claimFormat: presentationToCreate.claimFormat,
})
}

if (verifiablePresentationResultsWithFormat.length === 0) {
throw new DifPresentationExchangeError('No verifiable presentations created')
}
if (verifiablePresentationResultsWithFormat.length === 0) {
throw new DifPresentationExchangeError('No verifiable presentations created')
}

if (presentationsToCreate.length !== verifiablePresentationResultsWithFormat.length) {
throw new DifPresentationExchangeError('Invalid amount of verifiable presentations created')
}
if (presentationsToCreate.length !== verifiablePresentationResultsWithFormat.length) {
throw new DifPresentationExchangeError('Invalid amount of verifiable presentations created')
}

const presentationSubmission: DifPresentationExchangeSubmission = {
id: verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmission.id,
definition_id:
verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmission.definition_id,
descriptor_map: [],
}
const presentationSubmission: DifPresentationExchangeSubmission = {
id: verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmission.id,
definition_id:
verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmission.definition_id,
descriptor_map: [],
}

verifiablePresentationResultsWithFormat.forEach(({ verifiablePresentationResult }, index) => {
const descriptorMap = verifiablePresentationResult.presentationSubmission.descriptor_map.map((d) => {
const descriptor = { ...d }

// when multiple presentations are submitted, path should be $[0], $[1]
// FIXME: this should be addressed in the PEX/OID4VP lib.
// See https://github.com/Sphereon-Opensource/SIOP-OID4VP/issues/62
if (
presentationSubmissionLocation === DifPresentationExchangeSubmissionLocation.EXTERNAL &&
verifiablePresentationResultsWithFormat.length > 1
) {
descriptor.path = `$[${index}]`
}
verifiablePresentationResultsWithFormat.forEach(({ verifiablePresentationResult }, index) => {
const descriptorMap = verifiablePresentationResult.presentationSubmission.descriptor_map.map((d) => {
const descriptor = { ...d }

// when multiple presentations are submitted, path should be $[0], $[1]
// FIXME: this should be addressed in the PEX/OID4VP lib.
// See https://github.com/Sphereon-Opensource/SIOP-OID4VP/issues/62
if (
presentationSubmissionLocation === DifPresentationExchangeSubmissionLocation.EXTERNAL &&
verifiablePresentationResultsWithFormat.length > 1
) {
descriptor.path = `$[${index}]`
}

return descriptor
})

return descriptor
presentationSubmission.descriptor_map.push(...descriptorMap)
})

presentationSubmission.descriptor_map.push(...descriptorMap)
})

return {
verifiablePresentations: verifiablePresentationResultsWithFormat.map((resultWithFormat) =>
getVerifiablePresentationFromEncoded(
agentContext,
resultWithFormat.verifiablePresentationResult.verifiablePresentation
)
),
presentationSubmission,
presentationSubmissionLocation:
verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmissionLocation,
return {
verifiablePresentations: verifiablePresentationResultsWithFormat.map((resultWithFormat) =>
getVerifiablePresentationFromEncoded(
agentContext,
resultWithFormat.verifiablePresentationResult.verifiablePresentation
)
),
presentationSubmission,
presentationSubmissionLocation:
verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmissionLocation,
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export async function getCredentialsForRequest(
credentialRecords: Array<W3cCredentialRecord | SdJwtVcRecord | MdocRecord>
): Promise<DifPexCredentialsForRequest> {
const encodedCredentials = credentialRecords
.filter((c) => c instanceof MdocRecord === false)
.map((c) => getSphereonOriginalVerifiableCredential(c as SdJwtVcRecord | W3cCredentialRecord))
.filter((c): c is Exclude<typeof c, MdocRecord> => c instanceof MdocRecord === false)
.map((c) => getSphereonOriginalVerifiableCredential(c))

const { mdocPresentationDefinition, nonMdocPresentationDefinition } =
MdocDeviceResponse.partitionPresentationDefinition(presentationDefinition)
Expand All @@ -41,12 +41,6 @@ export async function getCredentialsForRequest(

const selectResults: CredentialRecordSelectResults = {
...selectResultsRaw,
areRequiredCredentialsPresent:
mdocPresentationDefinition.input_descriptors.length >= 1
? 'warn' // we don't know yet wheater the required credentials are present
: nonMdocPresentationDefinition.input_descriptors.length >= 1
? selectResultsRaw.areRequiredCredentialsPresent
: 'error',
// Map the encoded credential to their respective w3c credential record
verifiableCredential: selectResultsRaw.verifiableCredential?.map((selectedEncoded): SubmissionEntryCredential => {
const credentialRecordIndex = encodedCredentials.findIndex((encoded) => {
Expand Down Expand Up @@ -126,7 +120,11 @@ export async function getCredentialsForRequest(
)
}

if (submissionRequirementMatch.vc_path.length >= 1) selectResults.matches.push(submissionRequirementMatch)
if (submissionRequirementMatch.vc_path.length >= 1) {
selectResults.matches.push(submissionRequirementMatch)
} else {
selectResultsRaw.areRequiredCredentialsPresent = 'error'
}
}

const presentationSubmission: DifPexCredentialsForRequest = {
Expand Down
38 changes: 30 additions & 8 deletions packages/core/src/modules/mdoc/Mdoc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { MdocCreateOptions, MdocNameSpaces, MdocVerifyOptions } from './MdocOptions'
import type { MdocSignOptions, MdocNameSpaces, MdocVerifyOptions } from './MdocOptions'
import type { AgentContext } from '../../agent'
import type { IssuerSignedDocument } from '@protokoll/mdoc-client'

Expand Down Expand Up @@ -62,7 +62,7 @@ export class Mdoc {
return this.issuerSignedDocument.allIssuerSignedNamespaces
}

public static async create(agentContext: AgentContext, options: MdocCreateOptions) {
public static async sign(agentContext: AgentContext, options: MdocSignOptions) {
const { docType, validityInfo, namespaces, holderPublicKey, issuerCertificate } = options
const mdocContext = getMdocContext(agentContext)

Expand All @@ -77,11 +77,30 @@ export class Mdoc {
}

const cert = X509Certificate.fromEncodedCertificate(issuerCertificate)
const issuerPrivateJwk = await getJwkFromKey(options.issuerKey ?? cert.publicKey)
const issuerKey = await getJwkFromKey(options.issuerKey ?? cert.publicKey)

const alg = issuerKey.supportedSignatureAlgorithms.find(
(alg): alg is JwaSignatureAlgorithm.ES256 | JwaSignatureAlgorithm.ES384 | JwaSignatureAlgorithm.ES512 => {
return (
alg === JwaSignatureAlgorithm.ES256 ||
alg === JwaSignatureAlgorithm.ES384 ||
alg === JwaSignatureAlgorithm.ES512
)
}
)

if (!alg) {
throw new MdocError(
`Cannot find a suitable JwaSignatureAlgorithm for signing the mdoc. Supported algorithms are 'ES256', 'ES384', 'ES512'. The issuer key supports: ${issuerKey.supportedSignatureAlgorithms.join(
', '
)}`
)
}

const issuerSignedDocument = await document.sign(
{
issuerPrivateKey: issuerPrivateJwk.toJson(),
alg: issuerPrivateJwk.supportedSignatureAlgorithms[0] as 'ES256' | 'ES384' | 'ES512' | 'EdDSA',
issuerPrivateKey: issuerKey.toJson(),
alg,
issuerCertificate,
kid: cert.publicKey.fingerprint,
},
Expand All @@ -91,7 +110,10 @@ export class Mdoc {
return new Mdoc(issuerSignedDocument)
}

public async verify(agentContext: AgentContext, options?: MdocVerifyOptions): Promise<boolean> {
public async verify(
agentContext: AgentContext,
options?: MdocVerifyOptions
): Promise<{ isValid: true } | { isValid: false; error: string }> {
const trustedCerts =
options?.trustedCertificates ?? agentContext.dependencyManager.resolve(X509ModuleConfig).trustedCertificates

Expand All @@ -113,9 +135,9 @@ export class Mdoc {
)

await verifier.verifyData({ mdoc: this.issuerSignedDocument }, mdocContext)
return true
return { isValid: true }
} catch (error) {
return false
return { isValid: false, error: error.message }
}
}
}
6 changes: 3 additions & 3 deletions packages/core/src/modules/mdoc/MdocApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { MdocCreateOptions, MdocVerifyOptions } from './MdocOptions'
import type { MdocSignOptions, MdocVerifyOptions } from './MdocOptions'
import type { MdocRecord } from './repository'
import type { Query, QueryOptions } from '../../storage/StorageService'

Expand All @@ -24,10 +24,10 @@ export class MdocApi {
/**
* Create a new Mdoc, with a spcific doctype, namespace, and validity info.
*
* @param options {MdocCreateOptions}
* @param options {MdocSignOptions}
* @returns {Promise<Mdoc>}
*/
public async create(options: MdocCreateOptions) {
public async create(options: MdocSignOptions) {
return await this.mdocService.createMdoc(this.agentContext, options)
}

Expand Down
24 changes: 19 additions & 5 deletions packages/core/src/modules/mdoc/MdocContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { p256 } from '@noble/curves/p256'
import { hkdf } from '@noble/hashes/hkdf'
import { sha256 } from '@noble/hashes/sha2'
import * as x509 from '@peculiar/x509'
import { exportJwk, importX509, verifyWithJwk } from '@protokoll/crypto'
import { exportJwk, importX509 } from '@protokoll/crypto'

import { CredoWebCrypto, getJwkFromJson, Hasher } from '../../crypto'
import { Buffer, TypedArrayEncoder } from '../../utils'
Expand Down Expand Up @@ -54,8 +54,13 @@ export const getMdocContext = (agentContext: AgentContext): MdocContext => {
},
verify: async (input) => {
const { mac0, jwk, options } = input
const { data, signature, alg } = mac0.getRawVerificationData(options)
return await verifyWithJwk({ jwk, signature, data, alg })
const { data, signature } = mac0.getRawVerificationData(options)

return await agentContext.wallet.verify({
key: getJwkFromJson(jwk as JwkJson).key,
data: Buffer.from(data),
signature: new Buffer(signature),
})
},
},
sign1: {
Expand All @@ -69,8 +74,12 @@ export const getMdocContext = (agentContext: AgentContext): MdocContext => {
},
verify: async (input) => {
const { sign1, jwk, options } = input
const { data, signature, alg } = sign1.getRawVerificationData(options)
return await verifyWithJwk({ jwk, signature, data, alg, crypto })
const { data, signature } = sign1.getRawVerificationData(options)
return await agentContext.wallet.verify({
key: getJwkFromJson(jwk as JwkJson).key,
data: Buffer.from(data),
signature: new Buffer(signature),
})
},
},
},
Expand All @@ -82,6 +91,11 @@ export const getMdocContext = (agentContext: AgentContext): MdocContext => {
return x509Certificate.getIssuerNameField(field)
},
getPublicKey: async (input) => {
//const comp = X509Certificate.fromRawCertificate(input.certificate)
//const x = getJwkFromKey(comp.publicKey).toJson()
//////// eslint-disable-next-line @typescript-eslint/no-unused-vars
//return x

const certificate = new x509.X509Certificate(input.certificate)
const key = await importX509({
x509: certificate.toString(),
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/modules/mdoc/MdocDeviceResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {

import { getJwkFromKey } from '../../crypto/jose/jwk/transform'
import { CredoError } from '../../error'
import { uuid } from '../../utils/uuid'
import { X509Certificate } from '../x509/X509Certificate'
import { X509ModuleConfig } from '../x509/X509ModuleConfig'

Expand Down Expand Up @@ -136,7 +137,6 @@ export class MdocDeviceResponse {

const publicDeviceJwk = COSEKey.import(deviceKeyInfo.deviceKey).toJWK()

deviceKeyInfo.deviceKey
const deviceResponseBuilder = await DeviceResponse.from(mdoc)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.usingPresentationDefinition(presentationDefinition as any)
Expand All @@ -152,7 +152,7 @@ export class MdocDeviceResponse {
return {
deviceResponseBase64Url: TypedArrayEncoder.toBase64URL(deviceResponseMdoc.encode()),
presentationSubmission: MdocDeviceResponse.createPresentationSubmission({
id: 'MdocPresentationSubmission ' + agentContext.wallet.generateNonce(),
id: 'MdocPresentationSubmission ' + uuid(),
presentationDefinition,
}),
}
Expand Down
Loading

0 comments on commit 95eb0db

Please sign in to comment.