From d98b358ff7f9c787667b4bf48fd748ae9f58197a Mon Sep 17 00:00:00 2001 From: nklomp Date: Wed, 11 Jan 2023 20:03:33 +0100 Subject: [PATCH] feat: did utils package --- package.json | 4 +- packages/did-utils/CHANGELOG.md | 59 +++++++ packages/did-utils/LICENSE | 201 ++++++++++++++++++++++ packages/did-utils/README.md | 49 ++++++ packages/did-utils/api-extractor.json | 3 + packages/did-utils/package.json | 39 +++++ packages/did-utils/src/didFunctions.ts | 154 +++++++++++++++++ packages/did-utils/src/index.ts | 3 + packages/did-utils/src/types.ts | 16 ++ packages/did-utils/src/x509-utils.ts | 138 +++++++++++++++ packages/did-utils/tsconfig.json | 8 + packages/tsconfig.json | 1 + packages/vc-handler-ld-local/src/index.ts | 2 +- patches/@veramo+core+4.2.0.patch | 60 +++++++ yarn.lock | 121 ++++++++++++- 15 files changed, 851 insertions(+), 7 deletions(-) create mode 100644 packages/did-utils/CHANGELOG.md create mode 100644 packages/did-utils/LICENSE create mode 100644 packages/did-utils/README.md create mode 100644 packages/did-utils/api-extractor.json create mode 100644 packages/did-utils/package.json create mode 100644 packages/did-utils/src/didFunctions.ts create mode 100644 packages/did-utils/src/index.ts create mode 100644 packages/did-utils/src/types.ts create mode 100644 packages/did-utils/src/x509-utils.ts create mode 100644 packages/did-utils/tsconfig.json create mode 100644 patches/@veramo+core+4.2.0.patch diff --git a/package.json b/package.json index d9d961f13..7a8618958 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "build-clean": "rimraf ./packages/*/dist ./packages/*/api ./packages/*/node_modules ./packages/*/tsconfig.tsbuildinfo && jest --clearCache", "publish:latest": "lerna publish --conventional-commits --include-merged-tags --create-release github --yes --dist-tag latest --registry https://registry.npmjs.org", "publish:next": "lerna publish --conventional-prerelease --force-publish --canary --no-git-tag-version --include-merged-tags --preid next --pre-dist-tag next --yes --registry https://registry.npmjs.org", - "publish:unstable": "lerna publish --conventional-prerelease --force-publish --canary --no-git-tag-version --include-merged-tags --preid unstable --pre-dist-tag unstable --yes --registry https://registry.npmjs.org" + "publish:unstable": "lerna publish --conventional-prerelease --force-publish --canary --no-git-tag-version --include-merged-tags --preid unstable --pre-dist-tag unstable --yes --registry https://registry.npmjs.org", + "postinstall": "patch-package" }, "workspaces": { "nohoist": [ @@ -68,6 +69,7 @@ "openapi-types": "^10.0.0", "prettier": "^2.5.1", "pretty-quick": "^3.1.3", + "patch-package": "^6.5.1", "rimraf": "^3.0.2", "semantic-release": "^19.0.5", "ts-jest": "^27.1.3", diff --git a/packages/did-utils/CHANGELOG.md b/packages/did-utils/CHANGELOG.md new file mode 100644 index 000000000..7ce2a77f8 --- /dev/null +++ b/packages/did-utils/CHANGELOG.md @@ -0,0 +1,59 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.8.0](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.7.0...v0.8.0) (2022-09-03) + +### Bug Fixes + +- Remove most deps from ssi-sdk-core to prevent circular deps ([b4151a9](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/b4151a9cde3e5e5dcabb32367e7a6b6ab99cb6cd)) + +### Features + +- Create common SSI types package ([0fdc372](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/0fdc3722e3bc47ac13c3c586535937fa1ebe6f68)) + +# [0.7.0](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.6.0...v0.7.0) (2022-08-05) + +**Note:** Version bump only for package @sphereon/ssi-sdk-core + +# [0.6.0](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.5.1...v0.6.0) (2022-07-01) + +**Note:** Version bump only for package @sphereon/ssi-sdk-core + +# [0.5.0](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.4.0...v0.5.0) (2022-02-23) + +**Note:** Version bump only for package @sphereon/ssi-sdk-core + +# [0.4.0](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.3.4...v0.4.0) (2022-02-11) + +**Note:** Version bump only for package @sphereon/ssi-sdk-core + +## [0.3.4](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.3.3...v0.3.4) (2022-02-11) + +**Note:** Version bump only for package @sphereon/ssi-sdk-core + +## [0.3.1](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.3.0...v0.3.1) (2022-01-28) + +**Note:** Version bump only for package @sphereon/ssi-sdk-core + +# [0.3.0](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.2.0...v0.3.0) (2022-01-16) + +**Note:** Version bump only for package @sphereon/ssi-sdk-core + +# [0.2.0](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.1.0...v0.2.0) (2021-12-16) + +### Bug Fixes + +- Multibase encoding didn't include the prefix char ([1be44b7](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/1be44b7f281b82370a59a321f25057bee34d58de)) + +### Features + +- Add JSON-LD Credential and Presentation handling/sign support that is compatible with React-Native ([995f55e](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/995f55efd5237e3fbd76e6569e09ee3bbcbb686c)) + +# 0.1.0 (2021-11-26) + +### Features + +- Add ssi-sdk core module ([42a5b65](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/42a5b65fa3795284fc16b06d2a36c4bf4ea87668)) +- Add workspace/lerna files and structures ([2c2b112](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/2c2b11244c2e5e3d2d1b1db76af3d86ec300bc72)) diff --git a/packages/did-utils/LICENSE b/packages/did-utils/LICENSE new file mode 100644 index 000000000..ef0dcdac2 --- /dev/null +++ b/packages/did-utils/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this fragment. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2021] [Sphereon BV] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/did-utils/README.md b/packages/did-utils/README.md new file mode 100644 index 000000000..473f11611 --- /dev/null +++ b/packages/did-utils/README.md @@ -0,0 +1,49 @@ + +

+
+ Sphereon +
SSI SDK DID Utils +
+

+ +--- + +**Warning: This package still is in very early development. Breaking changes without notice will happen at this point!** + +--- + +# ssi-sdk-core + +A plugin with different DID functions + +### Installation + +```shell +yarn add @sphereon/ssi-sdk-did-utils +``` + +### Build + +```shell +yarn build +``` + +### Test + +The test command runs: + +- `prettier` +- `jest` +- `coverage` + +You can also run only a single section of these tests, using for example `yarn test:unit`. + +```shell +yarn test +``` + +### Utility scripts + +There are other utility scripts that help with development. + +- `yarn fix` - runs `eslint --fix` as well as `prettier` to fix code style. diff --git a/packages/did-utils/api-extractor.json b/packages/did-utils/api-extractor.json new file mode 100644 index 000000000..94c2c6a9f --- /dev/null +++ b/packages/did-utils/api-extractor.json @@ -0,0 +1,3 @@ +{ + "extends": "../include/api-extractor-base.json" +} diff --git a/packages/did-utils/package.json b/packages/did-utils/package.json new file mode 100644 index 000000000..129d3b1d4 --- /dev/null +++ b/packages/did-utils/package.json @@ -0,0 +1,39 @@ +{ + "name": "@sphereon/ssi-sdk-did-utils", + "description": "DID Utils", + "version": "0.8.0", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc --build" + }, + "dependencies": { + "@sphereon/ssi-types": "^0.8.0", + "@sphereon/ssi-sdk-core": "^0.8.0", + "@veramo/core": "^4.2.0", + "@veramo/utils": "^4.2.0", + "elliptic": "^6.5.4", + "uint8arrays": "^3.1.1", + "@trust/keyto": "^2.0.0-alpha1", + "@sphereon/jsencrypt": "3.3.2-unstable.0" + }, + "devDependencies": { + "@types/debug": "4.1.7", + "did-resolver": "^4.0.1", + "typescript": "4.6.4" + }, + "files": [ + "dist/**/*", + "src/**/*", + "plugin.schema.json", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "repository": "git@github.com:Sphereon-OpenSource/ssi-sdk.git", + "author": "Sphereon ", + "license": "Apache-2.0", + "keywords": [] +} diff --git a/packages/did-utils/src/didFunctions.ts b/packages/did-utils/src/didFunctions.ts new file mode 100644 index 000000000..3f78f028e --- /dev/null +++ b/packages/did-utils/src/didFunctions.ts @@ -0,0 +1,154 @@ +import { DIDDocument, DIDDocumentSection, IAgentContext, IIdentifier, IResolver } from '@veramo/core' +import { + _ExtendedIKey, + _ExtendedVerificationMethod, + _NormalizedVerificationMethod, + extractPublicKeyHex, + isDefined, + mapIdentifierKeysToDoc, + resolveDidOrThrow, +} from '@veramo/utils' +import { VerificationMethod } from 'did-resolver' +// @ts-ignore +import elliptic from 'elliptic' +import * as u8a from 'uint8arrays' + +export const getFirstKeyWithRelation = async ( + identifier: IIdentifier, + context: IAgentContext, + vmRelationship?: DIDDocumentSection, + errorOnNotFound?: boolean +): Promise<_ExtendedIKey | undefined> => { + const section = vmRelationship || 'verificationMethod' // search all VMs in case no relationship is provided + const matchedKeys = await mapIdentifierKeysToDocWithJwkSupport(identifier, section, context) + if (Array.isArray(matchedKeys) && matchedKeys.length > 0) { + return matchedKeys[0] + } + if (errorOnNotFound === true) { + throw new Error(`Could not find key with relationship ${section} in DID document for ${identifier.did}`) + } + return undefined +} + +//TODO: Move to ssi-sdk/core and create PR upstream +/** + * Dereferences keys from DID document and normalizes them for easy comparison. + * + * When dereferencing keyAgreement keys, only Ed25519 and X25519 curves are supported. + * Other key types are omitted from the result and Ed25519 keys are converted to X25519 + * + * @returns a Promise that resolves to the list of dereferenced keys. + * + * @beta This API may change without a BREAKING CHANGE notice. + */ +export async function dereferenceDidKeysWithJwkSupport( + didDocument: DIDDocument, + section: DIDDocumentSection = 'keyAgreement', + context: IAgentContext +): Promise<_NormalizedVerificationMethod[]> { + const convert = section === 'keyAgreement' + if (section === 'service') { + return [] + } + return ( + await Promise.all( + (didDocument[section] || []).map(async (key: string | VerificationMethod) => { + if (typeof key === 'string') { + try { + return (await context.agent.getDIDComponentById({ + didDocument, + didUrl: key, + section, + })) as _ExtendedVerificationMethod + } catch (e) { + return null + } + } else { + return key as _ExtendedVerificationMethod + } + }) + ) + ) + .filter(isDefined) + .map((key) => { + const hexKey = extractPublicKeyHexWithJwkSupport(key, convert) + const { publicKeyHex, publicKeyBase58, publicKeyBase64, publicKeyJwk, ...keyProps } = key + const newKey = { ...keyProps, publicKeyHex: hexKey } + if (convert && 'Ed25519VerificationKey2018' === newKey.type) { + newKey.type = 'X25519KeyAgreementKey2019' + } + return newKey + }) +} + +/** + * Converts the publicKey of a VerificationMethod to hex encoding (publicKeyHex) + * + * @param pk - the VerificationMethod to be converted + * @param convert - when this flag is set to true, Ed25519 keys are converted to their X25519 pairs + * @returns the hex encoding of the public key + * + * @beta This API may change without a BREAKING CHANGE notice. + */ +export function extractPublicKeyHexWithJwkSupport(pk: _ExtendedVerificationMethod, convert = false): string { + if (pk.publicKeyJwk && pk.publicKeyJwk.kty === 'EC') { + const secp256 = new elliptic.ec(pk.publicKeyJwk.crv === 'secp256k1' ? 'secp256k1' : 'p256') + const prefix = pk.publicKeyJwk.crv === 'secp256k1' ? '04' : '03' + const x = u8a.fromString(pk.publicKeyJwk.x!, 'base64url') + const y = u8a.fromString(pk.publicKeyJwk.y!, 'base64url') + const hex = `${prefix}${u8a.toString(x, 'base16')}${u8a.toString(y, 'base16')}` + // We return directly as we don't want to convert the result back into Uint8Array and then convert again to hex as the elliptic lib already returns hex strings + return secp256.keyFromPublic(hex, 'hex').getPublic(true, 'hex') + } else if (pk.publicKeyJwk && pk.publicKeyJwk.crv === 'Ed25519') { + return u8a.toString(u8a.fromString(pk.publicKeyJwk.x!, 'base64url'), 'base16') + } else { + // delegate the other types to the original Veramo function + return extractPublicKeyHex(pk, convert) + } +} + +/** + * Maps the keys of a locally managed {@link @veramo/core#IIdentifier | IIdentifier} to the corresponding + * {@link did-resolver#VerificationMethod | VerificationMethod} entries from the DID document. + * + * @param identifier - the identifier to be mapped + * @param section - the section of the DID document to be mapped (see + * {@link https://www.w3.org/TR/did-core/#verification-relationships | verification relationships}), but can also be + * `verificationMethod` to map all the keys. + * @param context - the veramo agent context, which must contain a {@link @veramo/core#IResolver | IResolver} + * implementation that can resolve the DID document of the identifier. + * + * @returns an array of mapped keys. The corresponding verification method is added to the `meta.verificationMethod` + * property of the key. + * + * @beta This API may change without a BREAKING CHANGE notice. + */ +export async function mapIdentifierKeysToDocWithJwkSupport( + identifier: IIdentifier, + section: DIDDocumentSection = 'keyAgreement', + context: IAgentContext +): Promise<_ExtendedIKey[]> { + const keys = await mapIdentifierKeysToDoc(identifier, section, context) + const didDocument = await resolveDidOrThrow(identifier.did, context) + // dereference all key agreement keys from DID document and normalize + const documentKeys: VerificationMethod[] = await dereferenceDidKeysWithJwkSupport(didDocument, section, context) + + const localKeys = identifier.keys.filter(isDefined) + // finally map the didDocument keys to the identifier keys by comparing `publicKeyHex` + const extendedKeys: _ExtendedIKey[] = documentKeys + .map((verificationMethod) => { + if (verificationMethod.type !== 'JsonWebKey2020') { + return null + } + const localKey = localKeys.find((localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex) + if (localKey) { + const { meta, ...localProps } = localKey + return { ...localProps, meta: { ...meta, verificationMethod } } + } else { + return null + } + }) + .filter(isDefined) + + return keys.concat(extendedKeys) +} diff --git a/packages/did-utils/src/index.ts b/packages/did-utils/src/index.ts new file mode 100644 index 000000000..a5f05afc4 --- /dev/null +++ b/packages/did-utils/src/index.ts @@ -0,0 +1,3 @@ +export * from './didFunctions' +export * from './types' +export * from './x509-utils' diff --git a/packages/did-utils/src/types.ts b/packages/did-utils/src/types.ts new file mode 100644 index 000000000..961736585 --- /dev/null +++ b/packages/did-utils/src/types.ts @@ -0,0 +1,16 @@ +export interface JWK extends JsonWebKey { + x5c?: string + x5u?: string +} + +export type KeyVisibility = 'public' | 'private' + +export interface X509Opts { + cn?: string // The certificate Common Name. Will be used as the KID for the private key. Uses alias if not provided. + privateKeyPEM?: string // Optional as you also need to provide it in hex format, but advisable to use it + certificatePEM?: string // Optional, as long as the certificate then is part of the certificateChainPEM + certificateChainURL?: string // Certificate chain URL. If used this is where the certificateChainPEM will be hosted/found. + certificateChainPEM?: string // Base64 (not url!) encoded DER certificate chain. Please provide even if certificateChainURL is used! +} + +export const DID_PREFIX = 'did:' diff --git a/packages/did-utils/src/x509-utils.ts b/packages/did-utils/src/x509-utils.ts new file mode 100644 index 000000000..1436c923e --- /dev/null +++ b/packages/did-utils/src/x509-utils.ts @@ -0,0 +1,138 @@ +import * as u8a from 'uint8arrays' +// @ts-ignore +import keyto from '@trust/keyto' +import { JWK, KeyVisibility } from './types' + +// Based on (MIT licensed): +// https://github.com/hildjj/node-posh/blob/master/lib/index.js +export function pemCertChainTox5c(cert: string, maxDepth?: number): string[] { + if (!maxDepth) { + maxDepth = 0 + } + /* + * Convert a PEM-encoded certificate to the version used in the x5c element + * of a [JSON Web Key](http://tools.ietf.org/html/draft-ietf-jose-json-web-key). + * + * `cert` PEM-encoded certificate chain + * `maxdepth` The maximum number of certificates to use from the chain. + */ + + const intermediate = cert.replace(/-----[^\n]+\n?/gm, ',').replace(/\n/g, '') + let x5c = intermediate.split(',').filter(function (c) { + return c.length > 0 + }) + if (maxDepth > 0) { + x5c = x5c.splice(0, maxDepth) + } + return x5c +} + +export function x5cToPemCertChain(x5c: string[], maxDepth?: number): string { + if (!maxDepth) { + maxDepth = 0 + } + const length = maxDepth === 0 ? x5c.length : Math.min(maxDepth, x5c.length) + let pem = '' + for (let i = 0; i < length; i++) { + pem += base64ToPEM(x5c[i], 'CERTIFICATE') + } + return pem +} + +export const toKeyObject = (PEM: string, visibility: KeyVisibility = 'public') => { + const jwk = PEMToJwk(PEM, visibility) + const keyVisibility: KeyVisibility = jwk.d ? 'private' : 'public' + const keyHex = keyVisibility === 'private' ? privateKeyHexFromPEM(PEM) : publicKeyHexFromPEM(PEM) + + return { + pem: hexToPEM(keyHex, visibility), + jwk, + keyHex, + keyType: keyVisibility, + } +} + +export const jwkToPEM = (jwk: JWK, visibility: KeyVisibility = 'public'): string => { + return keyto.from(jwk, 'jwk').toString('pem', visibility === 'public' ? 'public_pkcs8' : 'private_pkcs8') +} + +export const PEMToJwk = (pem: string, visibility: KeyVisibility = 'public'): JWK => { + return keyto.from(pem, 'pem').toJwk(visibility) +} +export const privateKeyHexFromPEM = (PEM: string) => { + return PEMToHex(PEM) +} + +export const privateKeyHexFromPEMBasedJwk = (jwk: JWK) => { + privateKeyHexFromPEM(jwkToPEM(jwk)) +} + +export const publicKeyHexFromPEM = (PEM: string) => { + const hex = PEMToHex(PEM) + if (PEM.includes('CERTIFICATE')) { + throw Error('Cannot directly deduce public Key from PEM Certificate yet') + } else if (!PEM.includes('PRIVATE')) { + return hex + } + const publicJwk = PEMToJwk(PEM, 'public') + const publicPEM = jwkToPEM(publicJwk, 'public') + return PEMToHex(publicPEM) +} + +export const PEMToHex = (PEM: string, headerKey?: string): string => { + if (PEM.indexOf('-----BEGIN ') == -1) { + throw Error(`PEM header not found: ${headerKey}`) + } + + let strippedPem: string + if (headerKey) { + strippedPem = PEM.replace(new RegExp('^[^]*-----BEGIN ' + headerKey + '-----'), '') + strippedPem = strippedPem.replace(new RegExp('-----END ' + headerKey + '-----[^]*$'), '') + } else { + strippedPem = PEM.replace(/^[^]*-----BEGIN [^-]+-----/, '') + strippedPem = strippedPem.replace(/-----END [^-]+-----[^]*$/, '') + } + return base64ToHex(strippedPem, 'base64pad') +} + +/** + * Converts a base64 encoded string to hex string, removing any non-base64 characters, including newlines + * @param input The input in base64, with optional newlines + * @param inputEncoding + */ +export const base64ToHex = (input: string, inputEncoding?: 'base64pad' | 'base64urlpad') => { + const base64NoNewlines = input.replace(/[^0-9A-Za-z\/+=]*/g, '') + return u8a.toString(u8a.fromString(base64NoNewlines, inputEncoding ? inputEncoding : 'base64pad'), 'base16') +} + +const hexToBase64 = (input: number | object | string, targetEncoding?: 'base64pad' | 'base64urlpad'): string => { + let hex = typeof input === 'string' ? input : input.toString(16) + if (hex.length % 2 === 1) { + hex = `0${hex}` + } + return u8a.toString(u8a.fromString(hex, 'base16'), targetEncoding ? targetEncoding : 'base64pad') +} + +export const hexToPEM = (hex: string, type: KeyVisibility): string => { + const base64 = hexToBase64(hex, 'base64pad') + const headerKey = type === 'private' ? 'RSA PRIVATE KEY' : 'PUBLIC KEY' + if (type === 'private') { + const pem = base64ToPEM(base64, headerKey) + try { + PEMToJwk(pem) // We only use it to test the private key + return pem + } catch (error) { + return base64ToPEM(base64, 'PRIVATE KEY') + } + } + return base64ToPEM(base64, headerKey) +} + +export function base64ToPEM(cert: string, headerKey?: 'PUBLIC KEY' | 'RSA PRIVATE KEY' | 'PRIVATE KEY' | 'CERTIFICATE'): string { + const key = headerKey ?? 'CERTIFICATE' + const matches = cert.match(/.{1,64}/g) + if (!matches) { + throw Error('Invalid cert input value supplied') + } + return `-----BEGIN ${key}-----\n${matches.join('\n')}\n-----END ${key}-----\n` +} diff --git a/packages/did-utils/tsconfig.json b/packages/did-utils/tsconfig.json new file mode 100644 index 000000000..8053382d6 --- /dev/null +++ b/packages/did-utils/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig-base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "references": [{ "path": "../ssi-types" }, { "path": "../ssi-sdk-core" }] +} diff --git a/packages/tsconfig.json b/packages/tsconfig.json index f616e39c8..7ef34fc73 100644 --- a/packages/tsconfig.json +++ b/packages/tsconfig.json @@ -3,6 +3,7 @@ "references": [ { "path": "ssi-types" }, { "path": "ssi-sdk-core" }, + { "path": "did-utils" }, { "path": "factom-did-provider" }, { "path": "lto-did-provider" }, { "path": "mnemonic-seed-manager" }, diff --git a/packages/vc-handler-ld-local/src/index.ts b/packages/vc-handler-ld-local/src/index.ts index aa4a6bfa3..57c4c273d 100644 --- a/packages/vc-handler-ld-local/src/index.ts +++ b/packages/vc-handler-ld-local/src/index.ts @@ -8,7 +8,7 @@ export { SphereonEd25519Signature2018, SphereonEd25519Signature2020, SphereonEcdsaSecp256k1RecoverySignature2020, - SphereonJsonWebSignature2020 + SphereonJsonWebSignature2020, } from './suites' export * from './ld-credential-module' export * from './ld-context-loader' diff --git a/patches/@veramo+core+4.2.0.patch b/patches/@veramo+core+4.2.0.patch new file mode 100644 index 000000000..338feed41 --- /dev/null +++ b/patches/@veramo+core+4.2.0.patch @@ -0,0 +1,60 @@ +diff --git a/node_modules/@veramo/core/build/types/IIdentifier.d.ts b/node_modules/@veramo/core/build/types/IIdentifier.d.ts +index ace5497..d69eccd 100644 +--- a/node_modules/@veramo/core/build/types/IIdentifier.d.ts ++++ b/node_modules/@veramo/core/build/types/IIdentifier.d.ts +@@ -43,7 +43,7 @@ export type MinimalImportableIdentifier = { + * + * @public + */ +-export type TKeyType = 'Ed25519' | 'Secp256k1' | 'Secp256r1' | 'X25519' | 'Bls12381G1' | 'Bls12381G2'; ++export type TKeyType = 'Ed25519' | 'Secp256k1' | 'Secp256r1' | 'X25519' | 'Bls12381G1' | 'Bls12381G2' | 'RSA'; + /** + * Cryptographic key + * @public +diff --git a/node_modules/@veramo/core/plugin.schema.json b/node_modules/@veramo/core/plugin.schema.json +index f33bd89..6b686d4 100644 +--- a/node_modules/@veramo/core/plugin.schema.json ++++ b/node_modules/@veramo/core/plugin.schema.json +@@ -476,7 +476,8 @@ + "Secp256r1", + "X25519", + "Bls12381G1", +- "Bls12381G2" ++ "Bls12381G2", ++ "RSA" + ], + "description": "Cryptographic key type." + }, +@@ -1046,7 +1047,8 @@ + "Secp256r1", + "X25519", + "Bls12381G1", +- "Bls12381G2" ++ "Bls12381G2", ++ "RSA" + ], + "description": "Cryptographic key type." + }, +@@ -2570,7 +2572,8 @@ + "Secp256r1", + "X25519", + "Bls12381G1", +- "Bls12381G2" ++ "Bls12381G2", ++ "RSA" + ], + "description": "Cryptographic key type." + }, +diff --git a/node_modules/@veramo/core/src/types/IIdentifier.ts b/node_modules/@veramo/core/src/types/IIdentifier.ts +index ee063bd..3002bb8 100644 +--- a/node_modules/@veramo/core/src/types/IIdentifier.ts ++++ b/node_modules/@veramo/core/src/types/IIdentifier.ts +@@ -51,7 +51,7 @@ export type MinimalImportableIdentifier = { + * + * @public + */ +-export type TKeyType = 'Ed25519' | 'Secp256k1' | 'Secp256r1' | 'X25519' | 'Bls12381G1' | 'Bls12381G2' ++export type TKeyType = 'Ed25519' | 'Secp256k1' | 'Secp256r1' | 'X25519' | 'Bls12381G1' | 'Bls12381G2' | 'RSA' + + /** + * Cryptographic key diff --git a/yarn.lock b/yarn.lock index 26e8ad83a..8686eb50e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2965,6 +2965,11 @@ fast-text-encoding "^1.0.3" isomorphic-webcrypto "^2.3.8" +"@sphereon/jsencrypt@3.3.2-unstable.0": + version "3.3.2-unstable.0" + resolved "https://registry.yarnpkg.com/@sphereon/jsencrypt/-/jsencrypt-3.3.2-unstable.0.tgz#25538a6071e0db0a4f6f284967da447228c0babe" + integrity sha512-HurQBvkjAW8t4oZ629sAKTAEi7B/rjYfmSJOquGdyJZeMPT+5R/9DW6i2eQv6kWHWLBa1Iji14zMUvM/NmF8Ew== + "@sphereon/lto-did-ts@0.1.8-unstable.0": version "0.1.8-unstable.0" resolved "https://registry.yarnpkg.com/@sphereon/lto-did-ts/-/lto-did-ts-0.1.8-unstable.0.tgz#831fe98c759c2b27dd57807c5def88eb998b0a95" @@ -3684,6 +3689,15 @@ base64url "^3.0.1" elliptic "^6.5.2" +"@trust/keyto@^2.0.0-alpha1": + version "2.0.0-alpha1" + resolved "https://registry.yarnpkg.com/@trust/keyto/-/keyto-2.0.0-alpha1.tgz#b097679bc8fc60ad3e73e982e27654d8c4c6ddf2" + integrity sha512-VmlOa+nOaDzhEUfprnVp7RxFQyuEwA4fJ5+smnsud5WM01gU16yQnO/ejZnDVMGXuq/sUwTa5pCej4JhkKA5Sg== + dependencies: + asn1.js "^5.2.0" + base64url "^3.0.1" + elliptic "^6.5.2" + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -4601,6 +4615,11 @@ js-sha3 "^0.8.0" node-forge "^0.10.0" +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -6186,6 +6205,17 @@ cross-fetch@^3.0.4, cross-fetch@^3.1.4, cross-fetch@^3.1.5: dependencies: node-fetch "2.6.7" +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -7709,6 +7739,13 @@ find-versions@^4.0.0: dependencies: semver-regex "^3.1.2" +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -7807,7 +7844,7 @@ fs-extra@^11.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^9.1.0: +fs-extra@^9.0.0, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -8669,6 +8706,11 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: dependencies: has-tostringtag "^1.0.0" +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -8876,6 +8918,13 @@ is-weakset@^2.0.1: call-bind "^1.0.2" get-intrinsic "^1.1.1" +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -10182,6 +10231,13 @@ kind-of@^6.0.2, kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -11198,6 +11254,11 @@ nerf-dart@^1.0.0: resolved "https://registry.yarnpkg.com/nerf-dart/-/nerf-dart-1.0.0.tgz#e6dab7febf5ad816ea81cf5c629c5a0ebde72c1a" integrity sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g== +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + nock@*, nock@^13.2.1, nock@^13.2.9: version "13.2.9" resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.9.tgz#4faf6c28175d36044da4cfa68e33e5a15086ad4c" @@ -11846,6 +11907,14 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + openapi-types@12.0.2: version "12.0.2" resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.0.2.tgz#b752ab0a463c594fd7e3142e0e5983a9645503f6" @@ -12237,6 +12306,26 @@ passport@^0.6.0: pause "0.0.1" utils-merge "^1.0.1" +patch-package@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.5.1.tgz#3e5d00c16997e6160291fee06a521c42ac99b621" + integrity sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^4.1.2" + cross-spawn "^6.0.5" + find-yarn-workspace-root "^2.0.0" + fs-extra "^9.0.0" + is-ci "^2.0.0" + klaw-sync "^6.0.0" + minimist "^1.2.6" + open "^7.4.2" + rimraf "^2.6.3" + semver "^5.6.0" + slash "^2.0.0" + tmp "^0.0.33" + yaml "^1.10.2" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -12252,6 +12341,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -13314,7 +13408,7 @@ semver-regex@^3.1.2: resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.4.tgz#13053c0d4aa11d070a2f2872b6b1e3ae1e1971b4" integrity sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA== -"semver@2 || 3 || 4 || 5", semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -13411,6 +13505,13 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -13418,6 +13519,11 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" @@ -13456,6 +13562,11 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -14523,7 +14634,7 @@ uid-safe@~2.1.5: dependencies: random-bytes "~1.0.0" -uint8arrays@^3.0.0, uint8arrays@^3.1.0: +uint8arrays@^3.0.0, uint8arrays@^3.1.0, uint8arrays@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== @@ -14937,7 +15048,7 @@ which-typed-array@^1.1.8: has-tostringtag "^1.0.0" is-typed-array "^1.1.10" -which@^1.3.1: +which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -15118,7 +15229,7 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0: +yaml@^1.10.0, yaml@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==