From 723456e297c889542ff1b5f792717ab314a91f3a Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 15 Nov 2023 18:09:13 -0500 Subject: [PATCH 01/10] Implement signWithPasskey --- src/api/aptos.ts | 6 + src/api/passkeysBrowser.ts | 34 +++++ src/bcs/deserializer.ts | 14 ++ src/bcs/serializer.ts | 12 ++ src/core/crypto/anyPublicKey.ts | 6 + src/core/crypto/anySignature.ts | 6 + src/core/crypto/p256.ts | 252 ++++++++++++++++++++++++++++++++ src/core/crypto/webauthn.ts | 113 ++++++++++++++ src/internal/passkeysBrowser.ts | 69 +++++++++ src/types/index.ts | 20 ++- 10 files changed, 531 insertions(+), 1 deletion(-) create mode 100644 src/api/passkeysBrowser.ts create mode 100644 src/core/crypto/p256.ts create mode 100644 src/core/crypto/webauthn.ts create mode 100644 src/internal/passkeysBrowser.ts diff --git a/src/api/aptos.ts b/src/api/aptos.ts index 92dc0e106..d3dff8c60 100644 --- a/src/api/aptos.ts +++ b/src/api/aptos.ts @@ -12,6 +12,7 @@ import { General } from "./general"; import { ANS } from "./ans"; import { Staking } from "./staking"; import { Transaction } from "./transaction"; +import { PasskeysBrowser } from "./passkeysBrowser"; /** * This class is the main entry point into Aptos's @@ -39,6 +40,8 @@ export class Aptos { readonly general: General; + readonly passkeysBrowser: PasskeysBrowser; + readonly staking: Staking; readonly transaction: Transaction; @@ -53,6 +56,7 @@ export class Aptos { this.faucet = new Faucet(this.config); this.fungibleAsset = new FungibleAsset(this.config); this.general = new General(this.config); + this.passkeysBrowser = new PasskeysBrowser(this.config); this.staking = new Staking(this.config); this.transaction = new Transaction(this.config); } @@ -69,6 +73,7 @@ export interface Aptos Faucet, FungibleAsset, General, + PasskeysBrowser, Staking, Omit {} @@ -101,5 +106,6 @@ applyMixin(Aptos, Event, "event"); applyMixin(Aptos, Faucet, "faucet"); applyMixin(Aptos, FungibleAsset, "fungibleAsset"); applyMixin(Aptos, General, "general"); +applyMixin(Aptos, PasskeysBrowser, "passkeysBrowser"); applyMixin(Aptos, Staking, "staking"); applyMixin(Aptos, Transaction, "transaction"); diff --git a/src/api/passkeysBrowser.ts b/src/api/passkeysBrowser.ts new file mode 100644 index 000000000..b13f153ea --- /dev/null +++ b/src/api/passkeysBrowser.ts @@ -0,0 +1,34 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +import { AptosConfig } from "./aptosConfig"; +import { HexInput, PendingTransactionResponse } from "../types"; +import { AnyRawTransaction } from "../transactions"; +import { signWithPasskey } from "../internal/passkeysBrowser"; + +/** + * A class for all `Passkeys` related operations on Aptos on the browser. + */ +export class PasskeysBrowser { + readonly config: AptosConfig; + + constructor(config: AptosConfig) { + this.config = config; + } + + /** + * Given a credentialId and a transaction, it prompts the client to sign the transaction + * + * @param args.credentialId The credential ID of the passkey + * @param args.publicKey The public key associated with the passkey + * @param args.transaction The transaction to sign + * @returns The pending transaction response + */ + async signWithPasskey(args: { + credentialId: HexInput; + publicKey: HexInput; + transaction: AnyRawTransaction; + }): Promise { + return signWithPasskey({ aptosConfig: this.config, ...args }); + } +} diff --git a/src/bcs/deserializer.ts b/src/bcs/deserializer.ts index 67a9781a7..28d5651c3 100644 --- a/src/bcs/deserializer.ts +++ b/src/bcs/deserializer.ts @@ -67,6 +67,20 @@ export class Deserializer { return new Uint8Array(this.read(len)); } + /** + * Deserializes a vector of array of bytes. + * + * BCS layout for "vector bytes": vector_length | bytes_length | bytes | bytes_length | bytes... + */ + deserializeVectorBytes(): Array { + const length = this.deserializeUleb128AsU32(); + const vector = new Array(); + for (let i = 0; i < length; i += 1) { + vector.push(this.deserializeBytes()); + } + return vector; + } + /** * Deserializes an array of bytes. The number of bytes to read is already known. * diff --git a/src/bcs/serializer.ts b/src/bcs/serializer.ts index 9852b0cc3..2b5492d8b 100644 --- a/src/bcs/serializer.ts +++ b/src/bcs/serializer.ts @@ -112,6 +112,18 @@ export class Serializer { this.appendToBuffer(value); } + /** + * Serializes a vector of array of bytes. + * + * BCS layout for "vector bytes": vector_length | bytes_length | bytes | bytes_length | bytes... + */ + serializeVectorBytes(values: Array) { + this.serializeU32AsUleb128(values.length); + values.forEach((item) => { + this.serializeBytes(item); + }); + } + /** * Serializes an array of bytes with known length. Therefore, length doesn't need to be * serialized to help deserialization. diff --git a/src/core/crypto/anyPublicKey.ts b/src/core/crypto/anyPublicKey.ts index 1797b2083..54e25e7c8 100644 --- a/src/core/crypto/anyPublicKey.ts +++ b/src/core/crypto/anyPublicKey.ts @@ -4,6 +4,7 @@ import { AnySignature } from "./anySignature"; import { PublicKey } from "./asymmetricCrypto"; import { Ed25519PublicKey } from "./ed25519"; import { Secp256k1PublicKey } from "./secp256k1"; +import { P256PublicKey } from "./p256"; /** * Represents any public key supported by Aptos. @@ -61,6 +62,9 @@ export class AnyPublicKey extends PublicKey { } else if (this.publicKey instanceof Secp256k1PublicKey) { serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Secp256k1); this.publicKey.serialize(serializer); + } else if (this.publicKey instanceof P256PublicKey) { + serializer.serializeU32AsUleb128(AnyPublicKeyVariant.P256); + this.publicKey.serialize(serializer); } else { throw new Error("Unknown public key type"); } @@ -73,6 +77,8 @@ export class AnyPublicKey extends PublicKey { return new AnyPublicKey(Ed25519PublicKey.load(deserializer)); case AnyPublicKeyVariant.Secp256k1: return new AnyPublicKey(Secp256k1PublicKey.load(deserializer)); + case AnyPublicKeyVariant.P256: + return new AnyPublicKey(P256PublicKey.load(deserializer)); default: throw new Error(`Unknown variant index for AnyPublicKey: ${index}`); } diff --git a/src/core/crypto/anySignature.ts b/src/core/crypto/anySignature.ts index f8f99acf3..b18dd6376 100644 --- a/src/core/crypto/anySignature.ts +++ b/src/core/crypto/anySignature.ts @@ -3,6 +3,7 @@ import { AnySignatureVariant } from "../../types"; import { Signature } from "./asymmetricCrypto"; import { Ed25519Signature } from "./ed25519"; import { Secp256k1Signature } from "./secp256k1"; +import { WebAuthnSignature } from "./webauthn"; export class AnySignature extends Signature { public readonly signature: Signature; @@ -37,6 +38,9 @@ export class AnySignature extends Signature { } else if (this.signature instanceof Secp256k1Signature) { serializer.serializeU32AsUleb128(AnySignatureVariant.Secp256k1); this.signature.serialize(serializer); + } else if (this.signature instanceof WebAuthnSignature) { + serializer.serializeU32AsUleb128(AnySignatureVariant.WebAuthn); + this.signature.serialize(serializer); } else { throw new Error("Unknown signature type"); } @@ -49,6 +53,8 @@ export class AnySignature extends Signature { return new AnySignature(Ed25519Signature.load(deserializer)); case AnySignatureVariant.Secp256k1: return new AnySignature(Secp256k1Signature.load(deserializer)); + case AnySignatureVariant.WebAuthn: + return new AnySignature(WebAuthnSignature.load(deserializer)); default: throw new Error(`Unknown variant index for AnySignature: ${index}`); } diff --git a/src/core/crypto/p256.ts b/src/core/crypto/p256.ts new file mode 100644 index 000000000..5381747f7 --- /dev/null +++ b/src/core/crypto/p256.ts @@ -0,0 +1,252 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +import { sha3_256 } from "@noble/hashes/sha3"; +import { p256 } from "@noble/curves/p256"; +import base64url from "base64url"; +import { PrivateKey, PublicKey, Signature } from "./asymmetricCrypto"; +import { Deserializer, Serializer } from "../../bcs"; +import { Hex } from "../hex"; +import { HexInput } from "../../types"; +import type { WebAuthnSignature } from "./webauthn"; + +/** + * Represents the P256 public key + * + * P256 authentication key is represented in the SDK as `AnyPublicKey`. It is used to verify WebAuthnSignatures. + */ +export class P256PublicKey extends PublicKey { + // P256 ecdsa public keys contain a prefix indicating compression and two 32-byte coordinates. + static readonly LENGTH: number = 65; + + // Hex value of the public key + private readonly key: Hex; + + /** + * Create a new PublicKey instance from a Uint8Array or String. + * + * @param hexInput A HexInput (string or Uint8Array) + */ + constructor(hexInput: HexInput) { + super(); + + const hex = Hex.fromHexInput(hexInput); + if (hex.toUint8Array().length !== P256PublicKey.LENGTH) { + throw new Error(`PublicKey length should be ${P256PublicKey.LENGTH}`); + } + this.key = hex; + } + + /** + * Get the public key in bytes (Uint8Array). + * + * @returns Uint8Array representation of the public key + */ + toUint8Array(): Uint8Array { + return this.key.toUint8Array(); + } + + /** + * Get the public key as a hex string with the 0x prefix. + * + * @returns string representation of the public key + */ + toString(): string { + return this.key.toString(); + } + + /** + * Verifies a signed data with a public key + * + * @param args.message message + * @param args.signature The signature + * @returns true if the signature is valid + */ + verifySignature(args: { message: HexInput; signature: WebAuthnSignature }): boolean { + const { message, signature } = args; + + // Check challenge + const { challenge } = signature.getCollectedClientData(); + const challengeStr = base64url.decode(challenge); + if (challengeStr !== message.toString()) { + return false; + } + + // Get verification data. + const verificationData = signature.getVerificationData(); + const p256Signature = signature.paar.signature; + const rawSignature = p256Signature.toUint8Array(); + + return p256.verify(rawSignature, verificationData, this.toUint8Array()); + } + + serialize(serializer: Serializer): void { + serializer.serializeBytes(this.key.toUint8Array()); + } + + static deserialize(deserializer: Deserializer): P256PublicKey { + const bytes = deserializer.deserializeBytes(); + return new P256PublicKey(bytes); + } + + static load(deserializer: Deserializer): P256PublicKey { + const bytes = deserializer.deserializeBytes(); + return new P256PublicKey(bytes); + } +} + +/** + * A P256 ecdsa private key - this is only used for test purposes as signing is done via passkeys + */ +export class P256PrivateKey extends PrivateKey { + /** + * Length of P256 ecdsa private key + */ + static readonly LENGTH: number = 32; + + /** + * The private key bytes + * @private + */ + private readonly key: Hex; + + /** + * Create a new PrivateKey instance from a Uint8Array or String. + * + * @param hexInput A HexInput (string or Uint8Array) + */ + constructor(hexInput: HexInput) { + super(); + + const privateKeyHex = Hex.fromHexInput(hexInput); + if (privateKeyHex.toUint8Array().length !== P256PrivateKey.LENGTH) { + throw new Error(`PrivateKey length should be ${P256PrivateKey.LENGTH}`); + } + + this.key = privateKeyHex; + } + + /** + * Get the private key in bytes (Uint8Array). + * + * @returns + */ + toUint8Array(): Uint8Array { + return this.key.toUint8Array(); + } + + /** + * Get the private key as a hex string with the 0x prefix. + * + * @returns string representation of the private key + */ + toString(): string { + return this.key.toString(); + } + + /** + * Sign the given message with the private key. + * + * @param message in HexInput format + * @returns Signature + */ + sign(message: HexInput): P256Signature { + const msgHex = Hex.fromHexInput(message); + const sha3Message = sha3_256(msgHex.toUint8Array()); + const signature = p256.sign(sha3Message, this.key.toUint8Array()); + return new P256Signature(signature.toCompactRawBytes()); + } + + serialize(serializer: Serializer): void { + serializer.serializeBytes(this.toUint8Array()); + } + + static deserialize(deserializer: Deserializer): P256PrivateKey { + const bytes = deserializer.deserializeBytes(); + return new P256PrivateKey(bytes); + } + + /** + * Generate a new random private key. + * + * @returns P256PrivateKey + */ + static generate(): P256PrivateKey { + const hexInput = p256.utils.randomPrivateKey(); + return new P256PrivateKey(hexInput); + } + + /** + * Derive the P256PublicKey from this private key. + * + * @returns P256PublicKey + */ + publicKey(): P256PublicKey { + const bytes = p256.getPublicKey(this.key.toUint8Array(), false); + return new P256PublicKey(bytes); + } +} + +/** + * A signature of a message signed using an P256 ecdsa private key + */ +export class P256Signature extends Signature { + /** + * P256 ecdsa signatures are 256-bit. + */ + static readonly LENGTH = 64; + + /** + * The signature bytes + * @private + */ + private readonly data: Hex; + + /** + * Create a new Signature instance from a Uint8Array or String. It will convert the signature to its canonical if needed. + * + * @param hexInput A HexInput (string or Uint8Array) + */ + constructor(hexInput: HexInput) { + super(); + + const hex = Hex.fromHexInput(hexInput); + if (hex.toUint8Array().length !== P256Signature.LENGTH) { + throw new Error(`Signature length should be ${P256Signature.LENGTH}, recieved ${hex.toUint8Array().length}`); + } + const signature = p256.Signature.fromCompact(hexInput).normalizeS().toCompactRawBytes(); + this.data = Hex.fromHexInput(signature); + } + + /** + * Get the signature in bytes (Uint8Array). + * + * @returns Uint8Array representation of the signature + */ + toUint8Array(): Uint8Array { + return this.data.toUint8Array(); + } + + /** + * Get the signature as a hex string with the 0x prefix. + * + * @returns string representation of the signature + */ + toString(): string { + return this.data.toString(); + } + + serialize(serializer: Serializer): void { + serializer.serializeBytes(this.data.toUint8Array()); + } + + static deserialize(deserializer: Deserializer): P256Signature { + const hex = deserializer.deserializeBytes(); + return new P256Signature(hex); + } + + static load(deserializer: Deserializer): P256Signature { + const bytes = deserializer.deserializeBytes(); + return new P256Signature(bytes); + } +} diff --git a/src/core/crypto/webauthn.ts b/src/core/crypto/webauthn.ts new file mode 100644 index 000000000..775c3e1f6 --- /dev/null +++ b/src/core/crypto/webauthn.ts @@ -0,0 +1,113 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +import { sha3_256 } from "@noble/hashes/sha3"; +import { Signature } from "./asymmetricCrypto"; +import { Deserializer, Serializer } from "../../bcs"; +import { Hex } from "../hex"; +import { HexInput } from "../../types"; +import { P256Signature } from "./p256"; + +export class PartialAuthenticatorAssertionResponse { + readonly signature: P256Signature; + + readonly authenticatorData: Uint8Array; + + readonly clientDataJSON: Uint8Array; + + constructor(signature: HexInput, authenticatorData: HexInput, clientDataJSON: HexInput) { + this.signature = new P256Signature(signature); + + this.authenticatorData = Hex.fromHexInput(authenticatorData).toUint8Array(); + + this.clientDataJSON = Hex.fromHexInput(clientDataJSON).toUint8Array(); + } +} + +export type ClientDataJSON = { + type: string; + challenge: string; + origin: string; + crossOrigin?: boolean; + tokenBinding?: { + id?: string; + status: "present" | "supported" | "not-supported"; + }; +}; + +/** + * A signature of WebAuthn transaction + */ +export class WebAuthnSignature extends Signature { + /** + * The signature bytes + */ + readonly paar: PartialAuthenticatorAssertionResponse; + + /** + * Create a new Signature instance from a Uint8Array or String. + * + * @param hexInput A HexInput (string or Uint8Array) + */ + constructor(signature: HexInput, authenticatorData: HexInput, clientDataJSON: HexInput) { + super(); + this.paar = new PartialAuthenticatorAssertionResponse(signature, authenticatorData, clientDataJSON); + } + + /** + * Get the signature in bytes (Uint8Array). + * + * @returns Uint8Array representation of the signature + */ + toUint8Array(): Uint8Array { + return this.paar.signature.toUint8Array(); + } + + /** + * Get the signature as a hex string with the 0x prefix. + * + * @returns string representation of the signature + */ + toString(): string { + return this.paar.toString(); + } + + getCollectedClientData(): ClientDataJSON { + const jsonString = Buffer.from(this.paar.clientDataJSON).toString("utf8"); + return JSON.parse(jsonString); + } + + getVerificationData(): Uint8Array { + const clientDataJSONHash = sha3_256(this.paar.clientDataJSON); + + const mergedArray = new Uint8Array(clientDataJSONHash.length + this.paar.authenticatorData.length); + mergedArray.set(clientDataJSONHash); + mergedArray.set(this.paar.authenticatorData, clientDataJSONHash.length); + + return mergedArray; + } + + serialize(serializer: Serializer): void { + serializer.serializeVectorBytes([ + this.paar.signature.toUint8Array(), + this.paar.authenticatorData, + this.paar.clientDataJSON, + ]); + } + + static deserialize(deserializer: Deserializer): WebAuthnSignature { + const vectorBytes = deserializer.deserializeVectorBytes(); + if (vectorBytes.length !== 3) { + throw Error(); + } + return new WebAuthnSignature(vectorBytes[0], vectorBytes[1], vectorBytes[2]); + } + + static load(deserializer: Deserializer): WebAuthnSignature { + const vectorBytes = deserializer.deserializeVectorBytes(); + if (vectorBytes.length !== 3) { + throw Error(); + } + return new WebAuthnSignature(vectorBytes[0], vectorBytes[1], vectorBytes[2]); + } +} diff --git a/src/internal/passkeysBrowser.ts b/src/internal/passkeysBrowser.ts new file mode 100644 index 000000000..d0c1bbb02 --- /dev/null +++ b/src/internal/passkeysBrowser.ts @@ -0,0 +1,69 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +/** + * This file contains the underlying implementations for exposed API surface in + * the {@link api/passkeysBrowser}. By moving the methods out into a separate file, + * other namespaces and processes can access these methods without depending on the entire + * faucet namespace and without having a dependency cycle error. + */ +import { sha3_256 } from "@noble/hashes/sha3"; +import { p256 } from "@noble/curves/p256"; +import { AptosConfig } from "../api/aptosConfig"; +import { Hex } from "../core"; +import { + AnyRawTransaction, + deriveTransactionType, + getAuthenticatorForWebAuthn, + getSigningMessage, +} from "../transactions"; +import { HexInput, PendingTransactionResponse } from "../types"; +import { submitTransaction } from "./transactionSubmission"; + +export async function signWithPasskey(args: { + aptosConfig: AptosConfig; + credentialId: HexInput; + publicKey: HexInput; + transaction: AnyRawTransaction; +}): Promise { + const { aptosConfig, credentialId, publicKey, transaction } = args; + const allowCredentials: PublicKeyCredentialDescriptor[] = [ + { + type: "public-key", + id: Hex.fromHexInput(credentialId).toUint8Array(), + }, + ]; + + // get the signing message and hash it to create the challenge + const transactionToSign = deriveTransactionType(transaction); + const signingMessage = getSigningMessage(transactionToSign); + const challenge = sha3_256(signingMessage); + + const publicKeyCredReqOptions: PublicKeyCredentialRequestOptions = { + challenge, + allowCredentials, + }; + + const authenticationResponse = (await navigator.credentials.get({ + publicKey: publicKeyCredReqOptions, + })) as PublicKeyCredential; + + const authenticatorAssertionResponse = authenticationResponse.response as AuthenticatorAssertionResponse; + + const { clientDataJSON, authenticatorData, signature } = authenticatorAssertionResponse; + + const signatureCompact = p256.Signature.fromDER(new Uint8Array(signature)).toCompactRawBytes(); + + const authenticator = getAuthenticatorForWebAuthn({ + publicKey, + clientDataJSON: new Uint8Array(clientDataJSON), + authenticatorData: new Uint8Array(authenticatorData), + signature: signatureCompact, + }); + + return submitTransaction({ + aptosConfig, + transaction, + senderAuthenticator: authenticator, + }); +} diff --git a/src/types/index.ts b/src/types/index.ts index d63e131bd..670909991 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -107,11 +107,13 @@ export enum AccountAuthenticatorVariant { export enum AnyPublicKeyVariant { Ed25519 = 0, Secp256k1 = 1, + P256 = 2, } export enum AnySignatureVariant { Ed25519 = 0, Secp256k1 = 1, + WebAuthn = 2, } /** @@ -1025,6 +1027,10 @@ export enum SigningSchemeInput { * For Secp256k1Ecdsa */ Secp256k1Ecdsa = 2, + /** + * For P256Ecdsa + */ + P256Ecdsa = 3, } /** @@ -1080,4 +1086,16 @@ export type GenerateAccountWithSingleSignerSecp256k1Key = { legacy?: false; }; -export type GenerateAccount = GenerateAccountWithEd25519 | GenerateAccountWithSingleSignerSecp256k1Key; +/** + * Input type to generate an account using Single Signer + * P256 + */ +export type GenerateAccountWithSingleSignerP256Key = { + scheme: SigningSchemeInput.P256Ecdsa; + legacy?: false; +}; + +export type GenerateAccount = + | GenerateAccountWithEd25519 + | GenerateAccountWithSingleSignerSecp256k1Key + | GenerateAccountWithSingleSignerP256Key; From 9b504c51163876c50306a20b8bd4586e9e48a48c Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 15 Nov 2023 18:13:23 -0500 Subject: [PATCH 02/10] add missing file --- .../transactionBuilder/transactionBuilder.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/transactions/transactionBuilder/transactionBuilder.ts b/src/transactions/transactionBuilder/transactionBuilder.ts index fd797237e..0c5e1daa1 100644 --- a/src/transactions/transactionBuilder/transactionBuilder.ts +++ b/src/transactions/transactionBuilder/transactionBuilder.ts @@ -76,6 +76,8 @@ import { convertArgument, fetchEntryFunctionAbi, standardizeTypeTags } from "./r import { memoizeAsync } from "../../utils/memoize"; import { AnyNumber, SigningScheme } from "../../types"; import { getFunctionParts, isScriptDataInput } from "./helpers"; +import { WebAuthnSignature } from "../../core/crypto/webauthn"; +import { P256PublicKey } from "../../core/crypto/p256"; /** * We are defining function signatures, each with its specific input and output. @@ -452,6 +454,29 @@ export function sign(args: { signer: Account; transaction: AnyRawTransaction }): } } +/** + * Creates the and returns the Authenticator for passkey signed transactions. + * + * @param args.publicKey The public key of the passkey credential. + * @param args.signature The P256signature which is the signed challenge. + * @param args.authenticatorData The AuthenticatorData of the assertion. + * @param args.clientDataJSON The clientDataJSON of the assertion. + * + * @return The signer AccountAuthenticator + */ +export function getAuthenticatorForWebAuthn(args: { + publicKey: HexInput; + signature: HexInput; + authenticatorData: HexInput; + clientDataJSON: HexInput; +}): AccountAuthenticator { + const { publicKey, signature, authenticatorData, clientDataJSON } = args; + const p256PublicKey = new P256PublicKey(publicKey); + const webAuthnSignature = new WebAuthnSignature(signature, authenticatorData, clientDataJSON); + + return new AccountAuthenticatorSingleKey(new AnyPublicKey(p256PublicKey), new AnySignature(webAuthnSignature)); +} + /** * Prepare a transaction to be submitted to chain * @@ -494,6 +519,7 @@ export function generateSignedTransaction(args: InputSubmitTransactionData): Uin senderAuthenticator instanceof AccountAuthenticatorMultiKey) && transactionToSubmit instanceof RawTransaction ) { + console.log("single sender"); const transactionAuthenticator = new TransactionAuthenticatorSingleSender(senderAuthenticator); return new SignedTransaction(transactionToSubmit, transactionAuthenticator).bcsToBytes(); } From 0f156f8dad2dacf4ac12beddab3cbe178de6403a Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 7 Dec 2023 00:15:13 +0900 Subject: [PATCH 03/10] updates --- src/api/passkeysBrowser.ts | 49 ++++++- src/bcs/deserializer.ts | 14 -- src/bcs/serializer.ts | 12 -- src/core/authenticationKey.ts | 11 +- src/core/crypto/anyPublicKey.ts | 10 +- src/core/crypto/index.ts | 1 + src/core/crypto/{p256.ts => secp256r1.ts} | 105 +++++++++------ src/core/crypto/webauthn.ts | 100 ++++++++++---- src/internal/passkeysBrowser.ts | 127 ++++++++++++------ .../transactionBuilder/transactionBuilder.ts | 73 ++++++++-- src/types/index.ts | 18 ++- 11 files changed, 355 insertions(+), 165 deletions(-) rename src/core/crypto/{p256.ts => secp256r1.ts} (56%) diff --git a/src/api/passkeysBrowser.ts b/src/api/passkeysBrowser.ts index b13f153ea..19d583b95 100644 --- a/src/api/passkeysBrowser.ts +++ b/src/api/passkeysBrowser.ts @@ -1,10 +1,19 @@ +/* eslint-disable class-methods-use-this */ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 +import { PublicKeyCredentialCreationOptionsJSON, RegistrationResponseJSON } from "@simplewebauthn/server/esm/deps"; import { AptosConfig } from "./aptosConfig"; import { HexInput, PendingTransactionResponse } from "../types"; import { AnyRawTransaction } from "../transactions"; -import { signWithPasskey } from "../internal/passkeysBrowser"; +import { + generateRegistrationOptions, + getPasskeyAccountAddress, + parsePublicKey, + registerCredential, + signAndSubmitWithPasskey, +} from "../internal/passkeysBrowser"; +import { AccountAddress, PublicKey } from "../core"; /** * A class for all `Passkeys` related operations on Aptos on the browser. @@ -24,11 +33,41 @@ export class PasskeysBrowser { * @param args.transaction The transaction to sign * @returns The pending transaction response */ - async signWithPasskey(args: { - credentialId: HexInput; - publicKey: HexInput; + async signAndSubmitWithPasskey(args: { + credentialId: string | Uint8Array; + publicKey: PublicKey; transaction: AnyRawTransaction; + timeout?: number; + rpID?: string; }): Promise { - return signWithPasskey({ aptosConfig: this.config, ...args }); + return signAndSubmitWithPasskey({ aptosConfig: this.config, ...args }); + } + + async getPasskeyAccountAddress(args: { publicKey: HexInput }): Promise { + return getPasskeyAccountAddress(args); + } + + async generateRegistrationOptions(args: { + rpName: string; + rpID: string; + userID: string; + userName: string; + challenge?: string | Uint8Array; + userDisplayName?: string; + timeout?: number; + attestationType?: AttestationConveyancePreference; + authenticatorAttachment?: AuthenticatorAttachment; + }): Promise { + return generateRegistrationOptions(args); + } + + async registerCredential( + creationOptionsJSON: PublicKeyCredentialCreationOptionsJSON, + ): Promise { + return registerCredential(creationOptionsJSON); + } + + parsePublicKey(response: RegistrationResponseJSON): PublicKey { + return parsePublicKey(response); } } diff --git a/src/bcs/deserializer.ts b/src/bcs/deserializer.ts index 28d5651c3..67a9781a7 100644 --- a/src/bcs/deserializer.ts +++ b/src/bcs/deserializer.ts @@ -67,20 +67,6 @@ export class Deserializer { return new Uint8Array(this.read(len)); } - /** - * Deserializes a vector of array of bytes. - * - * BCS layout for "vector bytes": vector_length | bytes_length | bytes | bytes_length | bytes... - */ - deserializeVectorBytes(): Array { - const length = this.deserializeUleb128AsU32(); - const vector = new Array(); - for (let i = 0; i < length; i += 1) { - vector.push(this.deserializeBytes()); - } - return vector; - } - /** * Deserializes an array of bytes. The number of bytes to read is already known. * diff --git a/src/bcs/serializer.ts b/src/bcs/serializer.ts index 2b5492d8b..9852b0cc3 100644 --- a/src/bcs/serializer.ts +++ b/src/bcs/serializer.ts @@ -112,18 +112,6 @@ export class Serializer { this.appendToBuffer(value); } - /** - * Serializes a vector of array of bytes. - * - * BCS layout for "vector bytes": vector_length | bytes_length | bytes | bytes_length | bytes... - */ - serializeVectorBytes(values: Array) { - this.serializeU32AsUleb128(values.length); - values.forEach((item) => { - this.serializeBytes(item); - }); - } - /** * Serializes an array of bytes with known length. Therefore, length doesn't need to be * serialized to help deserialization. diff --git a/src/core/authenticationKey.ts b/src/core/authenticationKey.ts index b9cea2a3e..c3d61bb38 100644 --- a/src/core/authenticationKey.ts +++ b/src/core/authenticationKey.ts @@ -12,6 +12,7 @@ import { AnyPublicKey } from "./crypto/anyPublicKey"; import { MultiKey } from "./crypto/multiKey"; import { Serializable, Serializer } from "../bcs/serializer"; import { Deserializer } from "../bcs/deserializer"; +import { Secp256k1PublicKey, Secp256r1PublicKey } from "./crypto"; /** * Each account stores an authentication key. Authentication key enables account owners to rotate @@ -110,7 +111,7 @@ export class AuthenticationKey extends Serializable { * @returns AuthenticationKey */ static fromPublicKey(args: { publicKey: PublicKey }): AuthenticationKey { - const { publicKey } = args; + let { publicKey } = args; let scheme: number; if (publicKey instanceof Ed25519PublicKey) { @@ -119,6 +120,14 @@ export class AuthenticationKey extends Serializable { } else if (publicKey instanceof MultiEd25519PublicKey) { // for legacy support scheme = SigningScheme.MultiEd25519.valueOf(); + } else if (publicKey instanceof Secp256k1PublicKey) { + // Only single sender supported for Secp256k1 + scheme = SigningScheme.SingleKey.valueOf(); + publicKey = new AnyPublicKey(publicKey); + } else if (publicKey instanceof Secp256r1PublicKey) { + // Only single sender supported for Secp256r1 + scheme = SigningScheme.SingleKey.valueOf(); + publicKey = new AnyPublicKey(publicKey); } else if (publicKey instanceof AnyPublicKey) { scheme = SigningScheme.SingleKey.valueOf(); } else if (publicKey instanceof MultiKey) { diff --git a/src/core/crypto/anyPublicKey.ts b/src/core/crypto/anyPublicKey.ts index 54e25e7c8..f8c90bb3a 100644 --- a/src/core/crypto/anyPublicKey.ts +++ b/src/core/crypto/anyPublicKey.ts @@ -4,7 +4,7 @@ import { AnySignature } from "./anySignature"; import { PublicKey } from "./asymmetricCrypto"; import { Ed25519PublicKey } from "./ed25519"; import { Secp256k1PublicKey } from "./secp256k1"; -import { P256PublicKey } from "./p256"; +import { Secp256r1PublicKey } from "./secp256r1"; /** * Represents any public key supported by Aptos. @@ -62,8 +62,8 @@ export class AnyPublicKey extends PublicKey { } else if (this.publicKey instanceof Secp256k1PublicKey) { serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Secp256k1); this.publicKey.serialize(serializer); - } else if (this.publicKey instanceof P256PublicKey) { - serializer.serializeU32AsUleb128(AnyPublicKeyVariant.P256); + } else if (this.publicKey instanceof Secp256r1PublicKey) { + serializer.serializeU32AsUleb128(AnyPublicKeyVariant.Secp256r1); this.publicKey.serialize(serializer); } else { throw new Error("Unknown public key type"); @@ -77,8 +77,8 @@ export class AnyPublicKey extends PublicKey { return new AnyPublicKey(Ed25519PublicKey.load(deserializer)); case AnyPublicKeyVariant.Secp256k1: return new AnyPublicKey(Secp256k1PublicKey.load(deserializer)); - case AnyPublicKeyVariant.P256: - return new AnyPublicKey(P256PublicKey.load(deserializer)); + case AnyPublicKeyVariant.Secp256r1: + return new AnyPublicKey(Secp256r1PublicKey.load(deserializer)); default: throw new Error(`Unknown variant index for AnyPublicKey: ${index}`); } diff --git a/src/core/crypto/index.ts b/src/core/crypto/index.ts index 7b63a1dd0..7afcd73c7 100644 --- a/src/core/crypto/index.ts +++ b/src/core/crypto/index.ts @@ -5,6 +5,7 @@ export * from "./asymmetricCrypto"; export * from "./ed25519"; export * from "./multiEd25519"; export * from "./secp256k1"; +export * from "./secp256r1"; export * from "./multiKey"; export * from "./hdKey"; export * from "./anyPublicKey"; diff --git a/src/core/crypto/p256.ts b/src/core/crypto/secp256r1.ts similarity index 56% rename from src/core/crypto/p256.ts rename to src/core/crypto/secp256r1.ts index 5381747f7..a84a42319 100644 --- a/src/core/crypto/p256.ts +++ b/src/core/crypto/secp256r1.ts @@ -3,7 +3,8 @@ import { sha3_256 } from "@noble/hashes/sha3"; import { p256 } from "@noble/curves/p256"; -import base64url from "base64url"; +import { bufferToBase64URLString } from "@simplewebauthn/browser"; +import { sha256 } from "@noble/hashes/sha256"; import { PrivateKey, PublicKey, Signature } from "./asymmetricCrypto"; import { Deserializer, Serializer } from "../../bcs"; import { Hex } from "../hex"; @@ -11,12 +12,12 @@ import { HexInput } from "../../types"; import type { WebAuthnSignature } from "./webauthn"; /** - * Represents the P256 public key + * Represents the Secp256r1 public key * - * P256 authentication key is represented in the SDK as `AnyPublicKey`. It is used to verify WebAuthnSignatures. + * Secp256r1 authentication key is represented in the SDK as `AnyPublicKey`. It is used to verify WebAuthnSignatures. */ -export class P256PublicKey extends PublicKey { - // P256 ecdsa public keys contain a prefix indicating compression and two 32-byte coordinates. +export class Secp256r1PublicKey extends PublicKey { + // Secp256r1 ecdsa public keys contain a prefix indicating compression and two 32-byte coordinates. static readonly LENGTH: number = 65; // Hex value of the public key @@ -31,8 +32,8 @@ export class P256PublicKey extends PublicKey { super(); const hex = Hex.fromHexInput(hexInput); - if (hex.toUint8Array().length !== P256PublicKey.LENGTH) { - throw new Error(`PublicKey length should be ${P256PublicKey.LENGTH}`); + if (hex.toUint8Array().length !== Secp256r1PublicKey.LENGTH) { + throw new Error(`PublicKey length should be ${Secp256r1PublicKey.LENGTH}`); } this.key = hex; } @@ -62,45 +63,65 @@ export class P256PublicKey extends PublicKey { * @param args.signature The signature * @returns true if the signature is valid */ - verifySignature(args: { message: HexInput; signature: WebAuthnSignature }): boolean { + verifySignature(args: { message: HexInput; signature: Secp256r1Signature }): boolean { const { message, signature } = args; + const msgHex = Hex.fromHexInput(message).toUint8Array(); + const sha3Message = sha256(msgHex); + const rawSignature = signature.toUint8Array(); + return p256.verify(rawSignature, sha3Message, this.toUint8Array()); + } + + /** + * Verifies a signed data with a public key + * + * @param args.message message + * @param args.signature The signature + * @returns true if the signature is valid + */ + verifyWebAuthnSignature(args: { message: HexInput; signature: WebAuthnSignature }): boolean { + const { message, signature } = args; + + if (!(signature.paar.signature.signature instanceof Secp256r1Signature)) { + throw new Error("Attestation signature is not a Secp256r1Signature"); + } + // Check challenge const { challenge } = signature.getCollectedClientData(); - const challengeStr = base64url.decode(challenge); - if (challengeStr !== message.toString()) { + + const messageBase64URLString = bufferToBase64URLString(Hex.fromHexInput(message).toUint8Array()); + if (challenge !== messageBase64URLString) { return false; } // Get verification data. const verificationData = signature.getVerificationData(); - const p256Signature = signature.paar.signature; - const rawSignature = p256Signature.toUint8Array(); - return p256.verify(rawSignature, verificationData, this.toUint8Array()); + // Verify the the signature is the signed verification data. + return this.verifySignature({ message: verificationData, signature: signature.paar.signature.signature }); } serialize(serializer: Serializer): void { serializer.serializeBytes(this.key.toUint8Array()); } - static deserialize(deserializer: Deserializer): P256PublicKey { + static deserialize(deserializer: Deserializer): Secp256r1PublicKey { const bytes = deserializer.deserializeBytes(); - return new P256PublicKey(bytes); + return new Secp256r1PublicKey(bytes); } - static load(deserializer: Deserializer): P256PublicKey { + static load(deserializer: Deserializer): Secp256r1PublicKey { const bytes = deserializer.deserializeBytes(); - return new P256PublicKey(bytes); + return new Secp256r1PublicKey(bytes); } } /** - * A P256 ecdsa private key - this is only used for test purposes as signing is done via passkeys + * A Secp256r1 ecdsa private key - this is only used for test purposes as signing is done via passkeys */ -export class P256PrivateKey extends PrivateKey { +export class Secp256r1PrivateKey extends PrivateKey { /** - * Length of P256 ecdsa private key + * Length of Secp256r1 ecdsa private key */ static readonly LENGTH: number = 32; @@ -119,8 +140,8 @@ export class P256PrivateKey extends PrivateKey { super(); const privateKeyHex = Hex.fromHexInput(hexInput); - if (privateKeyHex.toUint8Array().length !== P256PrivateKey.LENGTH) { - throw new Error(`PrivateKey length should be ${P256PrivateKey.LENGTH}`); + if (privateKeyHex.toUint8Array().length !== Secp256r1PrivateKey.LENGTH) { + throw new Error(`PrivateKey length should be ${Secp256r1PrivateKey.LENGTH}`); } this.key = privateKeyHex; @@ -150,49 +171,49 @@ export class P256PrivateKey extends PrivateKey { * @param message in HexInput format * @returns Signature */ - sign(message: HexInput): P256Signature { + sign(message: HexInput): Secp256r1Signature { const msgHex = Hex.fromHexInput(message); const sha3Message = sha3_256(msgHex.toUint8Array()); const signature = p256.sign(sha3Message, this.key.toUint8Array()); - return new P256Signature(signature.toCompactRawBytes()); + return new Secp256r1Signature(signature.toCompactRawBytes()); } serialize(serializer: Serializer): void { serializer.serializeBytes(this.toUint8Array()); } - static deserialize(deserializer: Deserializer): P256PrivateKey { + static deserialize(deserializer: Deserializer): Secp256r1PrivateKey { const bytes = deserializer.deserializeBytes(); - return new P256PrivateKey(bytes); + return new Secp256r1PrivateKey(bytes); } /** * Generate a new random private key. * - * @returns P256PrivateKey + * @returns Secp256r1PrivateKey */ - static generate(): P256PrivateKey { + static generate(): Secp256r1PrivateKey { const hexInput = p256.utils.randomPrivateKey(); - return new P256PrivateKey(hexInput); + return new Secp256r1PrivateKey(hexInput); } /** - * Derive the P256PublicKey from this private key. + * Derive the Secp256r1PublicKey from this private key. * - * @returns P256PublicKey + * @returns Secp256r1PublicKey */ - publicKey(): P256PublicKey { + publicKey(): Secp256r1PublicKey { const bytes = p256.getPublicKey(this.key.toUint8Array(), false); - return new P256PublicKey(bytes); + return new Secp256r1PublicKey(bytes); } } /** - * A signature of a message signed using an P256 ecdsa private key + * A signature of a message signed using an Secp256r1 ecdsa private key */ -export class P256Signature extends Signature { +export class Secp256r1Signature extends Signature { /** - * P256 ecdsa signatures are 256-bit. + * Secp256r1 ecdsa signatures are 256-bit. */ static readonly LENGTH = 64; @@ -211,8 +232,8 @@ export class P256Signature extends Signature { super(); const hex = Hex.fromHexInput(hexInput); - if (hex.toUint8Array().length !== P256Signature.LENGTH) { - throw new Error(`Signature length should be ${P256Signature.LENGTH}, recieved ${hex.toUint8Array().length}`); + if (hex.toUint8Array().length !== Secp256r1Signature.LENGTH) { + throw new Error(`Signature length should be ${Secp256r1Signature.LENGTH}, recieved ${hex.toUint8Array().length}`); } const signature = p256.Signature.fromCompact(hexInput).normalizeS().toCompactRawBytes(); this.data = Hex.fromHexInput(signature); @@ -240,13 +261,13 @@ export class P256Signature extends Signature { serializer.serializeBytes(this.data.toUint8Array()); } - static deserialize(deserializer: Deserializer): P256Signature { + static deserialize(deserializer: Deserializer): Secp256r1Signature { const hex = deserializer.deserializeBytes(); - return new P256Signature(hex); + return new Secp256r1Signature(hex); } - static load(deserializer: Deserializer): P256Signature { + static load(deserializer: Deserializer): Secp256r1Signature { const bytes = deserializer.deserializeBytes(); - return new P256Signature(bytes); + return new Secp256r1Signature(bytes); } } diff --git a/src/core/crypto/webauthn.ts b/src/core/crypto/webauthn.ts index 775c3e1f6..cadd0d9e9 100644 --- a/src/core/crypto/webauthn.ts +++ b/src/core/crypto/webauthn.ts @@ -1,22 +1,68 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { sha3_256 } from "@noble/hashes/sha3"; +import { sha256 } from "@noble/hashes/sha256"; import { Signature } from "./asymmetricCrypto"; import { Deserializer, Serializer } from "../../bcs"; import { Hex } from "../hex"; -import { HexInput } from "../../types"; -import { P256Signature } from "./p256"; +import { AssertionSignatureVariant, HexInput } from "../../types"; +import { Secp256r1Signature } from "./secp256r1"; + +export class AssertionSignature extends Signature { + public readonly signature: Signature; + + constructor(signature: Signature) { + super(); + this.signature = signature; + } + + /** + * Get the signature in bytes (Uint8Array). + * + * @returns Uint8Array representation of the signature + */ + toUint8Array(): Uint8Array { + return this.signature.toUint8Array(); + } + + /** + * Get the signature as a hex string with the 0x prefix. + * + * @returns string representation of the signature + */ + toString(): string { + return this.signature.toString(); + } + + serialize(serializer: Serializer): void { + if (this.signature instanceof Secp256r1Signature) { + serializer.serializeU32AsUleb128(AssertionSignatureVariant.Secp256r1); + this.signature.serialize(serializer); + } else { + throw new Error("Unknown signature type for AssertionSignature"); + } + } + + static deserialize(deserializer: Deserializer): AssertionSignature { + const index = deserializer.deserializeUleb128AsU32(); + switch (index) { + case AssertionSignatureVariant.Secp256r1: + return new AssertionSignature(Secp256r1Signature.load(deserializer)); + default: + throw new Error(`Unknown variant index for AssertionSignature: ${index}`); + } + } +} export class PartialAuthenticatorAssertionResponse { - readonly signature: P256Signature; + readonly signature: AssertionSignature; readonly authenticatorData: Uint8Array; readonly clientDataJSON: Uint8Array; - constructor(signature: HexInput, authenticatorData: HexInput, clientDataJSON: HexInput) { - this.signature = new P256Signature(signature); + constructor(signature: Signature, authenticatorData: HexInput, clientDataJSON: HexInput) { + this.signature = new AssertionSignature(signature); this.authenticatorData = Hex.fromHexInput(authenticatorData).toUint8Array(); @@ -49,8 +95,9 @@ export class WebAuthnSignature extends Signature { * * @param hexInput A HexInput (string or Uint8Array) */ - constructor(signature: HexInput, authenticatorData: HexInput, clientDataJSON: HexInput) { + constructor(signature: Signature, authenticatorData: HexInput, clientDataJSON: HexInput) { super(); + this.paar = new PartialAuthenticatorAssertionResponse(signature, authenticatorData, clientDataJSON); } @@ -73,41 +120,36 @@ export class WebAuthnSignature extends Signature { } getCollectedClientData(): ClientDataJSON { - const jsonString = Buffer.from(this.paar.clientDataJSON).toString("utf8"); - return JSON.parse(jsonString); + const utf8Decoder = new TextDecoder("utf-8"); + const decodedClientData = utf8Decoder.decode(this.paar.clientDataJSON); + return JSON.parse(decodedClientData); } getVerificationData(): Uint8Array { - const clientDataJSONHash = sha3_256(this.paar.clientDataJSON); - + const clientDataJSONHash = sha256(this.paar.clientDataJSON); const mergedArray = new Uint8Array(clientDataJSONHash.length + this.paar.authenticatorData.length); - mergedArray.set(clientDataJSONHash); - mergedArray.set(this.paar.authenticatorData, clientDataJSONHash.length); - + mergedArray.set(this.paar.authenticatorData); + mergedArray.set(clientDataJSONHash, this.paar.authenticatorData.length); return mergedArray; } serialize(serializer: Serializer): void { - serializer.serializeVectorBytes([ - this.paar.signature.toUint8Array(), - this.paar.authenticatorData, - this.paar.clientDataJSON, - ]); + this.paar.signature.serialize(serializer); + serializer.serializeBytes(this.paar.authenticatorData); + serializer.serializeBytes(this.paar.clientDataJSON); } static deserialize(deserializer: Deserializer): WebAuthnSignature { - const vectorBytes = deserializer.deserializeVectorBytes(); - if (vectorBytes.length !== 3) { - throw Error(); - } - return new WebAuthnSignature(vectorBytes[0], vectorBytes[1], vectorBytes[2]); + const sig = AssertionSignature.deserialize(deserializer); + const authData = deserializer.deserializeBytes(); + const clientDataJSON = deserializer.deserializeBytes(); + return new WebAuthnSignature(sig.signature, authData, clientDataJSON); } static load(deserializer: Deserializer): WebAuthnSignature { - const vectorBytes = deserializer.deserializeVectorBytes(); - if (vectorBytes.length !== 3) { - throw Error(); - } - return new WebAuthnSignature(vectorBytes[0], vectorBytes[1], vectorBytes[2]); + const sig = AssertionSignature.deserialize(deserializer); + const authData = deserializer.deserializeBytes(); + const clientDataJSON = deserializer.deserializeBytes(); + return new WebAuthnSignature(sig.signature, authData, clientDataJSON); } } diff --git a/src/internal/passkeysBrowser.ts b/src/internal/passkeysBrowser.ts index d0c1bbb02..41a953078 100644 --- a/src/internal/passkeysBrowser.ts +++ b/src/internal/passkeysBrowser.ts @@ -7,63 +7,106 @@ * other namespaces and processes can access these methods without depending on the entire * faucet namespace and without having a dependency cycle error. */ -import { sha3_256 } from "@noble/hashes/sha3"; -import { p256 } from "@noble/curves/p256"; -import { AptosConfig } from "../api/aptosConfig"; -import { Hex } from "../core"; import { - AnyRawTransaction, - deriveTransactionType, - getAuthenticatorForWebAuthn, - getSigningMessage, -} from "../transactions"; + generateRegistrationOptions as _generateRegistrationOptions, + verifyRegistrationResponse as _verifyRegistrationResponse, + VerifiedRegistrationResponse, +} from "@simplewebauthn/server"; +import { startRegistration } from "@simplewebauthn/browser"; +import { isoBase64URL, cose, parseAuthenticatorData, convertCOSEtoPKCS } from "@simplewebauthn/server/helpers"; +import { PublicKeyCredentialCreationOptionsJSON, RegistrationResponseJSON } from "@simplewebauthn/server/esm/deps"; +import { AptosConfig } from "../api/aptosConfig"; +import { AccountAddress, AuthenticationKey, PublicKey } from "../core"; +import { AnyRawTransaction, signWithPasskey } from "../transactions"; import { HexInput, PendingTransactionResponse } from "../types"; import { submitTransaction } from "./transactionSubmission"; +import { Secp256r1PublicKey } from "../core/crypto/secp256r1"; -export async function signWithPasskey(args: { - aptosConfig: AptosConfig; - credentialId: HexInput; - publicKey: HexInput; - transaction: AnyRawTransaction; -}): Promise { - const { aptosConfig, credentialId, publicKey, transaction } = args; - const allowCredentials: PublicKeyCredentialDescriptor[] = [ - { - type: "public-key", - id: Hex.fromHexInput(credentialId).toUint8Array(), - }, - ]; - - // get the signing message and hash it to create the challenge - const transactionToSign = deriveTransactionType(transaction); - const signingMessage = getSigningMessage(transactionToSign); - const challenge = sha3_256(signingMessage); +const supportedAlgorithmIDs = [cose.COSEALG.ES256]; - const publicKeyCredReqOptions: PublicKeyCredentialRequestOptions = { - challenge, - allowCredentials, +export async function generateRegistrationOptions(args: { + rpName: string; + rpID: string; + userID: string; + userName: string; + challenge?: string | Uint8Array; + userDisplayName?: string; + timeout?: number; + attestationType?: AttestationConveyancePreference; + authenticatorAttachment?: AuthenticatorAttachment; +}): Promise { + const { authenticatorAttachment } = args; + const authenticatorSelection: AuthenticatorSelectionCriteria = { + authenticatorAttachment, + residentKey: "required", + userVerification: "required", }; - const authenticationResponse = (await navigator.credentials.get({ - publicKey: publicKeyCredReqOptions, - })) as PublicKeyCredential; + return _generateRegistrationOptions({ + ...args, + authenticatorSelection, + supportedAlgorithmIDs, + }); +} - const authenticatorAssertionResponse = authenticationResponse.response as AuthenticatorAssertionResponse; +export async function registerCredential( + creationOptionsJSON: PublicKeyCredentialCreationOptionsJSON, +): Promise { + return startRegistration(creationOptionsJSON); +} - const { clientDataJSON, authenticatorData, signature } = authenticatorAssertionResponse; +export async function verifyRegistrationResponse(args: { + response: RegistrationResponseJSON; + expectedChallenge: string | ((challenge: string) => boolean | Promise); + expectedOrigin: string | string[]; + expectedRPID?: string | string[]; +}): Promise { + return _verifyRegistrationResponse({ + ...args, + requireUserVerification: true, + supportedAlgorithmIDs, + }); +} - const signatureCompact = p256.Signature.fromDER(new Uint8Array(signature)).toCompactRawBytes(); +export function parsePublicKey(response: RegistrationResponseJSON): PublicKey { + const authData = isoBase64URL.toBuffer(response.response.authenticatorData!); + const parsedAuthenticatorData = parseAuthenticatorData(authData); + // Convert from COSE + const publicKey = convertCOSEtoPKCS(parsedAuthenticatorData.credentialPublicKey!); + return new Secp256r1PublicKey(publicKey); +} - const authenticator = getAuthenticatorForWebAuthn({ - publicKey, - clientDataJSON: new Uint8Array(clientDataJSON), - authenticatorData: new Uint8Array(authenticatorData), - signature: signatureCompact, - }); +export async function signAndSubmitWithPasskey(args: { + aptosConfig: AptosConfig; + credentialId: string | Uint8Array; + publicKey: PublicKey; + transaction: AnyRawTransaction; + timeout?: number; + rpID?: string; +}): Promise { + const { aptosConfig, transaction } = args; + const authenticator = await signWithPasskey({ ...args }); return submitTransaction({ aptosConfig, transaction, senderAuthenticator: authenticator, }); } + +export async function getPasskeyAccountAddress(args: { publicKey: HexInput; alg?: number }): Promise { + const { publicKey, alg } = args; + const algorithm = alg ?? cose.COSEALG.ES256; + + let publicKeyObj: PublicKey; + switch (algorithm) { + // ES256, P256, Secp256r1 are all the same thing. + case cose.COSEALG.ES256: + publicKeyObj = new Secp256r1PublicKey(publicKey); + break; + default: + throw new Error("Algorithm is not supported"); + } + const authKey = AuthenticationKey.fromPublicKey({ publicKey: publicKeyObj }); + return AccountAddress.from(authKey.toString()); +} diff --git a/src/transactions/transactionBuilder/transactionBuilder.ts b/src/transactions/transactionBuilder/transactionBuilder.ts index 0c5e1daa1..a93e226ff 100644 --- a/src/transactions/transactionBuilder/transactionBuilder.ts +++ b/src/transactions/transactionBuilder/transactionBuilder.ts @@ -7,6 +7,10 @@ * and a signed transaction that can be simulated, signed and submitted to chain. */ import { sha3_256 as sha3Hash } from "@noble/hashes/sha3"; +import { isoBase64URL } from "@simplewebauthn/server/helpers"; +import { p256 } from "@noble/curves/p256"; +import { base64URLStringToBuffer, startAuthentication } from "@simplewebauthn/browser"; +import { generateAuthenticationOptions } from "@simplewebauthn/server"; import { AptosConfig } from "../../api/aptosConfig"; import { AccountAddress, AccountAddressInput, Hex, PublicKey } from "../../core"; import { Account } from "../../core/account"; @@ -14,6 +18,7 @@ import { AnyPublicKey } from "../../core/crypto/anyPublicKey"; import { AnySignature } from "../../core/crypto/anySignature"; import { Ed25519PublicKey, Ed25519Signature } from "../../core/crypto/ed25519"; import { Secp256k1PublicKey, Secp256k1Signature } from "../../core/crypto/secp256k1"; +import { Secp256r1PublicKey, Secp256r1Signature } from "../../core/crypto/secp256r1"; import { getInfo } from "../../internal/account"; import { getLedgerInfo } from "../../internal/general"; import { getGasPriceEstimation } from "../../internal/transaction"; @@ -77,7 +82,6 @@ import { memoizeAsync } from "../../utils/memoize"; import { AnyNumber, SigningScheme } from "../../types"; import { getFunctionParts, isScriptDataInput } from "./helpers"; import { WebAuthnSignature } from "../../core/crypto/webauthn"; -import { P256PublicKey } from "../../core/crypto/p256"; /** * We are defining function signatures, each with its specific input and output. @@ -454,8 +458,58 @@ export function sign(args: { signer: Account; transaction: AnyRawTransaction }): } } +export async function signWithPasskey(args: { + publicKey: PublicKey; + credentialId: string | Uint8Array; + transaction: AnyRawTransaction; + timeout?: number; + rpID?: string; +}): Promise { + const { credentialId, publicKey, transaction, timeout, rpID } = args; + + if (!(publicKey instanceof Secp256r1PublicKey)) { + throw new Error("Unsupported public key for passkey signing."); + } + + const allowCredentials: PublicKeyCredentialDescriptor[] = [ + { + type: "public-key", + id: typeof credentialId === "string" ? isoBase64URL.toBuffer(credentialId) : credentialId, + }, + ]; + + // Get the signing message and hash it to create the challenge + const transactionToSign = deriveTransactionType(transaction); + const signingMessage = getSigningMessage(transactionToSign); + const challenge = sha3Hash(signingMessage); + + const options = await generateAuthenticationOptions({ + allowCredentials, + challenge, + timeout, + rpID, + userVerification: "required", + }); + const authenticationResponse = await startAuthentication(options); + + const authenticatorAssertionResponse = authenticationResponse.response; + + const { clientDataJSON, authenticatorData, signature } = authenticatorAssertionResponse; + + const signatureCompact = p256.Signature.fromDER( + new Uint8Array(base64URLStringToBuffer(signature)), + ).toCompactRawBytes(); + + const webAuthnSignature = new WebAuthnSignature( + new Secp256r1Signature(signatureCompact), + isoBase64URL.toBuffer(authenticatorData), + isoBase64URL.toBuffer(clientDataJSON), + ); + return new AccountAuthenticatorSingleKey(new AnyPublicKey(publicKey), new AnySignature(webAuthnSignature)); +} + /** - * Creates the and returns the Authenticator for passkey signed transactions. + * Creates and returns the Authenticator for passkey signed transactions. * * @param args.publicKey The public key of the passkey credential. * @param args.signature The P256signature which is the signed challenge. @@ -465,16 +519,20 @@ export function sign(args: { signer: Account; transaction: AnyRawTransaction }): * @return The signer AccountAuthenticator */ export function getAuthenticatorForWebAuthn(args: { - publicKey: HexInput; + publicKey: PublicKey; signature: HexInput; authenticatorData: HexInput; clientDataJSON: HexInput; }): AccountAuthenticator { const { publicKey, signature, authenticatorData, clientDataJSON } = args; - const p256PublicKey = new P256PublicKey(publicKey); - const webAuthnSignature = new WebAuthnSignature(signature, authenticatorData, clientDataJSON); - - return new AccountAuthenticatorSingleKey(new AnyPublicKey(p256PublicKey), new AnySignature(webAuthnSignature)); + let signatureObj: Signature; + if (publicKey instanceof Secp256r1PublicKey) { + signatureObj = new Secp256r1Signature(signature); + } else { + throw new Error("Unsupported public key"); + } + const webAuthnSignature = new WebAuthnSignature(signatureObj, authenticatorData, clientDataJSON); + return new AccountAuthenticatorSingleKey(new AnyPublicKey(publicKey), new AnySignature(webAuthnSignature)); } /** @@ -519,7 +577,6 @@ export function generateSignedTransaction(args: InputSubmitTransactionData): Uin senderAuthenticator instanceof AccountAuthenticatorMultiKey) && transactionToSubmit instanceof RawTransaction ) { - console.log("single sender"); const transactionAuthenticator = new TransactionAuthenticatorSingleSender(senderAuthenticator); return new SignedTransaction(transactionToSubmit, transactionAuthenticator).bcsToBytes(); } diff --git a/src/types/index.ts b/src/types/index.ts index 670909991..bd3739297 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -107,7 +107,7 @@ export enum AccountAuthenticatorVariant { export enum AnyPublicKeyVariant { Ed25519 = 0, Secp256k1 = 1, - P256 = 2, + Secp256r1 = 2, } export enum AnySignatureVariant { @@ -116,6 +116,10 @@ export enum AnySignatureVariant { WebAuthn = 2, } +export enum AssertionSignatureVariant { + Secp256r1 = 0, +} + /** * BCS types */ @@ -1028,9 +1032,9 @@ export enum SigningSchemeInput { */ Secp256k1Ecdsa = 2, /** - * For P256Ecdsa + * For Secp256r1Ecdsa */ - P256Ecdsa = 3, + Secp256r1Ecdsa = 3, } /** @@ -1088,14 +1092,14 @@ export type GenerateAccountWithSingleSignerSecp256k1Key = { /** * Input type to generate an account using Single Signer - * P256 + * Secp256r1 */ -export type GenerateAccountWithSingleSignerP256Key = { - scheme: SigningSchemeInput.P256Ecdsa; +export type GenerateAccountWithSingleSignerSecp256r1Key = { + scheme: SigningSchemeInput.Secp256r1Ecdsa; legacy?: false; }; export type GenerateAccount = | GenerateAccountWithEd25519 | GenerateAccountWithSingleSignerSecp256k1Key - | GenerateAccountWithSingleSignerP256Key; + | GenerateAccountWithSingleSignerSecp256r1Key; From c83a482248a89f77e6ad700272ef0d3634a198ee Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 7 Dec 2023 00:19:02 +0900 Subject: [PATCH 04/10] add demo --- examples/typescript/passkey/.eslintrc.cjs | 18 + examples/typescript/passkey/.gitignore | 24 + examples/typescript/passkey/README.md | 27 + examples/typescript/passkey/index.html | 13 + examples/typescript/passkey/package.json | 35 + examples/typescript/passkey/pnpm-lock.yaml | 2276 +++++++++++++++++ examples/typescript/passkey/public/vite.svg | 1 + examples/typescript/passkey/src/App.css | 49 + examples/typescript/passkey/src/App.tsx | 183 ++ .../typescript/passkey/src/assets/react.svg | 1 + examples/typescript/passkey/src/index.css | 69 + examples/typescript/passkey/src/main.tsx | 10 + examples/typescript/passkey/src/vite-env.d.ts | 1 + examples/typescript/passkey/tsconfig.json | 25 + .../typescript/passkey/tsconfig.node.json | 10 + examples/typescript/passkey/vite.config.ts | 7 + package.json | 3 + pnpm-lock.yaml | 157 +- 18 files changed, 2896 insertions(+), 13 deletions(-) create mode 100644 examples/typescript/passkey/.eslintrc.cjs create mode 100644 examples/typescript/passkey/.gitignore create mode 100644 examples/typescript/passkey/README.md create mode 100644 examples/typescript/passkey/index.html create mode 100644 examples/typescript/passkey/package.json create mode 100644 examples/typescript/passkey/pnpm-lock.yaml create mode 100644 examples/typescript/passkey/public/vite.svg create mode 100644 examples/typescript/passkey/src/App.css create mode 100644 examples/typescript/passkey/src/App.tsx create mode 100644 examples/typescript/passkey/src/assets/react.svg create mode 100644 examples/typescript/passkey/src/index.css create mode 100644 examples/typescript/passkey/src/main.tsx create mode 100644 examples/typescript/passkey/src/vite-env.d.ts create mode 100644 examples/typescript/passkey/tsconfig.json create mode 100644 examples/typescript/passkey/tsconfig.node.json create mode 100644 examples/typescript/passkey/vite.config.ts diff --git a/examples/typescript/passkey/.eslintrc.cjs b/examples/typescript/passkey/.eslintrc.cjs new file mode 100644 index 000000000..d6c953795 --- /dev/null +++ b/examples/typescript/passkey/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/examples/typescript/passkey/.gitignore b/examples/typescript/passkey/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/examples/typescript/passkey/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/typescript/passkey/README.md b/examples/typescript/passkey/README.md new file mode 100644 index 000000000..1ebe379f5 --- /dev/null +++ b/examples/typescript/passkey/README.md @@ -0,0 +1,27 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./tsconfig.json', './tsconfig.node.json'], + tsconfigRootDir: __dirname, + }, +``` + +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/examples/typescript/passkey/index.html b/examples/typescript/passkey/index.html new file mode 100644 index 000000000..e4b78eae1 --- /dev/null +++ b/examples/typescript/passkey/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/examples/typescript/passkey/package.json b/examples/typescript/passkey/package.json new file mode 100644 index 000000000..182f28dce --- /dev/null +++ b/examples/typescript/passkey/package.json @@ -0,0 +1,35 @@ +{ + "name": "passkey-ts", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@noble/hashes": "^1.3.2", + "@simplewebauthn/server": "^8.3.5", + "aptos": "^1.20.0", + "base64url": "^3.0.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "peerDependencies": { + "@aptos-labs/ts-sdk": "link:../../.." + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "typescript": "^5.0.2", + "vite": "^4.4.5" + } +} diff --git a/examples/typescript/passkey/pnpm-lock.yaml b/examples/typescript/passkey/pnpm-lock.yaml new file mode 100644 index 000000000..141eee6a8 --- /dev/null +++ b/examples/typescript/passkey/pnpm-lock.yaml @@ -0,0 +1,2276 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@aptos-labs/ts-sdk': + specifier: link:../../.. + version: link:../../.. + '@noble/hashes': + specifier: ^1.3.2 + version: 1.3.2 + '@simplewebauthn/server': + specifier: ^8.3.5 + version: 8.3.5 + aptos: + specifier: ^1.20.0 + version: 1.20.0 + base64url: + specifier: ^3.0.1 + version: 3.0.1 + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + +devDependencies: + '@types/react': + specifier: ^18.2.15 + version: 18.2.28 + '@types/react-dom': + specifier: ^18.2.7 + version: 18.2.13 + '@typescript-eslint/eslint-plugin': + specifier: ^6.0.0 + version: 6.8.0(@typescript-eslint/parser@6.8.0)(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/parser': + specifier: ^6.0.0 + version: 6.8.0(eslint@8.51.0)(typescript@5.2.2) + '@vitejs/plugin-react': + specifier: ^4.0.3 + version: 4.1.0(vite@4.4.11) + eslint: + specifier: ^8.45.0 + version: 8.51.0 + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.0(eslint@8.51.0) + eslint-plugin-react-refresh: + specifier: ^0.4.3 + version: 0.4.3(eslint@8.51.0) + typescript: + specifier: ^5.0.2 + version: 5.2.2 + vite: + specifier: ^4.4.5 + version: 4.4.11 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + dev: true + + /@aptos-labs/aptos-client@0.0.2: + resolution: {integrity: sha512-FgKZb5zDPz8MmAcVxXzYhxP6OkzuIPoDRJp48YJ8+vrZ9EOZ35HaWGN2M3u+GPdnFE9mODFqkxw3azh3kHGZjQ==} + engines: {node: '>=15.10.0'} + dependencies: + axios: 0.27.2 + got: 11.8.6 + transitivePeerDependencies: + - debug + dev: false + + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.20 + chalk: 2.4.2 + dev: true + + /@babel/compat-data@7.23.2: + resolution: {integrity: sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.23.2: + resolution: {integrity: sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.0 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) + '@babel/helpers': 7.23.2 + '@babel/parser': 7.23.0 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.2 + '@babel/types': 7.23.0 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.23.0: + resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + jsesc: 2.5.2 + dev: true + + /@babel/helper-compilation-targets@7.22.15: + resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.2 + '@babel/helper-validator-option': 7.22.15 + browserslist: 4.22.1 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.2): + resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.22.15: + resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.23.2: + resolution: {integrity: sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.2 + '@babel/types': 7.23.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser@7.23.0: + resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/plugin-transform-react-jsx-self@7.22.5(@babel/core@7.23.2): + resolution: {integrity: sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-react-jsx-source@7.22.5(@babel/core@7.23.2): + resolution: {integrity: sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + dev: true + + /@babel/traverse@7.23.2: + resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.23.0: + resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + + /@cbor-extract/cbor-extract-darwin-arm64@2.1.1: + resolution: {integrity: sha512-blVBy5MXz6m36Vx0DfLd7PChOQKEs8lK2bD1WJn/vVgG4FXZiZmZb2GECHFvVPA5T7OnODd9xZiL3nMCv6QUhA==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@cbor-extract/cbor-extract-darwin-x64@2.1.1: + resolution: {integrity: sha512-h6KFOzqk8jXTvkOftyRIWGrd7sKQzQv2jVdTL9nKSf3D2drCvQB/LHUxAOpPXo3pv2clDtKs3xnHalpEh3rDsw==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@cbor-extract/cbor-extract-linux-arm64@2.1.1: + resolution: {integrity: sha512-SxAaRcYf8S0QHaMc7gvRSiTSr7nUYMqbUdErBEu+HYA4Q6UNydx1VwFE68hGcp1qvxcy9yT5U7gA+a5XikfwSQ==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@cbor-extract/cbor-extract-linux-arm@2.1.1: + resolution: {integrity: sha512-ds0uikdcIGUjPyraV4oJqyVE5gl/qYBpa/Wnh6l6xLE2lj/hwnjT2XcZCChdXwW/YFZ1LUHs6waoYN8PmK0nKQ==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@cbor-extract/cbor-extract-linux-x64@2.1.1: + resolution: {integrity: sha512-GVK+8fNIE9lJQHAlhOROYiI0Yd4bAZ4u++C2ZjlkS3YmO6hi+FUxe6Dqm+OKWTcMpL/l71N6CQAmaRcb4zyJuA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@cbor-extract/cbor-extract-win32-x64@2.1.1: + resolution: {integrity: sha512-2Niq1C41dCRIDeD8LddiH+mxGlO7HJ612Ll3D/E73ZWBmycued+8ghTr/Ho3CMOWPUEr08XtyBMVXAjqF+TcKw==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.51.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.51.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.9.1: + resolution: {integrity: sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.2: + resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.23.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.51.0: + resolution: {integrity: sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@hexagon/base64@1.1.28: + resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==} + dev: false + + /@humanwhocodes/config-array@0.11.11: + resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.19 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.19: + resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@noble/hashes@1.1.3: + resolution: {integrity: sha512-CE0FCR57H2acVI5UOzIGSSIYxZ6v/HOhDR0Ro9VLyhnzLwx0o8W1mmgaqlEUx4049qJDlIBRztv5k+MM8vbO3A==} + dev: false + + /@noble/hashes@1.3.2: + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + dev: false + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@peculiar/asn1-android@2.3.10: + resolution: {integrity: sha512-z9Rx9cFJv7UUablZISe7uksNbFJCq13hO0yEAOoIpAymALTLlvUOSLnGiQS7okPaM5dP42oTLhezH6XDXRXjGw==} + dependencies: + '@peculiar/asn1-schema': 2.3.8 + asn1js: 3.0.5 + tslib: 2.6.2 + dev: false + + /@peculiar/asn1-ecc@2.3.8: + resolution: {integrity: sha512-Ah/Q15y3A/CtxbPibiLM/LKcMbnLTdUdLHUgdpB5f60sSvGkXzxJCu5ezGTFHogZXWNX3KSmYqilCrfdmBc6pQ==} + dependencies: + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/asn1-x509': 2.3.8 + asn1js: 3.0.5 + tslib: 2.6.2 + dev: false + + /@peculiar/asn1-rsa@2.3.8: + resolution: {integrity: sha512-ES/RVEHu8VMYXgrg3gjb1m/XG0KJWnV4qyZZ7mAg7rrF3VTmRbLxO8mk+uy0Hme7geSMebp+Wvi2U6RLLEs12Q==} + dependencies: + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/asn1-x509': 2.3.8 + asn1js: 3.0.5 + tslib: 2.6.2 + dev: false + + /@peculiar/asn1-schema@2.3.8: + resolution: {integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==} + dependencies: + asn1js: 3.0.5 + pvtsutils: 1.3.5 + tslib: 2.6.2 + dev: false + + /@peculiar/asn1-x509@2.3.8: + resolution: {integrity: sha512-voKxGfDU1c6r9mKiN5ZUsZWh3Dy1BABvTM3cimf0tztNwyMJPhiXY94eRTgsMQe6ViLfT6EoXxkWVzcm3mFAFw==} + dependencies: + '@peculiar/asn1-schema': 2.3.8 + asn1js: 3.0.5 + ipaddr.js: 2.1.0 + pvtsutils: 1.3.5 + tslib: 2.6.2 + dev: false + + /@scure/base@1.1.3: + resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==} + dev: false + + /@scure/bip39@1.1.0: + resolution: {integrity: sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==} + dependencies: + '@noble/hashes': 1.1.3 + '@scure/base': 1.1.3 + dev: false + + /@simplewebauthn/server@8.3.5: + resolution: {integrity: sha512-Y6FkggTkzUdPk3cG3LLCiv7rqPQ3QI7g//RU9937G1pxogChvx12Y7/AZdWeMoeP+LFl0fPpdc1bIE0etJOxGA==} + engines: {node: '>=16.0.0'} + dependencies: + '@hexagon/base64': 1.1.28 + '@peculiar/asn1-android': 2.3.10 + '@peculiar/asn1-ecc': 2.3.8 + '@peculiar/asn1-rsa': 2.3.8 + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/asn1-x509': 2.3.8 + '@simplewebauthn/typescript-types': 8.3.4 + cbor-x: 1.5.4 + cross-fetch: 4.0.0 + transitivePeerDependencies: + - encoding + dev: false + + /@simplewebauthn/typescript-types@8.3.4: + resolution: {integrity: sha512-38xtca0OqfRVNloKBrFB5LEM6PN5vzFbJG6rAutPVrtGHFYxPdiV3btYWq0eAZAZmP+dqFPYJxJWeJrGfmYHng==} + dev: false + + /@sindresorhus/is@4.6.0: + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + dev: false + + /@szmarczak/http-timer@4.0.6: + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + dependencies: + defer-to-connect: 2.0.1 + dev: false + + /@types/babel__core@7.20.2: + resolution: {integrity: sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==} + dependencies: + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + '@types/babel__generator': 7.6.5 + '@types/babel__template': 7.4.2 + '@types/babel__traverse': 7.20.2 + dev: true + + /@types/babel__generator@7.6.5: + resolution: {integrity: sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@types/babel__template@7.4.2: + resolution: {integrity: sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==} + dependencies: + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + dev: true + + /@types/babel__traverse@7.20.2: + resolution: {integrity: sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@types/cacheable-request@6.0.3: + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + dependencies: + '@types/http-cache-semantics': 4.0.3 + '@types/keyv': 3.1.4 + '@types/node': 20.8.7 + '@types/responselike': 1.0.2 + dev: false + + /@types/http-cache-semantics@4.0.3: + resolution: {integrity: sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==} + dev: false + + /@types/json-schema@7.0.13: + resolution: {integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==} + dev: true + + /@types/keyv@3.1.4: + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + dependencies: + '@types/node': 20.8.7 + dev: false + + /@types/node@20.8.7: + resolution: {integrity: sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==} + dependencies: + undici-types: 5.25.3 + dev: false + + /@types/prop-types@15.7.8: + resolution: {integrity: sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==} + dev: true + + /@types/react-dom@18.2.13: + resolution: {integrity: sha512-eJIUv7rPP+EC45uNYp/ThhSpE16k22VJUknt5OLoH9tbXoi8bMhwLf5xRuWMywamNbWzhrSmU7IBJfPup1+3fw==} + dependencies: + '@types/react': 18.2.28 + dev: true + + /@types/react@18.2.28: + resolution: {integrity: sha512-ad4aa/RaaJS3hyGz0BGegdnSRXQBkd1CCYDCdNjBPg90UUpLgo+WlJqb9fMYUxtehmzF3PJaTWqRZjko6BRzBg==} + dependencies: + '@types/prop-types': 15.7.8 + '@types/scheduler': 0.16.4 + csstype: 3.1.2 + dev: true + + /@types/responselike@1.0.2: + resolution: {integrity: sha512-/4YQT5Kp6HxUDb4yhRkm0bJ7TbjvTddqX7PZ5hz6qV3pxSo72f/6YPRo+Mu2DU307tm9IioO69l7uAwn5XNcFA==} + dependencies: + '@types/node': 20.8.7 + dev: false + + /@types/scheduler@0.16.4: + resolution: {integrity: sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==} + dev: true + + /@types/semver@7.5.3: + resolution: {integrity: sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==} + dev: true + + /@typescript-eslint/eslint-plugin@6.8.0(@typescript-eslint/parser@6.8.0)(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-GosF4238Tkes2SHPQ1i8f6rMtG6zlKwMEB0abqSJ3Npvos+doIlc/ATG+vX1G9coDF3Ex78zM3heXHLyWEwLUw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.9.1 + '@typescript-eslint/parser': 6.8.0(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.8.0 + '@typescript-eslint/type-utils': 6.8.0(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.8.0(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.8.0 + debug: 4.3.4 + eslint: 8.51.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.8.0(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.8.0 + '@typescript-eslint/types': 6.8.0 + '@typescript-eslint/typescript-estree': 6.8.0(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.8.0 + debug: 4.3.4 + eslint: 8.51.0 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.8.0: + resolution: {integrity: sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.8.0 + '@typescript-eslint/visitor-keys': 6.8.0 + dev: true + + /@typescript-eslint/type-utils@6.8.0(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.8.0(typescript@5.2.2) + '@typescript-eslint/utils': 6.8.0(eslint@8.51.0)(typescript@5.2.2) + debug: 4.3.4 + eslint: 8.51.0 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.8.0: + resolution: {integrity: sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.8.0(typescript@5.2.2): + resolution: {integrity: sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.8.0 + '@typescript-eslint/visitor-keys': 6.8.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.8.0(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) + '@types/json-schema': 7.0.13 + '@types/semver': 7.5.3 + '@typescript-eslint/scope-manager': 6.8.0 + '@typescript-eslint/types': 6.8.0 + '@typescript-eslint/typescript-estree': 6.8.0(typescript@5.2.2) + eslint: 8.51.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@6.8.0: + resolution: {integrity: sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.8.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@vitejs/plugin-react@4.1.0(vite@4.4.11): + resolution: {integrity: sha512-rM0SqazU9iqPUraQ2JlIvReeaxOoRj6n+PzB1C0cBzIbd8qP336nC39/R9yPi3wVcah7E7j/kdU1uCUqMEU4OQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 + dependencies: + '@babel/core': 7.23.2 + '@babel/plugin-transform-react-jsx-self': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-react-jsx-source': 7.22.5(@babel/core@7.23.2) + '@types/babel__core': 7.20.2 + react-refresh: 0.14.0 + vite: 4.4.11 + transitivePeerDependencies: + - supports-color + dev: true + + /acorn-jsx@5.3.2(acorn@8.10.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.10.0 + dev: true + + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /aptos@1.20.0: + resolution: {integrity: sha512-driZt7qEr4ndKqqVHMyuFsQAHy4gJ4HPQttgVIpeDfnOIEnIV7A2jyJ9EYO2A+MayuyxXB+7yCNXT4HyBFJdpA==} + engines: {node: '>=11.0.0'} + dependencies: + '@aptos-labs/aptos-client': 0.0.2 + '@noble/hashes': 1.1.3 + '@scure/bip39': 1.1.0 + eventemitter3: 5.0.1 + form-data: 4.0.0 + tweetnacl: 1.0.3 + transitivePeerDependencies: + - debug + dev: false + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /asn1js@3.0.5: + resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} + engines: {node: '>=12.0.0'} + dependencies: + pvtsutils: 1.3.5 + pvutils: 1.1.3 + tslib: 2.6.2 + dev: false + + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + + /axios@0.27.2: + resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} + dependencies: + follow-redirects: 1.15.3 + form-data: 4.0.0 + transitivePeerDependencies: + - debug + dev: false + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /base64url@3.0.1: + resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} + engines: {node: '>=6.0.0'} + dev: false + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001549 + electron-to-chromium: 1.4.556 + node-releases: 2.0.13 + update-browserslist-db: 1.0.13(browserslist@4.22.1) + dev: true + + /cacheable-lookup@5.0.4: + resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} + engines: {node: '>=10.6.0'} + dev: false + + /cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} + engines: {node: '>=8'} + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + dev: false + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /caniuse-lite@1.0.30001549: + resolution: {integrity: sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==} + dev: true + + /cbor-extract@2.1.1: + resolution: {integrity: sha512-1UX977+L+zOJHsp0mWFG13GLwO6ucKgSmSW6JTl8B9GUvACvHeIVpFqhU92299Z6PfD09aTXDell5p+lp1rUFA==} + hasBin: true + requiresBuild: true + dependencies: + node-gyp-build-optional-packages: 5.0.3 + optionalDependencies: + '@cbor-extract/cbor-extract-darwin-arm64': 2.1.1 + '@cbor-extract/cbor-extract-darwin-x64': 2.1.1 + '@cbor-extract/cbor-extract-linux-arm': 2.1.1 + '@cbor-extract/cbor-extract-linux-arm64': 2.1.1 + '@cbor-extract/cbor-extract-linux-x64': 2.1.1 + '@cbor-extract/cbor-extract-win32-x64': 2.1.1 + dev: false + optional: true + + /cbor-x@1.5.4: + resolution: {integrity: sha512-PVKILDn+Rf6MRhhcyzGXi5eizn1i0i3F8Fe6UMMxXBnWkalq9+C5+VTmlIjAYM4iF2IYF2N+zToqAfYOp+3rfw==} + optionalDependencies: + cbor-extract: 2.1.1 + dev: false + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + dependencies: + mimic-response: 1.0.1 + dev: false + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /cross-fetch@4.0.0: + resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + dev: false + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /csstype@3.1.2: + resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + dev: false + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + dev: false + + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /electron-to-chromium@1.4.556: + resolution: {integrity: sha512-6RPN0hHfzDU8D56E72YkDvnLw5Cj2NMXZGg3UkgyoHxjVhG99KZpsKgBWMmTy0Ei89xwan+rbRsVB9yzATmYzQ==} + dev: true + + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: false + + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-plugin-react-hooks@4.6.0(eslint@8.51.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.51.0 + dev: true + + /eslint-plugin-react-refresh@0.4.3(eslint@8.51.0): + resolution: {integrity: sha512-Hh0wv8bUNY877+sI0BlCUlsS0TYYQqvzEwJsJJPM2WF4RnTStSnSR3zdJYa2nPOJgg3UghXi54lVyMSmpCalzA==} + peerDependencies: + eslint: '>=7' + dependencies: + eslint: 8.51.0 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.51.0: + resolution: {integrity: sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) + '@eslint-community/regexpp': 4.9.1 + '@eslint/eslintrc': 2.1.2 + '@eslint/js': 8.51.0 + '@humanwhocodes/config-array': 0.11.11 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.23.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.10.0 + acorn-jsx: 5.3.2(acorn@8.10.0) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + dev: false + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.1.1 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.1.1: + resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} + engines: {node: '>=12.0.0'} + dependencies: + flatted: 3.2.9 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + /follow-redirects@1.15.3: + resolution: {integrity: sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: false + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals@13.23.0: + resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /got@11.8.6: + resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} + engines: {node: '>=10.19.0'} + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.2 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + dev: false + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + dev: false + + /http2-wrapper@1.0.3: + resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} + engines: {node: '>=10.19.0'} + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + dev: false + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /ipaddr.js@2.1.0: + resolution: {integrity: sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==} + engines: {node: '>= 10'} + dev: false + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: false + + /lowercase-keys@2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + dev: false + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /mimic-response@1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + dev: false + + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: false + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: false + + /node-gyp-build-optional-packages@5.0.3: + resolution: {integrity: sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==} + hasBin: true + requiresBuild: true + dev: false + optional: true + + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: true + + /normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + dev: false + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + dev: false + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: false + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + /pvtsutils@1.3.5: + resolution: {integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==} + dependencies: + tslib: 2.6.2 + dev: false + + /pvutils@1.1.3: + resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} + engines: {node: '>=6.0.0'} + dev: false + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: false + + /react-dom@18.2.0(react@18.2.0): + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + dev: false + + /react-refresh@0.14.0: + resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} + engines: {node: '>=0.10.0'} + dev: true + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + + /resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + dev: false + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + dependencies: + lowercase-keys: 2.0.0 + dev: false + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /scheduler@0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false + + /ts-api-utils@1.0.3(typescript@5.2.2): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.2.2 + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + + /tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + dev: false + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /undici-types@5.25.3: + resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} + dev: false + + /update-browserslist-db@1.0.13(browserslist@4.22.1): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.1 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /vite@4.4.11: + resolution: {integrity: sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.18.20 + postcss: 8.4.31 + rollup: 3.29.4 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/examples/typescript/passkey/public/vite.svg b/examples/typescript/passkey/public/vite.svg new file mode 100644 index 000000000..e7b8dfb1b --- /dev/null +++ b/examples/typescript/passkey/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/typescript/passkey/src/App.css b/examples/typescript/passkey/src/App.css new file mode 100644 index 000000000..a5c23eb40 --- /dev/null +++ b/examples/typescript/passkey/src/App.css @@ -0,0 +1,49 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.comfy-row { + display: flex; + flex-direction: row; + justify-content: center; + gap: 8px; +} + +.read-the-docs { + color: #888; +} diff --git a/examples/typescript/passkey/src/App.tsx b/examples/typescript/passkey/src/App.tsx new file mode 100644 index 000000000..451f8338d --- /dev/null +++ b/examples/typescript/passkey/src/App.tsx @@ -0,0 +1,183 @@ +import { useState } from "react"; +import reactLogo from "./assets/react.svg"; +import viteLogo from "/vite.svg"; +import "./App.css"; +import { AccountAddress, Aptos, AptosConfig, parseTypeTag, Network, Secp256r1PublicKey, + U64, } from "@aptos-labs/ts-sdk"; + +const APTOS_COIN = "0x1::aptos_coin::AptosCoin"; +const ALICE_INITIAL_BALANCE = 100_000_000; +const BOB_INITIAL_BALANCE = 100; +const TRANSFER_AMOUNT = 7777777; +const COIN_STORE = "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"; +const config = new AptosConfig({ network: Network.LOCAL }); +const aptos = new Aptos(config); +const BOB_ADDR = "0x6c2fe73e11ed74b32a2f583f4df93190edd521d023925d148fbf7d66a2891835" + +function App() { + const [credentialId, setCredentialId] = useState( + window.localStorage.getItem("credentialId") + ); + + const [publicKey, setPublicKey] = useState( + window.localStorage.getItem("publicKey") + ); + +/** + * Prints the balance of an account + * @param aptos + * @param name + * @param address + * @returns {Promise<*>} + * + */ +const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { + type Coin = { coin: { value: string } }; + const resource = await aptos.getAccountResource({ + accountAddress: address, + resourceType: COIN_STORE, + }); + const amount = Number(resource.coin.value); + + console.log(`${name}'s balance is: ${amount}`); + return amount; +}; + + // Create the passkey via credential registration ceremony + const createPasskey = async () => { + const options = await aptos.generateRegistrationOptions({ + rpName: window.location.origin, + rpID: window.location.hostname, + userName: "Andrew", + userID: "andrew.apt", + authenticatorAttachment: "platform" + }); + + const cred = await aptos.registerCredential(options) + + setCredentialId(cred.rawId); + setPublicKey(aptos.parsePublicKey(cred).toString()); + window.localStorage.setItem("credentialId", cred.rawId); + window.localStorage.setItem("publicKey", aptos.parsePublicKey(cred).toString()); + }; + + const fundAccount = async () => { + if (!publicKey) { + alert("No registered publicKey"); + return; + } + + const addr = await aptos.getPasskeyAccountAddress({publicKey}) + + await aptos.faucet.fundAccount({ + accountAddress: addr.toUint8Array(), + amount: ALICE_INITIAL_BALANCE, + }); + + console.log("\n=== Balances ===\n"); + await balance(aptos, "Passkey Account", new AccountAddress(addr.toUint8Array())); + } + + const fundBobAccount = async () => { + await aptos.faucet.fundAccount({ + accountAddress: AccountAddress.fromString(BOB_ADDR), + amount: BOB_INITIAL_BALANCE, + }); + + console.log("\n=== Balances ===\n"); + await balance(aptos, "Bob Account", AccountAddress.fromString(BOB_ADDR)); + } + + const checkBalance = async () => { + if (!publicKey) { + alert("No registered publicKey"); + return; + } + + const addr = await aptos.getPasskeyAccountAddress({publicKey}) + + console.log("\n=== Balances ===\n"); + const bal = await balance(aptos, "Passkey Account", new AccountAddress(addr.toUint8Array())); + + window.alert(bal); + } + + const checkBobBalance = async () => { + + console.log("\n=== Balances ===\n"); + const bal = await balance(aptos, "Bob Account", AccountAddress.fromString(BOB_ADDR)); + + window.alert(bal); + } + + /** + * Use the passkey credential registered to the user to sign a coin transfer + */ + const signWithPasskey = async () => { + if (!credentialId) { + alert("No registered credential"); + return; + } + + if (!publicKey) { + alert("No registered publicKey"); + return; + } + + const addr = await aptos.getPasskeyAccountAddress({publicKey}) + + const txn = await aptos.generateTransaction({ + sender: addr, + data: { + function: "0x1::coin::transfer", + typeArguments: [parseTypeTag(APTOS_COIN)], + functionArguments: [AccountAddress.fromString(BOB_ADDR), new U64(TRANSFER_AMOUNT)], + }, + }); + + console.log("\n=== Transfer transaction ===\n"); + // const committedTxn = + await aptos.signAndSubmitWithPasskey({ + credentialId: credentialId, + transaction: txn, + publicKey: new Secp256r1PublicKey(publicKey), + }); + + // This doesn't work until the indexer works for passkeys + // await aptos.waitForTransaction({ transactionHash: committedTxn.hash }); + // console.log(`Committed transaction: ${committedTxn.hash}`); + }; + + return ( + <> + +

Passkeys Demo

+
+
+ + + + + + +
+

+ Edit src/App.tsx and save to test HMR +

+

rpId: {window.location.hostname}

+
+

+ Click on the Vite and React logos to learn more +

+ + ); +} + +export default App; diff --git a/examples/typescript/passkey/src/assets/react.svg b/examples/typescript/passkey/src/assets/react.svg new file mode 100644 index 000000000..6c87de9bb --- /dev/null +++ b/examples/typescript/passkey/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/typescript/passkey/src/index.css b/examples/typescript/passkey/src/index.css new file mode 100644 index 000000000..2c3fac689 --- /dev/null +++ b/examples/typescript/passkey/src/index.css @@ -0,0 +1,69 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/examples/typescript/passkey/src/main.tsx b/examples/typescript/passkey/src/main.tsx new file mode 100644 index 000000000..3d7150da8 --- /dev/null +++ b/examples/typescript/passkey/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.tsx' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/examples/typescript/passkey/src/vite-env.d.ts b/examples/typescript/passkey/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/examples/typescript/passkey/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/typescript/passkey/tsconfig.json b/examples/typescript/passkey/tsconfig.json new file mode 100644 index 000000000..a7fc6fbf2 --- /dev/null +++ b/examples/typescript/passkey/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/typescript/passkey/tsconfig.node.json b/examples/typescript/passkey/tsconfig.node.json new file mode 100644 index 000000000..42872c59f --- /dev/null +++ b/examples/typescript/passkey/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/typescript/passkey/vite.config.ts b/examples/typescript/passkey/vite.config.ts new file mode 100644 index 000000000..9cc50ead1 --- /dev/null +++ b/examples/typescript/passkey/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}); diff --git a/package.json b/package.json index 1bf32656f..463ea45c4 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,9 @@ "@noble/hashes": "^1.3.3", "@scure/bip32": "^1.3.3", "@scure/bip39": "^1.2.1", + "@simplewebauthn/browser": "^8.3.4", + "@simplewebauthn/server": "^8.3.5", + "base64url": "^3.0.1", "form-data": "^4.0.0", "tweetnacl": "^1.0.3", "eventemitter3": "^5.0.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e6d383949..95b59f5b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -985,6 +985,54 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true + /@cbor-extract/cbor-extract-darwin-arm64@2.1.1: + resolution: {integrity: sha512-blVBy5MXz6m36Vx0DfLd7PChOQKEs8lK2bD1WJn/vVgG4FXZiZmZb2GECHFvVPA5T7OnODd9xZiL3nMCv6QUhA==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@cbor-extract/cbor-extract-darwin-x64@2.1.1: + resolution: {integrity: sha512-h6KFOzqk8jXTvkOftyRIWGrd7sKQzQv2jVdTL9nKSf3D2drCvQB/LHUxAOpPXo3pv2clDtKs3xnHalpEh3rDsw==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@cbor-extract/cbor-extract-linux-arm64@2.1.1: + resolution: {integrity: sha512-SxAaRcYf8S0QHaMc7gvRSiTSr7nUYMqbUdErBEu+HYA4Q6UNydx1VwFE68hGcp1qvxcy9yT5U7gA+a5XikfwSQ==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@cbor-extract/cbor-extract-linux-arm@2.1.1: + resolution: {integrity: sha512-ds0uikdcIGUjPyraV4oJqyVE5gl/qYBpa/Wnh6l6xLE2lj/hwnjT2XcZCChdXwW/YFZ1LUHs6waoYN8PmK0nKQ==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@cbor-extract/cbor-extract-linux-x64@2.1.1: + resolution: {integrity: sha512-GVK+8fNIE9lJQHAlhOROYiI0Yd4bAZ4u++C2ZjlkS3YmO6hi+FUxe6Dqm+OKWTcMpL/l71N6CQAmaRcb4zyJuA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@cbor-extract/cbor-extract-win32-x64@2.1.1: + resolution: {integrity: sha512-2Niq1C41dCRIDeD8LddiH+mxGlO7HJ612Ll3D/E73ZWBmycued+8ghTr/Ho3CMOWPUEr08XtyBMVXAjqF+TcKw==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -1887,6 +1935,10 @@ packages: graphql: 16.8.1 dev: true + /@hexagon/base64@1.1.28: + resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==} + dev: false + /@humanwhocodes/config-array@0.11.13: resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} engines: {node: '>=10.10.0'} @@ -2233,13 +2285,48 @@ packages: fastq: 1.15.0 dev: true - /@peculiar/asn1-schema@2.3.6: - resolution: {integrity: sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA==} + /@peculiar/asn1-android@2.3.10: + resolution: {integrity: sha512-z9Rx9cFJv7UUablZISe7uksNbFJCq13hO0yEAOoIpAymALTLlvUOSLnGiQS7okPaM5dP42oTLhezH6XDXRXjGw==} + dependencies: + '@peculiar/asn1-schema': 2.3.8 + asn1js: 3.0.5 + tslib: 2.6.2 + dev: false + + /@peculiar/asn1-ecc@2.3.8: + resolution: {integrity: sha512-Ah/Q15y3A/CtxbPibiLM/LKcMbnLTdUdLHUgdpB5f60sSvGkXzxJCu5ezGTFHogZXWNX3KSmYqilCrfdmBc6pQ==} + dependencies: + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/asn1-x509': 2.3.8 + asn1js: 3.0.5 + tslib: 2.6.2 + dev: false + + /@peculiar/asn1-rsa@2.3.8: + resolution: {integrity: sha512-ES/RVEHu8VMYXgrg3gjb1m/XG0KJWnV4qyZZ7mAg7rrF3VTmRbLxO8mk+uy0Hme7geSMebp+Wvi2U6RLLEs12Q==} + dependencies: + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/asn1-x509': 2.3.8 + asn1js: 3.0.5 + tslib: 2.6.2 + dev: false + + /@peculiar/asn1-schema@2.3.8: + resolution: {integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==} dependencies: asn1js: 3.0.5 pvtsutils: 1.3.5 tslib: 2.6.2 - dev: true + + /@peculiar/asn1-x509@2.3.8: + resolution: {integrity: sha512-voKxGfDU1c6r9mKiN5ZUsZWh3Dy1BABvTM3cimf0tztNwyMJPhiXY94eRTgsMQe6ViLfT6EoXxkWVzcm3mFAFw==} + dependencies: + '@peculiar/asn1-schema': 2.3.8 + asn1js: 3.0.5 + ipaddr.js: 2.1.0 + pvtsutils: 1.3.5 + tslib: 2.6.2 + dev: false /@peculiar/json-schema@1.1.12: resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==} @@ -2252,7 +2339,7 @@ packages: resolution: {integrity: sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A==} engines: {node: '>=10.12.0'} dependencies: - '@peculiar/asn1-schema': 2.3.6 + '@peculiar/asn1-schema': 2.3.8 '@peculiar/json-schema': 1.1.12 pvtsutils: 1.3.5 tslib: 2.6.2 @@ -2390,6 +2477,33 @@ packages: '@scure/base': 1.1.3 dev: false + /@simplewebauthn/browser@8.3.4: + resolution: {integrity: sha512-rO0hZ0ESD28bZl6Qe8k7RUuYvDLbsS6oPezkMMTtZ5vC80U07j4qBELKBzojDD6BsdL3dIJ9SExVp8E7pqQ5fA==} + dependencies: + '@simplewebauthn/typescript-types': 8.3.4 + dev: false + + /@simplewebauthn/server@8.3.5: + resolution: {integrity: sha512-Y6FkggTkzUdPk3cG3LLCiv7rqPQ3QI7g//RU9937G1pxogChvx12Y7/AZdWeMoeP+LFl0fPpdc1bIE0etJOxGA==} + engines: {node: '>=16.0.0'} + dependencies: + '@hexagon/base64': 1.1.28 + '@peculiar/asn1-android': 2.3.10 + '@peculiar/asn1-ecc': 2.3.8 + '@peculiar/asn1-rsa': 2.3.8 + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/asn1-x509': 2.3.8 + '@simplewebauthn/typescript-types': 8.3.4 + cbor-x: 1.5.4 + cross-fetch: 4.0.0 + transitivePeerDependencies: + - encoding + dev: false + + /@simplewebauthn/typescript-types@8.3.4: + resolution: {integrity: sha512-38xtca0OqfRVNloKBrFB5LEM6PN5vzFbJG6rAutPVrtGHFYxPdiV3btYWq0eAZAZmP+dqFPYJxJWeJrGfmYHng==} + dev: false + /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true @@ -3076,7 +3190,6 @@ packages: pvtsutils: 1.3.5 pvutils: 1.1.3 tslib: 2.6.2 - dev: true /astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} @@ -3226,6 +3339,11 @@ packages: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true + /base64url@3.0.1: + resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} + engines: {node: '>=6.0.0'} + dev: false + /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -3661,6 +3779,14 @@ packages: - encoding dev: true + /cross-fetch@4.0.0: + resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + dev: false + /cross-inspect@1.0.0: resolution: {integrity: sha512-4PFfn4b5ZN6FMNGSZlyb7wUhuN8wvj8t/VQHZdM4JsDcruGJ8L2kf9zao98QIrBPFCpdk27qst/AGTl7pL3ypQ==} engines: {node: '>=16.0.0'} @@ -4848,6 +4974,11 @@ packages: loose-envify: 1.4.0 dev: true + /ipaddr.js@2.1.0: + resolution: {integrity: sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==} + engines: {node: '>= 10'} + dev: false + /is-absolute@1.0.0: resolution: {integrity: sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==} engines: {node: '>=0.10.0'} @@ -5926,7 +6057,13 @@ packages: optional: true dependencies: whatwg-url: 5.0.0 - dev: true + + /node-gyp-build-optional-packages@5.0.3: + resolution: {integrity: sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==} + hasBin: true + requiresBuild: true + dev: false + optional: true /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -6296,12 +6433,10 @@ packages: resolution: {integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==} dependencies: tslib: 2.6.2 - dev: true /pvutils@1.1.3: resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} engines: {node: '>=6.0.0'} - dev: true /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -6923,7 +7058,6 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true /tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -7054,7 +7188,6 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - dev: true /tsup@8.0.1(ts-node@10.9.2)(typescript@5.3.3): resolution: {integrity: sha512-hvW7gUSG96j53ZTSlT4j/KL0q1Q2l6TqGBFc6/mu/L46IoNWqLLUzLRLP1R8Q7xrJTmkDxxDoojV5uCVs1sVOg==} @@ -7302,7 +7435,7 @@ packages: /webcrypto-core@1.7.7: resolution: {integrity: sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g==} dependencies: - '@peculiar/asn1-schema': 2.3.6 + '@peculiar/asn1-schema': 2.3.8 '@peculiar/json-schema': 1.1.12 asn1js: 3.0.5 pvtsutils: 1.3.5 @@ -7311,7 +7444,6 @@ packages: /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true /webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -7367,7 +7499,6 @@ packages: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true /whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} From 6fa7ee5d41bed4a22181768b62ed4b4823219be5 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 7 Dec 2023 00:31:29 +0900 Subject: [PATCH 05/10] fixes post merge --- examples/typescript/passkey/src/App.tsx | 2 +- src/api/aptos.ts | 1 + src/api/{passkeysBrowser.ts => passkey.ts} | 2 +- src/internal/{passkeysBrowser.ts => passkey.ts} | 0 4 files changed, 3 insertions(+), 2 deletions(-) rename src/api/{passkeysBrowser.ts => passkey.ts} (98%) rename src/internal/{passkeysBrowser.ts => passkey.ts} (100%) diff --git a/examples/typescript/passkey/src/App.tsx b/examples/typescript/passkey/src/App.tsx index 451f8338d..ec56899bc 100644 --- a/examples/typescript/passkey/src/App.tsx +++ b/examples/typescript/passkey/src/App.tsx @@ -126,7 +126,7 @@ const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { const addr = await aptos.getPasskeyAccountAddress({publicKey}) - const txn = await aptos.generateTransaction({ + const txn = await aptos.build.transaction({ sender: addr, data: { function: "0x1::coin::transfer", diff --git a/src/api/aptos.ts b/src/api/aptos.ts index d3dff8c60..b458ad81e 100644 --- a/src/api/aptos.ts +++ b/src/api/aptos.ts @@ -10,6 +10,7 @@ import { Faucet } from "./faucet"; import { FungibleAsset } from "./fungibleAsset"; import { General } from "./general"; import { ANS } from "./ans"; +import { PasskeysBrowser } from "./passkey"; import { Staking } from "./staking"; import { Transaction } from "./transaction"; import { PasskeysBrowser } from "./passkeysBrowser"; diff --git a/src/api/passkeysBrowser.ts b/src/api/passkey.ts similarity index 98% rename from src/api/passkeysBrowser.ts rename to src/api/passkey.ts index 19d583b95..9bbb39e5c 100644 --- a/src/api/passkeysBrowser.ts +++ b/src/api/passkey.ts @@ -12,7 +12,7 @@ import { parsePublicKey, registerCredential, signAndSubmitWithPasskey, -} from "../internal/passkeysBrowser"; +} from "../internal/passkey"; import { AccountAddress, PublicKey } from "../core"; /** diff --git a/src/internal/passkeysBrowser.ts b/src/internal/passkey.ts similarity index 100% rename from src/internal/passkeysBrowser.ts rename to src/internal/passkey.ts From 84d3f5efe1e6ef95bcf3710bdedd369436526563 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 12 Dec 2023 17:38:05 +0900 Subject: [PATCH 06/10] add authentication options --- src/internal/passkey.ts | 45 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/internal/passkey.ts b/src/internal/passkey.ts index 41a953078..d461c50bb 100644 --- a/src/internal/passkey.ts +++ b/src/internal/passkey.ts @@ -10,11 +10,15 @@ import { generateRegistrationOptions as _generateRegistrationOptions, verifyRegistrationResponse as _verifyRegistrationResponse, + generateAuthenticationOptions as _generateAuthenticationOptions, + verifyAuthenticationResponse as _verifyAuthenticationResponse, VerifiedRegistrationResponse, + VerifiedAuthenticationResponse, } from "@simplewebauthn/server"; -import { startRegistration } from "@simplewebauthn/browser"; +import { startRegistration, startAuthentication } from "@simplewebauthn/browser"; import { isoBase64URL, cose, parseAuthenticatorData, convertCOSEtoPKCS } from "@simplewebauthn/server/helpers"; -import { PublicKeyCredentialCreationOptionsJSON, RegistrationResponseJSON } from "@simplewebauthn/server/esm/deps"; +import { AuthenticationResponseJSON, AuthenticatorDevice, PublicKeyCredentialCreationOptionsJSON, + PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON } from "@simplewebauthn/server/esm/deps"; import { AptosConfig } from "../api/aptosConfig"; import { AccountAddress, AuthenticationKey, PublicKey } from "../core"; import { AnyRawTransaction, signWithPasskey } from "../transactions"; @@ -68,6 +72,43 @@ export async function verifyRegistrationResponse(args: { }); } +export async function generateAuthenticationOptions(args: { + credentialId: string | Uint8Array; + timeout?: number; + rpID?: string; +}): Promise { + const {credentialId} = args; + const allowCredentials: PublicKeyCredentialDescriptor[] = [ + { + type: "public-key", + id: typeof credentialId === "string" ? isoBase64URL.toBuffer(credentialId) : credentialId, + }, + ]; + return _generateAuthenticationOptions({...args, allowCredentials, userVerification: "required"}) +} + +export async function authenticateCredential( + requestOptionsJSON: PublicKeyCredentialRequestOptionsJSON +): Promise { + return startAuthentication(requestOptionsJSON) +} + +export async function verifyAuthenticationResponse(args: { + response: AuthenticationResponseJSON; + expectedChallenge: string | ((challenge: string) => boolean | Promise); + expectedOrigin: string | string[]; + expectedRPID: string | string[]; + expectedType?: string | string[]; + authenticator: AuthenticatorDevice; + requireUserVerification?: boolean; + advancedFIDOConfig?: { + userVerification?: UserVerificationRequirement; + }; +}): Promise { + + return _verifyAuthenticationResponse({...args}) +} + export function parsePublicKey(response: RegistrationResponseJSON): PublicKey { const authData = isoBase64URL.toBuffer(response.response.authenticatorData!); const parsedAuthenticatorData = parseAuthenticatorData(authData); From 66d3d1206b9f3d993e22a4fcc1e2334b8d1adb61 Mon Sep 17 00:00:00 2001 From: Andrew Hariri Date: Fri, 19 Jan 2024 08:24:13 -0800 Subject: [PATCH 07/10] [passkey] Fix network to Devnet --- examples/typescript/passkey/src/App.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/typescript/passkey/src/App.tsx b/examples/typescript/passkey/src/App.tsx index ec56899bc..8f05c703b 100644 --- a/examples/typescript/passkey/src/App.tsx +++ b/examples/typescript/passkey/src/App.tsx @@ -10,7 +10,7 @@ const ALICE_INITIAL_BALANCE = 100_000_000; const BOB_INITIAL_BALANCE = 100; const TRANSFER_AMOUNT = 7777777; const COIN_STORE = "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"; -const config = new AptosConfig({ network: Network.LOCAL }); +const config = new AptosConfig({ network: Network.DEVNET }); const aptos = new Aptos(config); const BOB_ADDR = "0x6c2fe73e11ed74b32a2f583f4df93190edd521d023925d148fbf7d66a2891835" @@ -54,9 +54,14 @@ const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { }); const cred = await aptos.registerCredential(options) + const pubKey = aptos.parsePublicKey(cred); + const addr = await aptos.getPasskeyAccountAddress({ publicKey: pubKey.toString() }); + + console.log(addr.toString()); setCredentialId(cred.rawId); setPublicKey(aptos.parsePublicKey(cred).toString()); + console.log(cred) window.localStorage.setItem("credentialId", cred.rawId); window.localStorage.setItem("publicKey", aptos.parsePublicKey(cred).toString()); }; @@ -126,7 +131,8 @@ const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { const addr = await aptos.getPasskeyAccountAddress({publicKey}) - const txn = await aptos.build.transaction({ + console.log(aptos) + const txn = await aptos.build.simple({ sender: addr, data: { function: "0x1::coin::transfer", From dfa9fc32dcdadc27b685eae0f4260707cad533b5 Mon Sep 17 00:00:00 2001 From: Andrew Hariri Date: Mon, 22 Jan 2024 11:42:21 -0800 Subject: [PATCH 08/10] [passkey] update example --- examples/typescript/passkey/src/App.css | 7 + examples/typescript/passkey/src/App.tsx | 198 +++++++++--------- pnpm-lock.yaml | 61 ++++-- src/api/aptos.ts | 1 - src/api/passkey.ts | 3 + src/internal/passkey.ts | 6 +- src/internal/transactionSubmission.ts | 2 +- .../transactionBuilder/transactionBuilder.ts | 27 ++- 8 files changed, 184 insertions(+), 121 deletions(-) diff --git a/examples/typescript/passkey/src/App.css b/examples/typescript/passkey/src/App.css index a5c23eb40..389dfbe2c 100644 --- a/examples/typescript/passkey/src/App.css +++ b/examples/typescript/passkey/src/App.css @@ -3,6 +3,7 @@ margin: 0 auto; padding: 2rem; text-align: center; + width: 100%; } .logo { @@ -41,9 +42,15 @@ display: flex; flex-direction: row; justify-content: center; + flex-wrap: wrap; gap: 8px; + width: 100%; } .read-the-docs { color: #888; } + +.text-wrap { + word-break: break-all; +} \ No newline at end of file diff --git a/examples/typescript/passkey/src/App.tsx b/examples/typescript/passkey/src/App.tsx index 8f05c703b..ed6cf6857 100644 --- a/examples/typescript/passkey/src/App.tsx +++ b/examples/typescript/passkey/src/App.tsx @@ -1,47 +1,39 @@ import { useState } from "react"; -import reactLogo from "./assets/react.svg"; -import viteLogo from "/vite.svg"; import "./App.css"; -import { AccountAddress, Aptos, AptosConfig, parseTypeTag, Network, Secp256r1PublicKey, - U64, } from "@aptos-labs/ts-sdk"; +import { AccountAddress, Aptos, AptosConfig, DEFAULT_NETWORK, Network, Secp256r1PublicKey, postAptosFaucet } from "@aptos-labs/ts-sdk"; -const APTOS_COIN = "0x1::aptos_coin::AptosCoin"; -const ALICE_INITIAL_BALANCE = 100_000_000; -const BOB_INITIAL_BALANCE = 100; -const TRANSFER_AMOUNT = 7777777; const COIN_STORE = "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"; +// const config = new AptosConfig({ network: Network.LOCAL, fullnode: 'http://127.0.0.1:8080', faucet: 'http://127.0.0.1:8081' }); const config = new AptosConfig({ network: Network.DEVNET }); const aptos = new Aptos(config); -const BOB_ADDR = "0x6c2fe73e11ed74b32a2f583f4df93190edd521d023925d148fbf7d66a2891835" function App() { - const [credentialId, setCredentialId] = useState( - window.localStorage.getItem("credentialId") - ); + const [credentialId, setCredentialId] = useState(window.localStorage.getItem("credentialId")); + const [publicKey, setPublicKey] = useState(window.localStorage.getItem("publicKey")); + const [recipientAddress, setRecipientAddress] = useState(null); + const [sendAmount, setSendAmount] = useState(0); + const [faucetIsLoading, setFaucetIsLoading] = useState(false); + const [passkeyAddr, setPasskeyAddr] = useState(null); - const [publicKey, setPublicKey] = useState( - window.localStorage.getItem("publicKey") - ); + /** + * Prints the balance of an account + * @param aptos + * @param name + * @param address + * @returns {Promise<*>} + * + */ + const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { + type Coin = { coin: { value: string } }; + const resource = await aptos.getAccountResource({ + accountAddress: address, + resourceType: COIN_STORE, + }); + const amount = Number(resource.coin.value); -/** - * Prints the balance of an account - * @param aptos - * @param name - * @param address - * @returns {Promise<*>} - * - */ -const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { - type Coin = { coin: { value: string } }; - const resource = await aptos.getAccountResource({ - accountAddress: address, - resourceType: COIN_STORE, - }); - const amount = Number(resource.coin.value); - - console.log(`${name}'s balance is: ${amount}`); - return amount; -}; + console.log(`${name}'s balance is: ${amount}`); + return amount; + }; // Create the passkey via credential registration ceremony const createPasskey = async () => { @@ -50,10 +42,10 @@ const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { rpID: window.location.hostname, userName: "Andrew", userID: "andrew.apt", - authenticatorAttachment: "platform" + authenticatorAttachment: "platform", }); - const cred = await aptos.registerCredential(options) + const cred = await aptos.registerCredential(options); const pubKey = aptos.parsePublicKey(cred); const addr = await aptos.getPasskeyAccountAddress({ publicKey: pubKey.toString() }); @@ -61,7 +53,8 @@ const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { setCredentialId(cred.rawId); setPublicKey(aptos.parsePublicKey(cred).toString()); - console.log(cred) + setPasskeyAddr(addr.toString()); + console.log(cred); window.localStorage.setItem("credentialId", cred.rawId); window.localStorage.setItem("publicKey", aptos.parsePublicKey(cred).toString()); }; @@ -72,26 +65,32 @@ const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { return; } - const addr = await aptos.getPasskeyAccountAddress({publicKey}) + setFaucetIsLoading(true); + + const addr = await aptos.getPasskeyAccountAddress({ publicKey }); - await aptos.faucet.fundAccount({ - accountAddress: addr.toUint8Array(), - amount: ALICE_INITIAL_BALANCE, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { data } = await postAptosFaucet }>({ + aptosConfig: new AptosConfig({ + network: DEFAULT_NETWORK + }), + path: "fund", + body: { + address: AccountAddress.from(addr).toString(), + amount: 1e9, + }, + originMethod: "fundAccount", }); - console.log("\n=== Balances ===\n"); - await balance(aptos, "Passkey Account", new AccountAddress(addr.toUint8Array())); - } + const txnHash = data.txn_hashes[0]; - const fundBobAccount = async () => { - await aptos.faucet.fundAccount({ - accountAddress: AccountAddress.fromString(BOB_ADDR), - amount: BOB_INITIAL_BALANCE, - }); + const confirmedTxn = await aptos.waitForTransaction({ transactionHash: txnHash }); + alert("Faucet 1 APT deposited, txn hash: " + confirmedTxn.hash); console.log("\n=== Balances ===\n"); - await balance(aptos, "Bob Account", AccountAddress.fromString(BOB_ADDR)); - } + await balance(aptos, "Passkey Account", new AccountAddress(addr.toUint8Array())); + setFaucetIsLoading(false); + }; const checkBalance = async () => { if (!publicKey) { @@ -99,21 +98,13 @@ const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { return; } - const addr = await aptos.getPasskeyAccountAddress({publicKey}) + const addr = await aptos.getPasskeyAccountAddress({ publicKey }); console.log("\n=== Balances ===\n"); const bal = await balance(aptos, "Passkey Account", new AccountAddress(addr.toUint8Array())); - window.alert(bal); - } - - const checkBobBalance = async () => { - - console.log("\n=== Balances ===\n"); - const bal = await balance(aptos, "Bob Account", AccountAddress.fromString(BOB_ADDR)); - - window.alert(bal); - } + window.alert(bal / 1e8 + " APT"); + }; /** * Use the passkey credential registered to the user to sign a coin transfer @@ -129,59 +120,76 @@ const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { return; } - const addr = await aptos.getPasskeyAccountAddress({publicKey}) + const addr = await aptos.getPasskeyAccountAddress({ publicKey }); + const recipient = AccountAddress.fromString(recipientAddress || "0x1"); - console.log(aptos) - const txn = await aptos.build.simple({ + const txn = await aptos.transferCoinTransaction({ sender: addr, - data: { - function: "0x1::coin::transfer", - typeArguments: [parseTypeTag(APTOS_COIN)], - functionArguments: [AccountAddress.fromString(BOB_ADDR), new U64(TRANSFER_AMOUNT)], - }, + recipient: recipient, + amount: sendAmount * 1e8, }); console.log("\n=== Transfer transaction ===\n"); - // const committedTxn = - await aptos.signAndSubmitWithPasskey({ - credentialId: credentialId, - transaction: txn, - publicKey: new Secp256r1PublicKey(publicKey), - }); + const pendingTxn = await aptos.signAndSubmitWithPasskey({ + credentialId: credentialId, + transaction: txn, + publicKey: new Secp256r1PublicKey(publicKey), + options: {} + }); + console.log("PENDING TXN", pendingTxn); + + const committedTxn = await aptos.waitForTransaction({ transactionHash: pendingTxn.hash }); + console.log("COMMITTED TXN", committedTxn); // This doesn't work until the indexer works for passkeys // await aptos.waitForTransaction({ transactionHash: committedTxn.hash }); // console.log(`Committed transaction: ${committedTxn.hash}`); }; + const getAddress = async () => { + if (!credentialId) { + alert("No registered credential"); + return; + } + + if (!publicKey) { + alert("No registered publicKey"); + return; + } + + const addr = await aptos.getPasskeyAccountAddress({ publicKey: publicKey }); + setPasskeyAddr(addr.toString()); + + alert(addr); + }; + return ( <> -

Passkeys Demo

+ {passkeyAddr ?

{"Your address: " + passkeyAddr}

: null} + {passkeyAddr ? ( + + Explorer link + + ) : null}
- - + + - - - +
-

- Edit src/App.tsx and save to test HMR -

+

Recipient address

+ setRecipientAddress(e.currentTarget.value)} /> +

Send amount (APT)

+ setSendAmount(Number(e.currentTarget.value))} />

rpId: {window.location.hostname}

+
-

- Click on the Vite and React logos to learn more -

); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95b59f5b2..a805d7c0b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -985,48 +985,48 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@cbor-extract/cbor-extract-darwin-arm64@2.1.1: - resolution: {integrity: sha512-blVBy5MXz6m36Vx0DfLd7PChOQKEs8lK2bD1WJn/vVgG4FXZiZmZb2GECHFvVPA5T7OnODd9xZiL3nMCv6QUhA==} + /@cbor-extract/cbor-extract-darwin-arm64@2.2.0: + resolution: {integrity: sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==} cpu: [arm64] os: [darwin] requiresBuild: true dev: false optional: true - /@cbor-extract/cbor-extract-darwin-x64@2.1.1: - resolution: {integrity: sha512-h6KFOzqk8jXTvkOftyRIWGrd7sKQzQv2jVdTL9nKSf3D2drCvQB/LHUxAOpPXo3pv2clDtKs3xnHalpEh3rDsw==} + /@cbor-extract/cbor-extract-darwin-x64@2.2.0: + resolution: {integrity: sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==} cpu: [x64] os: [darwin] requiresBuild: true dev: false optional: true - /@cbor-extract/cbor-extract-linux-arm64@2.1.1: - resolution: {integrity: sha512-SxAaRcYf8S0QHaMc7gvRSiTSr7nUYMqbUdErBEu+HYA4Q6UNydx1VwFE68hGcp1qvxcy9yT5U7gA+a5XikfwSQ==} + /@cbor-extract/cbor-extract-linux-arm64@2.2.0: + resolution: {integrity: sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==} cpu: [arm64] os: [linux] requiresBuild: true dev: false optional: true - /@cbor-extract/cbor-extract-linux-arm@2.1.1: - resolution: {integrity: sha512-ds0uikdcIGUjPyraV4oJqyVE5gl/qYBpa/Wnh6l6xLE2lj/hwnjT2XcZCChdXwW/YFZ1LUHs6waoYN8PmK0nKQ==} + /@cbor-extract/cbor-extract-linux-arm@2.2.0: + resolution: {integrity: sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==} cpu: [arm] os: [linux] requiresBuild: true dev: false optional: true - /@cbor-extract/cbor-extract-linux-x64@2.1.1: - resolution: {integrity: sha512-GVK+8fNIE9lJQHAlhOROYiI0Yd4bAZ4u++C2ZjlkS3YmO6hi+FUxe6Dqm+OKWTcMpL/l71N6CQAmaRcb4zyJuA==} + /@cbor-extract/cbor-extract-linux-x64@2.2.0: + resolution: {integrity: sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==} cpu: [x64] os: [linux] requiresBuild: true dev: false optional: true - /@cbor-extract/cbor-extract-win32-x64@2.1.1: - resolution: {integrity: sha512-2Niq1C41dCRIDeD8LddiH+mxGlO7HJ612Ll3D/E73ZWBmycued+8ghTr/Ho3CMOWPUEr08XtyBMVXAjqF+TcKw==} + /@cbor-extract/cbor-extract-win32-x64@2.2.0: + resolution: {integrity: sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==} cpu: [x64] os: [win32] requiresBuild: true @@ -2494,7 +2494,7 @@ packages: '@peculiar/asn1-schema': 2.3.8 '@peculiar/asn1-x509': 2.3.8 '@simplewebauthn/typescript-types': 8.3.4 - cbor-x: 1.5.4 + cbor-x: 1.5.8 cross-fetch: 4.0.0 transitivePeerDependencies: - encoding @@ -3493,6 +3493,28 @@ packages: upper-case-first: 2.0.2 dev: true + /cbor-extract@2.2.0: + resolution: {integrity: sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==} + hasBin: true + requiresBuild: true + dependencies: + node-gyp-build-optional-packages: 5.1.1 + optionalDependencies: + '@cbor-extract/cbor-extract-darwin-arm64': 2.2.0 + '@cbor-extract/cbor-extract-darwin-x64': 2.2.0 + '@cbor-extract/cbor-extract-linux-arm': 2.2.0 + '@cbor-extract/cbor-extract-linux-arm64': 2.2.0 + '@cbor-extract/cbor-extract-linux-x64': 2.2.0 + '@cbor-extract/cbor-extract-win32-x64': 2.2.0 + dev: false + optional: true + + /cbor-x@1.5.8: + resolution: {integrity: sha512-gc3bHBsvG6GClCY6c0/iip+ghlqizkVp+TtaL927lwvP4VP9xBdi1HmqPR5uj/Mj/0TOlngMkIYa25wKg+VNrQ==} + optionalDependencies: + cbor-extract: 2.2.0 + dev: false + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -3898,6 +3920,13 @@ packages: engines: {node: '>=8'} dev: true + /detect-libc@2.0.2: + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + engines: {node: '>=8'} + requiresBuild: true + dev: false + optional: true + /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -6058,10 +6087,12 @@ packages: dependencies: whatwg-url: 5.0.0 - /node-gyp-build-optional-packages@5.0.3: - resolution: {integrity: sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==} + /node-gyp-build-optional-packages@5.1.1: + resolution: {integrity: sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==} hasBin: true requiresBuild: true + dependencies: + detect-libc: 2.0.2 dev: false optional: true diff --git a/src/api/aptos.ts b/src/api/aptos.ts index b458ad81e..a71847c1d 100644 --- a/src/api/aptos.ts +++ b/src/api/aptos.ts @@ -13,7 +13,6 @@ import { ANS } from "./ans"; import { PasskeysBrowser } from "./passkey"; import { Staking } from "./staking"; import { Transaction } from "./transaction"; -import { PasskeysBrowser } from "./passkeysBrowser"; /** * This class is the main entry point into Aptos's diff --git a/src/api/passkey.ts b/src/api/passkey.ts index 9bbb39e5c..d973ea6ba 100644 --- a/src/api/passkey.ts +++ b/src/api/passkey.ts @@ -39,6 +39,9 @@ export class PasskeysBrowser { transaction: AnyRawTransaction; timeout?: number; rpID?: string; + options?: { + allowCredentials?: PublicKeyCredentialDescriptor[] + } }): Promise { return signAndSubmitWithPasskey({ aptosConfig: this.config, ...args }); } diff --git a/src/internal/passkey.ts b/src/internal/passkey.ts index d461c50bb..fb40d8aea 100644 --- a/src/internal/passkey.ts +++ b/src/internal/passkey.ts @@ -102,7 +102,7 @@ export async function verifyAuthenticationResponse(args: { authenticator: AuthenticatorDevice; requireUserVerification?: boolean; advancedFIDOConfig?: { - userVerification?: UserVerificationRequirement; + userVerification?: UserVerificationRequirement; }; }): Promise { @@ -124,10 +124,14 @@ export async function signAndSubmitWithPasskey(args: { transaction: AnyRawTransaction; timeout?: number; rpID?: string; + options?: { + allowCredentials?: PublicKeyCredentialDescriptor[] + } }): Promise { const { aptosConfig, transaction } = args; const authenticator = await signWithPasskey({ ...args }); + console.log(authenticator); return submitTransaction({ aptosConfig, transaction, diff --git a/src/internal/transactionSubmission.ts b/src/internal/transactionSubmission.ts index c73ab7830..4d7f5ae47 100644 --- a/src/internal/transactionSubmission.ts +++ b/src/internal/transactionSubmission.ts @@ -21,7 +21,7 @@ import { sign, generateSigningMessage, } from "../transactions/transactionBuilder/transactionBuilder"; -import { +import type { InputGenerateTransactionData, AnyRawTransaction, InputSimulateTransactionData, diff --git a/src/transactions/transactionBuilder/transactionBuilder.ts b/src/transactions/transactionBuilder/transactionBuilder.ts index a93e226ff..fbb79de09 100644 --- a/src/transactions/transactionBuilder/transactionBuilder.ts +++ b/src/transactions/transactionBuilder/transactionBuilder.ts @@ -82,6 +82,7 @@ import { memoizeAsync } from "../../utils/memoize"; import { AnyNumber, SigningScheme } from "../../types"; import { getFunctionParts, isScriptDataInput } from "./helpers"; import { WebAuthnSignature } from "../../core/crypto/webauthn"; +import { getSigningMessage } from "../../internal/transactionSubmission"; /** * We are defining function signatures, each with its specific input and output. @@ -464,14 +465,18 @@ export async function signWithPasskey(args: { transaction: AnyRawTransaction; timeout?: number; rpID?: string; + options?: { + allowCredentials?: PublicKeyCredentialDescriptor[] + } }): Promise { - const { credentialId, publicKey, transaction, timeout, rpID } = args; + const { credentialId, publicKey, transaction, timeout, rpID, options } = args; + console.log("PublicKey", publicKey.toString()); if (!(publicKey instanceof Secp256r1PublicKey)) { throw new Error("Unsupported public key for passkey signing."); } - const allowCredentials: PublicKeyCredentialDescriptor[] = [ + const allowCredentials: PublicKeyCredentialDescriptor[] = options?.allowCredentials ?? [ { type: "public-key", id: typeof credentialId === "string" ? isoBase64URL.toBuffer(credentialId) : credentialId, @@ -479,32 +484,38 @@ export async function signWithPasskey(args: { ]; // Get the signing message and hash it to create the challenge - const transactionToSign = deriveTransactionType(transaction); - const signingMessage = getSigningMessage(transactionToSign); + const transactionToSign: SimpleTransaction = { rawTransaction: deriveTransactionType(transaction) as RawTransaction }; + const signingMessage = getSigningMessage({ transaction: transactionToSign as AnyRawTransaction }); const challenge = sha3Hash(signingMessage); - const options = await generateAuthenticationOptions({ + const authOptions = await generateAuthenticationOptions({ allowCredentials, challenge, timeout, rpID, userVerification: "required", }); - const authenticationResponse = await startAuthentication(options); + const authenticationResponse = await startAuthentication(authOptions); const authenticatorAssertionResponse = authenticationResponse.response; + console.log(authenticatorAssertionResponse); const { clientDataJSON, authenticatorData, signature } = authenticatorAssertionResponse; + console.log("DER Signature: ", signature) const signatureCompact = p256.Signature.fromDER( new Uint8Array(base64URLStringToBuffer(signature)), ).toCompactRawBytes(); + + console.log("COMPACT Signature: ", new Hex(signatureCompact).toString()); const webAuthnSignature = new WebAuthnSignature( new Secp256r1Signature(signatureCompact), isoBase64URL.toBuffer(authenticatorData), isoBase64URL.toBuffer(clientDataJSON), ); + + console.log("WEBAUTHN Signature: ", webAuthnSignature); return new AccountAuthenticatorSingleKey(new AnyPublicKey(publicKey), new AnySignature(webAuthnSignature)); } @@ -525,9 +536,9 @@ export function getAuthenticatorForWebAuthn(args: { clientDataJSON: HexInput; }): AccountAuthenticator { const { publicKey, signature, authenticatorData, clientDataJSON } = args; - let signatureObj: Signature; + let signatureObj: AnySignature; if (publicKey instanceof Secp256r1PublicKey) { - signatureObj = new Secp256r1Signature(signature); + signatureObj = new AnySignature(new Secp256r1Signature(signature)); } else { throw new Error("Unsupported public key"); } From 3ed6598d6cfd58913518d0bb1f3971aa358ee8d3 Mon Sep 17 00:00:00 2001 From: Andrew Hariri Date: Fri, 9 Feb 2024 13:20:17 -0800 Subject: [PATCH 09/10] [rebase] Update pnpm lock and HexInput from rebase --- pnpm-lock.yaml | 10 ++++++++++ .../transactionBuilder/transactionBuilder.ts | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a805d7c0b..49f479cb2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,15 @@ dependencies: '@scure/bip39': specifier: ^1.2.1 version: 1.2.1 + '@simplewebauthn/browser': + specifier: ^8.3.4 + version: 8.3.4 + '@simplewebauthn/server': + specifier: ^8.3.5 + version: 8.3.5 + base64url: + specifier: ^3.0.1 + version: 3.0.1 eventemitter3: specifier: ^5.0.1 version: 5.0.1 @@ -2502,6 +2511,7 @@ packages: /@simplewebauthn/typescript-types@8.3.4: resolution: {integrity: sha512-38xtca0OqfRVNloKBrFB5LEM6PN5vzFbJG6rAutPVrtGHFYxPdiV3btYWq0eAZAZmP+dqFPYJxJWeJrGfmYHng==} + deprecated: This package has been renamed to @simplewebauthn/types. Please install @simplewebauthn/types instead to ensure you receive future updates. dev: false /@sinclair/typebox@0.27.8: diff --git a/src/transactions/transactionBuilder/transactionBuilder.ts b/src/transactions/transactionBuilder/transactionBuilder.ts index fbb79de09..4f276c3de 100644 --- a/src/transactions/transactionBuilder/transactionBuilder.ts +++ b/src/transactions/transactionBuilder/transactionBuilder.ts @@ -79,7 +79,7 @@ import { } from "../types"; import { convertArgument, fetchEntryFunctionAbi, standardizeTypeTags } from "./remoteAbi"; import { memoizeAsync } from "../../utils/memoize"; -import { AnyNumber, SigningScheme } from "../../types"; +import { AnyNumber, HexInput, SigningScheme } from "../../types"; import { getFunctionParts, isScriptDataInput } from "./helpers"; import { WebAuthnSignature } from "../../core/crypto/webauthn"; import { getSigningMessage } from "../../internal/transactionSubmission"; From 080db990608e6f31c08716f04fbacefe49e95946 Mon Sep 17 00:00:00 2001 From: Andrew Hariri Date: Thu, 7 Mar 2024 16:43:08 -0800 Subject: [PATCH 10/10] [passkey] Add network --- examples/typescript/passkey/package.json | 5 ++ examples/typescript/passkey/pnpm-lock.yaml | 81 ++++++++++++++++++- examples/typescript/passkey/src/App.tsx | 27 +++++-- examples/typescript/passkey/src/index.css | 22 +++++ .../typescript/passkey/src/recoverPubKey.ts | 65 +++++++++++++++ 5 files changed, 189 insertions(+), 11 deletions(-) create mode 100644 examples/typescript/passkey/src/recoverPubKey.ts diff --git a/examples/typescript/passkey/package.json b/examples/typescript/passkey/package.json index 182f28dce..2f8a785ee 100644 --- a/examples/typescript/passkey/package.json +++ b/examples/typescript/passkey/package.json @@ -14,6 +14,8 @@ "@simplewebauthn/server": "^8.3.5", "aptos": "^1.20.0", "base64url": "^3.0.1", + "crypto-js": "^4.2.0", + "elliptic": "^6.5.4", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -21,6 +23,8 @@ "@aptos-labs/ts-sdk": "link:../../.." }, "devDependencies": { + "@types/crypto-js": "^4.2.2", + "@types/elliptic": "^6.4.18", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", "@typescript-eslint/eslint-plugin": "^6.0.0", @@ -29,6 +33,7 @@ "eslint": "^8.45.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", + "sha256": "link:@types/crypto-js/sha256", "typescript": "^5.0.2", "vite": "^4.4.5" } diff --git a/examples/typescript/passkey/pnpm-lock.yaml b/examples/typescript/passkey/pnpm-lock.yaml index 141eee6a8..f3ddfd4ba 100644 --- a/examples/typescript/passkey/pnpm-lock.yaml +++ b/examples/typescript/passkey/pnpm-lock.yaml @@ -20,6 +20,12 @@ dependencies: base64url: specifier: ^3.0.1 version: 3.0.1 + crypto-js: + specifier: ^4.2.0 + version: 4.2.0 + elliptic: + specifier: ^6.5.4 + version: 6.5.4 react: specifier: ^18.2.0 version: 18.2.0 @@ -28,6 +34,12 @@ dependencies: version: 18.2.0(react@18.2.0) devDependencies: + '@types/crypto-js': + specifier: ^4.2.2 + version: 4.2.2 + '@types/elliptic': + specifier: ^6.4.18 + version: 6.4.18 '@types/react': specifier: ^18.2.15 version: 18.2.28 @@ -52,6 +64,9 @@ devDependencies: eslint-plugin-react-refresh: specifier: ^0.4.3 version: 0.4.3(eslint@8.51.0) + sha256: + specifier: link:@types/crypto-js/sha256 + version: link:@types/crypto-js/sha256 typescript: specifier: ^5.0.2 version: 5.2.2 @@ -784,6 +799,12 @@ packages: '@babel/types': 7.23.0 dev: true + /@types/bn.js@5.1.5: + resolution: {integrity: sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==} + dependencies: + '@types/node': 20.8.7 + dev: true + /@types/cacheable-request@6.0.3: resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} dependencies: @@ -793,6 +814,16 @@ packages: '@types/responselike': 1.0.2 dev: false + /@types/crypto-js@4.2.2: + resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} + dev: true + + /@types/elliptic@6.4.18: + resolution: {integrity: sha512-UseG6H5vjRiNpQvrhy4VF/JXdA3V/Fp5amvveaL+fs28BZ6xIKJBPnUPRlEaZpysD9MbpfaLi8lbl7PGUAkpWw==} + dependencies: + '@types/bn.js': 5.1.5 + dev: true + /@types/http-cache-semantics@4.0.3: resolution: {integrity: sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==} dev: false @@ -811,7 +842,6 @@ packages: resolution: {integrity: sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==} dependencies: undici-types: 5.25.3 - dev: false /@types/prop-types@15.7.8: resolution: {integrity: sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==} @@ -1088,6 +1118,10 @@ packages: engines: {node: '>=6.0.0'} dev: false + /bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + dev: false + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -1102,6 +1136,10 @@ packages: fill-range: 7.0.1 dev: true + /brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + dev: false + /browserslist@4.22.1: resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1238,6 +1276,10 @@ packages: which: 2.0.2 dev: true + /crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + dev: false + /csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} dev: true @@ -1293,6 +1335,18 @@ packages: resolution: {integrity: sha512-6RPN0hHfzDU8D56E72YkDvnLw5Cj2NMXZGg3UkgyoHxjVhG99KZpsKgBWMmTy0Ei89xwan+rbRsVB9yzATmYzQ==} dev: true + /elliptic@6.5.4: + resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + dev: false + /end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: @@ -1644,6 +1698,21 @@ packages: engines: {node: '>=8'} dev: true + /hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + dev: false + + /hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + dev: false + /http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} dev: false @@ -1683,7 +1752,6 @@ packages: /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true /ipaddr.js@2.1.0: resolution: {integrity: sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==} @@ -1833,6 +1901,14 @@ packages: engines: {node: '>=10'} dev: false + /minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + dev: false + + /minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + dev: false + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -2186,7 +2262,6 @@ packages: /undici-types@5.25.3: resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} - dev: false /update-browserslist-db@1.0.13(browserslist@4.22.1): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} diff --git a/examples/typescript/passkey/src/App.tsx b/examples/typescript/passkey/src/App.tsx index ed6cf6857..1c0043bc0 100644 --- a/examples/typescript/passkey/src/App.tsx +++ b/examples/typescript/passkey/src/App.tsx @@ -1,11 +1,8 @@ import { useState } from "react"; import "./App.css"; -import { AccountAddress, Aptos, AptosConfig, DEFAULT_NETWORK, Network, Secp256r1PublicKey, postAptosFaucet } from "@aptos-labs/ts-sdk"; +import { AccountAddress, Aptos, AptosConfig, Network, Secp256r1PublicKey, postAptosFaucet } from "@aptos-labs/ts-sdk"; const COIN_STORE = "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"; -// const config = new AptosConfig({ network: Network.LOCAL, fullnode: 'http://127.0.0.1:8080', faucet: 'http://127.0.0.1:8081' }); -const config = new AptosConfig({ network: Network.DEVNET }); -const aptos = new Aptos(config); function App() { const [credentialId, setCredentialId] = useState(window.localStorage.getItem("credentialId")); @@ -14,6 +11,10 @@ function App() { const [sendAmount, setSendAmount] = useState(0); const [faucetIsLoading, setFaucetIsLoading] = useState(false); const [passkeyAddr, setPasskeyAddr] = useState(null); + const [currentNetwork, setCurrentNetwork] = useState(Network.DEVNET); + + const config = new AptosConfig({ network: currentNetwork }); + const aptos = new Aptos(config); /** * Prints the balance of an account @@ -71,9 +72,7 @@ function App() { // eslint-disable-next-line @typescript-eslint/no-explicit-any const { data } = await postAptosFaucet }>({ - aptosConfig: new AptosConfig({ - network: DEFAULT_NETWORK - }), + aptosConfig: config, path: "fund", body: { address: AccountAddress.from(addr).toString(), @@ -163,13 +162,21 @@ function App() { alert(addr); }; + const switchNetwork: React.ChangeEventHandler = (event) => { + if (Object.values(Network).includes(event.target.value as Network)) { + setCurrentNetwork(event.target.value as Network); + } else { + alert("Error: Incorrect network selected"); + } + } + return ( <>

Passkeys Demo

{passkeyAddr ?

{"Your address: " + passkeyAddr}

: null} {passkeyAddr ? ( @@ -182,6 +189,10 @@ function App() { +

Recipient address

setRecipientAddress(e.currentTarget.value)} /> diff --git a/examples/typescript/passkey/src/index.css b/examples/typescript/passkey/src/index.css index 2c3fac689..23f70f293 100644 --- a/examples/typescript/passkey/src/index.css +++ b/examples/typescript/passkey/src/index.css @@ -55,6 +55,25 @@ button:focus-visible { outline: 4px auto -webkit-focus-ring-color; } +select { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +select:hover { + border-color: #646cff; +} +select:focus, +select:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + @media (prefers-color-scheme: light) { :root { color: #213547; @@ -66,4 +85,7 @@ button:focus-visible { button { background-color: #f9f9f9; } + select { + background-color: #f9f9f9; + } } diff --git a/examples/typescript/passkey/src/recoverPubKey.ts b/examples/typescript/passkey/src/recoverPubKey.ts new file mode 100644 index 000000000..ed7fdeabd --- /dev/null +++ b/examples/typescript/passkey/src/recoverPubKey.ts @@ -0,0 +1,65 @@ +import EC from 'elliptic'; +const ec = new EC.ec('secp256r1'); + +export async function sha256(message: Uint8Array) { + const hashBuffer = await crypto.subtle.digest('SHA-256', message); + return new Uint8Array(hashBuffer); +} + +export function concatenateUint8Arrays(array1: Uint8Array, array2: Uint8Array) { + const result = new Uint8Array(array1.length + array2.length); + result.set(array1, 0); + result.set(array2, array1.length); + + return result; +} + +export async function recoverPublicKey( + authenticatorData: Uint8Array, + clientDataJSON: Uint8Array, + signature: Uint8Array +) { + const shaClientDataJSON = await sha256(clientDataJSON); + const message = concatenateUint8Arrays(authenticatorData, shaClientDataJSON); + + const result = await recoverPublicKeyFromMessageAndSignature(message, signature); + return result; +} + +export async function recoverPublicKeyFromMessageAndSignature(message: Uint8Array, signature: Uint8Array) { + // Hash the message + const msgHash = await sha256(message); + + // Assume signature is an object with r and s components + // In real scenarios, ensure to parse the signature from its encoded form + let publicKey; + for (let recoveryId = 0; recoveryId < 2; recoveryId++) { + try { + publicKey = ec.recoverPubKey(msgHash, signature, recoveryId); + // If recovery was successful and publicKey is valid, break out of the loop + if (publicKey) break; + } catch (error) { + // If this recoveryId does not work, catch the error and try the next one + console.error("Recovery with ID", recoveryId, "failed:", error); + } + } + + if (!publicKey) { + throw new Error('Failed to recover public key'); + } + + // Convert the public key to its byte representation + const publicKeyBytes = publicKey.encode('array', false); + console.log(publicKeyBytes); + return publicKeyBytes; +} + +// Example usage (pseudo-code, depends on actual signature format and input) +// const message = 'Your message here'; +// const signature = { r: 'signatureRValue', s: 'signatureSValue' }; +// try { +// const publicKeyBytes = recoverPublicKeyFromSignatureWithoutRecoveryId(message, signature); +// console.log(publicKeyBytes); +// } catch (error) { +// console.error(error); +// }