zksync-sso
Version:
ZKsync Smart Sign On SDK
245 lines • 8.92 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPasskeySignatureFromPublicKeyBytes = exports.getPublicKeyBytesFromPasskeySignature = void 0;
exports.unwrapEC2Signature = unwrapEC2Signature;
exports.normalizeS = normalizeS;
exports.base64UrlToUint8Array = base64UrlToUint8Array;
exports.passkeyHashSignatureResponseFormat = passkeyHashSignatureResponseFormat;
const asn1_ecc_1 = require("@peculiar/asn1-ecc");
const asn1_schema_1 = require("@peculiar/asn1-schema");
const bigint_conversion_1 = require("bigint-conversion");
const buffer_1 = require("buffer");
const viem_1 = require("viem");
var COSEKEYS;
(function (COSEKEYS) {
COSEKEYS[COSEKEYS["kty"] = 1] = "kty";
COSEKEYS[COSEKEYS["alg"] = 3] = "alg";
COSEKEYS[COSEKEYS["crv"] = -1] = "crv";
COSEKEYS[COSEKEYS["x"] = -2] = "x";
COSEKEYS[COSEKEYS["y"] = -3] = "y";
})(COSEKEYS || (COSEKEYS = {}));
function encodeInt(int) {
if (int >= 0 && int <= 23) {
return buffer_1.Buffer.from([int]);
}
else if (int >= 24 && int <= 255) {
return buffer_1.Buffer.from([0x18, int]);
}
else if (int >= 256 && int <= 65535) {
const buf = buffer_1.Buffer.alloc(3);
buf[0] = 0x19;
buf.writeUInt16BE(int, 1);
return buf;
}
else if (int < 0 && int >= -24) {
return buffer_1.Buffer.from([0x20 - (int + 1)]);
}
else if (int < -24 && int >= -256) {
return buffer_1.Buffer.from([0x38, -int - 1]);
}
else if (int < -256 && int >= -65536) {
const buf = buffer_1.Buffer.alloc(3);
buf[0] = 0x39;
buf.writeUInt16BE(-int - 1, 1);
return buf;
}
else {
throw new Error("Unsupported integer range");
}
}
function encodeBytes(bytes) {
if (bytes.length <= 23) {
return buffer_1.Buffer.concat([buffer_1.Buffer.from([0x40 + bytes.length]), bytes]);
}
else if (bytes.length < 256) {
return buffer_1.Buffer.concat([buffer_1.Buffer.from([0x58, bytes.length]), bytes]);
}
else {
throw new Error("Unsupported byte array length");
}
}
function encodeMap(map) {
const encodedItems = [];
const mapHeader = 0xA0 | map.size;
encodedItems.push(buffer_1.Buffer.from([mapHeader]));
map.forEach((value, key) => {
encodedItems.push(encodeInt(key));
if (buffer_1.Buffer.isBuffer(value)) {
encodedItems.push(encodeBytes(value));
}
else {
encodedItems.push(encodeInt(value));
}
});
return buffer_1.Buffer.concat(encodedItems);
}
function decodeMap(buffer) {
const map = new Map();
let offset = 1;
const mapHeader = buffer[0];
const mapSize = mapHeader & 0x1F;
for (let i = 0; i < mapSize; i++) {
const [key, keyLength] = decodeInt(buffer, offset);
offset += keyLength;
const [value, valueLength] = decodeValue(buffer, offset);
offset += valueLength;
map.set(key, value);
}
return map;
}
function decodeInt(buffer, offset) {
const intByte = buffer[offset];
if (intByte < 24) {
return [intByte, 1];
}
else if (intByte === 0x18) {
return [buffer[offset + 1], 2];
}
else if (intByte === 0x19) {
return [buffer.readUInt16BE(offset + 1), 3];
}
else if (intByte >= 0x20 && intByte <= 0x37) {
return [-(intByte - 0x20) - 1, 1];
}
else if (intByte === 0x38) {
return [-1 - buffer[offset + 1], 2];
}
else if (intByte === 0x39) {
return [-1 - buffer.readUInt16BE(offset + 1), 3];
}
else {
throw new Error("Unsupported integer format");
}
}
function decodeBytes(buffer, offset) {
const lengthByte = buffer[offset];
if (lengthByte >= 0x40 && lengthByte <= 0x57) {
const length = lengthByte - 0x40;
return [buffer.slice(offset + 1, offset + 1 + length), length + 1];
}
else if (lengthByte === 0x58) {
const length = buffer[offset + 1];
return [buffer.slice(offset + 2, offset + 2 + length), length + 2];
}
else {
throw new Error("Unsupported byte format");
}
}
function decodeValue(buffer, offset) {
const type = buffer[offset];
if (type >= 0x40 && type <= 0x5F) {
return decodeBytes(buffer, offset);
}
else {
return decodeInt(buffer, offset);
}
}
const getPublicKeyBytesFromPasskeySignature = (publicPasskey) => {
const cosePublicKey = decodeMap(buffer_1.Buffer.from(publicPasskey));
const x = cosePublicKey.get(COSEKEYS.x);
const y = cosePublicKey.get(COSEKEYS.y);
return [buffer_1.Buffer.from(x), buffer_1.Buffer.from(y)];
};
exports.getPublicKeyBytesFromPasskeySignature = getPublicKeyBytesFromPasskeySignature;
const getPasskeySignatureFromPublicKeyBytes = (coordinates) => {
const [xHex, yHex] = coordinates;
const x = buffer_1.Buffer.from(xHex.slice(2), "hex");
const y = buffer_1.Buffer.from(yHex.slice(2), "hex");
const cosePublicKey = new Map();
cosePublicKey.set(COSEKEYS.kty, 2);
cosePublicKey.set(COSEKEYS.alg, -7);
cosePublicKey.set(COSEKEYS.crv, 1);
cosePublicKey.set(COSEKEYS.x, x);
cosePublicKey.set(COSEKEYS.y, y);
const encodedPublicKey = encodeMap(cosePublicKey);
return new Uint8Array(encodedPublicKey);
};
exports.getPasskeySignatureFromPublicKeyBytes = getPasskeySignatureFromPublicKeyBytes;
function unwrapEC2Signature(signature) {
const parsedSignature = asn1_schema_1.AsnParser.parse(signature, asn1_ecc_1.ECDSASigValue);
let rBytes = new Uint8Array(parsedSignature.r);
let sBytes = new Uint8Array(parsedSignature.s);
if (shouldRemoveLeadingZero(rBytes)) {
rBytes = rBytes.slice(1);
}
if (shouldRemoveLeadingZero(sBytes)) {
sBytes = sBytes.slice(1);
}
return {
r: rBytes,
s: normalizeS(sBytes),
};
}
function normalizeS(sBuf) {
const n = BigInt("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
const halfN = n / BigInt(2);
const sNumber = (0, bigint_conversion_1.bufToBigint)(sBuf);
if (sNumber / halfN) {
return new Uint8Array((0, bigint_conversion_1.bigintToBuf)(n - sNumber));
}
else {
return sBuf;
}
}
function shouldRemoveLeadingZero(bytes) {
return bytes[0] === 0x0 && (bytes[1] & (1 << 7)) !== 0;
}
function base64UrlToUint8Array(base64urlString, isUrl = true) {
const _buffer = toArrayBuffer(base64urlString, isUrl);
return new Uint8Array(_buffer);
}
function toArrayBuffer(data, isUrl) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", charsUrl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", genLookup = (target) => {
const lookupTemp = typeof Uint8Array === "undefined" ? [] : new Uint8Array(256);
const len = chars.length;
for (let i = 0; i < len; i++) {
lookupTemp[target.charCodeAt(i)] = i;
}
return lookupTemp;
}, lookup = genLookup(chars), lookupUrl = genLookup(charsUrl);
const len = data.length;
let bufferLength = data.length * 0.75, i, p = 0, encoded1, encoded2, encoded3, encoded4;
if (data[data.length - 1] === "=") {
bufferLength--;
if (data[data.length - 2] === "=") {
bufferLength--;
}
}
const arraybuffer = new ArrayBuffer(bufferLength), bytes = new Uint8Array(arraybuffer), target = isUrl ? lookupUrl : lookup;
for (i = 0; i < len; i += 4) {
encoded1 = target[data.charCodeAt(i)];
encoded2 = target[data.charCodeAt(i + 1)];
encoded3 = target[data.charCodeAt(i + 2)];
encoded4 = target[data.charCodeAt(i + 3)];
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
}
return arraybuffer;
}
;
function passkeyHashSignatureResponseFormat(passkeyId, passkeyResponse, contracts) {
const signature = unwrapEC2Signature(base64UrlToUint8Array(passkeyResponse.signature));
const fatSignature = (0, viem_1.encodeAbiParameters)([
{ type: "bytes" },
{ type: "bytes" },
{ type: "bytes32[2]" },
{ type: "bytes" },
], [
(0, viem_1.toHex)(base64UrlToUint8Array(passkeyResponse.authenticatorData)),
(0, viem_1.toHex)(base64UrlToUint8Array(passkeyResponse.clientDataJSON)),
[(0, viem_1.pad)((0, viem_1.toHex)(signature.r)), (0, viem_1.pad)((0, viem_1.toHex)(signature.s))],
(0, viem_1.toHex)(base64UrlToUint8Array(passkeyId)),
]);
const fullFormattedSig = (0, viem_1.encodeAbiParameters)([
{ type: "bytes" },
{ type: "address" },
{ type: "bytes[]" },
], [
fatSignature,
contracts.passkey,
["0x"],
]);
return fullFormattedSig;
}
//# sourceMappingURL=passkey.js.map