Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: openid4vp-mdoc #2080

Merged
merged 6 commits into from
Nov 1, 2024

Conversation

auer-martin
Copy link
Contributor

No description provided.

Copy link

changeset-bot bot commented Nov 1, 2024

🦋 Changeset detected

Latest commit: 2bcedab

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@credo-ts/core Patch
@credo-ts/openid4vc Patch
@credo-ts/action-menu Patch
@credo-ts/anoncreds Patch
@credo-ts/askar Patch
@credo-ts/bbs-signatures Patch
@credo-ts/cheqd Patch
@credo-ts/drpc Patch
@credo-ts/indy-sdk-to-askar-migration Patch
@credo-ts/indy-vdr Patch
@credo-ts/node Patch
@credo-ts/question-answer Patch
@credo-ts/react-native Patch
@credo-ts/tenants Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@@ -35,16 +35,17 @@
"@peculiar/asn1-schema": "^2.3.8",
"@peculiar/asn1-x509": "^2.3.8",
"@peculiar/x509": "^1.11.0",
"@protokoll/mdoc-client": "0.2.35",
"@protokoll/mdoc-client": "0.2.36",
"@sd-jwt/core": "^0.7.0",
"@sd-jwt/decode": "^0.7.0",
"@sd-jwt/jwt-status-list": "^0.7.0",
"@sd-jwt/sd-jwt-vc": "^0.7.0",
"@sd-jwt/types": "^0.7.0",
"@sd-jwt/utils": "^0.7.0",
"@sphereon/pex": "5.0.0-unstable.18",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you update this to unstable 5.0.0-unstable.25

"@sd-jwt/core": "^0.7.0",
"@sd-jwt/decode": "^0.7.0",
"@sd-jwt/jwt-status-list": "^0.7.0",
"@sd-jwt/sd-jwt-vc": "^0.7.0",
"@sd-jwt/types": "^0.7.0",
"@sd-jwt/utils": "^0.7.0",
"@sphereon/pex": "5.0.0-unstable.18",
"@sphereon/kmp-mdl-mdoc": "0.2.0-SNAPSHOT.22",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this right?

"@sphereon/pex-models": "^2.3.1",
"@sphereon/ssi-types": "^0.30.1",
"@sphereon/ssi-types": "0.30.2-next.129",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this one to 0.30.2-next.135 (for latest mdoc features)

Comment on lines +46 to +49
nonMdocPresentationDefinition.input_descriptors.length === 0 &&
mdocPresentationDefinition.input_descriptors.length > 0
? Status.INFO
: selectResultsRaw.areRequiredCredentialsPresent,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't understand this? mdoc also handle mdoc selecting now right? And what if the mdoc is not available, it should'nt be info the nright?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we set it to error below when we also check if all the mdoc credentials are available.

Comment on lines 38 to 40
return com.sphereon.mdoc.data.device.DeviceResponseCbor.Static.cborDecode(
new Int8Array(TypedArrayEncoder.fromBase64(verifiablePresentation.base64Url))
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

original can just be a string for PEX / ssi-types now. That way we do'nt have to depend on / import the sphereon mdl lib

Comment on lines 33 to 41
try {
const parsed = parseDeviceResponse(TypedArrayEncoder.fromBase64(base64Url))
if (parsed.status === MDocStatus.OK) return true
return false
} catch (error) {
// no-op
}

return false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
try {
const parsed = parseDeviceResponse(TypedArrayEncoder.fromBase64(base64Url))
if (parsed.status === MDocStatus.OK) return true
return false
} catch (error) {
// no-op
}
return false
try {
const parsed = parseDeviceResponse(TypedArrayEncoder.fromBase64(base64Url))
return parsed.status === MDocStatus.OK
} catch (error) {
return false
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also seems quite expensive. That we try to parse. But maybe the best way. Are we doing in places:

if (isBase64DeviceReponse(data)) {
   parseBase64DeviceResponse()
}

Because in that case a tryParseBase64DeviceResponse that returns null on error might be better?


public static fromBase64Url(base64Url: string) {
const parsed = parseDeviceResponse(TypedArrayEncoder.fromBase64(base64Url))
if (parsed.status !== MDocStatus.OK) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no extra error provided in parsed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, not really atm

const issuerSigned = cborEncode(prepared.get('issuerSigned'))
const deviceSigned = cborEncode(prepared.get('deviceSigned'))

return Mdoc.fromIssuerSignedDocument(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fromIssuerSigned and then pasing deviceSigned is a bit weird maybe?

@@ -113,7 +148,18 @@ export class MdocDeviceResponse {

const inputDescriptor = this.assertMdocInputDescriptor(options.inputDescriptor)
const _mdoc = parseIssuerSigned(TypedArrayEncoder.fromBase64(mdoc.base64Url), mdoc.docType)
return mdocLimitDisclosureToId({ mdoc: _mdoc, inputDescriptor })

const disclosure = mdocLimitDisclosureToId(_mdoc, inputDescriptor)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Id is maybe a bit offputting as inputDescriptor ( i thought which ID?). So mabye just mdocLimitDisclosureToInputDescriptor?

Comment on lines 213 to 215
const x509ModuleConfig = agentContext.dependencyManager.isRegistered(X509ModuleConfig)
? agentContext.dependencyManager.resolve(X509ModuleConfig)
: undefined
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should always be configured, are you getting issues when you just reslve it directly?

"@sphereon/oid4vci-issuer": "0.16.1-next.168",
"@sphereon/ssi-types": "^0.30.1",
"@sphereon/did-auth-siop": "0.16.1-fix.173",
"@sphereon/kmp-mdl-mdoc": "0.2.0-SNAPSHOT.22",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, would be good to not depend on this.

"@sphereon/oid4vci-client": "0.16.1-fix.173",
"@sphereon/oid4vci-common": "0.16.1-fix.173",
"@sphereon/oid4vci-issuer": "0.16.1-fix.173",
"@sphereon/ssi-types": "0.30.2-next.129",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also update this one

iss: 'did:key:z6MkrzQPBr4pyqC776KKtrz13SchM5ePPbssuPuQZb5t4uKQ',
vct: 'OpenBadgeCredential2',
degree: 'bachelor2',
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should spit this up at some point 😅 but ok for now

@@ -78,9 +76,12 @@ export function getVerifiablePresentationFromSphereonWrapped(
} satisfies SdJwtVc
} else if (wrappedVerifiablePresentation.format === 'mso_mdoc') {
if (typeof wrappedVerifiablePresentation.original !== 'string') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think the original will ALWAYS be string if we also change the other code to always provide a string from the start. I think it's ok as we don't need to import the library here though. Just want to be careful in depending on two mdl librs. Ideally we jus tprovide string and get back string from PEX. For the rest it should be a black box (so we're also not prone to changes to their library)

@@ -608,9 +624,28 @@ export class OpenId4VcSiopVerifierService {

let isValid: boolean

// TODO: it might be better here to look at the presentation submission to know
// If presentation includes a ~, we assume it's an SD-JWT-VC
if (typeof encodedPresentation === 'string' && encodedPresentation.includes('~')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's keep sd-jwt first, as it's cheaper to check for inclusion of ~ than to parse the mdoc.

Also this is what i meant in other comment with check if isBase64DeviceResponse and then MdocDeviceResponse.fromBase64Url. Might as well save the result from the first call and then using that to not do the whole cbor decoding twice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the last one is JWT -- i think that one is also cheaoer than to do the cbor encoding. So maybe we could also do there if is string and then check for JWT regex to take priority over the mdoc parsing (we do that as the try )

if (typeof encodedPresentation === 'string' && encodedPresentation.includes('~')) {
if (typeof encodedPresentation === 'string' && MdocDeviceResponse.isBase64DeviceResponse(encodedPresentation)) {
if (!options.responseUri || !options.mdocGeneratedNonce) {
isValid = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add a reason to the return value. as this is very hard to debug that you didn't provide these values: https://github.com/Sphereon-Opensource/OID4VC/blob/3b516bef69484cf7af370ea0213e486f4f701d28/packages/siop-oid4vp/lib/authorization-response/types.ts#L94

@@ -553,9 +563,9 @@ export class OpenId4VcSiopVerifierService {
// to fix that in Sphereon lib
client_id: clientId,
passBy: PassBy.VALUE,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is also included in the final metadata. I think we can jsut remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that does not work. Many failing tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm okay, this should be fixed in sphereon's lib then.

@@ -229,6 +232,7 @@ export class OpenId4VcSiopVerifierService {
const requestClientId = await authorizationRequest.getMergedProperty<string>('client_id')
const requestNonce = await authorizationRequest.getMergedProperty<string>('nonce')
const requestState = await authorizationRequest.getMergedProperty<string>('state')
const responseUri = await authorizationRequest.getMergedProperty<string>('response_uri')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in other code you tried both response_uri and redirect_uri is it ok to jsut take the response uri here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mdoc requires using response_uri. So I think we should be fine

Copy link
Contributor

@TimoGlastra TimoGlastra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff, nice!!

Comment on lines +650 to +662
const mdocDeviceResponse = MdocDeviceResponse.fromBase64Url(encodedPresentation)
await mdocDeviceResponse.verify(agentContext, {
sessionTranscriptOptions: {
clientId: options.audience,
mdocGeneratedNonce: options.mdocGeneratedNonce,
responseUri: options.responseUri,
verifierGeneratedNonce: options.nonce,
},
verificationContext: {
openId4VcVerificationSessionId: options.correlationId,
},
})
isValid = true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, does thi throw if it fails, or should we set the isValid based on the verify result?

@auer-martin auer-martin marked this pull request as ready for review November 1, 2024 17:36
@TimoGlastra TimoGlastra merged commit 595c3d6 into openwallet-foundation:main Nov 1, 2024
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants