-
Notifications
You must be signed in to change notification settings - Fork 200
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: support w3c revocation #2024
base: main
Are you sure you want to change the base?
Changes from all commits
787cce6
cc1975a
67bc551
b946e5b
a3e0165
7975139
95d558c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import type { AgentContext } from '../../../agent/context' | ||
import type { W3cJsonCredential } from '../models/credential/W3cJsonCredential' | ||
import type { BitStringStatusListCredential } from '../models/credential/W3cJsonCredentialStatus' | ||
|
||
import * as pako from 'pako' | ||
|
||
import { CredoError } from '../../../error' | ||
import { validateStatus } from '../models/credential/W3cCredentialStatus' | ||
|
||
// Function to fetch and parse the bit string status list credential | ||
const fetchBitStringStatusListCredential = async ( | ||
agentContext: AgentContext, | ||
url: string | ||
): Promise<BitStringStatusListCredential> => { | ||
const response = await agentContext.config.agentDependencies.fetch(url, { method: 'GET' }) | ||
|
||
if (!response.ok) { | ||
throw new CredoError(`Failed to fetch bit string status list. HTTP Status: ${response.status}`) | ||
} | ||
|
||
try { | ||
return (await response.json()) as BitStringStatusListCredential | ||
} catch (error) { | ||
throw new CredoError('Failed to parse the bit string status list credential') | ||
} | ||
} | ||
|
||
export const verifyBitStringCredentialStatus = async (credential: W3cJsonCredential, agentContext: AgentContext) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't validate the signature of the status list credential yet |
||
const { credentialStatus } = credential | ||
|
||
if (Array.isArray(credentialStatus)) { | ||
throw new CredoError('Verifying credential status as an array for JSON-LD credentials is currently not supported') | ||
} | ||
|
||
if (!credentialStatus || credentialStatus.statusListIndex === undefined) { | ||
throw new CredoError('Invalid credential status format') | ||
} | ||
|
||
// Validate credentialStatus using the class-based approach | ||
const isValid = await validateStatus(credentialStatus, agentContext) | ||
|
||
if (!isValid) { | ||
throw new CredoError('Invalid credential status type. Expected BitstringStatusList') | ||
} | ||
|
||
// Fetch the bit string status list credential | ||
const bitStringStatusListCredential = await fetchBitStringStatusListCredential( | ||
agentContext, | ||
credentialStatus.statusListCredential | ||
) | ||
|
||
// Decode the encoded bit string | ||
const encodedBitString = bitStringStatusListCredential.credential.credentialSubject.encodedList | ||
const compressedBuffer = Uint8Array.from(atob(encodedBitString), (char) => char.charCodeAt(0)) | ||
|
||
// Decompress the bit string using pako | ||
const decodedBitString = pako.ungzip(compressedBuffer, { to: 'string' }) | ||
const statusListIndex = Number(credentialStatus.statusListIndex) | ||
|
||
// Ensure the statusListIndex is within bounds | ||
if (statusListIndex < 0 || statusListIndex >= decodedBitString.length) { | ||
throw new CredoError('Status list index is out of bounds') | ||
} | ||
|
||
// Check if the credential is revoked | ||
if (decodedBitString[statusListIndex] === '1') { | ||
throw new CredoError(`Credential at index ${credentialStatus.statusListIndex} is revoked.`) | ||
} | ||
|
||
return true | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' | |
import { w3cDate } from '../util' | ||
|
||
import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' | ||
import { verifyBitStringCredentialStatus } from './VerifyBitStringCredentialStatus' | ||
import { deriveProof } from './deriveProof' | ||
import { assertOnlyW3cJsonLdVerifiableCredentials } from './jsonldUtil' | ||
import jsonld from './libraries/jsonld' | ||
|
@@ -109,10 +110,9 @@ export class W3cJsonLdCredentialService { | |
credential: JsonTransformer.toJSON(options.credential), | ||
suite: suites, | ||
documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), | ||
checkStatus: ({ credential }: { credential: W3cJsonCredential }) => { | ||
// Only throw error if credentialStatus is present | ||
checkStatus: async ({ credential }: { credential: W3cJsonCredential }) => { | ||
if (verifyCredentialStatus && 'credentialStatus' in credential) { | ||
throw new CredoError('Verifying credential status for JSON-LD credentials is currently not supported') | ||
await verifyBitStringCredentialStatus(credential, agentContext) | ||
} | ||
return { | ||
verified: true, | ||
|
@@ -265,6 +265,14 @@ export class W3cJsonLdCredentialService { | |
challenge: options.challenge, | ||
domain: options.domain, | ||
documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), | ||
checkStatus: async ({ credential }: { credential: W3cJsonCredential }) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you extract this into a method and reuse that for both checkStatus methods? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @TimoGlastra I have developed a common function for this purpose. However, in the checkStatus method, there are specific conditions related to credential status that are dependent on the payload received from the verifyCredential and verifyPresentation functions. Therefore, the checkStatus method invokes the common function, as it contains the identical logic. |
||
if ('credentialStatus' in credential) { | ||
await verifyBitStringCredentialStatus(credential, agentContext) | ||
} | ||
Comment on lines
+269
to
+271
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. verifyPresentation also has options.verifyCredentialStatus, can you add that check here as well? |
||
return { | ||
verified: true, | ||
} | ||
}, | ||
} | ||
|
||
// this is a hack because vcjs throws if purpose is passed as undefined or null | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a lot of dupcliation in this file with the already present methods. Some suggestions:
sendRevocationNotification
method (which we can extend to support non-anoncreds revocadion notification messages as well)