text: class KeyPair extends Serializable {
      /**
       * @param {PrivateKey} privateKey
       * @param {PublicKey} publicKey
       * @param {boolean} locked
       * @param {Uint8Array} lockSalt
       * @private
       */
      constructor(privateKey, publicKey, locked = false, lockSalt = null) {
          if (!(privateKey instanceof Object)) throw new Error('Primitive: Invalid type');
          if (!(publicKey instanceof Object)) throw new Error('Primitive: Invalid type');
          super();
  
          /** @type {boolean} */
          this._locked = locked;
          /** @type {boolean} */
          this._lockedInternally = locked;
          /** @type {Uint8Array} */
          this._lockSalt = lockSalt;
          /** @type {PublicKey} */
          this._publicKey = publicKey;
          /** @type {PrivateKey} */
          this._internalPrivateKey = new PrivateKey(privateKey.serialize());
      }
  
      /**
       * @return {KeyPair}
       */
      static generate() {
          const privateKey = PrivateKey.generate();
          return new KeyPair(privateKey, PublicKey.derive(privateKey));
      }
  
      /**
       * @param {PrivateKey} privateKey
       * @return {KeyPair}
       */
      static derive(privateKey) {
          return new KeyPair(privateKey, PublicKey.derive(privateKey));
      }
  
      /**
       * @param {string} hexBuf
       * @return {KeyPair}
       */
      static fromHex(hexBuf) {
          return KeyPair.unserialize(BufferUtils.fromHex(hexBuf));
      }
  
      /**
       * @param {SerialBuffer} buf
       * @return {KeyPair}
       */
      static unserialize(buf) {
          const privateKey = PrivateKey.unserialize(buf);
          const publicKey = PublicKey.unserialize(buf);
          let locked = false;
          let lockSalt = null;
          if (buf.readPos < buf.byteLength) {
              const extra = buf.readUint8();
              if (extra === 1) {
                  locked = true;
                  lockSalt = buf.read(32);
              }
          }
          return new KeyPair(privateKey, publicKey, locked, lockSalt);
      }
  
      /**
       * @param {SerialBuffer} [buf]
       * @return {SerialBuffer}
       */
      serialize(buf) {
          buf = buf || new SerialBuffer(this.serializedSize);
          this._privateKey.serialize(buf);
          this.publicKey.serialize(buf);
          if (this._locked) {
              buf.writeUint8(1);
              buf.write(this._lockSalt);
          } else {
              buf.writeUint8(0);
          }
          return buf;
      }
  
      /**
       * The unlocked private key.
       * @type {PrivateKey}
       */
      get privateKey() {
          if (this.isLocked) throw new Error('KeyPair is locked');
          return this._privateKey;
      }
  
      /**
       * The private key in its current state, i.e., depending on this._locked.
       * If this._locked, it is the internally locked private key.
       * If !this._locked, it is either the internally unlocked private key (if !this._lockedInternally)
       * or this._unlockedPrivateKey.
       * @type {PrivateKey}
       */
      get _privateKey() {
          return this._unlockedPrivateKey || this._internalPrivateKey;
      }
  
      /** @type {PublicKey} */
      get publicKey() {
          return this._publicKey || (this._publicKey = new PublicKey(this._obj.publicKey));
      }
  
      /** @type {number} */
      get serializedSize() {
          return this._privateKey.serializedSize + this.publicKey.serializedSize + (this._locked ? this._lockSalt.byteLength + 1 : 1);
      }
  
      /**
       * @param {Uint8Array} key
       * @param {Uint8Array} [lockSalt]
       */
      async lock(key, lockSalt) {
          if (this._locked) throw new Error('KeyPair already locked');
  
          if (lockSalt) this._lockSalt = lockSalt;
          if (!this._lockSalt || this._lockSalt.length === 0) {
              this._lockSalt = new Uint8Array(32);
              CryptoWorker.lib.getRandomValues(this._lockSalt);
          }
  
          this._internalPrivateKey.overwrite(await this._otpPrivateKey(key));
          this._clearUnlockedPrivateKey();
          this._locked = true;
          this._lockedInternally = true;
      }
  
      /**
       * @param {Uint8Array} key
       */
      async unlock(key) {
          if (!this._locked) throw new Error('KeyPair not locked');
  
          const privateKey = await this._otpPrivateKey(key);
          const verifyPub = PublicKey.derive(privateKey);
          if (verifyPub.equals(this.publicKey)) {
              // Only set this._internalPrivateKey, but keep this._obj locked.
              this._unlockedPrivateKey = privateKey;
              this._locked = false;
          } else {
              throw new Error('Invalid key');
          }
      }
  
      /**
       * Destroy cached unlocked private key if the internal key is in locked state.
       */
      relock() {
          if (this._locked) throw new Error('KeyPair already locked');
          if (!this._lockedInternally) throw new Error('KeyPair was never locked');
          this._clearUnlockedPrivateKey();
          this._locked = true;
      }
  
      _clearUnlockedPrivateKey() {
          // If this wallet is not locked internally and unlocked, this method does not have any effect.
          if (!this._lockedInternally || this._locked) return;
  
          // Overwrite cached key in this._unlockedPrivateKey with 0s.
          this._unlockedPrivateKey.overwrite(PrivateKey.unserialize(new SerialBuffer(this._unlockedPrivateKey.serializedSize)));
          // Then, reset it.
          this._unlockedPrivateKey = null;
      }
  
      /**
       * @param {Uint8Array} key
       * @return {Promise<PrivateKey>}
       * @private
       */
      async _otpPrivateKey(key) {
          return new PrivateKey(await CryptoUtils.otpKdfLegacy(this._privateKey.serialize(), key, this._lockSalt, KeyPair.LOCK_KDF_ROUNDS));
      }
  
      get isLocked() {
          return this._locked;
      }
  
      /**
       * @param {SerialBuffer} buf
       * @param {Uint8Array} key
       * @return {Promise.<KeyPair>}
       */
      static async fromEncrypted(buf, key) {
          const privateKey = await Secret.fromEncrypted(buf, key);
          if (privateKey.type !== Secret.Type.PRIVATE_KEY) throw new Error('Expected privateKey, got Entropy');
          return KeyPair.derive(privateKey);
      }
  
      /**
       * @param {Uint8Array} key
       * @return {Promise.<SerialBuffer>}
       */
      exportEncrypted(key) {
          return this._privateKey.exportEncrypted(key);
      }
  
      /** @type {number} */
      get encryptedSize() {
          return this._privateKey.encryptedSize;
      }
  
      /**
       * @param {Serializable} o
       * @return {boolean}
       */
      equals(o) {
          return o instanceof KeyPair && super.equals(o);
      }
  }
  KeyPair.LOCK_KDF_ROUNDS = 256;
  
  
  Class.register(KeyPair);
  