From 2d0b423b90546c8d28735b600328c8a5937b57c8 Mon Sep 17 00:00:00 2001 From: Tim Shamilov Date: Tue, 20 Aug 2024 11:56:48 -0400 Subject: [PATCH] cleanup crypto utils (#838) * cleanup crypto utils (#830) * cleanup crypto utils * changeset * Update index.ts * finish: delete package.json utils export * bump dwn server * fix api bump * Update utils.ts * bump dwn server * maybe fix codecov --- .changeset/friendly-carrots-return.md | 12 ++ .github/workflows/tests-ci.yml | 2 +- codecov.yml | 3 + package.json | 2 +- packages/agent/src/dwn-api.ts | 4 +- packages/agent/src/dwn-registrar.ts | 6 +- .../clients/http-dwn-rpc-client.ts | 4 +- .../prototyping/clients/json-rpc-socket.ts | 6 +- .../prototyping/clients/web-socket-clients.ts | 8 +- .../prototyping/crypto/jose/jwe-flattened.ts | 4 +- packages/agent/src/rpc-client.ts | 4 +- packages/agent/tests/crypto-api.spec.ts | 4 +- .../agent/tests/local-key-manager.spec.ts | 4 +- .../clients/json-rpc-socket.spec.ts | 30 ++--- packages/agent/tests/rpc-client.spec.ts | 6 +- .../agent/tests/sync-engine-level.spec.ts | 8 +- .../api/tests/utils/test-data-generator.ts | 4 +- .../credentials/src/verifiable-credential.ts | 4 +- .../src/verifiable-presentation.ts | 4 +- packages/crypto/README.md | 75 ++++++----- packages/crypto/package.json | 7 +- packages/crypto/src/index.ts | 1 - packages/crypto/src/utils.ts | 116 +---------------- .../crypto/tests/algorithms/aes-ctr.spec.ts | 6 +- .../crypto/tests/algorithms/aes-gcm.spec.ts | 4 +- packages/crypto/tests/utils.spec.ts | 117 ++++-------------- packages/dids/src/bearer-did.ts | 4 +- pnpm-lock.yaml | 20 ++- 28 files changed, 157 insertions(+), 312 deletions(-) create mode 100644 .changeset/friendly-carrots-return.md diff --git a/.changeset/friendly-carrots-return.md b/.changeset/friendly-carrots-return.md new file mode 100644 index 000000000..708482b2d --- /dev/null +++ b/.changeset/friendly-carrots-return.md @@ -0,0 +1,12 @@ +--- +"@web5/crypto-aws-kms": patch +"@web5/identity-agent": patch +"@web5/credentials": patch +"@web5/proxy-agent": patch +"@web5/user-agent": patch +"@web5/crypto": patch +"@web5/agent": patch +"@web5/dids": patch +--- + +cleanup crypto utils diff --git a/.github/workflows/tests-ci.yml b/.github/workflows/tests-ci.yml index d416744dd..208117b60 100644 --- a/.github/workflows/tests-ci.yml +++ b/.github/workflows/tests-ci.yml @@ -72,7 +72,7 @@ jobs: run: kill $DWN_SERVER_BACKGROUND_PROCESS || true - name: Upload test coverage to Codecov - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 #v4.5.0 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/codecov.yml b/codecov.yml index 7f0d6841b..7d08c46c3 100644 --- a/codecov.yml +++ b/codecov.yml @@ -7,8 +7,11 @@ component_management: - type: project target: auto # auto compares coverage to the previous base commit threshold: 5% # allows a 5% drop from the previous base commit coverage + informational: true - type: patch target: 90 + informational: true + if_ci_failed: success individual_components: - component_id: package-agent diff --git a/package.json b/package.json index bcdf387ea..92a4b3bff 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@changesets/cli": "^2.27.5", "@npmcli/package-json": "5.0.0", "@typescript-eslint/eslint-plugin": "7.9.0", - "@web5/dwn-server": "0.4.6", + "@web5/dwn-server": "0.4.7", "audit-ci": "^7.0.1", "eslint-plugin-mocha": "10.4.3", "globals": "^13.24.0", diff --git a/packages/agent/src/dwn-api.ts b/packages/agent/src/dwn-api.ts index fb0614373..ca6414b67 100644 --- a/packages/agent/src/dwn-api.ts +++ b/packages/agent/src/dwn-api.ts @@ -14,7 +14,7 @@ import { } from '@tbd54566975/dwn-sdk-js'; import { NodeStream } from '@web5/common'; -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; import { DidDht, DidJwk, DidResolverCacheLevel, UniversalResolver } from '@web5/dids'; import type { Web5PlatformAgent } from './types/agent.js'; @@ -396,7 +396,7 @@ export class AgentDwnApi { const keyManager = this.agent.keyManager; return { - algorithm : cryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey), + algorithm : CryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey), keyId : signingMethod.id, sign : async (data: Uint8Array) => { return await keyManager.sign({ data, keyUri: keyUri! }); diff --git a/packages/agent/src/dwn-registrar.ts b/packages/agent/src/dwn-registrar.ts index d53a9a86b..a063c8743 100644 --- a/packages/agent/src/dwn-registrar.ts +++ b/packages/agent/src/dwn-registrar.ts @@ -1,4 +1,4 @@ -import { Sha256, utils } from '@web5/crypto'; +import { Sha256, CryptoUtils } from '@web5/crypto'; import { concatenateUrl } from './utils.js'; import { Convert } from '@web5/common'; @@ -120,8 +120,8 @@ export class DwnRegistrar { * Generates 32 random bytes expressed as a HEX string. */ public static async generateNonce(): Promise { - const randomBytes = utils.randomBytes(32); - const hexString = await Convert.uint8Array(randomBytes).toHex().toUpperCase(); + const randomBytes = CryptoUtils.randomBytes(32); + const hexString = Convert.uint8Array(randomBytes).toHex().toUpperCase(); return hexString; } } \ No newline at end of file diff --git a/packages/agent/src/prototyping/clients/http-dwn-rpc-client.ts b/packages/agent/src/prototyping/clients/http-dwn-rpc-client.ts index 29a39c265..887f7ead8 100644 --- a/packages/agent/src/prototyping/clients/http-dwn-rpc-client.ts +++ b/packages/agent/src/prototyping/clients/http-dwn-rpc-client.ts @@ -2,7 +2,7 @@ import type { JsonRpcResponse } from './json-rpc.js'; import type { DwnRpc, DwnRpcRequest, DwnRpcResponse } from './dwn-rpc-types.js'; import { createJsonRpcRequest, parseJson } from './json-rpc.js'; -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; import { DwnServerInfoCache, ServerInfo } from './server-info-types.js'; import { DwnServerInfoCacheMemory } from './dwn-server-info-cache-memory.js'; @@ -18,7 +18,7 @@ export class HttpDwnRpcClient implements DwnRpc { get transportProtocols() { return ['http:', 'https:']; } async sendDwnRequest(request: DwnRpcRequest): Promise { - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const jsonRpcRequest = createJsonRpcRequest(requestId, 'dwn.processMessage', { target : request.targetDid, message : request.message diff --git a/packages/agent/src/prototyping/clients/json-rpc-socket.ts b/packages/agent/src/prototyping/clients/json-rpc-socket.ts index 9486543d7..806ed9009 100644 --- a/packages/agent/src/prototyping/clients/json-rpc-socket.ts +++ b/packages/agent/src/prototyping/clients/json-rpc-socket.ts @@ -1,4 +1,4 @@ -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; import IsomorphicWebSocket from 'isomorphic-ws'; import { JsonRpcId, JsonRpcRequest, JsonRpcResponse, createJsonRpcSubscriptionRequest, parseJson } from './json-rpc.js'; @@ -81,7 +81,7 @@ export class JsonRpcSocket { */ async request(request: JsonRpcRequest): Promise { return new Promise((resolve, reject) => { - request.id ??= cryptoUtils.randomUuid(); + request.id ??= CryptoUtils.randomUuid(); const handleResponse = (event: { data: any }):void => { const jsonRpsResponse = parseJson(event.data) as JsonRpcResponse; @@ -155,7 +155,7 @@ export class JsonRpcSocket { } private closeSubscription(id: JsonRpcId): Promise { - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const request = createJsonRpcSubscriptionRequest(requestId, 'close', id, {}); return this.request(request); } diff --git a/packages/agent/src/prototyping/clients/web-socket-clients.ts b/packages/agent/src/prototyping/clients/web-socket-clients.ts index fbb5a2ce2..32b3e2003 100644 --- a/packages/agent/src/prototyping/clients/web-socket-clients.ts +++ b/packages/agent/src/prototyping/clients/web-socket-clients.ts @@ -1,7 +1,7 @@ import type { DwnRpc, DwnRpcRequest, DwnRpcResponse, DwnSubscriptionHandler } from './dwn-rpc-types.js'; import type { GenericMessage, MessageSubscription, UnionMessageReply } from '@tbd54566975/dwn-sdk-js'; -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; import { createJsonRpcRequest, createJsonRpcSubscriptionRequest } from './json-rpc.js'; import { JsonRpcSocket, JsonRpcSocketOptions } from './json-rpc-socket.js'; @@ -46,7 +46,7 @@ export class WebSocketDwnRpcClient implements DwnRpc { } private static async processMessage(connection: SocketConnection, target: string, message: GenericMessage): Promise { - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const request = createJsonRpcRequest(requestId, 'dwn.processMessage', { target, message }); const { socket } = connection; @@ -61,8 +61,8 @@ export class WebSocketDwnRpcClient implements DwnRpc { } private static async subscriptionRequest(connection: SocketConnection, target:string, message: GenericMessage, messageHandler: DwnSubscriptionHandler): Promise { - const requestId = cryptoUtils.randomUuid(); - const subscriptionId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); + const subscriptionId = CryptoUtils.randomUuid(); const request = createJsonRpcSubscriptionRequest(requestId, 'dwn.processMessage', subscriptionId, { target, message }); const { socket, subscriptions } = connection; diff --git a/packages/agent/src/prototyping/crypto/jose/jwe-flattened.ts b/packages/agent/src/prototyping/crypto/jose/jwe-flattened.ts index 09960dd09..af587fa3c 100644 --- a/packages/agent/src/prototyping/crypto/jose/jwe-flattened.ts +++ b/packages/agent/src/prototyping/crypto/jose/jwe-flattened.ts @@ -1,7 +1,7 @@ import type { Jwk, KeyIdentifier } from '@web5/crypto'; import { Convert } from '@web5/common'; -import { LocalKeyManager, utils as cryptoUtils } from '@web5/crypto'; +import { LocalKeyManager, CryptoUtils } from '@web5/crypto'; import type { CryptoApi } from '../types/crypto-api.js'; import type { KeyManager } from '../types/key-manager.js'; @@ -404,7 +404,7 @@ export class FlattenedJwe { case 'A128GCM': case 'A192GCM': case 'A256GCM': - iv = cryptoUtils.randomBytes(12); + iv = CryptoUtils.randomBytes(12); break; default: iv = new Uint8Array(0); diff --git a/packages/agent/src/rpc-client.ts b/packages/agent/src/rpc-client.ts index af4ea34f4..4bfa5b0b9 100644 --- a/packages/agent/src/rpc-client.ts +++ b/packages/agent/src/rpc-client.ts @@ -1,4 +1,4 @@ -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; import type { DwnRpc, DwnRpcRequest, DwnRpcResponse } from './prototyping/clients/dwn-rpc-types.js'; @@ -114,7 +114,7 @@ export class Web5RpcClient implements Web5Rpc { export class HttpWeb5RpcClient extends HttpDwnRpcClient implements Web5Rpc { async sendDidRequest(request: DidRpcRequest): Promise { - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const jsonRpcRequest = createJsonRpcRequest(requestId, request.method, { data: request.data }); diff --git a/packages/agent/tests/crypto-api.spec.ts b/packages/agent/tests/crypto-api.spec.ts index 27e755ab5..847ca041a 100644 --- a/packages/agent/tests/crypto-api.spec.ts +++ b/packages/agent/tests/crypto-api.spec.ts @@ -2,7 +2,7 @@ import type { Jwk } from '@web5/crypto'; import { expect } from 'chai'; import { Convert } from '@web5/common'; -import { utils as cryptoUtils, isOctPrivateJwk } from '@web5/crypto'; +import { CryptoUtils, isOctPrivateJwk } from '@web5/crypto'; import { isChrome } from './utils/runtimes.js'; import { AgentCryptoApi } from '../src/crypto-api.js'; @@ -379,7 +379,7 @@ describe('AgentCryptoApi', () => { kid : 'kpI8W6JS7O5ncakbn5dUOgP7uCuHGtZnkNOX2ZnRiss', }; const plaintext = new Uint8Array([1, 2, 3, 4]); - const iv = cryptoUtils.randomBytes(12); // Initialization vector. + const iv = CryptoUtils.randomBytes(12); // Initialization vector. const tagLength = 128; // Size in bits of the authentication tag. // Test the method. diff --git a/packages/agent/tests/local-key-manager.spec.ts b/packages/agent/tests/local-key-manager.spec.ts index 997b3d6a8..8e9b64163 100644 --- a/packages/agent/tests/local-key-manager.spec.ts +++ b/packages/agent/tests/local-key-manager.spec.ts @@ -3,7 +3,7 @@ import type { BearerDid } from '@web5/dids'; import { expect } from 'chai'; import { Convert } from '@web5/common'; -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; import type { Web5PlatformAgent } from '../src/types/agent.js'; @@ -89,7 +89,7 @@ describe('LocalKeyManager', () => { // Setup. const encryptionKeyUri = await testHarness.agent.keyManager.generateKey({ algorithm: 'A128GCM' }); const plaintext = new Uint8Array([1, 2, 3, 4]); - const iv = cryptoUtils.randomBytes(12); // Initialization vector. + const iv = CryptoUtils.randomBytes(12); // Initialization vector. const tagLength = 128; // Size in bits of the authentication tag. // Test the method. diff --git a/packages/agent/tests/prototyping/clients/json-rpc-socket.spec.ts b/packages/agent/tests/prototyping/clients/json-rpc-socket.spec.ts index f61f98168..b84f1d94f 100644 --- a/packages/agent/tests/prototyping/clients/json-rpc-socket.spec.ts +++ b/packages/agent/tests/prototyping/clients/json-rpc-socket.spec.ts @@ -3,7 +3,7 @@ import { expect } from 'chai'; import sinon from 'sinon'; import { JsonRpcSocket } from '../../../src/prototyping/clients/json-rpc-socket.js'; -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; import { JsonRpcErrorCodes, JsonRpcResponse, createJsonRpcErrorResponse, createJsonRpcRequest, createJsonRpcSubscriptionRequest, createJsonRpcSuccessResponse } from '../../../src/prototyping/clients/json-rpc.js'; import { testDwnUrl } from '../../utils/test-config.js'; import { Persona, TestDataGenerator } from '@tbd54566975/dwn-sdk-js'; @@ -37,7 +37,7 @@ describe('JsonRpcSocket', () => { it('generates a request id if one is not provided', async () => { const client = await JsonRpcSocket.connect(socketDwnUrl); - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const request = createJsonRpcRequest(requestId, 'dwn.processMessage', { param1: 'test-param1', param2: 'test-param2' }); delete request.id; @@ -47,7 +47,7 @@ describe('JsonRpcSocket', () => { it('resolves a request with given params', async () => { const client = await JsonRpcSocket.connect(socketDwnUrl); - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const request = createJsonRpcRequest(requestId, 'dwn.processMessage', { param1: 'test-param1', param2: 'test-param2' }); const response = await client.request(request); expect(response.id).to.equal(request.id); @@ -56,7 +56,7 @@ describe('JsonRpcSocket', () => { it('request times out', async () => { // time out after 1 ms const client = await JsonRpcSocket.connect(socketDwnUrl, { responseTimeout: 1 }); - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const request = createJsonRpcRequest(requestId, 'down.processMessage', { param1: 'test-param1', param2: 'test-param2' }); try { await client.request(request); @@ -69,7 +69,7 @@ describe('JsonRpcSocket', () => { it('adds a handler to the messageHandlers map when listening for a response to a request', async () => { const client = await JsonRpcSocket.connect(socketDwnUrl); const { message } = await TestDataGenerator.generateRecordsSubscribe({ author: alice }); - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const request = createJsonRpcRequest(requestId, 'dwn.processMessage', { target: alice.did, message }); const response = client.request(request); expect(client['messageHandlers'].has(requestId)).to.be.true; @@ -84,8 +84,8 @@ describe('JsonRpcSocket', () => { const client = await JsonRpcSocket.connect(socketDwnUrl); const { message } = await TestDataGenerator.generateRecordsSubscribe({ author: alice }); - const requestId = cryptoUtils.randomUuid(); - const subscriptionId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); + const subscriptionId = CryptoUtils.randomUuid(); const request = createJsonRpcSubscriptionRequest( requestId, 'dwn.processMessage', @@ -104,8 +104,8 @@ describe('JsonRpcSocket', () => { it('removes listener if subscription json rpc is rejected ', async () => { const client = await JsonRpcSocket.connect(socketDwnUrl); - const requestId = cryptoUtils.randomUuid(); - const subscribeId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); + const subscribeId = CryptoUtils.randomUuid(); const request = createJsonRpcSubscriptionRequest( requestId, @@ -126,8 +126,8 @@ describe('JsonRpcSocket', () => { const client = await JsonRpcSocket.connect(socketDwnUrl); const { message } = await TestDataGenerator.generateRecordsSubscribe({ author: alice }); - const requestId = cryptoUtils.randomUuid(); - const subscriptionId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); + const subscriptionId = CryptoUtils.randomUuid(); const request = createJsonRpcSubscriptionRequest( requestId, 'dwn.processMessage', @@ -149,7 +149,7 @@ describe('JsonRpcSocket', () => { it('only JSON RPC Methods prefixed with `rpc.subscribe.` are accepted for a subscription', async () => { const client = await JsonRpcSocket.connect(socketDwnUrl); - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const request = createJsonRpcRequest(requestId, 'test.method', { param1: 'test-param1', param2: 'test-param2' }); try { await client.subscribe(request, () => {}); @@ -161,7 +161,7 @@ describe('JsonRpcSocket', () => { it('subscribe methods must contain a subscribe object within the request which contains the subscription JsonRpcId', async () => { const client = await JsonRpcSocket.connect(socketDwnUrl); - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const request = createJsonRpcRequest(requestId, 'rpc.subscribe.test.method', { param1: 'test-param1', param2: 'test-param2' }); try { await client.subscribe(request, () => {}); @@ -235,8 +235,8 @@ describe('JsonRpcSocket', () => { const client = await JsonRpcSocket.connect(socketDwnUrl); const { message } = await TestDataGenerator.generateRecordsSubscribe({ author: alice }); - const requestId = cryptoUtils.randomUuid(); - const subscriptionId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); + const subscriptionId = CryptoUtils.randomUuid(); const request = createJsonRpcSubscriptionRequest( requestId, 'dwn.processMessage', diff --git a/packages/agent/tests/rpc-client.spec.ts b/packages/agent/tests/rpc-client.spec.ts index f526f688f..1d03fb534 100644 --- a/packages/agent/tests/rpc-client.spec.ts +++ b/packages/agent/tests/rpc-client.spec.ts @@ -1,7 +1,7 @@ import sinon from 'sinon'; import { expect } from 'chai'; import { testDwnUrl } from './utils/test-config.js'; -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; import { DidRpcMethod, HttpWeb5RpcClient, Web5RpcClient, WebSocketWeb5RpcClient } from '../src/rpc-client.js'; import { DwnServerInfoCacheMemory } from '../src/prototyping/clients/dwn-server-info-cache-memory.js'; @@ -296,7 +296,7 @@ describe('RPC Clients', () => { it('should throw if json rpc server responds with an error', async () => { const request = { method: DidRpcMethod.Resolve, url: testDwnUrl, data: 'some-data' }; - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const jsonRpcResponse = createJsonRpcErrorResponse( requestId, JsonRpcErrorCodes.InternalError, @@ -330,7 +330,7 @@ describe('RPC Clients', () => { it('should return json rpc result', async () => { const request = { method: DidRpcMethod.Resolve, url: testDwnUrl, data: 'some-data' }; - const requestId = cryptoUtils.randomUuid(); + const requestId = CryptoUtils.randomUuid(); const jsonRpcResponse = createJsonRpcSuccessResponse( requestId, { status: { code: 200 }, data: 'data' } diff --git a/packages/agent/tests/sync-engine-level.spec.ts b/packages/agent/tests/sync-engine-level.spec.ts index 22010c6f6..aa097d1f7 100644 --- a/packages/agent/tests/sync-engine-level.spec.ts +++ b/packages/agent/tests/sync-engine-level.spec.ts @@ -1,6 +1,6 @@ import sinon from 'sinon'; import { expect } from 'chai'; -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; import { DwnConstant, ProtocolDefinition } from '@tbd54566975/dwn-sdk-js'; import type { BearerIdentity } from '../src/bearer-identity.js'; @@ -66,7 +66,7 @@ describe('SyncEngineLevel', () => { }); beforeEach(async () => { - randomSchema = cryptoUtils.randomUuid(); + randomSchema = CryptoUtils.randomUuid(); sinon.restore(); @@ -821,7 +821,7 @@ describe('SyncEngineLevel', () => { it('silently ignores a messageCid from the eventLog that does not exist on the local DWN', async () => { // It's important to create a new DID here to avoid conflicts with the previous test on the remote DWN, // since we are not clearing the remote DWN's storage before each test. - const name = cryptoUtils.randomUuid(); + const name = CryptoUtils.randomUuid(); const alice = await testHarness.createIdentity({ name, testDwnUrls }); // scenario: The messageCids returned from the local eventLog contains a Cid that is not found when attempting to push it to the remote DWN @@ -926,7 +926,7 @@ describe('SyncEngineLevel', () => { it('silently ignores a messageCid that already exists on the remote DWN', async () => { // It's important to create a new DID here to avoid conflicts with the previous test on the remote DWN, // since we are not clearing the remote DWN's storage before each test. - const name = cryptoUtils.randomUuid(); + const name = CryptoUtils.randomUuid(); const alice = await testHarness.createIdentity({ name, testDwnUrls }); // Register Alice's DID to be synchronized. diff --git a/packages/api/tests/utils/test-data-generator.ts b/packages/api/tests/utils/test-data-generator.ts index 422df72e5..2c272efaf 100644 --- a/packages/api/tests/utils/test-data-generator.ts +++ b/packages/api/tests/utils/test-data-generator.ts @@ -1,11 +1,11 @@ -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; export class TestDataGenerator { /** * Generates a random byte array of given length. */ static randomBytes(length: number): Uint8Array { - return cryptoUtils.randomBytes(length); + return CryptoUtils.randomBytes(length); } /** diff --git a/packages/credentials/src/verifiable-credential.ts b/packages/credentials/src/verifiable-credential.ts index cfa191eba..90cca9e73 100644 --- a/packages/credentials/src/verifiable-credential.ts +++ b/packages/credentials/src/verifiable-credential.ts @@ -1,7 +1,7 @@ import type { BearerDid } from '@web5/dids'; import type { ICredential, ICredentialSubject} from '@sphereon/ssi-types'; -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; import { Jwt } from './jwt.js'; import { SsiValidator } from './validators.js'; @@ -196,7 +196,7 @@ export class VerifiableCredential { type : Array.isArray(type) ? [DEFAULT_VC_TYPE, ...type] : (type ? [DEFAULT_VC_TYPE, type] : [DEFAULT_VC_TYPE]), - id : `urn:uuid:${cryptoUtils.randomUuid()}`, + id : `urn:uuid:${CryptoUtils.randomUuid()}`, issuer : issuer, issuanceDate : issuanceDate || getCurrentXmlSchema112Timestamp(), credentialSubject : credentialSubject, diff --git a/packages/credentials/src/verifiable-presentation.ts b/packages/credentials/src/verifiable-presentation.ts index 2764fb7e3..330e1c04a 100644 --- a/packages/credentials/src/verifiable-presentation.ts +++ b/packages/credentials/src/verifiable-presentation.ts @@ -1,7 +1,7 @@ import type { BearerDid } from '@web5/dids'; import type { IPresentation} from '@sphereon/ssi-types'; -import { utils as cryptoUtils } from '@web5/crypto'; +import { CryptoUtils } from '@web5/crypto'; import { Jwt } from './jwt.js'; import { SsiValidator } from './validators.js'; @@ -149,7 +149,7 @@ export class VerifiablePresentation { type : Array.isArray(type) ? [DEFAULT_VP_TYPE, ...type] : (type ? [DEFAULT_VP_TYPE, type] : [DEFAULT_VP_TYPE]), - id : `urn:uuid:${cryptoUtils.randomUuid()}`, + id : `urn:uuid:${CryptoUtils.randomUuid()}`, holder : holder, verifiableCredential : vcJwts, ...additionalData, diff --git a/packages/crypto/README.md b/packages/crypto/README.md index fa5048881..221fb7e50 100644 --- a/packages/crypto/README.md +++ b/packages/crypto/README.md @@ -12,22 +12,35 @@ --- -- [Introduction](#introduction) - - [Supported Algorithms & Key Types](#supported-algorithms--key-types) +- [Web5 Crypto API](#web5-crypto-api) + - [Supported Algorithms \& Key Types](#supported-algorithms--key-types) - [Extensions](#extensions) -- [Getting Started](#getting-started) - - [Node.js](#nodejs) - - [Web Browsers](#web-browsers) - - [React Native](#react-native) -- [Contributing](#contributing) -- [Core Concepts](#usage) - - [Key URIs](#key-uris) - - [Using a Local KMS](#using-a-local-kms) - - [JSON Web Key (JWK)](#json-web-key-jwk) - - [Random Number Generation](#random-number-generation) -- [Customization](#customization) - - [Persistent Local KMS Key Store](#persistent-local-kms-key-store) -- [Cryptographic Primitives](#cryptographic-primitives) + - [Getting Started](#getting-started) + - [Node.js](#nodejs) + - [Web Browsers](#web-browsers) + - [React Native](#react-native) + - [Contributing](#contributing) + - [Core Concepts](#core-concepts) + - [Key URIs](#key-uris) + - [Using a Local KMS](#using-a-local-kms) + - [JSON Web Key (JWK)](#json-web-key-jwk) + - [Random Number Generation](#random-number-generation) + - [`randomBytes()`](#randombytes) + - [`randomUuid()`](#randomuuid) + - [Customization](#customization) + - [Persistent Local KMS Key Store](#persistent-local-kms-key-store) + - [Cryptographic Primitives](#cryptographic-primitives) + - [AES-CTR](#aes-ctr) + - [AES-GCM](#aes-gcm) + - [ConcatKDF](#concatkdf) + - [Ed25519](#ed25519) + - [PBKDF2](#pbkdf2) + - [secp256k1](#secp256k1) + - [SHA-256](#sha-256) + - [X25519](#x25519) + - [XChaCha20](#xchacha20) + - [XChaCha20-Poly1305](#xchacha20-poly1305) + - [Project Resources](#project-resources) --- @@ -366,9 +379,9 @@ nonces and other random values. > available. ```typescript -import { randomBytes } from "@web5/crypto/utils"; +import { CryptoUtils } from "@web5/crypto"; -const nonce = randomBytes(24); +const nonce = CryptoUtils.randomBytes(24); ``` #### `randomUuid()` @@ -387,9 +400,9 @@ The following is an example of the string representation of a UUID as a URN: > This function is available only in [secure contexts](#secure-context) (HTTPS and localhost). ```typescript -import { randomUuid } from "@web5/crypto/utils"; +import { CryptoUtils } from "@web5/crypto"; -const uuid = randomUuid(); +const uuid = CryptoUtils.randomUuid(); ``` ## Customization @@ -439,7 +452,7 @@ and key conversion algorithms for advanced use cases. #### AES-CTR ```ts -import { AesCtr, utils } from "@web5/crypto"; +import { AesCtr, CryptoUtils } from "@web5/crypto"; // Key Generation const length = 256; // Length of the key in bits (e.g., 128, 192, 256) @@ -447,7 +460,7 @@ const privateKey = await AesCtr.generateKey({ length }); // Encryption const data = new TextEncoder().encode("Message"); -const counter = utils.randomBytes(16); // Initial value of the counter block +const counter = CryptoUtils.randomBytes(16); // Initial value of the counter block const encryptedData = await AesCtr.encrypt({ data, counter, @@ -467,7 +480,7 @@ const decryptedData = await AesCtr.decrypt({ #### AES-GCM ```ts -import { AesGcm, utils } from "@web5/crypto"; +import { AesGcm, CryptoUtils } from "@web5/crypto"; // Key Generation const length = 256; // Length of the key in bits (e.g., 128, 192, 256) @@ -475,7 +488,7 @@ const privateKey = await AesGcm.generateKey({ length }); // Encryption const data = new TextEncoder().encode("Message"); -const iv = utils.randomBytes(12); // Initialization vector +const iv = CryptoUtils.randomBytes(12); // Initialization vector const encryptedData = await AesGcm.encrypt({ data, iv, @@ -493,11 +506,11 @@ const decryptedData = await AesGcm.decrypt({ #### ConcatKDF ```ts -import { ConcatKdf, utils } from "@web5/crypto"; +import { ConcatKdf, CryptoUtils } from "@web5/crypto"; // Key Derivation const derivedKeyingMaterial = await ConcatKdf.deriveKey({ - sharedSecret: utils.randomBytes(32), + sharedSecret: CryptoUtils.randomBytes(32), keyDataLen: 128, otherInfo: { algorithmId: "A128GCM", @@ -536,13 +549,13 @@ const isValid = await Ed25519.verify({ #### PBKDF2 ```ts -import { Pbkdf2, utils } from "@web5/crypto"; +import { Pbkdf2, CryptoUtils } from "@web5/crypto"; // Key Derivation const derivedKey = await Pbkdf2.deriveKey({ hash: "SHA-256", // Hash function to use ('SHA-256', 'SHA-384', 'SHA-512') password: new TextEncoder().encode("password"), // Password as a Uint8Array - salt: utils.randomBytes(16), // Salt value + salt: CryptoUtils.randomBytes(16), // Salt value iterations: 1000, // Number of iterations length: 256, // Length of the derived key in bits }); @@ -610,14 +623,14 @@ const sharedSecret = await X25519.sharedSecret({ #### XChaCha20 ```ts -import { XChaCha20, utils } from "@web5/crypto"; +import { XChaCha20, CryptoUtils } from "@web5/crypto"; // Key Generation const privateKey = await XChaCha20.generateKey(); // Encryption const data = new TextEncoder().encode("Message"); -const nonce = utils.randomBytes(24); +const nonce = CryptoUtils.randomBytes(24); const encryptedData = await XChaCha20.encrypt({ data, nonce, @@ -635,14 +648,14 @@ const decryptedData = await XChaCha20.decrypt({ #### XChaCha20-Poly1305 ```ts -import { XChaCha20Poly1305, utils } from "@web5/crypto"; +import { XChaCha20Poly1305, CryptoUtils } from "@web5/crypto"; // Key Generation const privateKey = await XChaCha20Poly1305.generateKey(); // Encryption const data = new TextEncoder().encode("Message"); -const nonce = utils.randomBytes(24); +const nonce = CryptoUtils.randomBytes(24); const additionalData = new TextEncoder().encode("Associated data"); const { ciphertext, tag } = await XChaCha20Poly1305.encrypt({ data, diff --git a/packages/crypto/package.json b/packages/crypto/package.json index 23bfdd601..f2ebecf10 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -50,11 +50,6 @@ "types": "./dist/types/index.d.ts", "import": "./dist/esm/index.js", "require": "./dist/cjs/index.js" - }, - "./utils": { - "types": "./dist/types/utils.d.ts", - "import": "./dist/esm/utils.js", - "require": "./dist/cjs/utils.js" } }, "browser": { @@ -106,4 +101,4 @@ "source-map-loader": "5.0.0", "typescript": "5.4.5" } -} \ No newline at end of file +} diff --git a/packages/crypto/src/index.ts b/packages/crypto/src/index.ts index 424820cc3..853d2a36b 100644 --- a/packages/crypto/src/index.ts +++ b/packages/crypto/src/index.ts @@ -1,5 +1,4 @@ export * from './local-key-manager.js'; -export * as utils from './utils.js'; export * from './utils.js'; export * from './algorithms/aes-ctr.js'; diff --git a/packages/crypto/src/utils.ts b/packages/crypto/src/utils.ts index 51f90d160..9eb6731e8 100644 --- a/packages/crypto/src/utils.ts +++ b/packages/crypto/src/utils.ts @@ -3,68 +3,6 @@ import type { Jwk } from './jose/jwk.js'; import { crypto } from '@noble/hashes/crypto'; import { randomBytes as nobleRandomBytes } from '@noble/hashes/utils'; -/** - * Checks whether the properties object provided contains the specified property. - * - * @example - * ```ts - * const obj = { a: 'Bob', t: 30 }; - * checkRequiredProperty({ property: 'a', inObject: obj }); // No error - * checkRequiredProperty({ property: 'z', inObject: obj }); // Throws TypeError - * ``` - * - * @param params - The parameters for the check. - * @param params.property - Property key to check for. - * @param params.properties - Properties object to check within. - * @returns void - * @throws {TypeError} If the property is not a key in the properties object. - */ -export function checkRequiredProperty(params: { - property: string, - inObject: object -}): void { - if (!params || params.property === undefined || params.inObject === undefined) { - throw new TypeError(`One or more required parameters missing: 'property, properties'`); - } - const { property, inObject } = params; - if (!(property in inObject)) { - throw new TypeError(`Required parameter missing: '${property}'`); - } -} - -/** - * Checks whether the property specified is a member of the list of valid properties. - * - * @example - * ```ts - * const property = 'color'; - * const allowedProperties = ['size', 'shape', 'color']; - * checkValidProperty({ property, allowedProperties }); // No error - * checkValidProperty({ property: 'weight', allowedProperties }); // Throws TypeError - * ``` - * - * @param property Property key to check for. - * @param allowedProperties Properties Array, Map, or Set to check within. - * @returns void - * @throws {TypeError} If the property is not a member of the allowedProperties Array, Map, or Set. - */ -export function checkValidProperty(params: { - property: string, allowedProperties: ReadonlyArray | Array | Map | Set -}): void { - if (!params || params.property === undefined || params.allowedProperties === undefined) { - throw new TypeError(`One or more required parameters missing: 'property, allowedProperties'`); - } - const { property, allowedProperties } = params; - if ( - (Array.isArray(allowedProperties) && !allowedProperties.includes(property)) || - (allowedProperties instanceof Set && !allowedProperties.has(property)) || - (allowedProperties instanceof Map && !allowedProperties.has(property)) - ) { - const validProperties = Array.from((allowedProperties instanceof Map) ? allowedProperties.keys() : allowedProperties).join(', '); - throw new TypeError(`Out of range: '${property}'. Must be one of '${validProperties}'`); - } -} - /** * Determines the JOSE algorithm identifier of the digital signature algorithm based on the `alg` or * `crv` property of a {@link Jwk | JWK}. @@ -90,7 +28,7 @@ export function checkValidProperty(params: { * @returns The name of the algorithm associated with the key. * @throws Error if the algorithm cannot be determined from the provided input. */ -export function getJoseSignatureAlgorithmFromPublicKey(publicKey: Jwk): string { +function getJoseSignatureAlgorithmFromPublicKey(publicKey: Jwk): string { const curveToJoseAlgorithm: Record = { 'Ed25519' : 'EdDSA', 'P-256' : 'ES256', @@ -117,41 +55,6 @@ export function getJoseSignatureAlgorithmFromPublicKey(publicKey: Jwk): string { ); } -/** - * Checks if the Web Crypto API is supported in the current runtime environment. - * - * @remarks - * The function uses `globalThis` to provide a universal reference to the global - * scope, regardless of the environment. `globalThis` is a standard feature introduced - * in ECMAScript 2020 that is agnostic to the underlying JavaScript environment, making - * the code portable across browser, Node.js, and Web Workers environments. - * - * In a web browser, `globalThis` is equivalent to the `window` object. In Node.js, it - * is equivalent to the `global` object, and in Web Workers, it corresponds to `self`. - * - * This method checks for the `crypto` object and its `subtle` property on the global scope - * to determine the availability of the Web Crypto API. If both are present, the API is - * supported; otherwise, it is not. - * - * @example - * ```ts - * if (isWebCryptoSupported()) { - * console.log('Crypto operations can be performed'); - * } else { - * console.log('Crypto operations are not supported in this environment'); - * } - * ``` - * - * @returns A boolean indicating whether the Web Crypto API is supported in the current environment. - */ -export function isWebCryptoSupported(): boolean { - if (globalThis.crypto && globalThis.crypto.subtle) { - return true; - } else { - return false; - } -} - /** * Generates secure pseudorandom values of the specified length using * `crypto.getRandomValues`, which defers to the operating system. @@ -173,7 +76,7 @@ export function isWebCryptoSupported(): boolean { * @param bytesLength - The number of bytes to generate. * @returns A Uint8Array containing the generated random bytes. */ -export function randomBytes(bytesLength: number): Uint8Array { +function randomBytes(bytesLength: number): Uint8Array { return nobleRandomBytes(bytesLength); } @@ -202,7 +105,7 @@ export function randomBytes(bytesLength: number): Uint8Array { * * @returns A string containing a randomly generated, 36 character long v4 UUID. */ -export function randomUuid(): string { +function randomUuid(): string { const uuid = crypto.randomUUID(); return uuid; @@ -237,7 +140,7 @@ export function randomUuid(): string { * * @throws Will throw an error if the requested PIN length is less than 3 or greater than 8. */ -export function randomPin({ length }: { length: number }): string { +function randomPin({ length }: { length: number }): string { if (3 > length || length > 10) { throw new Error('randomPin() can securely generate a PIN between 3 to 10 digits.'); } @@ -271,20 +174,9 @@ export function randomPin({ length }: { length: number }): string { return pin.toString().padStart(length, '0'); } -/** Utility functions for cryptographic operations. */ export const CryptoUtils = { - /** Generates a secure random PIN (Personal Identification Number) of a specified length. */ randomPin, - /** Generates a UUID following the version 4 format, as specified in RFC 4122. */ randomUuid, - /** Generates secure pseudorandom values of the specified length using `crypto.getRandomValues`, which defers to the operating system. */ randomBytes, - /** Checks if the Web Crypto API is supported in the current runtime environment. */ - isWebCryptoSupported, - /** Determines the JOSE algorithm identifier of the digital signature algorithm based on the `alg` or `crv` property of a {@link Jwk | JWK}. */ getJoseSignatureAlgorithmFromPublicKey, - /** Checks whether the property specified is a member of the list of valid properties. */ - checkValidProperty, - /** Checks whether the properties object provided contains the specified property. */ - checkRequiredProperty }; \ No newline at end of file diff --git a/packages/crypto/tests/algorithms/aes-ctr.spec.ts b/packages/crypto/tests/algorithms/aes-ctr.spec.ts index 30c537c18..5e2c1c2c9 100644 --- a/packages/crypto/tests/algorithms/aes-ctr.spec.ts +++ b/packages/crypto/tests/algorithms/aes-ctr.spec.ts @@ -4,7 +4,7 @@ import { Convert } from '@web5/common'; import type { Jwk } from '../../src/jose/jwk.js'; import { isChrome } from '../utils/runtimes.js'; -import { randomBytes } from '../../src/utils.js'; +import { CryptoUtils } from '../../src/utils.js'; import { AesCtrAlgorithm } from '../../src/algorithms/aes-ctr.js'; describe('AesCtrAlgorithm', () => { @@ -20,7 +20,7 @@ describe('AesCtrAlgorithm', () => { it('returns ciphertext as a Uint8Array', async () => { // Setup. const plaintext = new Uint8Array([1, 2, 3, 4]); - const counter = randomBytes(16); // Initial value of the counter block. + const counter = CryptoUtils.randomBytes(16); // Initial value of the counter block. const length = 64; // Number of bits in the counter block used for the counter. // Test the method. @@ -41,7 +41,7 @@ describe('AesCtrAlgorithm', () => { it('returns plaintext as a Uint8Array', async () => { // Setup. const ciphertext = new Uint8Array([1, 2, 3, 4]); - const counter = randomBytes(16); // Initial value of the counter block. + const counter = CryptoUtils.randomBytes(16); // Initial value of the counter block. const length = 64; // Number of bits in the counter block used for the counter. // Test the method. diff --git a/packages/crypto/tests/algorithms/aes-gcm.spec.ts b/packages/crypto/tests/algorithms/aes-gcm.spec.ts index 56413c4b4..f625a9252 100644 --- a/packages/crypto/tests/algorithms/aes-gcm.spec.ts +++ b/packages/crypto/tests/algorithms/aes-gcm.spec.ts @@ -4,7 +4,7 @@ import { Convert } from '@web5/common'; import type { Jwk } from '../../src/jose/jwk.js'; import { isChrome } from '../utils/runtimes.js'; -import { randomBytes } from '../../src/utils.js'; +import { CryptoUtils } from '../../src/utils.js'; import { AesGcmAlgorithm } from '../../src/algorithms/aes-gcm.js'; describe('AesGcmAlgorithm', () => { @@ -20,7 +20,7 @@ describe('AesGcmAlgorithm', () => { it('returns ciphertext as a Uint8Array', async () => { // Setup. const plaintext = new Uint8Array([1, 2, 3, 4]); - const iv = randomBytes(12); // Initialization vector. + const iv = CryptoUtils.randomBytes(12); // Initialization vector. const tagLength = 128; // Size in bits of the authentication tag. // Test the method. diff --git a/packages/crypto/tests/utils.spec.ts b/packages/crypto/tests/utils.spec.ts index 15a6a8886..eac6d3f0b 100644 --- a/packages/crypto/tests/utils.spec.ts +++ b/packages/crypto/tests/utils.spec.ts @@ -3,15 +3,7 @@ import * as sinon from 'sinon'; import type { Jwk } from '../src/jose/jwk.js'; -import { - randomUuid, - randomBytes, - checkValidProperty, - isWebCryptoSupported, - checkRequiredProperty, - getJoseSignatureAlgorithmFromPublicKey, - randomPin -} from '../src/utils.js'; +import { CryptoUtils } from '../src/utils.js'; // TODO: Remove this polyfill once Node.js v18 is no longer supported by @web5/crypto. if (!globalThis.crypto) { @@ -28,130 +20,71 @@ if (!globalThis.crypto) { } describe('Crypto Utils', () => { - describe('checkValidProperty()', () => { - it('should not throw for a property in the allowed list', () => { - expect(() => checkValidProperty({ property: 'foo', allowedProperties: ['foo', 'bar']})).to.not.throw(); - expect(() => checkValidProperty({ property: 'foo', allowedProperties: new Set(['foo', 'bar'])})).to.not.throw(); - expect(() => checkValidProperty({ property: 'foo', allowedProperties: new Map([['foo', 1], ['bar', 2]])})).to.not.throw(); - }); - - it('throws an error if required parameters are missing', () => { - expect(() => checkValidProperty({ property: 'foo' } as any)).to.throw(TypeError, 'required parameters missing'); - expect(() => checkValidProperty({ allowedProperties: ['foo', 'bar'] } as any)).to.throw(TypeError, 'required parameters missing'); - // @ts-expect-error because both arguments are intentionally omitted. - expect(() => checkValidProperty()).to.throw(TypeError, 'required parameters missing'); - }); - - it('throws an error if the property does not exist', () => { - expect(() => checkValidProperty({ property: 'baz', allowedProperties: ['foo', 'bar']})).to.throw(TypeError, 'Out of range'); - expect(() => checkValidProperty({ property: 'baz', allowedProperties: new Set(['foo', 'bar'])})).to.throw(TypeError, 'Out of range'); - expect(() => checkValidProperty({ property: 'baz', allowedProperties: new Map([['foo', 1], ['bar', 2]])})).to.throw(TypeError, 'Out of range'); - }); - - }); - - describe('checkRequiredProperty', () => { - it('throws an error if required parameters are missing', () => { - // @ts-expect-error because second argument is intentionally omitted. - expect(() => checkRequiredProperty({ property: 'foo' })).to.throw('required parameters missing'); - // @ts-expect-error because both arguments are intentionally omitted. - expect(() => checkRequiredProperty()).to.throw('required parameters missing'); - }); - - it('throws an error if the property is missing', () => { - const propertiesCollection = { foo: 'bar', baz: 'qux' }; - expect(() => checkRequiredProperty({ property: 'quux', inObject: propertiesCollection })).to.throw('Required parameter missing'); - }); - - it('does not throw an error if the property is present', () => { - const propertiesCollection = { foo: 'bar', baz: 'qux' }; - expect(() => checkRequiredProperty({ property: 'foo', inObject: propertiesCollection })).to.not.throw(); - }); - }); - describe('getJoseSignatureAlgorithmFromPublicKey()', () => { it('returns the algorithm specified by the alg property regardless of the crv property', () => { const publicKey: Jwk = { kty: 'OKP', alg: 'EdDSA', crv: 'P-256' }; - expect(getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.equal('EdDSA'); + expect(CryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.equal('EdDSA'); }); it('returns the correct algorithm for Ed25519 curve', () => { const publicKey: Jwk = { kty: 'OKP', crv: 'Ed25519' }; - expect(getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.equal('EdDSA'); + expect(CryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.equal('EdDSA'); }); it('returns the correct algorithm for P-256 curve', () => { const publicKey: Jwk = { kty: 'EC', crv: 'P-256' }; - expect(getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.equal('ES256'); + expect(CryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.equal('ES256'); }); it('returns the correct algorithm for P-384 curve', () => { const publicKey: Jwk = { kty: 'EC', crv: 'P-384' }; - expect(getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.equal('ES384'); + expect(CryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.equal('ES384'); }); it('returns the correct algorithm for P-521 curve', () => { const publicKey: Jwk = { kty: 'EC', crv: 'P-521' }; - expect(getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.equal('ES512'); + expect(CryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.equal('ES512'); }); it('throws an error for unsupported algorithms', () => { const publicKey: Jwk = { kty: 'EC', alg: 'UnsupportedAlgorithm' }; - expect(() => getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.throw(); + expect(() => CryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.throw(); }); it('throws an error for unsupported curves', () => { const publicKey: Jwk = { kty: 'EC', crv: 'UnsupportedCurve' }; - expect(() => getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.throw(); + expect(() => CryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.throw(); }); it('throws an error when neither alg nor crv is provided', () => { const publicKey: Jwk = { kty: 'EC' }; - expect(() => getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.throw(); - }); - }); - - describe('isWebCryptoSupported()', () => { - afterEach(() => { - // Restore the original state after each test - sinon.restore(); - }); - - it('returns true if the Web Crypto API is supported', () => { - expect(isWebCryptoSupported()).to.be.true; - }); - - it('returns false if Web Crypto API is not supported', function () { - // Mock an unsupported environment - sinon.stub(globalThis, 'crypto').value({}); - - expect(isWebCryptoSupported()).to.be.false; + expect(() => CryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey)).to.throw(); }); }); describe('randomBytes()', () => { it('returns a Uint8Array of the specified length', () => { const length = 16; - const result = randomBytes(length); + const result = CryptoUtils.randomBytes(length); expect(result).to.be.instanceof(Uint8Array); expect(result).to.have.length(length); }); it('handles invalid input gracefully', () => { - expect(() => randomBytes(-1)).to.throw(RangeError, 'length'); // Length cannot be negative. + expect(() => CryptoUtils.randomBytes(-1)).to.throw(RangeError, 'length'); // Length cannot be negative. // NOTE: only checking for Error being thrown because there is no meaningful message overlap between all browsers: // Webkit: The quota has been exceeded. // Firefox: Crypto.getRandomValues: getRandomValues can only generate maximum 65536 bytes // Chromium: The ArrayBufferView's byte length (1000000000) exceeds the number of bytes of entropy available via this API (65536). - expect(() => randomBytes(1e9)).to.throw(Error); // Extremely large number that exceeds the available entropy. + expect(() => CryptoUtils.randomBytes(1e9)).to.throw(Error); // Extremely large number that exceeds the available entropy. }); it('produces unique values on each call', () => { const set = new Set(); for (let i = 0; i < 100; i++) { - set.add(randomBytes(10).toString()); + set.add(CryptoUtils.randomBytes(10).toString()); } expect(set.size).to.equal(100); }); @@ -159,7 +92,7 @@ describe('Crypto Utils', () => { describe('randomUuid()', () => { it('generates a valid v4 UUID', () => { - const id = randomUuid(); + const id = CryptoUtils.randomUuid(); expect(id).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/); expect(id).to.have.length(36); }); @@ -167,7 +100,7 @@ describe('Crypto Utils', () => { it('produces unique values on each call', () => { const set = new Set(); for (let i = 0; i < 100; i++) { - set.add(randomUuid()); + set.add(CryptoUtils.randomUuid()); } expect(set.size).to.equal(100); }); @@ -175,54 +108,54 @@ describe('Crypto Utils', () => { describe('randomPin', () => { it('generates a 3-digit PIN', () => { - const pin = randomPin({ length: 3 }); + const pin = CryptoUtils.randomPin({ length: 3 }); expect(pin).to.match(/^\d{3}$/); }); it('generates a 4-digit PIN', () => { - const pin = randomPin({ length: 4 }); + const pin = CryptoUtils.randomPin({ length: 4 }); expect(pin).to.match(/^\d{4}$/); }); it('generates a 5-digit PIN', () => { - const pin = randomPin({ length: 5 }); + const pin = CryptoUtils.randomPin({ length: 5 }); expect(pin).to.match(/^\d{5}$/); }); it('generates a 6-digit PIN', () => { - const pin = randomPin({ length: 6 }); + const pin = CryptoUtils.randomPin({ length: 6 }); expect(pin).to.match(/^\d{6}$/); }); it('generates a 7-digit PIN', () => { - const pin = randomPin({ length: 7 }); + const pin = CryptoUtils.randomPin({ length: 7 }); expect(pin).to.match(/^\d{7}$/); }); it('generates an 8-digit PIN', () => { - const pin = randomPin({ length: 8 }); + const pin = CryptoUtils.randomPin({ length: 8 }); expect(pin).to.match(/^\d{8}$/); }); it('generates an 9-digit PIN', () => { - const pin = randomPin({ length: 9 }); + const pin = CryptoUtils.randomPin({ length: 9 }); expect(pin).to.match(/^\d{9}$/); }); it('generates an 10-digit PIN', () => { - const pin = randomPin({ length: 10 }); + const pin = CryptoUtils.randomPin({ length: 10 }); expect(pin).to.match(/^\d{10}$/); }); it('throws an error for a PIN length less than 3', () => { expect( - () => randomPin({ length: 2 }) + () => CryptoUtils.randomPin({ length: 2 }) ).to.throw(Error, 'randomPin() can securely generate a PIN between 3 to 10 digits.'); }); it('throws an error for a PIN length greater than 10', () => { expect( - () => randomPin({ length: 11 }) + () => CryptoUtils.randomPin({ length: 11 }) ).to.throw(Error, 'randomPin() can securely generate a PIN between 3 to 10 digits.'); }); }); diff --git a/packages/dids/src/bearer-did.ts b/packages/dids/src/bearer-did.ts index f8f312031..120f94186 100644 --- a/packages/dids/src/bearer-did.ts +++ b/packages/dids/src/bearer-did.ts @@ -10,7 +10,7 @@ import type { EnclosedVerifyParams, } from '@web5/crypto'; -import { LocalKeyManager, utils as cryptoUtils } from '@web5/crypto'; +import { LocalKeyManager, CryptoUtils } from '@web5/crypto'; import type { DidDocument } from './types/did-core.js'; import type { DidMetadata, PortableDid } from './types/portable-did.js'; @@ -193,7 +193,7 @@ export class BearerDid { const keyManager = this.keyManager; // Determine the signing algorithm. - const algorithm = cryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey); + const algorithm = CryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey); return { algorithm : algorithm, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4307a09ba..e8f78836b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,9 +26,9 @@ importers: "@typescript-eslint/eslint-plugin": specifier: 7.9.0 version: 7.9.0(@typescript-eslint/parser@7.14.1(eslint@9.7.0)(typescript@5.5.4))(eslint@9.7.0)(typescript@5.5.4) - "@web5/dwn-server": - specifier: 0.4.6 - version: 0.4.6 + '@web5/dwn-server': + specifier: 0.4.7 + version: 0.4.7 audit-ci: specifier: ^7.0.1 version: 7.1.0 @@ -3541,11 +3541,8 @@ packages: } engines: { node: ">=18.0.0" } - "@web5/dwn-server@0.4.6": - resolution: - { - integrity: sha512-92uNTJDBHGprneQtuD0A4XbcWirWHY+MrQVKd4fyrjwMh7cUMF3uE4l3QnBwn8kquzRrU+3sekcgzNjHvmTfHw==, - } + '@web5/dwn-server@0.4.7': + resolution: {integrity: sha512-ZCs1Ztc3wiywMEeINAIED/u5CA0qfuH8sr8gDeot1lBvu1viXMu1Ua8YqKc6CABPLXtWFmy5vuXIsf72TD5KHw==} hasBin: true "@webassemblyjs/ast@1.12.1": @@ -12091,10 +12088,11 @@ snapshots: level: 8.0.1 ms: 2.1.3 - "@web5/dwn-server@0.4.6": + '@web5/dwn-server@0.4.7': dependencies: - "@tbd54566975/dwn-sdk-js": 0.4.5 - "@tbd54566975/dwn-sql-store": 0.6.5 + '@tbd54566975/dwn-sdk-js': 0.4.5 + '@tbd54566975/dwn-sql-store': 0.6.5 + '@web5/crypto': 1.0.3 better-sqlite3: 8.7.0 body-parser: 1.20.2 bytes: 3.1.2