{"version":3,"file":"utils.mjs","names":["secp256r1"],"sources":["../../src/utils/utils.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { p256 as secp256r1 } from '@noble/curves/nist.js';\nimport { secp256k1 } from '@noble/curves/secp256k1.js';\nimport { ASN1Construction, ASN1TagClass, DERElement } from 'asn1-ts';\n\n/** The total number of bits in the DER bit string for the uncompressed public key. */\nexport const DER_BIT_STRING_LENGTH = 520;\n\n/** The total number of bytes corresponding to the DER bit string length. */\nexport const DER_BYTES_LENGTH = DER_BIT_STRING_LENGTH / 8;\n\n// Reference Specifications:\n// https://datatracker.ietf.org/doc/html/rfc5480#section-2.2\n// https://www.secg.org/sec1-v2.pdf\n\n/**\n * Converts an array of bits into a byte array.\n *\n * @param bitsArray - A `Uint8ClampedArray` representing the bits to convert.\n * @returns A `Uint8Array` containing the corresponding bytes.\n *\n * @throws {Error} If the input array does not have the expected length.\n */\nfunction bitsToBytes(bitsArray: Uint8ClampedArray): Uint8Array {\n\tconst bytes = new Uint8Array(DER_BYTES_LENGTH);\n\tfor (let i = 0; i < DER_BIT_STRING_LENGTH; i++) {\n\t\tif (bitsArray[i] === 1) {\n\t\t\tbytes[Math.floor(i / 8)] |= 1 << (7 - (i % 8));\n\t\t}\n\t}\n\treturn bytes;\n}\n\nexport function publicKeyFromDER(derBytes: Uint8Array) {\n\tconst encodedData: Uint8Array = derBytes;\n\tconst derElement = new DERElement();\n\tderElement.fromBytes(encodedData);\n\n\t// Validate the ASN.1 structure of the public key\n\tif (\n\t\t!(\n\t\t\tderElement.tagClass === ASN1TagClass.universal &&\n\t\t\tderElement.construction === ASN1Construction.constructed\n\t\t)\n\t) {\n\t\tthrow new Error('Unexpected ASN.1 structure');\n\t}\n\n\tconst components = derElement.components;\n\tconst publicKeyElement = components[1];\n\n\tif (!publicKeyElement) {\n\t\tthrow new Error('Public Key not found in the DER structure');\n\t}\n\n\treturn compressPublicKeyClamped(publicKeyElement.bitString);\n}\n\nexport function getConcatenatedSignature(signature: Uint8Array, keyScheme: string) {\n\tif (!signature || signature.length === 0) {\n\t\tthrow new Error('Invalid signature');\n\t}\n\n\t// Initialize a DERElement to parse the DER-encoded signature\n\tconst derElement = new DERElement();\n\tderElement.fromBytes(signature);\n\n\tconst [r, s] = derElement.toJSON() as [string, string];\n\n\tswitch (keyScheme) {\n\t\tcase 'Secp256k1': {\n\t\t\tconst sig = new secp256k1.Signature(BigInt(r), BigInt(s));\n\t\t\tconst normalized = sig.hasHighS()\n\t\t\t\t? new secp256k1.Signature(sig.r, secp256k1.Point.Fn.neg(sig.s))\n\t\t\t\t: sig;\n\n\t\t\treturn normalized.toBytes('compact') as Uint8Array<ArrayBuffer>;\n\t\t}\n\t\tcase 'Secp256r1': {\n\t\t\tconst sig = new secp256r1.Signature(BigInt(r), BigInt(s));\n\t\t\tconst normalized = sig.hasHighS()\n\t\t\t\t? new secp256r1.Signature(sig.r, secp256r1.Point.Fn.neg(sig.s))\n\t\t\t\t: sig;\n\n\t\t\treturn normalized.toBytes('compact') as Uint8Array<ArrayBuffer>;\n\t\t}\n\t\tdefault:\n\t\t\tthrow new Error('Unsupported key scheme');\n\t}\n}\n\n/**\n * Compresses an uncompressed public key into its compressed form.\n *\n * The uncompressed key must follow the DER bit string format as specified in [RFC 5480](https://datatracker.ietf.org/doc/html/rfc5480#section-2.2)\n * and [SEC 1: Elliptic Curve Cryptography](https://www.secg.org/sec1-v2.pdf).\n *\n * @param uncompressedKey - A `Uint8ClampedArray` representing the uncompressed public key bits.\n * @returns A `Uint8Array` containing the compressed public key.\n *\n * @throws {Error} If the uncompressed key has an unexpected length or does not start with the expected prefix.\n */\nexport function compressPublicKeyClamped(uncompressedKey: Uint8ClampedArray): Uint8Array {\n\tif (uncompressedKey.length !== DER_BIT_STRING_LENGTH) {\n\t\tthrow new Error('Unexpected length for an uncompressed public key');\n\t}\n\n\t// Convert bits to bytes\n\tconst uncompressedBytes = bitsToBytes(uncompressedKey);\n\n\t// Ensure the public key starts with the standard uncompressed prefix 0x04\n\tif (uncompressedBytes[0] !== 0x04) {\n\t\tthrow new Error('Public key does not start with 0x04');\n\t}\n\n\t// Extract X-Coordinate (skip the first byte, which is the prefix 0x04)\n\tconst xCoord = uncompressedBytes.slice(1, 33);\n\n\t// Determine parity byte for Y coordinate based on the last byte\n\tconst yCoordLastByte = uncompressedBytes[64];\n\tconst parityByte = yCoordLastByte % 2 === 0 ? 0x02 : 0x03;\n\n\t// Return the compressed public key consisting of the parity byte and X-coordinate\n\treturn new Uint8Array([parityByte, ...xCoord]);\n}\n"],"mappings":";;;;;;AAQA,MAAa,wBAAwB;;AAGrC,MAAa,mBAAmB,wBAAwB;;;;;;;;;AAcxD,SAAS,YAAY,WAA0C;CAC9D,MAAM,QAAQ,IAAI,WAAW,iBAAiB;AAC9C,MAAK,IAAI,IAAI,GAAG,IAAI,uBAAuB,IAC1C,KAAI,UAAU,OAAO,EACpB,OAAM,KAAK,MAAM,IAAI,EAAE,KAAK,KAAM,IAAK,IAAI;AAG7C,QAAO;;AAGR,SAAgB,iBAAiB,UAAsB;CACtD,MAAM,cAA0B;CAChC,MAAM,aAAa,IAAI,YAAY;AACnC,YAAW,UAAU,YAAY;AAGjC,KACC,EACC,WAAW,aAAa,aAAa,aACrC,WAAW,iBAAiB,iBAAiB,aAG9C,OAAM,IAAI,MAAM,6BAA6B;CAI9C,MAAM,mBADa,WAAW,WACM;AAEpC,KAAI,CAAC,iBACJ,OAAM,IAAI,MAAM,4CAA4C;AAG7D,QAAO,yBAAyB,iBAAiB,UAAU;;AAG5D,SAAgB,yBAAyB,WAAuB,WAAmB;AAClF,KAAI,CAAC,aAAa,UAAU,WAAW,EACtC,OAAM,IAAI,MAAM,oBAAoB;CAIrC,MAAM,aAAa,IAAI,YAAY;AACnC,YAAW,UAAU,UAAU;CAE/B,MAAM,CAAC,GAAG,KAAK,WAAW,QAAQ;AAElC,SAAQ,WAAR;EACC,KAAK,aAAa;GACjB,MAAM,MAAM,IAAI,UAAU,UAAU,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC;AAKzD,WAJmB,IAAI,UAAU,GAC9B,IAAI,UAAU,UAAU,IAAI,GAAG,UAAU,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC,GAC7D,KAEe,QAAQ,UAAU;;EAErC,KAAK,aAAa;GACjB,MAAM,MAAM,IAAIA,KAAU,UAAU,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC;AAKzD,WAJmB,IAAI,UAAU,GAC9B,IAAIA,KAAU,UAAU,IAAI,GAAGA,KAAU,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC,GAC7D,KAEe,QAAQ,UAAU;;EAErC,QACC,OAAM,IAAI,MAAM,yBAAyB;;;;;;;;;;;;;;AAe5C,SAAgB,yBAAyB,iBAAgD;AACxF,KAAI,gBAAgB,WAAW,sBAC9B,OAAM,IAAI,MAAM,mDAAmD;CAIpE,MAAM,oBAAoB,YAAY,gBAAgB;AAGtD,KAAI,kBAAkB,OAAO,EAC5B,OAAM,IAAI,MAAM,sCAAsC;CAIvD,MAAM,SAAS,kBAAkB,MAAM,GAAG,GAAG;CAI7C,MAAM,aADiB,kBAAkB,MACL,MAAM,IAAI,IAAO;AAGrD,QAAO,IAAI,WAAW,CAAC,YAAY,GAAG,OAAO,CAAC"}