From 5161e0f570ead5747c07befaad8bcaeada181698 Mon Sep 17 00:00:00 2001 From: Kayode Ezike Date: Tue, 23 Apr 2024 18:42:00 -0400 Subject: [PATCH] modifies id generation logic for status credentials --- README.md | 35 +++++++++++++++++---------- src/credential-status-manager-base.ts | 19 ++++++++++++--- src/helpers.ts | 6 ++--- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 9afaa42..736a5a1 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ const credential = { }; const credentialWithStatus = await statusManager.allocateStatus({ credential, - statusPurposes: ['revocation'] + statusPurposes: ['revocation', 'suspension'] }); console.log(credentialWithStatus); /* @@ -134,18 +134,27 @@ console.log(credentialWithStatus); issuer: 'did:key:z6MkhVTX9BF3NGYX6cc7jWpbNnR7cAjH8LUffabZP8Qu4ysC', validFrom: '2020-03-10T04:24:12.164Z', credentialSubject: { id: 'did:example:abcdef' }, - credentialStatus: { - id: 'https://credentials.example.edu/status/V27UAUYPNR#1', - type: 'BitstringStatusListEntry', - statusPurpose: 'revocation', - statusListIndex: '1', - statusListCredential: 'https://credentials.example.edu/status/V27UAUYPNR' - } + credentialStatus: [ + { + id: 'https://credentials.example.edu/status/Uz42qSDSXTcoLH7kZ6ST#1', + type: 'BitstringStatusListEntry', + statusPurpose: 'revocation', + statusListIndex: '1', + statusListCredential: 'https://credentials.example.edu/status/Uz42qSDSXTcoLH7kZ6ST' + }, + { + id: 'https://credentials.example.edu/status/9kGimd8POqM88l32F9aT#1', + type: 'BitstringStatusListEntry', + statusPurpose: 'suspension', + statusListIndex: '1', + statusListCredential: 'https://credentials.example.edu/status/9kGimd8POqM88l32F9aT' + } + ] } */ ``` -**Note:** You can also call `allocateRevocationStatus(credential)` to achieve the same effect as `allocateStatus({ credential, statusPurposes: ['revocation'] })` and `allocateSuspensionStatus(credential)` to achieve the same effect as `allocateStatus({ credential, statusPurposes: ['suspension'] })`. +**Note:** You can also call `allocateRevocationStatus(credential)` to achieve the same effect as `allocateStatus({ credential, statusPurposes: ['revocation'] })`, `allocateSuspensionStatus(credential)` to achieve the same effect as `allocateStatus({ credential, statusPurposes: ['suspension'] })`, and `allocateSupportedStatuses(credential)` to achieve the same effect as `allocateStatus({ credential, statusPurposes: ['revocation', 'suspension'] })`. Additionally, if the caller invokes `allocateStatus` multiple times with the same credential ID against the same instance of a credential status manager, the library will not allocate a new entry. It will just return a credential with the same status info as it did in the previous invocation. @@ -167,10 +176,10 @@ console.log(statusCredential); '@context': [ 'https://www.w3.org/ns/credentials/v2' ], - id: 'https://credentials.example.edu/status/V27UAUYPNR', + id: 'https://credentials.example.edu/status/Uz42qSDSXTcoLH7kZ6ST', type: [ 'VerifiableCredential', 'BitstringStatusListCredential' ], credentialSubject: { - id: 'https://credentials.example.edu/status/V27UAUYPNR#list', + id: 'https://credentials.example.edu/status/Uz42qSDSXTcoLH7kZ6ST#list', type: 'BitstringStatusList', encodedList: 'H4sIAAAAAAAAA-3BMQ0AAAACIGf_0LbwAhoAAAAAAAAAAAAAAIC_AfqBUGnUMAAA', statusPurpose: 'revocation' @@ -195,12 +204,12 @@ console.log(credentialStatus); /* { revocation: { - statusCredentialId: 'V27UAUYPNR', + statusCredentialId: 'Uz42qSDSXTcoLH7kZ6ST', statusListIndex: 1, valid: true }, suspension: { - statusCredentialId: '4R7EA3YPTR', + statusCredentialId: '9kGimd8POqM88l32F9aT', statusListIndex: 1, valid: false } diff --git a/src/credential-status-manager-base.ts b/src/credential-status-manager-base.ts index cadb4cf..abc00ce 100644 --- a/src/credential-status-manager-base.ts +++ b/src/credential-status-manager-base.ts @@ -14,7 +14,7 @@ import { } from './errors.js'; import { DidMethod, - MAX_ID_LENGTH, + MAX_CREDENTIAL_ID_LENGTH, getCredentialSubjectObject, getDateString, getSigningMaterial, @@ -26,6 +26,12 @@ import { // Number of credentials tracked in a list const CREDENTIAL_STATUS_LIST_SIZE = 100000; +// Length of status credential ID +const STATUS_CREDENTIAL_ID_LENGTH = 20; + +// Character set of status credential ID +const STATUS_CREDENTIAL_ID_CHAR_SET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + // Status credential type const STATUS_CREDENTIAL_TYPE = 'BitstringStatusListCredential'; @@ -328,7 +334,12 @@ export abstract class BaseCredentialStatusManager { * @returns {string} Returns new status credential ID. */ generateStatusCredentialId(): string { - return Math.random().toString(36).substring(2, 12).toUpperCase(); + let statusCredentialId = ''; + const charSetLength = STATUS_CREDENTIAL_ID_CHAR_SET.length; + for (let i = 0; i < STATUS_CREDENTIAL_ID_LENGTH; i++) { + statusCredentialId += STATUS_CREDENTIAL_ID_CHAR_SET.charAt(Math.floor(Math.random() * charSetLength)); + } + return statusCredentialId; } /** @@ -392,8 +403,8 @@ export abstract class BaseCredentialStatusManager { } else { if (!isValidCredentialId(credentialCopy.id)) { throw new BadRequestError({ - message: 'The ID must be a URL, UUID, or DID ' + - `that is no more than ${MAX_ID_LENGTH} characters in length.` + message: 'The credential ID must be a URL, UUID, or DID ' + + `that is no more than ${MAX_CREDENTIAL_ID_LENGTH} characters in length.` }); } } diff --git a/src/helpers.ts b/src/helpers.ts index 69926b7..4e43e7c 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -29,8 +29,8 @@ const didKeyDriver = DidKey.driver(); // Document loader const documentLoader = securityLoader().build(); -// Max length for IDs -export const MAX_ID_LENGTH = 64; +// Max length for credential IDs +export const MAX_CREDENTIAL_ID_LENGTH = 64; // DID method used to sign credentials export enum DidMethod { @@ -199,7 +199,7 @@ function extractId(objectOrString: any): string { // determines if credential ID is valid export function isValidCredentialId(credentialId: string): boolean { const isValidFormat = URL.canParse(credentialId) || uuid.validate(credentialId); - const isValidLength = credentialId.length <= MAX_ID_LENGTH; + const isValidLength = credentialId.length <= MAX_CREDENTIAL_ID_LENGTH; return isValidFormat && isValidLength; }