Skip to content

Commit

Permalink
chore(x509): added test to create self-signed certificate with Curve …
Browse files Browse the repository at this point in the history
…Ed25519

Signed-off-by: Berend Sliedrecht <[email protected]>
  • Loading branch information
berendsliedrecht committed Sep 12, 2024
1 parent 2110e4a commit 31a791f
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 4 deletions.
100 changes: 100 additions & 0 deletions credo.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts
index 99461b99..8789d22e 100644
--- a/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts
+++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts
@@ -160,13 +160,24 @@ export class OpenId4VcHolderApi {
* @param options.tokenResponse Obtained through @see requestAccessToken
*/
public async requestCredentials(options: OpenId4VciRequestCredentialOptions) {
- const { resolvedCredentialOffer, cNonce, accessToken, ...credentialRequestOptions } = options
+ const {
+ resolvedCredentialOffer,
+ cNonce,
+ accessToken,
+ additionalProofOfPossessionPayloadClaims,
+ additionalCredentialRequestPayloadClaims,
+ customFormat,
+ ...credentialRequestOptions
+ } = options

return this.openId4VciHolderService.acceptCredentialOffer(this.agentContext, {
resolvedCredentialOffer,
acceptCredentialOfferOptions: credentialRequestOptions,
+ customFormat,
accessToken,
cNonce,
+ additionalProofOfPossessionPayloadClaims,
+ additionalCredentialRequestPayloadClaims,
})
}

diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts
index 748a2847..7161cb61 100644
--- a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts
+++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts
@@ -312,9 +312,18 @@ export class OpenId4VciHolderService {
accessToken?: string
cNonce?: string
clientId?: string
+ additionalProofOfPossessionPayloadClaims?: Record<string, unknown>
+ additionalCredentialRequestPayloadClaims?: Record<string, unknown>
+ customFormat?: string
}
) {
- const { resolvedCredentialOffer, acceptCredentialOfferOptions } = options
+ const {
+ resolvedCredentialOffer,
+ acceptCredentialOfferOptions,
+ additionalProofOfPossessionPayloadClaims,
+ customFormat,
+ additionalCredentialRequestPayloadClaims,
+ } = options
const { metadata, version, offeredCredentials } = resolvedCredentialOffer

const { credentialsToRequest, credentialBindingResolver, verifyCredentialStatus } = acceptCredentialOfferOptions
@@ -394,6 +403,11 @@ export class OpenId4VciHolderService {
.withEndpointMetadata(metadata)
.withAlg(signatureAlgorithm)

+ if (additionalProofOfPossessionPayloadClaims) {
+ // @ts-expect-error: header can be empty is it won't be used and we only care about the payload claims
+ proofOfPossessionBuilder.withJwt({ header: {}, payload: additionalProofOfPossessionPayloadClaims })
+ }
+
// TODO: what if auth flow using did, and the did is different from client id. We now use the client_id
if (credentialBinding.method === 'did') {
proofOfPossessionBuilder.withClientId(parseDid(credentialBinding.didUrl).did).withKid(credentialBinding.didUrl)
@@ -424,11 +438,17 @@ export class OpenId4VciHolderService {
.withCredentialEndpoint(metadata.credential_endpoint)
.withToken(tokenResponse.access_token)

+ if (customFormat) {
+ credentialRequestBuilder.withFormat(customFormat)
+ }
+
const credentialRequestClient = credentialRequestBuilder.build()
const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({
proofInput: proofOfPossession,
credentialTypes: getTypesFromCredentialSupported(offeredCredentialConfiguration),
- format: offeredCredentialConfiguration.format,
+ format: customFormat ? undefined : offeredCredentialConfiguration.format,
+ // @ts-expect-error: custom patched file
+ additionalRequestClaims: additionalCredentialRequestPayloadClaims,
})

newCNonce = credentialResponse.successBody?.c_nonce
diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts
index 8b15fe97..9af2ac99 100644
--- a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts
+++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts
@@ -117,6 +117,10 @@ export interface OpenId4VciCredentialRequestOptions extends Omit<OpenId4VciAccep
* The client id used for authorization. Only required if authorization_code flow was used.
*/
clientId?: string
+
+ additionalProofOfPossessionPayloadClaims?: Record<string, unknown>
+ additionalCredentialRequestPayloadClaims?: Record<string, unknown>
+ customFormat?: string
}

/**
19 changes: 18 additions & 1 deletion packages/core/src/modules/x509/__tests__/X509Service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,24 @@ describe('X509Service', () => {
})
})

it('should generate a self-signed X509 Certificate', async () => {
it('should generate a self-signed X509 Certificate with Ed25519', async () => {
const key = await agentContext.wallet.createKey({ keyType: KeyType.Ed25519 })

const selfSignedCertificate = await X509Service.createSelfSignedCertificate(agentContext, {
key,
})

expect(selfSignedCertificate.publicKey.publicKeyBase58).toStrictEqual(key.publicKeyBase58)
expect(selfSignedCertificate.sanDnsNames).toStrictEqual([])
expect(selfSignedCertificate.sanUriNames).toStrictEqual([])

const pemCertificate = selfSignedCertificate.toString('pem')

expect(pemCertificate.startsWith('-----BEGIN CERTIFICATE-----\n')).toBeTruthy()
expect(pemCertificate.endsWith('\n-----END CERTIFICATE-----')).toBeTruthy()
})

it('should generate a self-signed X509 Certificate with P256', async () => {
const key = await agentContext.wallet.createKey({ keyType: KeyType.P256 })

const selfSignedCertificate = await X509Service.createSelfSignedCertificate(agentContext, {
Expand Down
13 changes: 12 additions & 1 deletion packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,24 @@ export class OpenId4VcHolderApi {
* @param options.tokenResponse Obtained through @see requestAccessToken
*/
public async requestCredentials(options: OpenId4VciRequestCredentialOptions) {
const { resolvedCredentialOffer, cNonce, accessToken, ...credentialRequestOptions } = options
const {
resolvedCredentialOffer,
cNonce,
accessToken,
additionalProofOfPossessionPayloadClaims,
additionalCredentialRequestPayloadClaims,
customFormat,
...credentialRequestOptions
} = options

return this.openId4VciHolderService.acceptCredentialOffer(this.agentContext, {
resolvedCredentialOffer,
acceptCredentialOfferOptions: credentialRequestOptions,
customFormat,
accessToken,
cNonce,
additionalProofOfPossessionPayloadClaims,
additionalCredentialRequestPayloadClaims,
})
}

Expand Down
24 changes: 22 additions & 2 deletions packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,18 @@ export class OpenId4VciHolderService {
accessToken?: string
cNonce?: string
clientId?: string
additionalProofOfPossessionPayloadClaims?: Record<string, unknown>
additionalCredentialRequestPayloadClaims?: Record<string, unknown>
customFormat?: string
}
) {
const { resolvedCredentialOffer, acceptCredentialOfferOptions } = options
const {
resolvedCredentialOffer,
acceptCredentialOfferOptions,
additionalProofOfPossessionPayloadClaims,
customFormat,
additionalCredentialRequestPayloadClaims,
} = options
const { metadata, version, offeredCredentials } = resolvedCredentialOffer

const { credentialsToRequest, credentialBindingResolver, verifyCredentialStatus } = acceptCredentialOfferOptions
Expand Down Expand Up @@ -394,6 +403,11 @@ export class OpenId4VciHolderService {
.withEndpointMetadata(metadata)
.withAlg(signatureAlgorithm)

if (additionalProofOfPossessionPayloadClaims) {
// @ts-expect-error: header can be empty is it won't be used and we only care about the payload claims
proofOfPossessionBuilder.withJwt({ header: {}, payload: additionalProofOfPossessionPayloadClaims })
}

// TODO: what if auth flow using did, and the did is different from client id. We now use the client_id
if (credentialBinding.method === 'did') {
proofOfPossessionBuilder.withClientId(parseDid(credentialBinding.didUrl).did).withKid(credentialBinding.didUrl)
Expand Down Expand Up @@ -424,11 +438,17 @@ export class OpenId4VciHolderService {
.withCredentialEndpoint(metadata.credential_endpoint)
.withToken(tokenResponse.access_token)

if (customFormat) {
credentialRequestBuilder.withFormat(customFormat)
}

const credentialRequestClient = credentialRequestBuilder.build()
const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({
proofInput: proofOfPossession,
credentialTypes: getTypesFromCredentialSupported(offeredCredentialConfiguration),
format: offeredCredentialConfiguration.format,
format: customFormat ? undefined : offeredCredentialConfiguration.format,
// @ts-expect-error: custom patched file
additionalRequestClaims: additionalCredentialRequestPayloadClaims,
})

newCNonce = credentialResponse.successBody?.c_nonce
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ export interface OpenId4VciCredentialRequestOptions extends Omit<OpenId4VciAccep
* The client id used for authorization. Only required if authorization_code flow was used.
*/
clientId?: string

additionalProofOfPossessionPayloadClaims?: Record<string, unknown>
additionalCredentialRequestPayloadClaims?: Record<string, unknown>
customFormat?: string
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ export class OpenId4VcSiopVerifierService {
state,
requestByReferenceURI: hostedAuthorizationRequestUri,
jwtIssuer,
responseURI: joinUriParts(this.config.baseUrl, [
options.verifier.verifierId,
this.config.authorizationEndpoint.endpointPath,
]),
responseURIType: 'response_uri',
})

// NOTE: it's not possible to set the uri scheme when using the RP to create an auth request, only lower level
Expand Down

0 comments on commit 31a791f

Please sign in to comment.