Skip to content

Commit

Permalink
Add passkeys dependencies as optional
Browse files Browse the repository at this point in the history
  • Loading branch information
yagopv committed Nov 21, 2024
1 parent c74f90d commit 8798aaf
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 38 deletions.
6 changes: 4 additions & 2 deletions packages/protocol-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,15 @@
"web3": "^4.12.1"
},
"dependencies": {
"@noble/curves": "^1.6.0",
"@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",
"abitype": "^1.0.2",
"semver": "^7.6.3",
"viem": "^2.21.8"
},
"optionalDependencies": {
"@noble/curves": "^1.6.0",
"@peculiar/asn1-schema": "^2.3.13"
}
}
74 changes: 38 additions & 36 deletions packages/protocol-kit/src/utils/passkeys/extractPasskeyData.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,11 @@
import { Buffer } from 'buffer'
import { p256 } from '@noble/curves/p256'
import { getFCLP256VerifierDeployment } from '@safe-global/safe-modules-deployments'
import {
PasskeyArgType,
PasskeyAuthenticatorAttestationResponse,
PasskeyCoordinates,
PasskeyCredential
} from '@safe-global/protocol-kit/types'
import { AsnParser, AsnProp, AsnPropTypes, AsnType, AsnTypeTypes } from '@peculiar/asn1-schema'

@AsnType({ type: AsnTypeTypes.Sequence })
class AlgorithmIdentifier {
@AsnProp({ type: AsnPropTypes.ObjectIdentifier })
public id: string = ''

@AsnProp({ type: AsnPropTypes.ObjectIdentifier, optional: true })
public curve: string = ''
}

@AsnType({ type: AsnTypeTypes.Sequence })
class ECPublicKey {
@AsnProp({ type: AlgorithmIdentifier })
public algorithm = new AlgorithmIdentifier()

@AsnProp({ type: AsnPropTypes.BitString })
public publicKey: ArrayBuffer = new ArrayBuffer(0)
}

/**
* Checks if a given string is Base64 encoded.
*
* This function uses a regular expression to verify if the string
* conforms to the Base64 encoding pattern, including optional padding.
*
* @param {string} str - The string to check.
* @returns {boolean} Returns true if the string is Base64 encoded, otherwise false.
*/
function isBase64Encoded(str: string): boolean {
const base64Regex = /^(?:[A-Za-z0-9+\/]{4})*?(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
return base64Regex.test(str)
}

/**
* Converts a Base64 URL-encoded string to a Uint8Array.
Expand All @@ -58,6 +24,40 @@ function base64ToUint8Array(base64: string): Uint8Array {
return new Uint8Array(binaryBuffer)
}

/**
* Dynamic import libraries required for decoding public keys.
*/
async function importLibs() {
const { p256 } = await import('@noble/curves/p256')

const { AsnParser, AsnProp, AsnPropTypes, AsnType, AsnTypeTypes } = await import(
'@peculiar/asn1-schema'
)

@AsnType({ type: AsnTypeTypes.Sequence })
class AlgorithmIdentifier {
@AsnProp({ type: AsnPropTypes.ObjectIdentifier })
public id: string = ''

@AsnProp({ type: AsnPropTypes.ObjectIdentifier, optional: true })
public curve: string = ''
}

@AsnType({ type: AsnTypeTypes.Sequence })
class ECPublicKey {
@AsnProp({ type: AlgorithmIdentifier })
public algorithm = new AlgorithmIdentifier()

@AsnProp({ type: AsnPropTypes.BitString })
public publicKey: ArrayBuffer = new ArrayBuffer(0)
}

return {
p256,
AsnParser,
ECPublicKey
}
}
/**
* Decodes a Base64-encoded ECDSA public key for React Native and extracts the x and y coordinates.
*
Expand All @@ -69,7 +69,9 @@ function base64ToUint8Array(base64: string): Uint8Array {
* @returns {PasskeyCoordinates} An object containing the x and y coordinates of the public key.
* @throws {Error} Throws an error if the key is empty or if the coordinates cannot be extracted.
*/
function decodePublicKeyForReactNative(publicKey: string): PasskeyCoordinates {
async function decodePublicKeyForReactNative(publicKey: string): Promise<PasskeyCoordinates> {
const { p256, AsnParser, ECPublicKey } = await importLibs()

let publicKeyBytes = base64ToUint8Array(publicKey)

if (publicKeyBytes.length === 0) {
Expand Down Expand Up @@ -153,7 +155,7 @@ async function decodePublicKey(
throw new Error('Failed to generate passkey coordinates. getPublicKey() failed')
}

if (typeof publicKey === 'string' && isBase64Encoded(publicKey)) {
if (typeof publicKey === 'string') {
// Public key is base64 encoded
// - React Native platform uses base64 encoded strings
return decodePublicKeyForReactNative(publicKey)
Expand Down

0 comments on commit 8798aaf

Please sign in to comment.