{"version":3,"sources":["../../src/account/KeylessAccount.ts"],"sourcesContent":["// Copyright © Endless\n// Copyright © Aptos Foundation\n// SPDX-License-Identifier: Apache-2.0\n\nimport { JwtPayload, jwtDecode } from \"jwt-decode\";\nimport EventEmitter from \"eventemitter3\";\nimport { EphemeralCertificateVariant, HexInput, SigningScheme } from \"../types\";\nimport { AccountAddress } from \"../core/accountAddress\";\nimport {\n  AnyPublicKey,\n  AnySignature,\n  KeylessPublicKey,\n  KeylessSignature,\n  EphemeralCertificate,\n  ZeroKnowledgeSig,\n} from \"../core/crypto\";\n\nimport { Account } from \"./Account\";\nimport { EphemeralKeyPair } from \"./EphemeralKeyPair\";\nimport { Hex } from \"../core/hex\";\nimport { AccountAuthenticatorSingleKey } from \"../transactions/authenticator/account\";\nimport { Deserializer, Serializable, Serializer } from \"../bcs\";\nimport { generateSigningMessageForTransaction } from \"../transactions/transactionBuilder/signingMessage\";\nimport { AnyRawTransaction } from \"../transactions/types\";\n\n\n/**\n * Account implementation for the Keyless authentication scheme.\n *\n * Used to represent a Keyless based account and sign transactions with it.\n *\n * Use KeylessAccount.fromJWTAndProof to instantiate a KeylessAccount with a JWT, proof and EphemeralKeyPair.\n *\n * When the proof expires or the JWT becomes invalid, the KeylessAccount must be instantiated again with a new JWT,\n * EphemeralKeyPair, and corresponding proof.\n */\nexport class KeylessAccount extends Serializable implements Account {\n  static readonly PEPPER_LENGTH: number = 31;\n\n  /**\n   * The KeylessPublicKey associated with the account\n   */\n  readonly publicKey: KeylessPublicKey;\n\n  /**\n   * The EphemeralKeyPair used to generate sign.\n   */\n  readonly ephemeralKeyPair: EphemeralKeyPair;\n\n  /**\n   * The claim on the JWT to identify a user.  This is typically 'sub' or 'email'.\n   */\n  readonly uidKey: string;\n\n  /**\n   * The value of the uidKey claim on the JWT.  This intended to be a stable user identifier.\n   */\n  readonly uidVal: string;\n\n  /**\n   * The value of the 'aud' claim on the JWT, also known as client ID.  This is the identifier for the dApp's\n   * OIDC registration with the identity provider.\n   */\n  readonly aud: string;\n\n  /**\n   * A value contains 31 bytes of entropy that preserves privacy of the account. Typically fetched from a pepper provider.\n   */\n  readonly pepper: Uint8Array;\n\n  /**\n   * Account address associated with the account\n   */\n  readonly accountAddress: AccountAddress;\n\n  /**\n   * The zero knowledge signature (if ready) which contains the proof used to validate the EphemeralKeyPair.\n   */\n  proof: ZeroKnowledgeSig | undefined;\n\n  /**\n   * The proof of the EphemeralKeyPair or a promise that provides the proof.  This is used to allow for awaiting on\n   * fetching the proof.\n   */\n  readonly proofOrPromise: ZeroKnowledgeSig | Promise<ZeroKnowledgeSig>;\n\n  /**\n   * Signing scheme used to sign transactions\n   */\n  readonly signingScheme: SigningScheme;\n\n  /**\n   * The JWT token used to derive the account\n   */\n  readonly jwt: string;\n\n  /**\n   * An event emitter used to assist in handling asycronous proof fetching.\n   */\n  private readonly emitter: EventEmitter<ProofFetchEvents>;\n\n  // Use the static constructor 'create' instead.\n  private constructor(args: {\n    address?: AccountAddress;\n    ephemeralKeyPair: EphemeralKeyPair;\n    iss: string;\n    uidKey: string;\n    uidVal: string;\n    aud: string;\n    pepper: HexInput;\n    proof: ZeroKnowledgeSig | Promise<ZeroKnowledgeSig>;\n    proofFetchCallback?: ProofFetchCallback;\n    jwt: string;\n  }) {\n    super();\n    const { address, ephemeralKeyPair, uidKey, uidVal, aud, pepper, proof, proofFetchCallback, jwt } = args;\n    this.ephemeralKeyPair = ephemeralKeyPair;\n    this.publicKey = KeylessPublicKey.create(args);\n    this.accountAddress = address ? AccountAddress.from(address) : this.publicKey.authKey().derivedAddress();\n    this.uidKey = uidKey;\n    this.uidVal = uidVal;\n    this.aud = aud;\n    this.jwt = jwt;\n    this.emitter = new EventEmitter<ProofFetchEvents>();\n    this.proofOrPromise = proof;\n    if (proof instanceof ZeroKnowledgeSig) {\n      this.proof = proof;\n    } else {\n      if (proofFetchCallback === undefined) {\n        throw new Error(\"Must provide callback for async proof fetch\");\n      }\n      this.emitter.on(\"proofFetchFinish\", async (status) => {\n        await proofFetchCallback(status);\n        this.emitter.removeAllListeners();\n      });\n      this.init(proof);\n    }\n    this.signingScheme = SigningScheme.SingleKey;\n    const pepperBytes = Hex.fromHexInput(pepper).toUint8Array();\n    if (pepperBytes.length !== KeylessAccount.PEPPER_LENGTH) {\n      throw new Error(`Pepper length in bytes should be ${KeylessAccount.PEPPER_LENGTH}`);\n    }\n    this.pepper = pepperBytes;\n  }\n\n  /**\n   * This initializes the asyncronous proof fetch\n   * @return\n   */\n  async init(promise: Promise<ZeroKnowledgeSig>) {\n    try {\n      this.proof = await promise;\n      this.emitter.emit(\"proofFetchFinish\", { status: \"Success\" });\n    } catch (error) {\n      if (error instanceof Error) {\n        this.emitter.emit(\"proofFetchFinish\", { status: \"Failed\", error: error.toString() });\n      } else {\n        this.emitter.emit(\"proofFetchFinish\", { status: \"Failed\", error: \"Unknown\" });\n      }\n    }\n  }\n\n  serialize(serializer: Serializer): void {\n    serializer.serializeStr(this.jwt);\n    serializer.serializeStr(this.uidKey);\n    serializer.serializeFixedBytes(this.pepper);\n    this.ephemeralKeyPair.serialize(serializer);\n    if (this.proof === undefined) {\n      throw new Error(\"Connot serialize - proof undefined\");\n    }\n    this.proof.serialize(serializer);\n  }\n\n  static deserialize(deserializer: Deserializer): KeylessAccount {\n    const jwt = deserializer.deserializeStr();\n    const uidKey = deserializer.deserializeStr();\n    const pepper = deserializer.deserializeFixedBytes(31);\n    const ephemeralKeyPair = EphemeralKeyPair.deserialize(deserializer);\n    const proof = ZeroKnowledgeSig.deserialize(deserializer);\n    return KeylessAccount.create({\n      proof,\n      pepper,\n      uidKey,\n      jwt,\n      ephemeralKeyPair,\n    });\n  }\n\n  /**\n   * Checks if the proof is expired.  If so the account must be rederived with a new EphemeralKeyPair\n   * and JWT token.\n   * @return boolean\n   */\n  isExpired(): boolean {\n    return this.ephemeralKeyPair.isExpired();\n  }\n\n  /**\n   * Sign a message using Keyless.\n   * @param message the message to sign, as binary input\n   * @return the AccountAuthenticator containing the signature, together with the account's public key\n   */\n  signWithAuthenticator(message: HexInput): AccountAuthenticatorSingleKey {\n    const signature = new AnySignature(this.sign(message));\n    const publicKey = new AnyPublicKey(this.publicKey);\n    return new AccountAuthenticatorSingleKey(publicKey, signature);\n  }\n\n  /**\n   * Sign a transaction using Keyless.\n   * @param transaction the raw transaction\n   * @return the AccountAuthenticator containing the signature of the transaction, together with the account's public key\n   */\n  signTransactionWithAuthenticator(transaction: AnyRawTransaction): AccountAuthenticatorSingleKey {\n    const signature = new AnySignature(this.signTransaction(transaction));\n    const publicKey = new AnyPublicKey(this.publicKey);\n    return new AccountAuthenticatorSingleKey(publicKey, signature);\n  }\n\n  /**\n   * Waits for asyncronous proof fetching to finish.\n   * @return\n   */\n  async waitForProofFetch() {\n    if (this.proofOrPromise instanceof Promise) {\n      await this.proofOrPromise;\n    }\n  }\n\n  /**\n   * Sign the given message using Keyless.\n   * @param message in HexInput format\n   * @returns Signature\n   */\n  sign(data: HexInput): KeylessSignature {\n    const { expiryDateSecs } = this.ephemeralKeyPair;\n    if (this.isExpired()) {\n      throw new Error(\"EphemeralKeyPair is expired\");\n    }\n    if (this.proof === undefined) {\n      throw new Error(\"Proof not defined\");\n    }\n    const ephemeralPublicKey = this.ephemeralKeyPair.getPublicKey();\n    const ephemeralSignature = this.ephemeralKeyPair.sign(data);\n\n    return new KeylessSignature({\n      jwtHeader: this.jwt.split(\".\")[0],\n      ephemeralCertificate: new EphemeralCertificate(this.proof, EphemeralCertificateVariant.ZkProof),\n      expiryDateSecs,\n      ephemeralPublicKey,\n      ephemeralSignature,\n    });\n  }\n\n  /**\n   * Sign the given transaction with Keyless.\n   * Signs the transaction and proof to guard against proof malleability.\n   * @param transaction the transaction to be signed\n   * @returns KeylessSignature\n   */\n  signTransaction(transaction: AnyRawTransaction): KeylessSignature {\n    if (this.proof === undefined) {\n      throw new Error(\"Proof not found\");\n    }\n    // const raw = deriveTransactionType(transaction);\n    // const txnAndProof = new TransactionAndProof(raw, this.proof.proof);\n    // const signMess = txnAndProof.hash();\n    const signMess = generateSigningMessageForTransaction(transaction);\n    return this.sign(signMess);\n  }\n\n  /**\n   * Note - This function is currently incomplete and should only be used to verify ownership of the KeylessAccount\n   *\n   * Verifies a signature given the message.\n   *\n   * TODO: Groth16 proof verification\n   *\n   * @param args.message the message that was signed.\n   * @param args.signature the KeylessSignature to verify\n   * @returns boolean\n   */\n  verifySignature(args: { message: HexInput; signature: KeylessSignature }): boolean {\n    const { message, signature } = args;\n    if (this.isExpired()) {\n      return false;\n    }\n    if (!this.ephemeralKeyPair.getPublicKey().verifySignature({ message, signature: signature.ephemeralSignature })) {\n      return false;\n    }\n    return true;\n  }\n\n  static fromBytes(bytes: Uint8Array): KeylessAccount {\n    return KeylessAccount.deserialize(new Deserializer(bytes));\n  }\n\n  static create(args: {\n    address?: AccountAddress;\n    proof: ZeroKnowledgeSig | Promise<ZeroKnowledgeSig>;\n    jwt: string;\n    ephemeralKeyPair: EphemeralKeyPair;\n    pepper: HexInput;\n    uidKey?: string;\n    proofFetchCallback?: ProofFetchCallback;\n  }): KeylessAccount {\n    const { address, proof, jwt, ephemeralKeyPair, pepper, uidKey = \"sub\", proofFetchCallback } = args;\n\n    const jwtPayload = jwtDecode<JwtPayload & { [key: string]: string }>(jwt);\n    const iss = jwtPayload.iss!;\n    if (typeof jwtPayload.aud !== \"string\") {\n      throw new Error(\"aud was not found or an array of values\");\n    }\n    const aud = jwtPayload.aud!;\n    const uidVal = jwtPayload[uidKey];\n    return new KeylessAccount({\n      address,\n      proof,\n      ephemeralKeyPair,\n      iss,\n      uidKey,\n      uidVal,\n      aud,\n      pepper,\n      jwt,\n      proofFetchCallback,\n    });\n  }\n}\n\nexport type ProofFetchSuccess = {\n  status: \"Success\";\n};\n\nexport type ProofFetchFailure = {\n  status: \"Failed\";\n  error: string;\n};\n\nexport type ProofFetchStatus = ProofFetchSuccess | ProofFetchFailure;\n\nexport type ProofFetchCallback = (status: ProofFetchStatus) => Promise<void>;\n\nexport interface ProofFetchEvents {\n  proofFetchFinish: (status: ProofFetchStatus) => void;\n}\n"],"mappings":"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","names":["jwtDecode","EventEmitter","_KeylessAccount","Serializable","args","address","ephemeralKeyPair","uidKey","uidVal","aud","pepper","proof","proofFetchCallback","jwt","KeylessPublicKey","AccountAddress","EventEmitter","ZeroKnowledgeSig","status","pepperBytes","Hex","promise","error","serializer","deserializer","EphemeralKeyPair","message","signature","AnySignature","publicKey","AnyPublicKey","AccountAuthenticatorSingleKey","transaction","data","expiryDateSecs","ephemeralPublicKey","ephemeralSignature","KeylessSignature","EphemeralCertificate","signMess","generateSigningMessageForTransaction","bytes","Deserializer","jwtPayload","jwtDecode","iss","KeylessAccount"]}