@biconomy/passkey
Version:
Passkey validator plug in for Biconomy SDK
110 lines • 4.53 kB
JavaScript
import { p256 } from "@noble/curves/p256";
import { bytesToBigInt, hexToBytes } from "viem";
const RIP7212_SUPPORTED_NETWORKS = [80001, 137];
export const isRIP7212SupportedNetwork = (chainId) => RIP7212_SUPPORTED_NETWORKS.includes(chainId);
export const uint8ArrayToHexString = (array) => {
return `0x${Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
};
export const hexStringToUint8Array = (hexString) => {
const formattedHexString = hexString.startsWith("0x")
? hexString.slice(2)
: hexString;
const byteArray = new Uint8Array(formattedHexString.length / 2);
for (let i = 0; i < formattedHexString.length; i += 2) {
byteArray[i / 2] = Number.parseInt(formattedHexString.substring(i, i + 2), 16);
}
return byteArray;
};
export const b64ToBytes = (base64) => {
const paddedBase64 = base64
.replace(/-/g, "+")
.replace(/_/g, "/")
.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), "=");
const binString = atob(paddedBase64);
return Uint8Array.from(binString, (m) => m.codePointAt(0) ?? 0);
};
export const findQuoteIndices = (input) => {
const beforeTypeIndex = BigInt(input.lastIndexOf('"type":"webauthn.get"'));
const beforeChallengeIndex = BigInt(input.indexOf('"challenge'));
return {
beforeType: beforeTypeIndex,
beforeChallenge: beforeChallengeIndex
};
};
// Parse DER-encoded P256-SHA256 signature to contract-friendly signature
// and normalize it so the signature is not malleable.
export function parseAndNormalizeSig(derSig) {
const parsedSignature = p256.Signature.fromDER(derSig.slice(2));
const bSig = hexToBytes(`0x${parsedSignature.toCompactHex()}`);
// assert(bSig.length === 64, "signature is not 64 bytes");
const bR = bSig.slice(0, 32);
const bS = bSig.slice(32);
// Avoid malleability. Ensure low S (<= N/2 where N is the curve order)
const r = bytesToBigInt(bR);
let s = bytesToBigInt(bS);
const n = BigInt("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
if (s > n / 2n) {
s = n - s;
}
return { r, s };
}
export const serializePasskeyValidatorData = (params) => {
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
const replacer = (_, value) => {
if (typeof value === "bigint") {
return value.toString();
}
return value;
};
const jsonString = JSON.stringify(params, replacer);
const uint8Array = new TextEncoder().encode(jsonString);
const base64String = bytesToBase64(uint8Array);
return base64String;
};
export const deserializePasskeyValidatorData = (params) => {
const uint8Array = base64ToBytes(params);
const jsonString = new TextDecoder().decode(uint8Array);
const parsed = JSON.parse(jsonString);
return parsed;
};
function base64ToBytes(base64) {
const binString = atob(base64);
return Uint8Array.from(binString, (m) => m.codePointAt(0));
}
function bytesToBase64(bytes) {
const binString = Array.from(bytes, (x) => String.fromCodePoint(x)).join("");
return btoa(binString);
}
/**
* Convenience function for creating a base64 encoded string from an ArrayBuffer instance
* Copied from @hexagon/base64 package (base64.fromArrayBuffer)
* @public
*
* @param {Uint8Array} uint8Arr - Uint8Array to be encoded
* @param {boolean} [urlMode] - If set to true, URL mode string will be returned
* @returns {string} - Base64 representation of data
*/
export const base64FromUint8Array = (uint8Arr, urlMode) => {
const // Regular base64 characters
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const // Base64url characters
charsUrl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
let result = "";
const len = uint8Arr.length;
const target = urlMode ? charsUrl : chars;
for (let i = 0; i < len; i += 3) {
result += target[uint8Arr[i] >> 2];
result += target[((uint8Arr[i] & 3) << 4) | (uint8Arr[i + 1] >> 4)];
result += target[((uint8Arr[i + 1] & 15) << 2) | (uint8Arr[i + 2] >> 6)];
result += target[uint8Arr[i + 2] & 63];
}
const remainder = len % 3;
if (remainder === 2) {
result = result.substring(0, result.length - 1) + (urlMode ? "" : "=");
}
else if (remainder === 1) {
result = result.substring(0, result.length - 2) + (urlMode ? "" : "==");
}
return result;
};
//# sourceMappingURL=utils.js.map