From e813a3f95fbb5d89c171134e5518ded16a35884e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yago=20P=C3=A9rez=20V=C3=A1zquez?= Date: Mon, 18 Nov 2024 13:03:18 +0100 Subject: [PATCH] Remove libs --- packages/protocol-kit/package.json | 2 - .../src/utils/passkeys/extractPasskeyData.ts | 135 ++++++++++++------ 2 files changed, 93 insertions(+), 44 deletions(-) diff --git a/packages/protocol-kit/package.json b/packages/protocol-kit/package.json index 1c57b4541..b06b67970 100644 --- a/packages/protocol-kit/package.json +++ b/packages/protocol-kit/package.json @@ -67,8 +67,6 @@ }, "dependencies": { "@noble/curves": "^1.6.0", - "@noble/hashes": "^1.3.3", - "@peculiar/asn1-schema": "^2.3.13", "@safe-global/safe-deployments": "^1.37.14", "@safe-global/safe-modules-deployments": "^2.2.4", "@safe-global/types-kit": "^1.0.0", diff --git a/packages/protocol-kit/src/utils/passkeys/extractPasskeyData.ts b/packages/protocol-kit/src/utils/passkeys/extractPasskeyData.ts index ea6e43b9c..eda194008 100644 --- a/packages/protocol-kit/src/utils/passkeys/extractPasskeyData.ts +++ b/packages/protocol-kit/src/utils/passkeys/extractPasskeyData.ts @@ -1,50 +1,57 @@ import { p256 } from '@noble/curves/p256' -import { Buffer } from 'buffer' import { getFCLP256VerifierDeployment } from '@safe-global/safe-modules-deployments' import { PasskeyArgType, PasskeyCoordinates } from '@safe-global/protocol-kit/types' /** - * Extracts and returns the passkey data (coordinates and rawId) from a given passkey Credential. + * Converts a Base64 URL-encoded string to a Uint8Array. * - * @param {Credential} passkeyCredential - The passkey credential generated via `navigator.credentials.create()` or other method in another platforms. - * @returns {Promise} A promise that resolves to an object containing the coordinates and the rawId derived from the passkey. - * This is the important information in the Safe account context and should be stored securely as it is used to verify the passkey and to instantiate the SDK - * as a signer (`Safe.init()) - * @throws {Error} Throws an error if the coordinates could not be extracted + * This function handles Base64 URL variants by replacing URL-safe characters + * with standard Base64 characters, decodes the Base64 string into a binary string, + * and then converts it into a Uint8Array. + * + * @param {string} base64 - The Base64 URL-encoded string to convert. + * @returns {Uint8Array} The resulting Uint8Array from the decoded Base64 string. */ -export async function extractPasskeyData(passkeyCredential: Credential): Promise { - const passkey = passkeyCredential as PublicKeyCredential - const attestationResponse = passkey.response as AuthenticatorAttestationResponse - - const rawId = Buffer.from(passkey.rawId).toString('hex') - const coordinates = await decodePublicKey(attestationResponse) - - return { - rawId, - coordinates - } -} - function base64ToUint8Array(base64: string): Uint8Array { const base64Fixed = base64.replace(/-/g, '+').replace(/_/g, '/') - const binaryString = atob(base64Fixed) - const bytes = new Uint8Array(binaryString.length) - for (let i = 0; i < binaryString.length; i++) { - bytes[i] = binaryString.charCodeAt(i) - } - return bytes + const binaryBuffer = Buffer.from(base64Fixed, 'base64') + + return new Uint8Array(binaryBuffer) } +/** + * Ensures the elliptic curve public key is in the correct uncompressed format. + * + * Elliptic curve operations often require the public key to be in an uncompressed format, + * which starts with a `0x04` byte, followed by the x and y coordinates. This function + * checks the key length and prepends `0x04` if necessary. + * + * @param {Uint8Array} publicKey - The public key to format. + * @returns {Uint8Array} The formatted public key in uncompressed format. + */ function ensureCorrectFormat(publicKey: Uint8Array): Uint8Array { if (publicKey.length === 64) { const uncompressedKey = new Uint8Array(65) uncompressedKey[0] = 0x04 uncompressedKey.set(publicKey, 1) + return uncompressedKey } + return publicKey } +/** + * Decodes a Base64-encoded ECDSA public key for React Native and extracts the x and y coordinates. + * + * This function decodes a Base64-encoded public key, ensures it is in the correct uncompressed format, + * and extracts the x and y coordinates using the `@noble/curves` library. The coordinates are returned + * as hexadecimal strings prefixed with '0x'. + * + * @param {string} publicKey - The Base64-encoded public key to decode. + * @returns {PasskeyCoordinates} An object containing the x and y coordinates of the public key. + * @throws {Error} Throws an error if the key coordinates cannot be extracted. + */ function decodePublicKeyForReactNative(publicKey: string): PasskeyCoordinates { const publicKeyUint8Array = base64ToUint8Array(publicKey) @@ -53,6 +60,7 @@ function decodePublicKeyForReactNative(publicKey: string): PasskeyCoordinates { } const formattedKey = ensureCorrectFormat(publicKeyUint8Array) + const point = p256.ProjectivePoint.fromHex(formattedKey) const x = point.x.toString(16).padStart(64, '0') @@ -64,6 +72,18 @@ function decodePublicKeyForReactNative(publicKey: string): PasskeyCoordinates { } } +/** + * Decodes an ECDSA public key for the web platform and extracts the x and y coordinates. + * + * This function uses the Web Crypto API to import a public key in SPKI format and then + * exports it to a JWK format to retrieve the x and y coordinates. The coordinates are + * returned as hexadecimal strings prefixed with '0x'. + * + * @param {ArrayBuffer} publicKey - The public key in SPKI format to decode. + * @returns {Promise} A promise that resolves to an object containing + * the x and y coordinates of the public key. + * @throws {Error} Throws an error if the key coordinates cannot be extracted. + */ async function decodePublicKeyForWeb(publicKey: ArrayBuffer): Promise { const algorithm = { name: 'ECDSA', @@ -94,7 +114,7 @@ async function decodePublicKeyForWeb(publicKey: ArrayBuffer): Promise { const publicKey = response.getPublicKey() @@ -103,24 +123,55 @@ export async function decodePublicKey( throw new Error('Failed to generate passkey coordinates. getPublicKey() failed') } - try { - if (typeof publicKey === 'string') { - // Public key is base64 encoded - // React Native platform uses base64 encoded strings - return decodePublicKeyForReactNative(publicKey) - } else if (publicKey instanceof ArrayBuffer) { - // Public key is an ArrayBuffer - // Web platform uses ArrayBuffer - return await decodePublicKeyForWeb(publicKey) - } else { - throw new Error('Unsupported public key format.') - } - } catch (error) { - console.error('Error decoding public key:', error) - throw error + if (typeof publicKey === 'string') { + // Public key is base64 encoded + // - React Native platform uses base64 encoded strings + return decodePublicKeyForReactNative(publicKey) + } + + if (publicKey instanceof ArrayBuffer) { + // Public key is an ArrayBuffer + // - Web platform uses ArrayBuffer + return await decodePublicKeyForWeb(publicKey) } + + throw new Error('Unsupported public key format.') } +/** + * Extracts and returns the passkey data (coordinates and rawId) from a given passkey Credential. + * + * @param {Credential} passkeyCredential - The passkey credential generated via `navigator.credentials.create()` or other method in another platforms. + * @returns {Promise} A promise that resolves to an object containing the coordinates and the rawId derived from the passkey. + * This is the important information in the Safe account context and should be stored securely as it is used to verify the passkey and to instantiate the SDK + * as a signer (`Safe.init()) + * @throws {Error} Throws an error if the coordinates could not be extracted + */ +export async function extractPasskeyData(passkeyCredential: Credential): Promise { + const passkey = passkeyCredential as PublicKeyCredential + const attestationResponse = passkey.response as AuthenticatorAttestationResponse + + const rawId = Buffer.from(passkey.rawId).toString('hex') + const coordinates = await decodePublicKey(attestationResponse) + + return { + rawId, + coordinates + } +} + +/** + * Retrieves the default FCLP256 Verifier address for a given blockchain network. + * + * This function fetches the deployment information for the FCLP256 Verifier and + * returns the verifier address associated with the specified chain ID. It ensures + * that the correct version and release status are used. + * + * @param {string} chainId - The ID of the blockchain network to retrieve the verifier address for. + * @returns {string} The FCLP256 Verifier address for the specified chain ID. + * @throws {Error} Throws an error if the deployment information or address cannot be found. + */ + export function getDefaultFCLP256VerifierAddress(chainId: string): string { const FCLP256VerifierDeployment = getFCLP256VerifierDeployment({ version: '0.2.1',