From 9e4212d7425514dbc2e5c1c6dea0008a71fe9347 Mon Sep 17 00:00:00 2001 From: Eric Richardson Date: Thu, 3 Oct 2024 17:40:44 -0400 Subject: [PATCH] feat(azure-signing-manager): add azure signing manager add azure signing manager that allows for keys to be stored in an Azure Key Vault and to be used with the Polymesh SDK DA-1322 --- .vscode/settings.json | 1 + commitlint.config.js | 1 + package.json | 3 + packages/azure-signing-manager/.babelrc | 3 + packages/azure-signing-manager/.eslintrc.json | 18 + packages/azure-signing-manager/README.md | 7 + packages/azure-signing-manager/jest.config.js | 12 + packages/azure-signing-manager/package.json | 25 + packages/azure-signing-manager/project.json | 51 +++ packages/azure-signing-manager/src/index.ts | 2 + .../src/lib/azure-hsm/azure-hsm.spec.ts | 132 ++++++ .../src/lib/azure-hsm/azure-hsm.ts | 241 ++++++++++ .../src/lib/azure-hsm/index.ts | 3 + .../src/lib/azure-hsm/types.ts | 50 ++ .../src/lib/azure-hsm/util.ts | 48 ++ .../src/lib/azure-signing-manager.spec.ts | 148 ++++++ .../src/lib/azure-signing-manager.ts | 160 +++++++ .../azure-signing-manager/src/types/index.ts | 24 + packages/azure-signing-manager/tsconfig.json | 19 + .../azure-signing-manager/tsconfig.lib.json | 11 + .../azure-signing-manager/tsconfig.spec.json | 22 + tsconfig.base.json | 1 + workspace.json | 5 +- yarn.lock | 428 +++++++++++++++++- 24 files changed, 1410 insertions(+), 5 deletions(-) create mode 100644 packages/azure-signing-manager/.babelrc create mode 100644 packages/azure-signing-manager/.eslintrc.json create mode 100644 packages/azure-signing-manager/README.md create mode 100644 packages/azure-signing-manager/jest.config.js create mode 100644 packages/azure-signing-manager/package.json create mode 100644 packages/azure-signing-manager/project.json create mode 100644 packages/azure-signing-manager/src/index.ts create mode 100644 packages/azure-signing-manager/src/lib/azure-hsm/azure-hsm.spec.ts create mode 100644 packages/azure-signing-manager/src/lib/azure-hsm/azure-hsm.ts create mode 100644 packages/azure-signing-manager/src/lib/azure-hsm/index.ts create mode 100644 packages/azure-signing-manager/src/lib/azure-hsm/types.ts create mode 100644 packages/azure-signing-manager/src/lib/azure-hsm/util.ts create mode 100644 packages/azure-signing-manager/src/lib/azure-signing-manager.spec.ts create mode 100644 packages/azure-signing-manager/src/lib/azure-signing-manager.ts create mode 100644 packages/azure-signing-manager/src/types/index.ts create mode 100644 packages/azure-signing-manager/tsconfig.json create mode 100644 packages/azure-signing-manager/tsconfig.lib.json create mode 100644 packages/azure-signing-manager/tsconfig.spec.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 93a0c6a3..46b1987d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,6 +21,7 @@ "polywallet", "Resonse", "satoshi", + "secp", "sonarcloud", "tscpaths", "Unsub" diff --git a/commitlint.config.js b/commitlint.config.js index 67fbadc3..37bebdc7 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -15,6 +15,7 @@ module.exports = { 'approval-signing-manager', 'fireblocks-signing-manager', 'walletconnect-signing-manager', + 'azure-signing-manager', ], ], }, diff --git a/package.json b/package.json index 9ebc409c..3735c468 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@babel/preset-env": "^7.16.11", "@commitlint/cli": "^16.2.1", "@commitlint/config-conventional": "^16.2.1", + "@golevelup/ts-jest": "^0.5.6", "@ng-easy/builders": "^5.2.0", "@nrwl/cli": "13.8.2", "@nrwl/devkit": "13.8.2", @@ -48,6 +49,8 @@ "@types/node": "16.11.7", "@types/require-from-string": "^1.2.1", "@typescript-eslint/eslint-plugin": "~5.10.0", + "@types/jest-when": "^3.5.2", + "jest-when": "^3.6.0", "@typescript-eslint/parser": "~5.10.0", "babel-loader": "^8.2.3", "commitiquette": "^1.2.1", diff --git a/packages/azure-signing-manager/.babelrc b/packages/azure-signing-manager/.babelrc new file mode 100644 index 00000000..cf7ddd99 --- /dev/null +++ b/packages/azure-signing-manager/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] +} diff --git a/packages/azure-signing-manager/.eslintrc.json b/packages/azure-signing-manager/.eslintrc.json new file mode 100644 index 00000000..9d9c0db5 --- /dev/null +++ b/packages/azure-signing-manager/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/packages/azure-signing-manager/README.md b/packages/azure-signing-manager/README.md new file mode 100644 index 00000000..7aa4c7f4 --- /dev/null +++ b/packages/azure-signing-manager/README.md @@ -0,0 +1,7 @@ +# azure-signing-manager + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test azure-signing-manager` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/packages/azure-signing-manager/jest.config.js b/packages/azure-signing-manager/jest.config.js new file mode 100644 index 00000000..2778ee8e --- /dev/null +++ b/packages/azure-signing-manager/jest.config.js @@ -0,0 +1,12 @@ +module.exports = { + displayName: 'azure-signing-manager', + preset: '../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + testEnvironment: 'node', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/packages/azure-signing-manager', +}; diff --git a/packages/azure-signing-manager/package.json b/packages/azure-signing-manager/package.json new file mode 100644 index 00000000..4763e05b --- /dev/null +++ b/packages/azure-signing-manager/package.json @@ -0,0 +1,25 @@ +{ + "name": "@polymeshassociation/azure-signing-manager", + "version": "0.0.1", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@polymeshassociation/signing-manager-types": "^3.3.0", + "@azure/keyvault-keys": "4.8.0", + "@azure/identity": "4.4.1", + "secp256k1": "5.0.0" + }, + "devDependencies": { + "@types/secp256k1": "4.0.6" + }, + "peerDependencies": { + "@polymeshassociation/polymesh-sdk": ">=15.0.0" + }, + "main": "./index.js", + "typings": "./index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/PolymeshAssociation/signing-managers.git" + } +} diff --git a/packages/azure-signing-manager/project.json b/packages/azure-signing-manager/project.json new file mode 100644 index 00000000..9b20f2f1 --- /dev/null +++ b/packages/azure-signing-manager/project.json @@ -0,0 +1,51 @@ +{ + "root": "packages/azure-signing-manager", + "sourceRoot": "packages/azure-signing-manager/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nrwl/node:package", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/packages/azure-signing-manager", + "tsConfig": "packages/azure-signing-manager/tsconfig.lib.json", + "packageJson": "packages/azure-signing-manager/package.json", + "main": "packages/azure-signing-manager/src/index.ts", + "assets": ["packages/azure-signing-manager/*.md"], + "srcRootForCompilationRoot": "packages/azure-signing-manager/src" + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["packages/azure-signing-manager/**/*.ts"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/packages/azure-signing-manager"], + "options": { + "jestConfig": "packages/azure-signing-manager/jest.config.js", + "passWithNoTests": true + } + }, + "release": { + "executor": "@ng-easy/builders:semantic-release", + "configurations": { + "local": { + "force": true + } + } + }, + "run-local": { + "executor": "./tools/executors/run-local:run-local", + "options": { + "runInBrowser": false, + "path": "packages/azure-signing-manager/sandbox/index.ts", + "port": 9000 + } + } + }, + "tags": [] +} diff --git a/packages/azure-signing-manager/src/index.ts b/packages/azure-signing-manager/src/index.ts new file mode 100644 index 00000000..67543e87 --- /dev/null +++ b/packages/azure-signing-manager/src/index.ts @@ -0,0 +1,2 @@ +export * from './lib/azure-signing-manager'; +export * from './types'; diff --git a/packages/azure-signing-manager/src/lib/azure-hsm/azure-hsm.spec.ts b/packages/azure-signing-manager/src/lib/azure-hsm/azure-hsm.spec.ts new file mode 100644 index 00000000..268d2e62 --- /dev/null +++ b/packages/azure-signing-manager/src/lib/azure-hsm/azure-hsm.spec.ts @@ -0,0 +1,132 @@ +import { + CryptographyClient, + KeyClient, + KeyProperties, + KeyVaultKey, + KnownSignatureAlgorithms, +} from '@azure/keyvault-keys'; +import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { when } from 'jest-when'; + +import { AzureHsm } from './azure-hsm'; +import { AzureSignerError } from './types'; +import { createPagedAsyncIterableIterator } from './util'; + +const mockKeyClient = createMock(); +const mockCryptographyClient = createMock(); +const mockKeyVaultKey = createMock(); + +jest.mock('@azure/keyvault-keys', () => ({ + ...jest.requireActual('@azure/keyvault-keys'), + KeyClient: jest.fn().mockImplementation(() => mockKeyClient), + CryptographyClient: jest.fn().mockImplementation(() => mockCryptographyClient), + KeyVaultKey: jest.fn().mockImplementation(() => mockKeyVaultKey), +})); + +describe('AzureHsm', () => { + let azure: AzureHsm; + let mockKey: DeepMocked; + let mockNewKey: DeepMocked; + let mockPartialKey: DeepMocked; + + const x = Buffer.from('d6eb9ae6b44331da8c92a0bd88850b94c7f32981aaf42d1c1345e05af810470a', 'hex'); + const y = Buffer.from('7b4603950822dd8806494a3aff5d6bc2e437b71f7c7de64e9d84a60411dc1293', 'hex'); + const publicKey = '0x63eb973f33ad5737a1ea2e4100c04e806c403b5994d986f0c71a2fd80dbbd179'; + + beforeEach(() => { + azure = new AzureHsm({ keyVaultUrl: 'https://example.com' }); + mockKey = createMock({ key: { x, y } }); + mockNewKey = createMock({ key: { x, y } }); + mockPartialKey = createMock({ key: { x: undefined, y: undefined } }); + + const mockKeyProperties: KeyProperties[] = [ + { name: 'someKey', vaultUrl: 'http://example.com/keyOne' }, + ]; + + const mockIterator = createPagedAsyncIterableIterator(mockKeyProperties); + + mockKeyClient.listPropertiesOfKeys.mockReturnValue(mockIterator); + + mockKeyClient.getKey.mockRejectedValue({ code: 'KeyNotFound' }); + + mockKeyClient.createKey.mockImplementation(async name => { + when(mockKeyClient.getKey).calledWith(name).mockResolvedValue(mockKey); + + return mockKey; + }); + when(mockKeyClient.getKey).calledWith('someKey').mockResolvedValue(mockKey); + + when(mockKeyClient.getKey).calledWith('partialKey').mockResolvedValue(mockPartialKey); + + mockCryptographyClient.sign.mockResolvedValue({ + result: Buffer.from( + 'b4b24959854a6afdba21e1007955c6c728725009feab9886408d6dc4fb9712e3ab5f30be4f06d2f12f01a10ccd374278d44ab133ed8ee5998341034f9347f156', + 'hex' + ), + algorithm: KnownSignatureAlgorithms.ES256K, + }); + + mockKeyClient.getCryptographyClient.mockReturnValue(mockCryptographyClient); + }); + + describe('createKey', () => { + it('should create a key', async () => { + const result = await azure.createKey('newKey'); + + expect(result).toEqual( + expect.objectContaining({ + name: 'newKey', + keyVaultKey: mockNewKey, + }) + ); + }); + }); + + describe('fetchAllKeys', () => { + it('should return all of the key', async () => { + const result = await azure.fetchAllKeys(); + + expect(result).toEqual( + expect.arrayContaining([expect.objectContaining({ name: 'someKey', keyVaultKey: mockKey })]) + ); + }); + }); + + describe('getAzureKeyByPubKey', () => { + it('should return the key', async () => { + const key = await azure.getAzureKeyByPubKey(publicKey); + + expect(key).toEqual(expect.objectContaining({ name: 'someKey', keyVaultKey: mockKey })); + }); + }); + + describe('getAzureKey', () => { + it('should return the azure key', async () => { + const result = await azure.getAzureKey('someKey'); + + expect(result).toEqual(expect.objectContaining({ name: 'someKey', keyVaultKey: mockKey })); + }); + + it('should return undefined if key is not found', () => { + return expect(azure.getAzureKey('unknownKey')).resolves.toEqual(undefined); + }); + + it('should throw an error if the key is missing details', async () => { + const expectedError = new AzureSignerError({ message: 'essential key details missing' }); + + return expect(azure.getAzureKey('partialKey')).rejects.toThrow(expectedError); + }); + }); + + describe('signData', () => { + it('should sign the data', async () => { + const bytes = Buffer.from('00', 'hex'); + + const result = await azure.signData(publicKey, bytes); + + expect(result).toEqual( + '0x02b4b24959854a6afdba21e1007955c6c728725009feab9886408d6dc4fb9712e3ab5f30be4f06d2f12f01a10ccd374278d44ab133ed8ee5998341034f9347f15601' + ); + }); + }); +}); diff --git a/packages/azure-signing-manager/src/lib/azure-hsm/azure-hsm.ts b/packages/azure-signing-manager/src/lib/azure-hsm/azure-hsm.ts new file mode 100644 index 00000000..6007d1d4 --- /dev/null +++ b/packages/azure-signing-manager/src/lib/azure-hsm/azure-hsm.ts @@ -0,0 +1,241 @@ +import { DefaultAzureCredential } from '@azure/identity'; +import { + CreateKeyOptions, + CryptographyClient, + KeyClient, + KeyVaultKey, + KnownKeyTypes, + KnownSignatureAlgorithms, +} from '@azure/keyvault-keys'; +import { u8aToHex } from '@polkadot/util'; +import { HexString } from '@polkadot/util/types'; +import { blake2AsU8a } from '@polkadot/util-crypto'; +import { ecdsaRecover } from 'secp256k1'; + +import { AzureSigningManagerArgs } from '../../types'; +import { AzureKey, AzureSignerError } from './types'; +import { bufferToCompressedKey } from './util'; + +export class AzureHsm { + private credential: DefaultAzureCredential; + private keyClient: KeyClient; + + private pubKeyCache: Record = {}; + private nameVersionCache: Record = {}; + + /** + * @hidden + */ + constructor(args: AzureSigningManagerArgs) { + const { keyVaultUrl, credential } = args; + + this.credential = credential ?? new DefaultAzureCredential(); + this.keyClient = new KeyClient(keyVaultUrl, this.credential); + } + + public async createKey(name: string, options?: CreateKeyOptions): Promise { + const existingKey = await this.getAzureKey(name); + if (existingKey) { + throw new AzureSignerError({ + message: 'key already exists with the given name', + data: { name }, + }); + } + + await this.keyClient.createKey(name, 'EC', { ...options, curve: 'P-256K' }); + + const key = await this.getAzureKey(name); + if (key.length === 0) { + throw new AzureSignerError({ message: 'created key was not found by name', data: { name } }); + } + + return key[0]; + } + + public async fetchAllKeys(): Promise { + const { keyClient } = this; + + for await (const { name } of keyClient.listPropertiesOfKeys()) { + const key = await this.getAzureKey(name); + + if (!key) { + throw new AzureSignerError({ message: 'key not found by name', data: { name } }); + } + } + + return Object.values(this.pubKeyCache); + } + + public async getAzureKeyByPubKey(publicKey: string): Promise { + const cachedKey = this.pubKeyCache[publicKey]; + + if (cachedKey) { + return cachedKey; + } + + await this.fetchAllKeys(); + + const newKey = this.pubKeyCache[publicKey]; + + if (!newKey) { + throw new AzureSignerError({ + message: 'key was not found by its public key', + data: { publicKey }, + }); + } + + return newKey; + } + + public async getAzureKey(name: string): Promise { + const azureKeys: AzureKey[] = []; + + for await (const keyProperties of this.keyClient.listPropertiesOfKeyVersions(name)) { + const cachedKey = this.getCachedKeyByName(keyProperties.name, keyProperties.version); + if (cachedKey) { + azureKeys.push(cachedKey); + continue; + } + + const keyVaultKey = await this.getKeyVaultKey(name, keyProperties.version); + + if (!keyVaultKey) { + continue; + } + + const { key, id, properties } = keyVaultKey; + + // Non P-256K keys are not supported so we will just ignore them + if (key?.crv !== 'P-256K') { + continue; + } + + if (!key) { + throw new AzureSignerError({ message: 'key details were not found', data: { name } }); + } + + const { x, y } = key; + + if (!x || !y || !id) { + throw new AzureSignerError({ + message: 'essential key details missing', + data: { name, id, x: u8aToHex(x), y: u8aToHex(y) }, + }); + } + + const keyBuffer = Buffer.concat([Buffer.from([0x04]), x, y]); + const publicKey = bufferToCompressedKey(keyBuffer); + + const azureKey = { + name, + id, + publicKey, + properties, + keyVaultKey, + }; + + this.setPubkeyCache(azureKey); + + azureKeys.push(azureKey); + } + + return azureKeys; + } + + /** + * Sign data on behalf of a key and return the hex signature + * + * @param name - name of the key that will sign + * @param data - data that will be signed + */ + public async signData(pubKey: string, signingPayload: Uint8Array): Promise { + const standardPayload = + signingPayload.length > 256 ? blake2AsU8a(signingPayload) : signingPayload; + + const encoded = blake2AsU8a(standardPayload); + + const { name, publicKey } = await this.getAzureKeyByPubKey(pubKey); + + const cryptoClient = this.getCryptoClient(name); + + const { result: signatureRs } = await cryptoClient.sign( + KnownSignatureAlgorithms.ES256K, + encoded + ); + + // Compute the recovery ID + let recoveryId = null; + for (let recId = 0; recId < 2; recId++) { + try { + const recoveredPubKey = ecdsaRecover(signatureRs, recId, encoded, false); // Uncompressed + + const recoveredKey = bufferToCompressedKey(recoveredPubKey); + + if (Buffer.compare(recoveredKey, publicKey) === 0) { + recoveryId = recId; + break; + } + } catch (error) { + continue; + } + } + + if (recoveryId === null) { + throw new AzureSignerError({ + message: 'Failed to compute recovery ID', + data: { name, publicKey: publicKey.toString('hex') }, + }); + } + + // Construct the signature: [r (32 bytes), s (32 bytes), recoveryId (1 byte)] + const signatureWithRecovery = Buffer.concat([signatureRs, Buffer.from([recoveryId])]); + + // Prefix with 0x02 to indicate ECDSA signature + const ecdsaSignature = Buffer.concat([Buffer.from([2]), signatureWithRecovery]); + + return `0x${ecdsaSignature.toString('hex')}`; + } + + /** + * @hidden + * + * @param name - name of the key to fetch + */ + private async getKeyVaultKey(name: string, version?: string): Promise { + return this.keyClient.getKey(name, { version }).catch(error => { + if (error.code === 'KeyNotFound') { + return undefined; + } + + throw error; + }); + } + + private setPubkeyCache(key: AzureKey): void { + const cacheKey = `0x${key.publicKey.toString('hex')}`; + const cacheName = this.getKeyCacheName(key.name, key.properties.version); + + this.pubKeyCache[cacheKey] = key; + this.nameVersionCache[cacheName] = key; + } + + private getKeyCacheName(name: string, version?: string): string { + return `${name}-${version}`; + } + + private getCachedKeyByName(name: string, version?: string): AzureKey | undefined { + const key = this.getKeyCacheName(name, version); + + return this.nameVersionCache[key]; + } + + /** + * + * @hidden + * + * @param name = name of the key to fetch + */ + private getCryptoClient(name: string): CryptographyClient { + return this.keyClient.getCryptographyClient(name); + } +} diff --git a/packages/azure-signing-manager/src/lib/azure-hsm/index.ts b/packages/azure-signing-manager/src/lib/azure-hsm/index.ts new file mode 100644 index 00000000..ef84a4b0 --- /dev/null +++ b/packages/azure-signing-manager/src/lib/azure-hsm/index.ts @@ -0,0 +1,3 @@ +export * from './azure-hsm'; +export * from './types'; +export * from './util'; diff --git a/packages/azure-signing-manager/src/lib/azure-hsm/types.ts b/packages/azure-signing-manager/src/lib/azure-hsm/types.ts new file mode 100644 index 00000000..b9839fa6 --- /dev/null +++ b/packages/azure-signing-manager/src/lib/azure-hsm/types.ts @@ -0,0 +1,50 @@ +import { KeyProperties, KeyVaultKey } from '@azure/keyvault-keys'; + +export interface AzureKey { + /** + * Internal key ID. + */ + id: string; + /** + * Internal Azure name. This is used to identify the key within the Key Vault + */ + name: string; + + /** + * The public key in raw format + */ + publicKey: Buffer; + + /** + * Additional details about the key + */ + properties: KeyProperties; + + /** + * The Azure key SDK object. Used for interacting with the key + */ + keyVaultKey: KeyVaultKey; +} + +/** + * Signable data as expected by the Azure Key Vault API + */ +export interface SignRequestPayload { + /** + * base64 encoded data to sign + */ + input: string; +} + +export class AzureSignerError extends Error { + public data?: Record; + + /** + * @hidden + */ + constructor({ message, data }: { message?: string; data?: Record }) { + super(message); + + this.data = data; + } +} diff --git a/packages/azure-signing-manager/src/lib/azure-hsm/util.ts b/packages/azure-signing-manager/src/lib/azure-hsm/util.ts new file mode 100644 index 00000000..12a43aba --- /dev/null +++ b/packages/azure-signing-manager/src/lib/azure-hsm/util.ts @@ -0,0 +1,48 @@ +import { PagedAsyncIterableIterator, PageSettings } from '@azure/keyvault-keys'; +import { blake2AsU8a } from '@polkadot/util-crypto'; + +const compressPublicKey = (publicKeyBuffer: Uint8Array): Uint8Array => { + // Ensure the input is a 65-byte uncompressed key starting with 0x04 + if (publicKeyBuffer.length !== 65 || publicKeyBuffer[0] !== 0x04) { + throw new Error('Invalid uncompressed public key format'); + } + + // Extract x and y coordinates + const x = publicKeyBuffer.slice(1, 33); // First 32 bytes after 0x04 + const y = publicKeyBuffer.slice(33, 65); // Next 32 bytes after x + + // Determine whether y is even or odd + const isEvenY = (y[31] & 1) === 0; // Check the least significant bit of y + + // Compressed public key starts with 0x02 if y is even, 0x03 if odd + const prefix = isEvenY ? 0x02 : 0x03; + + // Create the compressed public key by concatenating the prefix with the x coordinate + const compressedPublicKey = new Uint8Array(33); + compressedPublicKey[0] = prefix; + compressedPublicKey.set(x, 1); // Copy the x coordinate starting at position 1 + + return compressedPublicKey; +}; + +export const createPagedAsyncIterableIterator = ( + items: T[] +): PagedAsyncIterableIterator => { + const pagedResults: T[] = items; + return { + async next() { + const value = pagedResults.shift(); + return { value, done: value === undefined }; + }, + [Symbol.asyncIterator]() { + return this; + }, + } as PagedAsyncIterableIterator; +}; + +export const bufferToCompressedKey = (key: Uint8Array): Buffer => { + const publicCompressedKey = compressPublicKey(key); + const publicAccountId = blake2AsU8a(publicCompressedKey); + + return Buffer.from(publicAccountId); +}; diff --git a/packages/azure-signing-manager/src/lib/azure-signing-manager.spec.ts b/packages/azure-signing-manager/src/lib/azure-signing-manager.spec.ts new file mode 100644 index 00000000..ace92157 --- /dev/null +++ b/packages/azure-signing-manager/src/lib/azure-signing-manager.spec.ts @@ -0,0 +1,148 @@ +import { CryptographyClient, KeyClient, KeyVaultKey } from '@azure/keyvault-keys'; +import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { TypeRegistry } from '@polkadot/types'; +import { SignerPayloadJSON } from '@polkadot/types/types'; +import { when } from 'jest-when'; + +import { AzureHsm, AzureSignerError, createPagedAsyncIterableIterator } from './azure-hsm'; +import { AzureSigner, AzureSigningManager } from './azure-signing-manager'; + +const mockKeyClient = createMock(); +const mockCryptographyClient = createMock(); +const mockKeyVaultKey = createMock(); + +jest.mock('@azure/keyvault-keys', () => ({ + ...jest.requireActual('@azure/keyvault-keys'), + KeyClient: jest.fn().mockImplementation(() => mockKeyClient), + CryptographyClient: jest.fn().mockImplementation(() => mockCryptographyClient), + KeyVaultKey: jest.fn().mockImplementation(() => mockKeyVaultKey), +})); + +const address = '5EKiakuf6cT9RZ3oJKto4EKhprtx6afUu5LJozeidGGBGkqy'; + +describe('AzureSigner', () => { + const payload: SignerPayloadJSON = { + address, + specVersion: '0x00000bba', + transactionVersion: '0x00000002', + blockHash: '0xdf06dca982acacbd5f0bcd7a8a062465b8441d569813561ed13ab81883bc08e7', + blockNumber: '0x00000280', + era: '0x0500', + genesisHash: '0x44748824f9798715435c421b5db9af2beae537974d192fab5fb6fc12e1523765', + method: '0x1a005041594c4f41445f54455354', + nonce: '0x00000004', + signedExtensions: [ + 'CheckSpecVersion', + 'CheckTxVersion', + 'CheckGenesis', + 'CheckMortality', + 'CheckNonce', + 'CheckWeight', + 'ChargeTransactionPayment', + ], + tip: '0x00000000000000000000000000000002', + version: 4, + }; + const signature = '0x123'; + + let signer: AzureSigner; + let mockHsm: DeepMocked; + + beforeEach(() => { + const registry = new TypeRegistry(); + mockHsm = createMock(); + mockHsm.signData.mockResolvedValue(signature); + signer = new AzureSigner(mockHsm, registry); + }); + + it('should sign a payload', async () => { + const result = await signer.signPayload(payload); + + expect(result).toEqual({ id: 0, signature }); + }); + + it('should sign a raw payload', async () => { + const raw = { + address, + data: '0x00', + type: 'bytes' as const, + }; + + const result = await signer.signRaw(raw); + + expect(result).toEqual({ id: 0, signature }); + }); +}); + +describe('AzureSigningManager', () => { + let manager: AzureSigningManager; + const x = Buffer.from('d6eb9ae6b44331da8c92a0bd88850b94c7f32981aaf42d1c1345e05af810470a', 'hex'); + const y = Buffer.from('7b4603950822dd8806494a3aff5d6bc2e437b71f7c7de64e9d84a60411dc1293', 'hex'); + + beforeEach(() => { + const mockKeyDetails = [{ name: 'someKey', vaultUrl: 'http://example.com' }]; + mockKeyClient.listPropertiesOfKeys.mockReturnValue( + createPagedAsyncIterableIterator(mockKeyDetails) + ); + + mockKeyClient.getKey.mockRejectedValue({ code: 'KeyNotFound' }); + + when(mockKeyClient.getKey) + .calledWith('someKey') + .mockResolvedValue(createMock({ key: { x, y } })); + + mockKeyClient.createKey.mockImplementation(async name => { + const mockKey = createMock({ key: { x, y } }); + when(mockKeyClient.getKey).calledWith(name).mockResolvedValue(mockKey); + + return mockKey; + }); + + manager = new AzureSigningManager({ keyVaultUrl: 'https://example.com' }); + }); + + describe('createKey', () => { + it('should call create key', async () => { + const result = await manager.createKey('someNewKey'); + + expect(result).toEqual(expect.objectContaining({ name: 'someNewKey' })); + }); + + it('should throw an error if creating a key that already exists', () => { + const expectedError = new AzureSignerError({ + message: 'a key already exists with that name', + }); + + return expect(manager.createKey('someKey')).rejects.toThrow(expectedError); + }); + }); + + describe('getAccounts', () => { + it('should return all of the accounts', async () => { + manager.setSs58Format(42); + const result = await manager.getAccounts(); + + expect(result).toEqual(['5EKiakuf6cT9RZ3oJKto4EKhprtx6afUu5LJozeidGGBGkqy']); + }); + + it('should throw an error if ss58 format was not set', async () => { + return expect(manager.getAccounts()).rejects.toThrow( + "Cannot call 'getAccounts' before calling 'setSs58Format'. Did you forget to use this Signing Manager to connect with the Polymesh SDK?" + ); + }); + }); + + describe('getExternalSigner', () => { + it('should return the external signer', () => { + const result = manager.getExternalSigner(); + + expect(result).toBeInstanceOf(AzureSigner); + }); + }); + + describe('setSs58Format', () => { + it('should set the ss58Format', () => { + expect(() => manager.setSs58Format(42)).not.toThrow(); + }); + }); +}); diff --git a/packages/azure-signing-manager/src/lib/azure-signing-manager.ts b/packages/azure-signing-manager/src/lib/azure-signing-manager.ts new file mode 100644 index 00000000..2d4c70b9 --- /dev/null +++ b/packages/azure-signing-manager/src/lib/azure-signing-manager.ts @@ -0,0 +1,160 @@ +import { CreateKeyOptions } from '@azure/keyvault-keys'; +import { TypeRegistry } from '@polkadot/types'; +import { SignerPayloadJSON, SignerPayloadRaw, SignerResult } from '@polkadot/types/types'; +import { hexToU8a, u8aToHex } from '@polkadot/util'; +import { blake2AsU8a, decodeAddress, encodeAddress } from '@polkadot/util-crypto'; +import { + PolkadotSigner, + signedExtensions, + SigningManager, +} from '@polymeshassociation/signing-manager-types'; + +import { AddressedAzureKey, AzureSigningManagerArgs } from '../types'; +import { AzureHsm, AzureSignerError } from './azure-hsm'; + +export class AzureSigner implements PolkadotSigner { + private currentId = -1; + + constructor(private readonly azure: AzureHsm, private readonly registry: TypeRegistry) {} + + public async signPayload(payload: SignerPayloadJSON): Promise { + const { registry } = this; + const { address, version } = payload; + + const signablePayload = registry.createType('ExtrinsicPayload', payload, { + version, + }); + + return this.signData(address, signablePayload.toU8a(true)); + } + + public async signRaw(raw: SignerPayloadRaw): Promise { + const { address, data } = raw; + + return this.signData(address, hexToU8a(data)); + } + + /** + * @hidden + * + * Use the Azure HSM to sign raw data and return the signature + update ID + */ + private async signData(address: string, data: Uint8Array): Promise { + const fixedData = data.length > 256 ? blake2AsU8a(data) : data; + + const pubKey = u8aToHex(decodeAddress(address)); + + const signature = await this.azure.signData(pubKey, fixedData); + + const id = (this.currentId += 1); + + return { + signature, + id, + }; + } +} + +/** + * Enables signing Polymesh SDK transactions using a Microsoft Azure key vault + * + * @note only elliptic curve keys on P-256K are valid Polymesh. Any other key type will be ignored + */ +export class AzureSigningManager implements SigningManager { + private azureHsm: AzureHsm; + private _ss58Format?: number; + private externalSigner: PolkadotSigner; + + constructor(args: AzureSigningManagerArgs) { + const registry = new TypeRegistry(); + registry.setSignedExtensions(signedExtensions); + + this.azureHsm = new AzureHsm(args); + this.externalSigner = new AzureSigner(this.azureHsm, registry); + } + + /** + * Creates a key with the given name + * + * @note keys must use the P-256K curve (aka secp256k1) + * + * @throws AzureSignerError if the name is already used + * + * @returns key details + */ + public async createKey(name: string, options?: CreateKeyOptions): Promise { + if (options?.curve && options.curve !== 'P-256K') { + throw new AzureSignerError({ + message: 'Only curve P-256K is supported on Polymesh', + data: { curve: options.curve }, + }); + } + + const ss58Format = this.getSs58Format('createKey'); + const key = await this.azureHsm.createKey(name, options); + + return { + ...key, + address: encodeAddress(key.publicKey, ss58Format), + }; + } + + /** + * @note `getAzureKeys` returns additional details + * + * @returns all addresses stored in the key vault + */ + public async getAccounts(): Promise { + const ss58Format = this.getSs58Format('getAccounts'); + const keys = await this.azureHsm.fetchAllKeys(); + + return keys.map(({ publicKey }) => encodeAddress(publicKey, ss58Format)); + } + + /** + * + * @returns signer compatible implementing polkadot-js signer interface + */ + public getExternalSigner(): PolkadotSigner { + return this.externalSigner; + } + + /** + * Sets the ss58 format so addresses can be properly formatted. This must be called before other methods are usable + * + * @note The Polymesh SDK will call this method internally. This only needs to be called if the SDK is not being used + */ + public setSs58Format(ss58Format: number): void { + this._ss58Format = ss58Format; + } + + /** + * @returns Details about all available signing keys within the key vault + */ + public async getAzureKeys(): Promise { + const ss58Format = this.getSs58Format('getVaultKeys'); + const keys = await this.azureHsm.fetchAllKeys(); + + return keys.map(key => ({ + ...key, + address: encodeAddress(key.publicKey, ss58Format), + })); + } + + /** + * @hidden + * + * @throws if the SS58 format hasn't been set yet + */ + private getSs58Format(methodName: string): number { + const { _ss58Format: format } = this; + + if (format === undefined) { + throw new Error( + `Cannot call '${methodName}' before calling 'setSs58Format'. Did you forget to use this Signing Manager to connect with the Polymesh SDK?` + ); + } + + return format; + } +} diff --git a/packages/azure-signing-manager/src/types/index.ts b/packages/azure-signing-manager/src/types/index.ts new file mode 100644 index 00000000..cb186977 --- /dev/null +++ b/packages/azure-signing-manager/src/types/index.ts @@ -0,0 +1,24 @@ +import { DefaultAzureCredential } from '@azure/identity'; + +import { AzureKey } from '../lib/azure-hsm'; + +export interface AddressedAzureKey extends AzureKey { + /** + * ss58 encoded version of the publicKey + */ + address: string; +} + +/** + * Arguments required to construct an Azure Signing Manager + */ +export interface AzureSigningManagerArgs { + /** + * The key vault URL where the keys are stored + */ + keyVaultUrl: string; + /** + * @optional The Azure credential object. If not provided `new DefaultAzureCredential()` will be used. More information can be found in Microsoft's documentation: https://learn.microsoft.com/en-us/javascript/api/@azure/identity/defaultazurecredential?view=azure-node-latest + */ + credential?: DefaultAzureCredential; +} diff --git a/packages/azure-signing-manager/tsconfig.json b/packages/azure-signing-manager/tsconfig.json new file mode 100644 index 00000000..e258886f --- /dev/null +++ b/packages/azure-signing-manager/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + } +} diff --git a/packages/azure-signing-manager/tsconfig.lib.json b/packages/azure-signing-manager/tsconfig.lib.json new file mode 100644 index 00000000..2ad6b016 --- /dev/null +++ b/packages/azure-signing-manager/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["**/*.spec.ts", "**/*.test.ts", "**/mocks.ts", "**/*.example.ts", "sandbox"], + "include": ["**/*.ts"] +} diff --git a/packages/azure-signing-manager/tsconfig.spec.json b/packages/azure-signing-manager/tsconfig.spec.json new file mode 100644 index 00000000..60a91956 --- /dev/null +++ b/packages/azure-signing-manager/tsconfig.spec.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"], + "allowJs": true, + "esModuleInterop": true + }, + "include": [ + "**/*.test.ts", + "**/*.spec.ts", + "**/*.test.tsx", + "**/*.spec.tsx", + "**/*.test.js", + "**/*.spec.js", + "**/*.test.jsx", + "**/*.spec.jsx", + "**/*.d.ts", + "**/mocks.ts" +, "src/lib/azure-signing-manager.spec.ts" ] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index bcfd0e8b..a6302754 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -16,6 +16,7 @@ "baseUrl": ".", "paths": { "@polymeshassociation/approval-signing-manager": ["packages/approval-signing-manager/src"], + "@polymeshassociation/azure-signing-manager": ["packages/azure-signing-manager/src"], "@polymeshassociation/browser-extension-signing-manager": [ "packages/browser-extension-signing-manager/src" ], diff --git a/workspace.json b/workspace.json index cafa59ec..7ae8237d 100644 --- a/workspace.json +++ b/workspace.json @@ -2,11 +2,12 @@ "version": 2, "projects": { "approval-signing-manager": "packages/approval-signing-manager", + "azure-signing-manager": "packages/azure-signing-manager", "browser-extension-signing-manager": "packages/browser-extension-signing-manager", "fireblocks-signing-manager": "packages/fireblocks-signing-manager", "hashicorp-vault-signing-manager": "packages/hashicorp-vault-signing-manager", "local-signing-manager": "packages/local-signing-manager", - "walletconnect-signing-manager": "packages/walletconnect-signing-manager", - "types": "packages/types" + "types": "packages/types", + "walletconnect-signing-manager": "packages/walletconnect-signing-manager" } } diff --git a/yarn.lock b/yarn.lock index a0c16bb6..455760cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -157,6 +157,162 @@ resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.10.1.tgz#70e45678f06c72fa2e350e8553ec4a4d72b92e06" integrity sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg== +"@azure/abort-controller@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.1.0.tgz#788ee78457a55af8a1ad342acb182383d2119249" + integrity sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw== + dependencies: + tslib "^2.2.0" + +"@azure/abort-controller@^2.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-2.1.2.tgz#42fe0ccab23841d9905812c58f1082d27784566d" + integrity sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA== + dependencies: + tslib "^2.6.2" + +"@azure/core-auth@^1.3.0", "@azure/core-auth@^1.4.0", "@azure/core-auth@^1.5.0", "@azure/core-auth@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.8.0.tgz#281b4a6d3309c3e7b15bcd967f01d4c79ae4a1d6" + integrity sha512-YvFMowkXzLbXNM11yZtVLhUCmuG0ex7JKOH366ipjmHBhL3vpDcPAeWF+jf0X+jVXwFqo3UhsWUq4kH0ZPdu/g== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.6.2" + +"@azure/core-client@^1.3.0", "@azure/core-client@^1.5.0", "@azure/core-client@^1.9.2": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@azure/core-client/-/core-client-1.9.2.tgz#6fc69cee2816883ab6c5cdd653ee4f2ff9774f74" + integrity sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-auth" "^1.4.0" + "@azure/core-rest-pipeline" "^1.9.1" + "@azure/core-tracing" "^1.0.0" + "@azure/core-util" "^1.6.1" + "@azure/logger" "^1.0.0" + tslib "^2.6.2" + +"@azure/core-http-compat@^2.0.1": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@azure/core-http-compat/-/core-http-compat-2.1.2.tgz#d1585ada24ba750dc161d816169b33b35f762f0d" + integrity sha512-5MnV1yqzZwgNLLjlizsU3QqOeQChkIXw781Fwh1xdAqJR5AA32IUaq6xv1BICJvfbHoa+JYcaij2HFkhLbNTJQ== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-client" "^1.3.0" + "@azure/core-rest-pipeline" "^1.3.0" + +"@azure/core-lro@^2.2.0": + version "2.7.2" + resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-2.7.2.tgz#787105027a20e45c77651a98b01a4d3b01b75a08" + integrity sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-util" "^1.2.0" + "@azure/logger" "^1.0.0" + tslib "^2.6.2" + +"@azure/core-paging@^1.1.1": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@azure/core-paging/-/core-paging-1.6.2.tgz#40d3860dc2df7f291d66350b2cfd9171526433e7" + integrity sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA== + dependencies: + tslib "^2.6.2" + +"@azure/core-rest-pipeline@^1.1.0", "@azure/core-rest-pipeline@^1.3.0", "@azure/core-rest-pipeline@^1.8.1", "@azure/core-rest-pipeline@^1.9.1": + version "1.17.0" + resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.17.0.tgz#55dafa1093553c549ed6d8dbca69aa505c7b3aa3" + integrity sha512-62Vv8nC+uPId3j86XJ0WI+sBf0jlqTqPUFCBNrGtlaUeQUIXWV/D8GE5A1d+Qx8H7OQojn2WguC8kChD6v0shA== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-auth" "^1.8.0" + "@azure/core-tracing" "^1.0.1" + "@azure/core-util" "^1.9.0" + "@azure/logger" "^1.0.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.0" + tslib "^2.6.2" + +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.1.2.tgz#065dab4e093fb61899988a1cdbc827d9ad90b4ee" + integrity sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA== + dependencies: + tslib "^2.6.2" + +"@azure/core-util@^1.0.0", "@azure/core-util@^1.1.0", "@azure/core-util@^1.2.0", "@azure/core-util@^1.3.0", "@azure/core-util@^1.6.1", "@azure/core-util@^1.9.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.10.0.tgz#cf3163382d40343972848c914869864df5d44bdb" + integrity sha512-dqLWQsh9Nro1YQU+405POVtXnwrIVqPyfUzc4zXCbThTg7+vNNaiMkwbX9AMXKyoFYFClxmB3s25ZFr3+jZkww== + dependencies: + "@azure/abort-controller" "^2.0.0" + tslib "^2.6.2" + +"@azure/identity@4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-4.4.1.tgz#490fa2ad26786229afa36411892bb53dfa3478d3" + integrity sha512-DwnG4cKFEM7S3T+9u05NstXU/HN0dk45kPOinUyNKsn5VWwpXd9sbPKEg6kgJzGbm1lMuhx9o31PVbCtM5sfBA== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-client" "^1.9.2" + "@azure/core-rest-pipeline" "^1.1.0" + "@azure/core-tracing" "^1.0.0" + "@azure/core-util" "^1.3.0" + "@azure/logger" "^1.0.0" + "@azure/msal-browser" "^3.14.0" + "@azure/msal-node" "^2.9.2" + events "^3.0.0" + jws "^4.0.0" + open "^8.0.0" + stoppable "^1.1.0" + tslib "^2.2.0" + +"@azure/keyvault-keys@4.8.0": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@azure/keyvault-keys/-/keyvault-keys-4.8.0.tgz#1513b3a187bb3a9a372b5980c593962fb793b2ad" + integrity sha512-jkuYxgkw0aaRfk40OQhFqDIupqblIOIlYESWB6DKCVDxQet1pyv86Tfk9M+5uFM0+mCs6+MUHU+Hxh3joiUn4Q== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-auth" "^1.3.0" + "@azure/core-client" "^1.5.0" + "@azure/core-http-compat" "^2.0.1" + "@azure/core-lro" "^2.2.0" + "@azure/core-paging" "^1.1.1" + "@azure/core-rest-pipeline" "^1.8.1" + "@azure/core-tracing" "^1.0.0" + "@azure/core-util" "^1.0.0" + "@azure/logger" "^1.0.0" + tslib "^2.2.0" + +"@azure/logger@^1.0.0": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.1.4.tgz#223cbf2b424dfa66478ce9a4f575f59c6f379768" + integrity sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ== + dependencies: + tslib "^2.6.2" + +"@azure/msal-browser@^3.14.0": + version "3.24.0" + resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-3.24.0.tgz#3208047672d0b0c943b0bef5f995d510d6582ae4" + integrity sha512-JGNV9hTYAa7lsum9IMIibn2kKczAojNihGo1hi7pG0kNrcKej530Fl6jxwM05A44/6I079CSn6WxYxbVhKUmWg== + dependencies: + "@azure/msal-common" "14.15.0" + +"@azure/msal-common@14.15.0": + version "14.15.0" + resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.15.0.tgz#0e27ac0bb88fe100f4f8d1605b64d5c268636a55" + integrity sha512-ImAQHxmpMneJ/4S8BRFhjt1MZ3bppmpRPYYNyzeQPeFN288YKbb8TmmISQEbtfkQ1BPASvYZU5doIZOPBAqENQ== + +"@azure/msal-node@^2.9.2": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-2.14.0.tgz#7881895d41b03d8b9b38a29550ba3bbb15f73b3c" + integrity sha512-rrfzIpG3Q1rHjVYZmHAEDidWAZZ2cgkxlIcMQ8dHebRISaZ2KCV33Q8Vs+uaV6lxweROabNxKFlR2lIKagZqYg== + dependencies: + "@azure/msal-common" "14.15.0" + jsonwebtoken "^9.0.0" + uuid "^8.3.0" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3": version "7.16.7" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz" @@ -1355,6 +1511,11 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== +"@golevelup/ts-jest@^0.5.6": + version "0.5.6" + resolved "https://registry.yarnpkg.com/@golevelup/ts-jest/-/ts-jest-0.5.6.tgz#e63e3d746417de07cbd5d45208de380cd185346a" + integrity sha512-QnxP42Qu9M2UogdrF2kxpZcgWeW9R3WoUr+LdpcsbkvX3LjEoDYgrJ/PnV/QUCbxB1gaKbhR0ZxlDxE1cPkpDg== + "@humanwhocodes/config-array@^0.9.2": version "0.9.3" resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz" @@ -1446,6 +1607,13 @@ "@types/node" "*" jest-mock "^27.5.1" +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + "@jest/fake-timers@^27.5.1": version "27.5.1" resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz" @@ -1528,6 +1696,13 @@ terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + "@jest/source-map@^27.5.1": version "27.5.1" resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz" @@ -1599,6 +1774,18 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jimp/bmp@^0.16.1": version "0.16.1" resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.16.1.tgz#6e2da655b2ba22e721df0795423f34e92ef13768" @@ -3119,6 +3306,11 @@ lodash "^4.17.4" read-pkg-up "^7.0.0" +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + "@sindresorhus/is@^0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" @@ -3614,6 +3806,21 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/jest-when@^3.5.2": + version "3.5.5" + resolved "https://registry.yarnpkg.com/@types/jest-when/-/jest-when-3.5.5.tgz#c23e97945959277946c15eff2a2fe51d18743045" + integrity sha512-H9MDPIrz7NOu6IXP9OHExNN9LnJbGYAzRsGIDKxWr7Fth9vovemNV8yFbkUWLSEmuA8PREvAEvt9yK0PPLmFHA== + dependencies: + "@types/jest" "*" + +"@types/jest@*": + version "29.5.13" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.13.tgz#8bc571659f401e6a719a7bf0dbcb8b78c71a8adc" + integrity sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/jest@27.0.2": version "27.0.2" resolved "https://registry.npmjs.org/@types/jest/-/jest-27.0.2.tgz" @@ -3687,6 +3894,13 @@ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065" integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g== +"@types/secp256k1@4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.6.tgz#d60ba2349a51c2cbc5e816dcd831a42029d376bf" + integrity sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ== + dependencies: + "@types/node" "*" + "@types/serve-index@^1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" @@ -3738,6 +3952,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yargs@^17.0.8": + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== + dependencies: + "@types/yargs-parser" "*" + "@typescript-eslint/eslint-plugin@~5.10.0": version "5.10.2" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.2.tgz" @@ -4260,6 +4481,13 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" +agent-base@^7.0.2, agent-base@^7.1.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + agentkeepalive@^4.1.3, agentkeepalive@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.0.tgz#616ce94ccb41d1a39a45d203d8076fe98713062d" @@ -6249,6 +6477,13 @@ debug@^3.1.1, debug@^3.2.6: dependencies: ms "^2.1.1" +debug@^4.3.4: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -6517,6 +6752,11 @@ diff-sequences@^27.5.1: resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz" integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + diff@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" @@ -6756,6 +6996,19 @@ elliptic@^6.5.3: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +elliptic@^6.5.4: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emittery@^0.8.1: version "0.8.1" resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz" @@ -7206,7 +7459,7 @@ eventemitter3@^5.0.1: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== -events@3.3.0, events@^3.2.0, events@^3.3.0: +events@3.3.0, events@^3.0.0, events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -7329,6 +7582,17 @@ expect@^27.5.1: jest-matcher-utils "^27.5.1" jest-message-util "^27.5.1" +expect@^29.0.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + express@^4.17.1: version "4.17.3" resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" @@ -8476,6 +8740,14 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" +http-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + http-proxy-middleware@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.3.tgz#5df04f69a89f530c2284cd71eeaa51ba52243289" @@ -8523,6 +8795,14 @@ https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-proxy-agent@^7.0.0: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -9402,6 +9682,16 @@ jest-diff@^27.0.0, jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-docblock@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz" @@ -9450,6 +9740,11 @@ jest-get-type@^27.0.6, jest-get-type@^27.5.1: resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz" integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + jest-haste-map@^27.2.2, jest-haste-map@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz" @@ -9511,6 +9806,16 @@ jest-matcher-utils@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-message-util@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz" @@ -9526,6 +9831,21 @@ jest-message-util@^27.5.1: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz" @@ -9700,6 +10020,18 @@ jest-util@^27.0.0, jest-util@^27.2.0, jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^27.2.2, jest-validate@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz" @@ -9725,6 +10057,11 @@ jest-watcher@^27.5.1: jest-util "^27.5.1" string-length "^4.0.1" +jest-when@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jest-when/-/jest-when-3.6.0.tgz#b46ee408d68f671447b218f2ae6bd93fb5028acf" + integrity sha512-+cZWTy0ekAJo7M9Om0Scdor1jm3wDiYJWmXE8U22UVnkH54YCXAuaqz3P+up/FdtOg8g4wHOxV7Thd7nKhT6Dg== + jest-worker@^27.2.2, jest-worker@^27.4.5, jest-worker@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" @@ -9965,6 +10302,22 @@ jsonwebtoken@8.5.1: ms "^2.1.1" semver "^5.6.0" +jsonwebtoken@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^7.5.4" + jsprim@^1.2.2: version "1.4.2" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" @@ -9999,6 +10352,15 @@ jwa@^1.4.1: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" +jwa@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" + integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + jws@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" @@ -10007,6 +10369,14 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +jws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== + dependencies: + jwa "^2.0.0" + safe-buffer "^5.0.1" + karma-source-map-support@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz#58526ceccf7e8730e56effd97a4de8d712ac0d6b" @@ -10983,7 +11353,7 @@ ms@2.1.2: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -11107,6 +11477,11 @@ node-addon-api@^4.3.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== +node-addon-api@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" + integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== + node-addon-api@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.0.tgz#71f609369379c08e251c558527a107107b5e0fdb" @@ -11162,6 +11537,11 @@ node-forge@^1.3.1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== +node-gyp-build@^4.2.0: + version "4.8.2" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.2.tgz#4f802b71c1ab2ca16af830e6c1ea7dd1ad9496fa" + integrity sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw== + node-gyp-build@^4.2.2, node-gyp-build@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz" @@ -11642,6 +12022,15 @@ open@8.4.0, open@^8.0.9, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +open@^8.0.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -12616,6 +13005,15 @@ pretty-format@^27.0.0, pretty-format@^27.2.2, pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + proc-log@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-1.0.0.tgz#0d927307401f69ed79341e83a0b2c9a13395eb77" @@ -12871,6 +13269,11 @@ react-is@^17.0.1: resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + read-cache@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" @@ -13399,6 +13802,15 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.0.0" +secp256k1@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.0.tgz#be6f0c8c7722e2481e9773336d351de8cddd12f7" + integrity sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^5.0.0" + node-gyp-build "^4.2.0" + seek-bzip@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4" @@ -13505,6 +13917,11 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.5.4: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + send@0.17.2: version "0.17.2" resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" @@ -13992,6 +14409,11 @@ std-env@^3.7.0: resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== +stoppable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" + integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== + stream-browserify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" @@ -15100,7 +15522,7 @@ uuid@^3.0.1, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.2: +uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==