UNPKG

@mysten/sui

Version:
1 lines 13 kB
{"version":3,"file":"publickey.mjs","names":["#client","#data","#legacyAddress","#toLegacyAddress"],"sources":["../../src/zklogin/publickey.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromBase64, toBase64, toHex } from '@mysten/bcs';\nimport { blake2b } from '@noble/hashes/blake2.js';\nimport { bytesToHex } from '@noble/hashes/utils.js';\n\nimport { PublicKey } from '../cryptography/publickey.js';\nimport type { PublicKeyInitData } from '../cryptography/publickey.js';\nimport { SIGNATURE_SCHEME_TO_FLAG } from '../cryptography/signature-scheme.js';\nimport { normalizeSuiAddress, SUI_ADDRESS_LENGTH } from '../utils/sui-types.js';\nimport type { ZkLoginSignatureInputs } from './bcs.js';\nimport { extractClaimValue } from './jwt-utils.js';\nimport { parseZkLoginSignature } from './signature.js';\nimport { normalizeZkLoginIssuer, toBigEndianBytes, toPaddedBigEndianBytes } from './utils.js';\nimport type { ClientWithCoreApi } from '../client/core.js';\n\n/**\n * A zkLogin public identifier\n */\nexport class ZkLoginPublicIdentifier extends PublicKey {\n\t#data: Uint8Array<ArrayBuffer>;\n\t#client?: ClientWithCoreApi;\n\t#legacyAddress: boolean;\n\n\t/**\n\t * Create a new ZkLoginPublicIdentifier object\n\t * @param value zkLogin public identifier as buffer or base-64 encoded string\n\t */\n\tconstructor(value: PublicKeyInitData, { client }: { client?: ClientWithCoreApi } = {}) {\n\t\tsuper();\n\n\t\tthis.#client = client;\n\n\t\tif (typeof value === 'string') {\n\t\t\tthis.#data = fromBase64(value);\n\t\t} else if (value instanceof Uint8Array) {\n\t\t\tthis.#data = value as Uint8Array<ArrayBuffer>;\n\t\t} else {\n\t\t\tthis.#data = Uint8Array.from(value);\n\t\t}\n\t\tthis.#legacyAddress = this.#data.length !== this.#data[0] + 1 + 32;\n\n\t\tif (this.#legacyAddress) {\n\t\t\tthis.#data = normalizeZkLoginPublicKeyBytes(this.#data, false);\n\t\t}\n\t}\n\n\tstatic fromBytes(\n\t\tbytes: Uint8Array,\n\t\t{\n\t\t\tclient,\n\t\t\taddress,\n\t\t\tlegacyAddress,\n\t\t}: { client?: ClientWithCoreApi; address?: string; legacyAddress?: boolean } = {},\n\t) {\n\t\tlet publicKey: ZkLoginPublicIdentifier;\n\n\t\tif (legacyAddress === true) {\n\t\t\tpublicKey = new ZkLoginPublicIdentifier(normalizeZkLoginPublicKeyBytes(bytes, true), {\n\t\t\t\tclient,\n\t\t\t});\n\t\t} else if (legacyAddress === false) {\n\t\t\tpublicKey = new ZkLoginPublicIdentifier(normalizeZkLoginPublicKeyBytes(bytes, false), {\n\t\t\t\tclient,\n\t\t\t});\n\t\t} else if (address) {\n\t\t\tpublicKey = new ZkLoginPublicIdentifier(normalizeZkLoginPublicKeyBytes(bytes, false), {\n\t\t\t\tclient,\n\t\t\t});\n\n\t\t\tif (publicKey.toSuiAddress() !== address) {\n\t\t\t\tpublicKey = new ZkLoginPublicIdentifier(normalizeZkLoginPublicKeyBytes(bytes, true), {\n\t\t\t\t\tclient,\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\tpublicKey = new ZkLoginPublicIdentifier(bytes, {\n\t\t\t\tclient,\n\t\t\t});\n\t\t}\n\n\t\tif (address && publicKey.toSuiAddress() !== address) {\n\t\t\tthrow new Error('Public key bytes do not match the provided address');\n\t\t}\n\n\t\treturn publicKey;\n\t}\n\n\tstatic fromProof(address: string, proof: ZkLoginSignatureInputs) {\n\t\tconst { issBase64Details, addressSeed } = proof;\n\t\tconst iss = extractClaimValue<string>(issBase64Details, 'iss');\n\n\t\tconst legacyPublicKey = toZkLoginPublicIdentifier(BigInt(addressSeed), iss, {\n\t\t\tlegacyAddress: true,\n\t\t});\n\n\t\tif (legacyPublicKey.toSuiAddress() === address) {\n\t\t\treturn legacyPublicKey;\n\t\t}\n\n\t\tconst publicKey = toZkLoginPublicIdentifier(BigInt(addressSeed), iss, {\n\t\t\tlegacyAddress: false,\n\t\t});\n\n\t\tif (publicKey.toSuiAddress() !== address) {\n\t\t\tthrow new Error('Proof does not match address');\n\t\t}\n\n\t\treturn publicKey;\n\t}\n\n\t/**\n\t * Checks if two zkLogin public identifiers are equal\n\t */\n\toverride equals(publicKey: ZkLoginPublicIdentifier): boolean {\n\t\treturn super.equals(publicKey);\n\t}\n\n\toverride toSuiAddress(): string {\n\t\tif (this.#legacyAddress) {\n\t\t\treturn this.#toLegacyAddress();\n\t\t}\n\n\t\treturn super.toSuiAddress();\n\t}\n\n\t#toLegacyAddress() {\n\t\tconst legacyBytes = normalizeZkLoginPublicKeyBytes(this.#data, true);\n\t\tconst addressBytes = new Uint8Array(legacyBytes.length + 1);\n\t\taddressBytes[0] = this.flag();\n\t\taddressBytes.set(legacyBytes, 1);\n\t\treturn normalizeSuiAddress(\n\t\t\tbytesToHex(blake2b(addressBytes, { dkLen: 32 })).slice(0, SUI_ADDRESS_LENGTH * 2),\n\t\t);\n\t}\n\n\t/**\n\t * Return the byte array representation of the zkLogin public identifier\n\t */\n\ttoRawBytes(): Uint8Array<ArrayBuffer> {\n\t\treturn this.#data;\n\t}\n\n\t/**\n\t * Return the Sui address associated with this ZkLogin public identifier\n\t */\n\tflag(): number {\n\t\treturn SIGNATURE_SCHEME_TO_FLAG['ZkLogin'];\n\t}\n\n\t/**\n\t * Verifies that the signature is valid for for the provided message\n\t */\n\tasync verify(_message: Uint8Array, _signature: Uint8Array | string): Promise<boolean> {\n\t\tthrow Error('does not support');\n\t}\n\n\t/**\n\t * Verifies that the signature is valid for for the provided PersonalMessage\n\t */\n\tverifyPersonalMessage(message: Uint8Array, signature: Uint8Array | string): Promise<boolean> {\n\t\tconst parsedSignature = parseSerializedZkLoginSignature(signature);\n\t\tconst address = new ZkLoginPublicIdentifier(parsedSignature.publicKey).toSuiAddress();\n\n\t\treturn graphqlVerifyZkLoginSignature({\n\t\t\taddress: address,\n\t\t\tbytes: toBase64(message),\n\t\t\tsignature: parsedSignature.serializedSignature,\n\t\t\tintentScope: 'PersonalMessage',\n\t\t\tclient: this.#client,\n\t\t});\n\t}\n\n\t/**\n\t * Verifies that the signature is valid for for the provided Transaction\n\t */\n\tverifyTransaction(transaction: Uint8Array, signature: Uint8Array | string): Promise<boolean> {\n\t\tconst parsedSignature = parseSerializedZkLoginSignature(signature);\n\t\tconst address = new ZkLoginPublicIdentifier(parsedSignature.publicKey).toSuiAddress();\n\t\treturn graphqlVerifyZkLoginSignature({\n\t\t\taddress: address,\n\t\t\tbytes: toBase64(transaction),\n\t\t\tsignature: parsedSignature.serializedSignature,\n\t\t\tintentScope: 'TransactionData',\n\t\t\tclient: this.#client,\n\t\t});\n\t}\n\n\t/**\n\t * Verifies that the public key is associated with the provided address\n\t */\n\toverride verifyAddress(address: string): boolean {\n\t\treturn address === super.toSuiAddress() || address === this.#toLegacyAddress();\n\t}\n}\n\n// Derive the public identifier for zklogin based on address seed and iss.\nexport function toZkLoginPublicIdentifier(\n\taddressSeed: bigint,\n\tiss: string,\n\toptions: { client?: ClientWithCoreApi; legacyAddress: boolean },\n): ZkLoginPublicIdentifier {\n\tif (options.legacyAddress === undefined) {\n\t\tthrow new Error('legacyAddress parameter must be specified');\n\t}\n\t// Consists of iss_bytes_len || iss_bytes || padded_32_byte_address_seed.\n\tconst addressSeedBytesBigEndian = options.legacyAddress\n\t\t? toBigEndianBytes(addressSeed, 32)\n\t\t: toPaddedBigEndianBytes(addressSeed, 32);\n\n\tconst issBytes = new TextEncoder().encode(normalizeZkLoginIssuer(iss));\n\tconst tmp = new Uint8Array(1 + issBytes.length + addressSeedBytesBigEndian.length);\n\ttmp.set([issBytes.length], 0);\n\ttmp.set(issBytes, 1);\n\ttmp.set(addressSeedBytesBigEndian, 1 + issBytes.length);\n\treturn new ZkLoginPublicIdentifier(tmp, options);\n}\n\nfunction normalizeZkLoginPublicKeyBytes(bytes: Uint8Array, legacyAddress: boolean) {\n\tconst issByteLength = bytes[0] + 1;\n\tconst addressSeed = BigInt(`0x${toHex(bytes.slice(issByteLength))}`);\n\tconst seedBytes = legacyAddress\n\t\t? toBigEndianBytes(addressSeed, 32)\n\t\t: toPaddedBigEndianBytes(addressSeed, 32);\n\tconst data = new Uint8Array(issByteLength + seedBytes.length);\n\tdata.set(bytes.slice(0, issByteLength), 0);\n\tdata.set(seedBytes, issByteLength);\n\treturn data;\n}\n\nasync function graphqlVerifyZkLoginSignature({\n\taddress,\n\tbytes,\n\tsignature,\n\tintentScope,\n\tclient,\n}: {\n\taddress: string;\n\tbytes: string;\n\tsignature: string;\n\tintentScope: 'PersonalMessage' | 'TransactionData';\n\tclient?: ClientWithCoreApi;\n}) {\n\tif (!client) {\n\t\tthrow new Error(\n\t\t\t'A Sui Client (GRPC, GraphQL, or JSON RPC) is required to verify zkLogin signatures',\n\t\t);\n\t}\n\tconst resp = await client.core.verifyZkLoginSignature({\n\t\tbytes,\n\t\tsignature,\n\t\tintentScope,\n\t\taddress: address,\n\t});\n\n\treturn resp.success === true && resp.errors.length === 0;\n}\n\nexport function parseSerializedZkLoginSignature(signature: Uint8Array | string) {\n\tconst bytes = typeof signature === 'string' ? fromBase64(signature) : signature;\n\n\tif (bytes[0] !== SIGNATURE_SCHEME_TO_FLAG.ZkLogin) {\n\t\tthrow new Error('Invalid signature scheme');\n\t}\n\n\tconst signatureBytes = bytes.slice(1);\n\tconst { inputs, maxEpoch, userSignature } = parseZkLoginSignature(signatureBytes);\n\tconst { issBase64Details, addressSeed } = inputs;\n\tconst iss = extractClaimValue<string>(issBase64Details, 'iss');\n\tconst publicIdentifier = toZkLoginPublicIdentifier(BigInt(addressSeed), iss, {\n\t\tlegacyAddress: false,\n\t});\n\treturn {\n\t\tserializedSignature: toBase64(bytes),\n\t\tsignatureScheme: 'ZkLogin' as const,\n\t\tzkLogin: {\n\t\t\tinputs,\n\t\t\tmaxEpoch,\n\t\t\tuserSignature,\n\t\t\tiss,\n\t\t\taddressSeed: BigInt(addressSeed),\n\t\t},\n\t\tsignature: bytes,\n\t\tpublicKey: publicIdentifier.toRawBytes(),\n\t};\n}\n"],"mappings":"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"}