Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol-kit): Add react native compatibility #1033

Merged
merged 20 commits into from
Nov 25, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Improve code
  • Loading branch information
yagopv committed Nov 19, 2024
commit bde71c4fad3c287442ebed550071465549dcdb27
59 changes: 21 additions & 38 deletions packages/protocol-kit/src/utils/passkeys/extractPasskeyData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,59 +39,42 @@ function base64ToUint8Array(base64: string): Uint8Array {
return new Uint8Array(binaryBuffer)
}

/**
* Formats the public key to ensure it is in the correct uncompressed format.
*
* If the public key is in ASN.1 DER format, it extracts the key. If the key is 64 bytes,
* it prepends 0x04 to indicate it's uncompressed. This is necessary for compatibility
* with elliptic curve operations.
*
* @param {Uint8Array} publicKeyBytes - The public key bytes to format.
* @returns {Uint8Array} The formatted public key in uncompressed format.
*/
function formatPublicKey(publicKeyBytes: Uint8Array): Uint8Array {
// ASN.1 DER encoding of an EC public key
// Android Keystore returns the public key in ASN.1 DER format
// https://developer.android.com/privacy-and-security/keystore#ImportingEncryptedKeys
if (publicKeyBytes.length > 65) {
const decodedPublicKey = AsnParser.parse(publicKeyBytes.buffer, ECPublicKey)

return new Uint8Array(decodedPublicKey.publicKey)
}

// Missing prefix
if (publicKeyBytes.length === 64) {
const uncompressedKey = new Uint8Array(65)
uncompressedKey[0] = 0x04
uncompressedKey.set(publicKeyBytes, 1)

return uncompressedKey
}

return publicKeyBytes
}

/**
* 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, formats it to ensure compatibility with
* elliptic curve operations, and extracts the x and y coordinates using the `@noble/curves` library.
* This function handles both ASN.1 DER-encoded keys and uncompressed keys. It decodes a Base64-encoded
* public key, checks its 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.
* @throws {Error} Throws an error if the key is empty or if the coordinates cannot be extracted.
*/
function decodePublicKeyForReactNative(publicKey: string): PasskeyCoordinates {
const publicKeyBytes = base64ToUint8Array(publicKey)
let publicKeyBytes = base64ToUint8Array(publicKey)

console.log('publicKey', publicKey, publicKeyBytes)

if (publicKeyBytes.length === 0) {
throw new Error('Decoded public key is empty.')
}

const formattedPublicKeyBytes = formatPublicKey(publicKeyBytes)
const isAsn1Encoded = publicKeyBytes[0] === 0x30
const isUncompressedKey = publicKeyBytes.length === 64

if (isAsn1Encoded) {
const asn1ParsedKey = AsnParser.parse(publicKeyBytes.buffer, ECPublicKey)

publicKeyBytes = new Uint8Array(asn1ParsedKey.publicKey)
} else if (isUncompressedKey) {
const uncompressedKey = new Uint8Array(65)
uncompressedKey[0] = 0x04
uncompressedKey.set(publicKeyBytes, 1)

publicKeyBytes = uncompressedKey
}

const point = p256.ProjectivePoint.fromHex(formattedPublicKeyBytes)
const point = p256.ProjectivePoint.fromHex(publicKeyBytes)

const x = point.x.toString(16).padStart(64, '0')
const y = point.y.toString(16).padStart(64, '0')
Expand Down
Loading